diff --git a/README.md b/README.md index b660c0a..fd18b6f 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,23 @@ end data-import provides a clean dsl to define your mappings from the legacy schema to the new one. +### Providing options ### + +You may want to make your mappings configurable. Any options you pass into DataImport.run_config! will be passed through to the evaluation context of the provided mappings. + +```ruby +import 'Things' do + if options[:validate_rows] + validate do ... end + end + + seeds options[:seeds] +end + +DataImport.run_config! 'path/to/mapping.rb', { :validate_rows => true, :seeds => { :key => value } } +``` + + ### Before Filter ### data-import allows you to definie a global filter. This filter can be used to make global transformations like encoding fixes. You can define a filter, which downcases every string like so: diff --git a/lib/data-import.rb b/lib/data-import.rb index 77ba735..9dee81c 100644 --- a/lib/data-import.rb +++ b/lib/data-import.rb @@ -21,7 +21,7 @@ module DataImport class << self def run_config!(config_paths, options = {}) - plan = DataImport::Dsl.evaluate_import_config(config_paths) + plan = DataImport::Dsl.evaluate_import_config(config_paths, options) run_plan!(plan, options) end diff --git a/lib/data-import/dsl.rb b/lib/data-import/dsl.rb index 4f82ab7..d767a82 100644 --- a/lib/data-import/dsl.rb +++ b/lib/data-import/dsl.rb @@ -5,18 +5,18 @@ module DataImport class Dsl class << self - def evaluate_import_config(files) + def evaluate_import_config(files, options = {}) plan = DataImport::ExecutionPlan.new Array(files).each do |file| - context = new(plan) + context = new(plan, options) context.instance_eval read_import_config(file), file end plan end - def define(&block) - plan = DataImport::ExecutionPlan.new - context = new(plan) + def define(options = {}, &block) + plan = DataImport::ExecutionPlan.new([], options) + context = new(plan, options) context.instance_eval &block plan end @@ -28,8 +28,11 @@ def read_import_config(file) end - def initialize(plan) + attr_reader :options + + def initialize(plan, options = {}) @plan = plan + @options = options end def source_database @@ -52,14 +55,14 @@ def import(name, &block) definition = DataImport::Definition::Simple.new(name, source_database, target_database) @plan.add_definition(definition) - Import.new(definition).instance_eval &block + Import.new(definition, options).instance_eval &block end def script(name, &block) definition = DataImport::Definition::Script.new(name, source_database, target_database) @plan.add_definition(definition) - Script.new(definition).instance_eval &block + Script.new(definition, options).instance_eval &block end def before_filter(&block) diff --git a/lib/data-import/dsl/import.rb b/lib/data-import/dsl/import.rb index d0140e6..08ceef8 100644 --- a/lib/data-import/dsl/import.rb +++ b/lib/data-import/dsl/import.rb @@ -5,9 +5,10 @@ class Dsl class Import include Dependencies - attr_reader :definition + attr_reader :definition, :options - def initialize(definition) + def initialize(definition, options = {}) + @options = options @definition = definition end diff --git a/lib/data-import/dsl/script.rb b/lib/data-import/dsl/script.rb index 36b3476..dd35e73 100644 --- a/lib/data-import/dsl/script.rb +++ b/lib/data-import/dsl/script.rb @@ -5,9 +5,10 @@ class Dsl class Script include Dependencies - attr_reader :definition + attr_reader :definition, :options - def initialize(definition) + def initialize(definition, options = {}) + @options = options @definition = definition end diff --git a/lib/data-import/execution_context.rb b/lib/data-import/execution_context.rb index 4a4e315..ad05769 100644 --- a/lib/data-import/execution_context.rb +++ b/lib/data-import/execution_context.rb @@ -1,9 +1,10 @@ class ExecutionContext - attr_reader :progress_reporter + attr_reader :progress_reporter, :options - def initialize(execution_plan, definition, progress_reporter) + def initialize(execution_plan, definition, progress_reporter, options = nil) @execution_plan = execution_plan + @options = options || execution_plan.options @definition = definition @progress_reporter = progress_reporter end @@ -33,7 +34,7 @@ def initialize(context) @context = context end - [:logger, :definition, :name, :source_database, :target_database].each do |method_symbol| + [:logger, :definition, :name, :source_database, :target_database, :options].each do |method_symbol| define_method method_symbol do |*args| @context.send(method_symbol, *args) end diff --git a/lib/data-import/execution_plan.rb b/lib/data-import/execution_plan.rb index 6cca2bb..7d50202 100644 --- a/lib/data-import/execution_plan.rb +++ b/lib/data-import/execution_plan.rb @@ -1,6 +1,9 @@ module DataImport class ExecutionPlan - def initialize(definitions = []) + attr_reader :options + + def initialize(definitions = [], options = {}) + @options = options @definitions = Hash[definitions.map do |definition| [definition.name, definition] end] diff --git a/lib/data-import/runner.rb b/lib/data-import/runner.rb index f81f7b7..4eb820c 100644 --- a/lib/data-import/runner.rb +++ b/lib/data-import/runner.rb @@ -13,7 +13,7 @@ def run(options = {}) bar = @progress_reporter.new(definition.name, definition.total_steps_required) DataImport.logger.info "Starting to import \"#{definition.name}\"" - context = ExecutionContext.new(resolved_plan, definition, bar) + context = ExecutionContext.new(resolved_plan, definition, bar, @plan.options) definition.run context bar.finish diff --git a/spec/acceptance/options_spec.rb b/spec/acceptance/options_spec.rb new file mode 100644 index 0000000..2c092c5 --- /dev/null +++ b/spec/acceptance/options_spec.rb @@ -0,0 +1,69 @@ +require 'acceptance/spec_helper' + +describe 'logger' do + + SEEDS = { :planet => 'Earth' } + + OPTIONS = { :name_mapping => :name, :map_gender? => false, :validate => false, :seeds => SEEDS } + + in_memory_mapping(OPTIONS) do + import 'with options' do + from 'Person' + to 'females' + + mapping 'Name' => options[:name_mapping] + + mapping 'options in a block mapping' do + if options[:map_gender?] + { :gender => :gender } + end + end + + if options[:validate] + validate_row do + if mapped_row[:gender] == 'f' + true + else + false + end + end + end + + seed options[:seeds] + end + + script 'Options test' do + body do + target_database.db[:females].insert(options[:seeds].merge(:name => 'Andy', :gender => 'm' )) + end + end + end + + database_setup do + source.create_table :Person do + String :Name + String :Gender + end + + target.create_table :females do + String :name + String :gender + String :planet + end + + source[:Person].insert('Name' => 'Tina', 'Gender' => 'f') + source[:Person].insert('Name' => 'Jack', 'Gender' => 'm') + end + + it 'provides options to the DSL' do + plan.options.should eq OPTIONS + + DataImport.run_plan!(plan) + + target_database[:females].count.should == 3 + target_database[:females].first[:gender].should be nil + target_database[:females].first[:planet].should eq SEEDS[:planet] + target_database[:females].first(:name => 'Andy')[:planet].should eq SEEDS[:planet] + end + +end diff --git a/spec/acceptance/support/macros.rb b/spec/acceptance/support/macros.rb index a8668bf..385839b 100644 --- a/spec/acceptance/support/macros.rb +++ b/spec/acceptance/support/macros.rb @@ -2,8 +2,8 @@ module TestingMacros - def in_memory_mapping(&block) - plan = DataImport::Dsl.define do + def in_memory_mapping(options = {}, &block) + plan = DataImport::Dsl.define(options) do source 'sqlite:/' target 'sqlite:/' diff --git a/spec/unit/data-import/dsl_spec.rb b/spec/unit/data-import/dsl_spec.rb index 06a4d0a..ce0d49c 100644 --- a/spec/unit/data-import/dsl_spec.rb +++ b/spec/unit/data-import/dsl_spec.rb @@ -85,16 +85,17 @@ subject.import('a') {} end - it "executes the block in an import context" do + it "executes the block in an import context with options" do subject.stub(:source_database).and_return { nil } subject.stub(:target_database).and_return { nil } + subject.stub(:options).and_return({}) my_block = lambda {} import_dsl = stub definition = stub DataImport::Definition::Simple.should_receive(:new).with(any_args).and_return(definition) plan.should_receive(:add_definition).with(definition) - DataImport::Dsl::Import.should_receive(:new).with(definition).and_return(import_dsl) + DataImport::Dsl::Import.should_receive(:new).with(definition, {}).and_return(import_dsl) import_dsl.should_receive(:instance_eval).with(&my_block) subject.import 'name', &my_block @@ -123,15 +124,16 @@ subject.script('a') {} end - it "executes the block in an script conext" do + it "executes the block in a script context with options" do subject.stub(:source_database).and_return { nil } subject.stub(:target_database).and_return { nil } + subject.stub(:options).and_return({}) my_block = lambda {} script_dsl = stub DataImport::Definition::Script.should_receive(:new).with(any_args).and_return(definition) plan.should_receive(:add_definition).with(definition) - DataImport::Dsl::Script.should_receive(:new).with(definition).and_return(script_dsl) + DataImport::Dsl::Script.should_receive(:new).with(definition, {}).and_return(script_dsl) script_dsl.should_receive(:instance_eval).with(&my_block) subject.script 'name', &my_block diff --git a/spec/unit/data-import_spec.rb b/spec/unit/data-import_spec.rb index 7ad1208..f3cfaab 100644 --- a/spec/unit/data-import_spec.rb +++ b/spec/unit/data-import_spec.rb @@ -10,7 +10,7 @@ let(:definitions) { [stub(:name => 'Artists'), stub(:name => 'Paints')] } it "can execute a configuration file" do - DataImport::Dsl.should_receive(:evaluate_import_config).with('my_file').and_return(plan) + DataImport::Dsl.should_receive(:evaluate_import_config).with('my_file', :only => ['C']).and_return(plan) DataImport::Runner.should_receive(:new).with(plan).and_return(runner) runner.should_receive(:run).with(:only => ['C'])