lib/physique/task_builders/fluent_migrator.rb in physique-0.3.0 vs lib/physique/task_builders/fluent_migrator.rb in physique-0.3.1

- old
+ new

@@ -1,5 +1,7 @@ +require 'active_support/core_ext/string' +require 'active_support/core_ext/array' require 'physique/project' module Physique class FluentMigratorConfig self.extend Albacore::ConfigDSL @@ -10,147 +12,170 @@ attr_writer :lang, # Programming language of the db project :instance, # Server instance name :name, # Database name :scripts_dir, # Scripts folder to examine to create tasks - :dialect # Dialect to use for generating SQL + :dialect, # Dialect to use for generating SQL + :task_alias # Alias used to construct rake task names def initialize @lang = :cs @scripts_dir = '_Scripts' end def opts + validate_config + Map.new({ - exe: @exe, instance: @instance, name: @name, project: @project, project_file: Physique::Project.get_path(@project, @lang), lang: @lang, + task_alias: (@task_alias || @name) }).apply( lang: :cs, project_dir: "src/#{@project}", scripts_dir: "src/#{@project}/#{@scripts_dir}" ) end + + private + + def validate_config + raise ArgumentError, 'You must specify a database instance' if @instance.blank? + raise ArgumentError, 'You must specify a database name' if @name.blank? + raise ArgumentError, 'You must specify the FluentMigrator project' if @project.blank? + raise ArgumentError, 'You must specify a language' if @lang.blank? + raise ArgumentError, 'You must specify a scripts_dir' if @scripts_dir.blank? + end end class FluentMigratorTasksBuilder < TasksBuilder def build_tasks - @options = solution.migrator - return if @options.nil? + dbs = solution.fluent_migrator_dbs + return if dbs.empty? - add_script_tasks - add_default_db_tasks - add_migrator_tasks - add_workflow_tasks - add_new_migration_task - end + dbs.each do |db| + task_namespace = db_task_name(db) - private - - def add_script_tasks - FileList["#{@options.scripts_dir}/*.sql"].each do |f| namespace :db do - task_name = File.basename(f, '.*') - task = sqlcmd task_name do |s| - s.file = f - s.server_name = @options.instance - s.set_variable 'DATABASE_NAME', @options.name + namespace task_namespace do + # First look at the scripts_dir and add a task for every sql file that you find + defaults = default_tasks(db) + add_script_tasks db, defaults + + # Then add the default minimum required tasks in case the scripts_dir didn't contain them + add_default_db_tasks db, defaults + + # Add the migrate and rollback tasks + add_migrator_tasks db + + # Add the tasks to create the db from scratch + add_create_tasks + + # Add a task to create a new migration in the db project + add_new_migration_task db end - task.add_description get_script_task_description(task_name, @options.scripts_dir) end + + # Rebuild the databases when running tests + task :test => "db:#{task_namespace}:rebuild" end + + alias_default_tasks end - def add_default_db_tasks - default_tasks(@options.name).each do |task_name,sql| - unless Rake::Task.task_defined? "db:#{task_name.to_s}" - namespace :db do - task = sqlcmd task_name do |s| - s.command = sql - s.server_name = @options.instance - s.set_variable 'DATABASE_NAME', @options.name - end - task.add_description get_script_task_description(task_name, @options.scripts_dir) - end + private + + def add_script_tasks(db, defaults) + FileList["#{db.scripts_dir}/*.sql"].each do |f| + task_name = File.basename(f, '.*') + + desc get_script_task_description(defaults, task_name, db) + sqlcmd task_name do |s| + s.file = f + s.server_name = db.instance + s.set_variable 'DATABASE_NAME', db.name end end end - def default_tasks(database) - { create: "CREATE DATABASE #{database}", - drop: "DROP DATABASE #{database}", - seed: 'SELECT 1' } # This is a no-op + def get_script_task_description(defaults, task_name, db) + default_task = defaults[task_name.to_sym] + default_task ? default_task[:description] : "Executes #{task_name}.sql on #{db.name} in the #{db.scripts_dir} folder." end - def get_script_task_description(task, dir) - well_known_scripts[task.to_sym] || "Executes #{task}.sql in the #{dir} folder." + def add_default_db_tasks(db, defaults) + defaults.each do |task_name,task_details| + unless Rake::Task.task_defined? "db:#{db_task_name(db)}:#{task_name.to_s}" + desc task_details[:description] + sqlcmd task_name do |s| + s.command = task_details[:command] + s.server_name = db.instance + s.set_variable 'DATABASE_NAME', db.name + end + end + end end - # TODO: Refactor this to combine this with default_tasks - def well_known_scripts - { create: 'Creates the database', - drop: 'Drops the database', - seed: 'Seeds the database with test data' } + def default_tasks(database) + { create: { description: 'Create the database', command: "CREATE DATABASE #{database}" }, + drop: { description: 'Drop the database', command: "DROP DATABASE #{database}"}, + seed: { description: 'Seed the database with test data', command: 'SELECT 1' } } # This is a no-op end - def add_migrator_tasks + def add_migrator_tasks(db) require 'physique/tasks/fluent_migrator' - namespace :db do - build :compile_db do |b| - b.target = [ 'Build' ] - b.file = solution.migrator.project_file - b.prop 'Configuration', solution.compile.configuration - b.logging = solution.compile.logging - end + build :compile_db do |b| + b.target = [ 'Build' ] + b.file = db.project_file + b.prop 'Configuration', solution.compile.configuration + b.logging = solution.compile.logging + end - block = lambda &method(:configure_migration) + block = lambda &method(:configure_migration) - # Migrate up - task = fluent_migrator :migrate => [ :compile_db ], &block.curry.('migrate:up') - task.add_description 'Migrate database to the latest version' + # Migrate up + desc 'Migrate database to the latest version' + fluent_migrator :migrate => [ :compile_db ], &block.curry.(db, 'migrate:up') - # Migrate down - task = fluent_migrator :rollback => [ :compile_db ], &block.curry.('rollback') - task.add_description 'Rollback the database to the previous version' - end + # Migrate down + desc 'Rollback the database to the previous version' + fluent_migrator :rollback => [ :compile_db ], &block.curry.(db, 'rollback') + + # Try the migration + desc 'Migrate and then immediately rollback' + task :try => [ :migrate, :rollback ] end - def configure_migration(task, config) - config.instance = solution.migrator.instance - config.database = solution.migrator.name + def configure_migration(db, task, config) + config.instance = db.instance + config.database = db.name config.task = task - config.dll = migration_dll - config.exe = locate_tool(tool_in_output_folder || tool_in_nuget_package) + config.dll = migration_dll db + config.exe = locate_tool(tool_in_output_folder(db) || tool_in_nuget_package) config.output_to_file end - def add_workflow_tasks - namespace :db do - # Try the migration - task = Rake::Task.define_task :try => [ :migrate, :rollback ] - task.add_description 'Migrate and then immediately rollback' + def add_create_tasks + # Setup the database from nothing + desc 'Create the database and run all migrations' + task :setup => [ :create, :migrate, :seed ] - # Setup the database from nothing - task = Rake::Task.define_task :setup => [ :create, :migrate, :seed ] - task.add_description 'Create the database and run all migrations' - - # Setup the database from nothing - task = Rake::Task.define_task :rebuild => [ :drop, :setup ] - task.add_description 'Drop and recreate the database' - end + # Drop and recreate the database + desc 'Drop and recreate the database' + task :rebuild => [ :drop, :setup ] end - def migration_dll - "#{solution.migrator.project_dir}/bin/#{solution.compile.configuration}/#{solution.migrator.project}.dll" + def migration_dll(db) + "#{db.project_dir}/bin/#{solution.compile.configuration}/#{db.project}.dll" end - def tool_in_output_folder - existing_path "#{solution.migrator.project_dir}/bin/#{solution.compile.configuration}/Migrate.exe" + def tool_in_output_folder(db) + existing_path "#{db.project_dir}/bin/#{solution.compile.configuration}/Migrate.exe" end def tool_in_nuget_package existing_path "#{solution.nuget.restore_location}/FluentMigrator.*/tools/Migrate.exe" end @@ -158,37 +183,37 @@ def existing_path(path) return path if FileList[path].any? { |p| File.exists? p } nil end - def add_new_migration_task - namespace :db do - task = Rake::Task.define_task :new_migration, :name, :description do |t, args| - name, description = args[:name], args[:description] + def db_task_name(db) + db.task_alias.downcase + end - unless name - abort [ - %Q{Usage: rake "#{t.name}[name[,description]]"}, - desc, - ].join "\n\n" - end + def add_new_migration_task(db) + desc 'Create a new migration file with the specified name' + task :new_migration, :name, :description do |t, args| + name, description = args[:name], args[:description] - project, project_dir, project_file = solution.migrator.project, solution.migrator.project_dir, solution.migrator.project_file - version = migration_version - migration_file_name = "#{version}_#{name}.cs" - migration_content = migration_template(version, name, description, project) + unless name + abort [ + %Q{Usage: rake "#{t.name}[name[,description]]"}, + desc, + ].join "\n\n" + end - # Save the new migration file - save_file migration_content, "#{project_dir}/Migrations/#{migration_file_name}" + # Save the new migration file + version = migration_version + migration_file_name = "#{version}_#{name}.cs" + migration_content = migration_template(version, name, description, db.project) + save_file migration_content, "#{db.project_dir}/Migrations/#{migration_file_name}" - # Add the new migration file to the project - Albacore::Project.new(project_file).tap do |p| - p.add_compile_node :Migrations, migration_file_name - p.save - end + # Add the new migration file to the project + Albacore::Project.new(db.project_file).tap do |p| + p.add_compile_node :Migrations, migration_file_name + p.save end - task.add_description 'Creates a new migration file with the specified name' end end def migration_version Time.now.utc.strftime('%Y%m%d%H%M%S') @@ -196,11 +221,10 @@ def migration_template(version, name, description, project_name) description = ", \"#{description}\"" unless description.nil? return <<TEMPLATE using FluentMigrator; -using FluentMigrator.Runner; namespace #{project_name}.Migrations { [Migration(#{version}#{description})] public class #{name} : Migration @@ -212,16 +236,38 @@ public override void Down() { // Add migration rollback code here } - } + }rake install } TEMPLATE end def save_file(content, file_path) raise "#{file_path} already exists, cancelling" if File.exists? file_path File.open(file_path, 'w') { |f| f.write(content) } end + + def alias_default_tasks + Rake.application.tasks + .select {|t| t.name.starts_with?('db') && GLOBAL_TASKS.has_key?(db_command(t))} + .group_by {|t| db_command(t) } + .each do |command,tasks| + desc GLOBAL_TASKS[command] + task "db:#{command}" => tasks.map {|t| t.name } + end + end + + def db_command(task) + task.name.split(':').last.to_sym + end + + GLOBAL_TASKS = { + create: 'Create all databases', + drop: 'Drop all databases', + seed: 'Seed all databases with test data', + setup: 'Build all databases and migrate them to the latest version', + rebuild: 'Drop and recreate all databases', + migrate: 'Migrates all databases to the latest version' } end end \ No newline at end of file