data-import 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe DataImport::Database do
4
+
5
+ subject { DataImport::Database }
6
+
7
+ class TestAdapter
8
+ end
9
+
10
+ describe ".connect" do
11
+ it "returns a connection object from the correct adapter" do
12
+ subject.stub(:find_adapter).and_return { TestAdapter }
13
+ TestAdapter.should_receive(:connect)
14
+ subject.connect(:sequel)
15
+ end
16
+ end
17
+
18
+ describe ".find_adapter" do
19
+ it "returns nil if the adapter is not supported" do
20
+ subject.send(:find_adapter, :abc).should be_nil
21
+ end
22
+ end
23
+
24
+ end
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe DataImport::Definition::Simple do
4
+
5
+ subject { DataImport::Definition::Simple.new('a', :source, :target) }
6
+
7
+ describe "#mappings" do
8
+ it "returns an empty hash by default" do
9
+ subject.mappings.should be_empty
10
+ end
11
+ end
12
+
13
+ describe "#add_id_mapping" do
14
+ it "adds a primary key mapping to the definition" do
15
+ subject.add_id_mapping 18 => 24
16
+ subject.id_mappings[18].should == 24
17
+ end
18
+ end
19
+
20
+ describe "#new_id_of" do
21
+ it "looks for the new id in the id mappings" do
22
+ subject.add_id_mapping 39 => 834
23
+ subject.new_id_of(39).should == 834
24
+ end
25
+ end
26
+
27
+ describe "#definition" do
28
+ it "returns the definition of the importer if nothing is passed" do
29
+ subject.definition.should == subject
30
+ end
31
+
32
+ it "looks for the definition in the registered definition list if a name is passed" do
33
+ DataImport.stub(:definitions).and_return { {'abc' => :def} }
34
+ subject.definition('abc').should == :def
35
+ end
36
+
37
+ it "raises an error if no definition was found" do
38
+ DataImport.stub(:definitions).and_return { {} }
39
+ lambda { subject.definition('abc') }.should raise_error
40
+ end
41
+ end
42
+
43
+ describe '#run' do
44
+
45
+ let(:source_db) { mock }
46
+ let(:target_db) { mock }
47
+ let(:source_columns) { [:name, :location, :location2] }
48
+ let(:source_distinct_columns) { [:name, :location] }
49
+
50
+ subject do
51
+ definition = DataImport::Definition::Simple.new('Houses', source_db, target_db)
52
+ definition.source_table_name = 'tblHouses'
53
+ definition.source_columns = source_columns
54
+ definition.source_distinct_columns = source_distinct_columns
55
+ definition
56
+ end
57
+
58
+ it 'executes the definition and displays the progress' do
59
+ source_db.should_receive(:count).with('tblHouses',
60
+ :columns => source_columns,
61
+ :distinct => source_distinct_columns).and_return(125)
62
+ Progress.should_receive(:start).with('Importing Houses', 125).and_yield
63
+ importer = mock
64
+ DataImport::Importer.should_receive(:new).with('CONTEXT', subject).and_return(importer)
65
+ importer.should_receive(:run)
66
+
67
+ subject.run('CONTEXT')
68
+ end
69
+ end
70
+
71
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe DataImport::Definition do
4
+
5
+ subject { DataImport::Definition.new('a', :source, :target) }
6
+
7
+ describe "#dependencies" do
8
+ it "can have dependent definitions which must run before" do
9
+ subject.add_dependency 'b'
10
+ subject.add_dependency 'c'
11
+ subject.dependencies.should == ['b', 'c']
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe DataImport::Dsl::Import::From do
4
+
5
+ let(:definition) { DataImport::Definition::Simple.new('d', :source, :target) }
6
+ subject { DataImport::Dsl::Import::From.new(definition) }
7
+
8
+ describe "#table" do
9
+ it "saves the source table name to the definition" do
10
+ subject.table 'source_table'
11
+ definition.source_table_name.should == 'source_table'
12
+ end
13
+ end
14
+
15
+ describe "#primary_key" do
16
+ it "saves the primary key to the definition" do
17
+ subject.primary_key 'my_key'
18
+ definition.source_primary_key.should == :my_key
19
+ end
20
+ end
21
+
22
+ describe "#columns" do
23
+ it "saves the columns to the definition" do
24
+ subject.columns 'col1', 'col2'
25
+ definition.source_columns.should include('col1')
26
+ definition.source_columns.should include('col2')
27
+ end
28
+
29
+ it "sets the distinct flag" do
30
+ subject.columns 'col1', :distinct => true
31
+ definition.source_distinct_columns.should be_true
32
+ end
33
+ end
34
+
35
+ describe "#order" do
36
+ it "saves the order columns to the definition" do
37
+ subject.order 'col1', 'col2'
38
+ definition.source_order_columns.should include('col1')
39
+ definition.source_order_columns.should include('col2')
40
+ end
41
+ end
42
+
43
+ end
@@ -0,0 +1,87 @@
1
+ require 'spec_helper'
2
+
3
+ describe DataImport::Dsl::Import do
4
+
5
+ let(:definition) { DataImport::Definition::Simple.new('d', :source, :target) }
6
+ subject { DataImport::Dsl::Import.new(definition) }
7
+
8
+ describe "#from" do
9
+ it "saves the source table name to the definition" do
10
+ subject.from 'source_table'
11
+ definition.source_table_name.should == 'source_table'
12
+ end
13
+
14
+ it "saves the primary key" do
15
+ subject.from 'source_table', :primary_key => 'my_key'
16
+ definition.source_primary_key.should == :my_key
17
+ end
18
+
19
+ let(:block) { lambda{} }
20
+ it "executes the passed block" do
21
+ DataImport::Dsl::Import::From.any_instance.should_receive(:instance_eval).with(&block)
22
+ subject.from &block
23
+ end
24
+ end
25
+
26
+ describe "#to" do
27
+ it "saves the target table name to the definition" do
28
+ subject.to 'target_table'
29
+ definition.target_table_name.should == 'target_table'
30
+ end
31
+
32
+ it 'accepts a :mode option' do
33
+ subject.to 'target_table', :mode => :update
34
+ definition.mode.should == :update
35
+ end
36
+ end
37
+
38
+ describe "#dependencies" do
39
+ it "sets the list of definitions it depends on" do
40
+ subject.dependencies 'a', 'b'
41
+ definition.dependencies.should == ['a', 'b']
42
+ end
43
+
44
+ it "can be called multiple times" do
45
+ subject.dependencies 'a', 'b'
46
+ subject.dependencies 'x'
47
+ subject.dependencies 'y'
48
+ definition.dependencies.should == ['a', 'b', 'x', 'y']
49
+ end
50
+ end
51
+
52
+ describe "#mapping" do
53
+ it "adds a column mapping to the definition" do
54
+ subject.mapping :a => :b
55
+ definition.mappings.should include(:a)
56
+ definition.mappings[:a].should == :b
57
+ end
58
+
59
+ let(:block) { lambda{|value|} }
60
+ it "adds a proc to the mappings" do
61
+ subject.mapping :a, &block
62
+ definition.mappings.should include(:a)
63
+ definition.mappings[:a].should == block
64
+ end
65
+
66
+ it "adds a proc with multiple fields to the mappings" do
67
+ subject.mapping :a, :b, &block
68
+ definition.mappings.should include([:a, :b])
69
+ definition.mappings[[:a, :b]].should == block
70
+ end
71
+ end
72
+
73
+ describe "#after" do
74
+ let(:block) { lambda{} }
75
+ it "adds a proc to be executed after the import" do
76
+ subject.after &block
77
+ definition.after_blocks.should include(block)
78
+ end
79
+ end
80
+
81
+ it "#after_row adds a block, which is executed after every row" do
82
+ my_block = lambda {}
83
+ subject.after_row &my_block
84
+ definition.after_row_blocks == [my_block]
85
+ end
86
+
87
+ end
@@ -0,0 +1,99 @@
1
+ require 'spec_helper'
2
+
3
+ describe DataImport::Dsl do
4
+
5
+ let(:plan) { stub }
6
+
7
+ context "class methods" do
8
+ subject { DataImport::Dsl }
9
+
10
+ describe ".evaluate_import_config" do
11
+ it "executes the content of the config in a new DSL context" do
12
+ File.stub(:read).and_return do
13
+ <<-RUBY
14
+ source :sequel, 'sqlite:/'
15
+ target :sequel, 'sqlite:/'
16
+ RUBY
17
+ end
18
+ DataImport::ExecutionPlan.should_receive(:new).and_return(plan)
19
+ result = subject.evaluate_import_config('my_file')
20
+ result.should == plan
21
+ end
22
+ end
23
+ end
24
+
25
+ context "instance methods" do
26
+ subject { DataImport::Dsl.new(plan) }
27
+
28
+ describe "#source" do
29
+ it "creates a connection to the database" do
30
+ DataImport::Database.should_receive(:connect).with(:options)
31
+ subject.source :options
32
+ end
33
+
34
+ let(:source) { Object.new }
35
+ it "sets the source" do
36
+ DataImport::Database.stub(:connect).and_return { source }
37
+ subject.source :options
38
+ subject.source_database.should == source
39
+ end
40
+ end
41
+
42
+ describe "#target" do
43
+ it "creates a connection to the database" do
44
+ DataImport::Database.should_receive(:connect).with(:options)
45
+ subject.target :options
46
+ end
47
+
48
+ let(:target) { Object.new }
49
+ it "sets the target" do
50
+ DataImport::Database.stub(:connect).and_return { target }
51
+ subject.target :options
52
+ subject.target_database.should == target
53
+ end
54
+ end
55
+
56
+ describe "#import" do
57
+ it "adds a new import config to the import" do
58
+ definition = stub
59
+ DataImport::Definition::Simple.should_receive(:new).with('Import 5', nil, nil).and_return(definition)
60
+ plan.should_receive(:add_definition).with(definition)
61
+ subject.import 'Import 5'
62
+ end
63
+
64
+ it "sets the source and target database in the definition" do
65
+ subject.stub(:source_database).and_return { :source }
66
+ subject.stub(:target_database).and_return { :target }
67
+
68
+ definition = stub
69
+ DataImport::Definition::Simple.should_receive(:new).with('a', :source, :target).and_return(definition)
70
+ plan.should_receive(:add_definition).with(definition)
71
+
72
+ subject.import 'a'
73
+ end
74
+
75
+ it "executes the block in an import conext" do
76
+ my_block = lambda {}
77
+ import_dsl = stub
78
+ definition = stub
79
+ DataImport::Definition::Simple.should_receive(:new).with(any_args).and_return(definition)
80
+ plan.should_receive(:add_definition).with(definition)
81
+ DataImport::Dsl::Import.should_receive(:new).with(definition).and_return(import_dsl)
82
+
83
+ import_dsl.should_receive(:instance_eval).with(&my_block)
84
+ subject.import 'name', &my_block
85
+ end
86
+ end
87
+
88
+ describe "#before_block" do
89
+ it 'adds the before block to the execution plan' do
90
+ my_filter = lambda {}
91
+ plan = DataImport::Dsl.define do
92
+ before_filter &my_filter
93
+ end
94
+ plan.before_filter.should == my_filter
95
+ end
96
+ end
97
+ end
98
+
99
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe DataImport::ExecutionPlan do
4
+
5
+ let(:people) { DataImport::Definition.new('People', :tblPerson, :people) }
6
+ let(:houses) { DataImport::Definition.new('House', :tblHouse, :houses) }
7
+
8
+ it 'kann mit einem Set von Definitions erstellt werden' do
9
+ plan = DataImport::ExecutionPlan.new([people, houses])
10
+ plan.definitions.should == [people, houses]
11
+ end
12
+
13
+ it 'beschreibt den Ablauf anhand von definitions' do
14
+ subject.add_definition(people)
15
+ subject.add_definition(houses)
16
+ subject.definitions.should == [people, houses]
17
+ end
18
+
19
+ it 'kann einen before_filter enthalten' do
20
+ my_filter = lambda {}
21
+ subject.before_filter = my_filter
22
+ subject.before_filter.should == my_filter
23
+ end
24
+
25
+ end
@@ -0,0 +1,150 @@
1
+ require 'spec_helper'
2
+
3
+ describe DataImport::Importer do
4
+
5
+ let(:source) { stub }
6
+ let(:target) { stub }
7
+ let(:other_definition) { DataImport::Definition::Simple.new 'C', source, target }
8
+ let(:definition) { DataImport::Definition::Simple.new 'A', source, target }
9
+ let(:context) { stub(:before_filter => nil) }
10
+ before { context.stub(:definition).with('C').and_return(other_definition) }
11
+ subject { DataImport::Importer.new(context, definition) }
12
+
13
+ describe "#run" do
14
+ it "runs the import in a transaction" do
15
+ definition.target_database.should_receive(:transaction)
16
+ subject.run
17
+ end
18
+
19
+ it "call #import_row for each row" do
20
+ definition.target_database.stub(:transaction).and_yield
21
+ definition.source_database.stub(:each_row).and_yield(:a => :b).and_yield(:c => :d)
22
+
23
+ subject.should_receive(:import_row).with(:a => :b)
24
+ subject.should_receive(:import_row).with(:c => :d)
25
+ subject.run
26
+ end
27
+
28
+ it 'uses the before_filter to modify the row before importing it' do
29
+ context.stub(:before_filter => lambda do |row|
30
+ row['a'] = row['a'].upcase
31
+ end)
32
+ definition.target_database.stub(:transaction).and_yield
33
+ definition.source_database.stub(:each_row).and_yield('a' => 'b').and_yield('a' => 'c')
34
+
35
+ subject.should_receive(:import_row).with('a' => 'B')
36
+ subject.should_receive(:import_row).with('a' => 'C')
37
+ subject.run
38
+ end
39
+
40
+ context 'after blocks' do
41
+ before do
42
+ definition.target_database.stub(:transaction).and_yield
43
+ definition.source_database.stub(:each_row)
44
+ end
45
+
46
+ it "run after the data import" do
47
+ executed = false
48
+ definition.after_blocks << Proc.new do
49
+ executed = true
50
+ end
51
+
52
+ subject.run
53
+ executed.should == true
54
+ end
55
+
56
+ it "have access to other definitions" do
57
+ found_definition = nil
58
+ definition.after_blocks << Proc.new do |context|
59
+ found_definition = context.definition('C')
60
+ end
61
+
62
+ subject.run
63
+ found_definition.should == other_definition
64
+ end
65
+
66
+ it 'have access to the definition instance' do
67
+ found_name = nil
68
+ definition.after_blocks << Proc.new do
69
+ found_name = name
70
+ end
71
+
72
+ subject.run
73
+ found_name.should == 'A'
74
+ end
75
+ end
76
+ end
77
+
78
+ context 'after row blocks' do
79
+ it "run after the data import" do
80
+ input_rows = []
81
+ output_rows = []
82
+ definition.after_row_blocks << Proc.new do |context, input_row, output_row|
83
+ input_rows << input_row
84
+ output_rows << output_row
85
+ end
86
+
87
+ definition.stub(:mappings).and_return { {:id => :new_id} }
88
+ definition.target_database.should_receive(:insert_row).any_number_of_times
89
+ subject.send(:import_row, :id => 1)
90
+ subject.send(:import_row, :id => 2)
91
+
92
+ input_rows.should == [{:id => 1}, {:id => 2}]
93
+ output_rows.should == [{:new_id => 1}, {:new_id => 2}]
94
+ end
95
+ end
96
+
97
+ describe "#import_row" do
98
+ it "executes the insertion" do
99
+ definition.stub(:mappings).and_return { {:id => :id} }
100
+ definition.stub(:target_table_name).and_return { :table }
101
+ definition.target_database.should_receive(:insert_row).with(:table, :id => 1)
102
+ subject.send(:import_row, :id => 1)
103
+ end
104
+
105
+ it "replaces the keys that occur in the field mapping" do
106
+ definition.stub(:mappings).and_return { {:personenid => :id} }
107
+ definition.stub(:target_table_name).and_return { :table }
108
+ definition.target_database.should_receive(:insert_row).with(:table, :id => 1)
109
+ subject.send(:import_row, :personenid => 1)
110
+ end
111
+
112
+ it "calls the proc if one is specified in the field mapping" do
113
+ definition.stub(:mappings).and_return { {:personenid => lambda{|context, value| {:id => 2}}} }
114
+ definition.stub(:target_table_name).and_return { :table }
115
+ definition.target_database.should_receive(:insert_row).with(:table, :id => 2)
116
+ subject.send(:import_row, :personenid => 1)
117
+ end
118
+
119
+ it "ignores the return value of the proc when it is nil" do
120
+ definition.stub(:mappings).and_return { {:personenid => lambda{|context, value| nil }} }
121
+ definition.stub(:target_table_name).and_return { :table }
122
+ definition.target_database.should_receive(:insert_row).with(:table, {})
123
+ subject.send(:import_row, :personenid => 1)
124
+ end
125
+
126
+ it "calls the proc for multiple columns" do
127
+ definition.stub(:mappings).and_return { {[:a, :b] => lambda{|context, a_value, b_value| {:id => 2}}} }
128
+ definition.stub(:target_table_name).and_return { :table }
129
+ definition.target_database.should_receive(:insert_row).with(:table, :id => 2)
130
+ subject.send(:import_row, :a => 4, :b => 9)
131
+ end
132
+
133
+ it "adds the generated id to the id mapping of the definition" do
134
+ definition.target_database.stub(:insert_row).and_return { 15 }
135
+ definition.stub(:source_primary_key).and_return { :id }
136
+ definition.should_receive(:add_id_mapping).with(1 => 15)
137
+ subject.send(:import_row, :id => 1)
138
+ end
139
+
140
+ it 'calls update_row for definitions with a mode uf :update' do
141
+ definition.stub(:mappings).and_return { {:id => :id} }
142
+ definition.target_table_name = 'target_table'
143
+ definition.use_mode(:update)
144
+ definition.target_database.should_receive(:update_row).with('target_table', {:id => 1})
145
+
146
+ subject.send(:import_row, :id => 1)
147
+ end
148
+ end
149
+
150
+ end