topographer 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.
Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +20 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +38 -0
  7. data/Rakefile +1 -0
  8. data/lib/Topographer/exceptions.rb +4 -0
  9. data/lib/Topographer/importer.rb +57 -0
  10. data/lib/Topographer/importer/helpers.rb +18 -0
  11. data/lib/Topographer/importer/helpers/write_log_to_csv.rb +85 -0
  12. data/lib/Topographer/importer/importable.rb +5 -0
  13. data/lib/Topographer/importer/input.rb +5 -0
  14. data/lib/Topographer/importer/input/base.rb +13 -0
  15. data/lib/Topographer/importer/input/roo.rb +28 -0
  16. data/lib/Topographer/importer/input/source_data.rb +8 -0
  17. data/lib/Topographer/importer/logger.rb +8 -0
  18. data/lib/Topographer/importer/logger/base.rb +69 -0
  19. data/lib/Topographer/importer/logger/fatal_error_entry.rb +19 -0
  20. data/lib/Topographer/importer/logger/file.rb +0 -0
  21. data/lib/Topographer/importer/logger/log_entry.rb +34 -0
  22. data/lib/Topographer/importer/logger/simple.rb +27 -0
  23. data/lib/Topographer/importer/mapper.rb +161 -0
  24. data/lib/Topographer/importer/mapper/default_field_mapping.rb +22 -0
  25. data/lib/Topographer/importer/mapper/field_mapping.rb +55 -0
  26. data/lib/Topographer/importer/mapper/ignored_field_mapping.rb +10 -0
  27. data/lib/Topographer/importer/mapper/result.rb +21 -0
  28. data/lib/Topographer/importer/mapper/validation_field_mapping.rb +36 -0
  29. data/lib/Topographer/importer/strategy.rb +7 -0
  30. data/lib/Topographer/importer/strategy/base.rb +42 -0
  31. data/lib/Topographer/importer/strategy/create_or_update_record.rb +50 -0
  32. data/lib/Topographer/importer/strategy/import_new_record.rb +17 -0
  33. data/lib/Topographer/importer/strategy/import_status.rb +28 -0
  34. data/lib/Topographer/importer/strategy/update_record.rb +33 -0
  35. data/lib/Topographer/version.rb +3 -0
  36. data/lib/topographer.rb +6 -0
  37. data/spec/Cartographer/importer/helpers/write_log_to_csv_spec.rb +69 -0
  38. data/spec/Cartographer/importer/helpers_spec.rb +33 -0
  39. data/spec/Cartographer/importer/importable_spec.rb +13 -0
  40. data/spec/Cartographer/importer/importer_spec.rb +132 -0
  41. data/spec/Cartographer/importer/logger/base_spec.rb +12 -0
  42. data/spec/Cartographer/importer/logger/fatal_error_entry_spec.rb +31 -0
  43. data/spec/Cartographer/importer/logger/simple_spec.rb +53 -0
  44. data/spec/Cartographer/importer/mapper/default_field_mapping_spec.rb +41 -0
  45. data/spec/Cartographer/importer/mapper/field_mapping_spec.rb +126 -0
  46. data/spec/Cartographer/importer/mapper/validation_field_mapping_spec.rb +42 -0
  47. data/spec/Cartographer/importer/mapper_spec.rb +318 -0
  48. data/spec/Cartographer/importer/strategy/base_spec.rb +43 -0
  49. data/spec/Cartographer/importer/strategy/create_or_update_record_spec.rb +46 -0
  50. data/spec/Cartographer/importer/strategy/import_new_records_spec.rb +66 -0
  51. data/spec/Cartographer/importer/strategy/import_status_spec.rb +24 -0
  52. data/spec/Cartographer/importer/strategy/mapped_model.rb +41 -0
  53. data/spec/Cartographer/importer/strategy/update_record_spec.rb +45 -0
  54. data/spec/spec_helper.rb +1 -0
  55. data/topographer.gemspec +26 -0
  56. metadata +175 -0
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe Importer::Mapper::DefaultFieldMapping do
4
+ let(:static_mapping) do
5
+ Importer::Mapper::DefaultFieldMapping.new('field1') do
6
+ 10+5
7
+ end
8
+ end
9
+ let(:failed_static_mapping) do
10
+ Importer::Mapper::DefaultFieldMapping.new('field1') do
11
+ raise 'FAILURE'
12
+ end
13
+ end
14
+
15
+ let(:result) { Importer::Mapper::Result.new('test') }
16
+ let(:result2) { Importer::Mapper::Result.new('test') }
17
+ describe '#initialize' do
18
+ it 'should not create a static mapping without a behavior block' do
19
+ expect { Importer::Mapper::DefaultFieldMapping.new('broken mapping') }.
20
+ to raise_error(Topographer::InvalidMappingError)
21
+ end
22
+ end
23
+ describe '#process_input' do
24
+ it 'should return the result of the behavior block' do
25
+ static_mapping.process_input({}, result)
26
+ expect(result.errors?).to be_false
27
+ expect(result.data['field1']).to be 15
28
+ end
29
+ it 'should record any exceptions that occur within the block as errors' do
30
+ failed_static_mapping.process_input({}, result)
31
+ expect(result.errors?).to be_true
32
+ expect(result.errors.values).to include('FAILURE')
33
+ end
34
+ it 'should not rescue Exceptions that do not inherit from standard error' do
35
+ mapper = Importer::Mapper::DefaultFieldMapping.new('output_column') do
36
+ raise Exception, 'Field1 MUST BE 4'
37
+ end
38
+ expect{ mapper.process_input({'field1' => false}, result) }.to raise_error(Exception)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,126 @@
1
+ require 'spec_helper'
2
+
3
+ describe Importer::Mapper::FieldMapping do
4
+ let(:required_simple_mapping) { Importer::Mapper::FieldMapping.new(true, ['field1'], 'output_column') }
5
+ let(:required_simple_mapping_with_validation) do
6
+ Importer::Mapper::FieldMapping.new(true, ['field1'], 'output_column') do |input|
7
+ if input['field1'] != 4
8
+ raise 'Field1 MUST BE 4'
9
+ end
10
+ end
11
+ end
12
+ let(:required_complex_mapping) do
13
+ Importer::Mapper::FieldMapping.new(true, ['field1', 'field2', 'field3'], 'output_column') do |input|
14
+ if input['field1'] != 4
15
+ raise 'Field1 MUST BE 4'
16
+ end
17
+ input.values.flatten.inject(0) {|sum, x| sum+x}
18
+ end
19
+ end
20
+ let(:optional_simple_mapping) { Importer::Mapper::FieldMapping.new(false, 'field1', 'output_column') }
21
+ let(:optional_complex_mapping) do
22
+ Importer::Mapper::FieldMapping.new(false, ['field1', 'field2', 'field3'], 'output_column') do |input|
23
+ if input['field1'] != 4
24
+ raise 'Field1 MUST BE 4'
25
+ end
26
+ input.values.flatten.inject(0) {|sum, x| sum+x}
27
+ end
28
+ end
29
+ let(:valid_input) do
30
+ {'field1' => 4, 'field2' => 5, 'field3' => 6}
31
+ end
32
+ let(:invalid_complex_input) do
33
+ {'field1' => 3, 'field2' => 4, 'field3' => 5}
34
+ end
35
+ let(:result) { Importer::Mapper::Result.new('test') }
36
+ describe '#process_input' do
37
+ context 'required mappings' do
38
+ it 'maps required simple mappings when input is valid' do
39
+ required_simple_mapping.process_input(valid_input, result)
40
+ expect(result.errors?).to be_false
41
+ expect(result.data['output_column']).to eql(4)
42
+ end
43
+ it 'maps required complex mappings when input is valid' do
44
+ required_complex_mapping.process_input(valid_input, result)
45
+ expect(result.errors?).to be_false
46
+ expect(result.data['output_column']).to eql(15)
47
+ end
48
+ it 'returns an error for required simple mappings when the input key is missing' do
49
+ required_simple_mapping.process_input({}, result)
50
+ expect(result.errors?).to be_true
51
+ expect(result.errors.values).to include('Missing required input(s): `field1` for `output_column`')
52
+ expect(result.data.empty?).to be_true
53
+ end
54
+ it 'returns an error for required simple mappings when the input data is blank' do
55
+ required_simple_mapping.process_input({'field1' => nil}, result)
56
+ expect(result.errors?).to be_true
57
+ expect(result.errors.values).to include('Missing required input(s): `field1` for `output_column`')
58
+ expect(result.data['output_column']).to be_nil
59
+ end
60
+ it 'returns an error for required simple mappings when the mapping block raises an exception' do
61
+ required_simple_mapping_with_validation.process_input({'field1' => 3}, result)
62
+ expect(result.errors?).to be_true
63
+ expect(result.errors.values).to include('Field1 MUST BE 4')
64
+ expect(result.data.empty?).to be_true
65
+ end
66
+ it 'returns an error for required complex mappings when the input key is missing' do
67
+ required_complex_mapping.process_input({'field1' => 4}, result)
68
+ expect(result.errors?).to be_true
69
+ expect(result.errors.values).to include('Missing required input(s): `field2, field3` for `output_column`')
70
+ expect(result.data.empty?).to be_true
71
+ end
72
+ it 'returns an error for required complex mappings when the mapping block raises an exception' do
73
+ required_complex_mapping.process_input(invalid_complex_input, result)
74
+ expect(result.errors?).to be_true
75
+ expect(result.errors.values).to include('Field1 MUST BE 4')
76
+ expect(result.data.empty?).to be_true
77
+ end
78
+ end
79
+ context 'optional mappings' do
80
+ it 'maps optional simple mappings when input is valid' do
81
+ optional_simple_mapping.process_input(valid_input, result)
82
+ expect(result.errors?).to be_false
83
+ expect(result.data['output_column']).to eql(4)
84
+ end
85
+ it 'maps optional complex mappings when input is valid' do
86
+ optional_complex_mapping.process_input(valid_input, result)
87
+ expect(result.errors?).to be_false
88
+ expect(result.data['output_column']).to eql(15)
89
+ end
90
+ it 'does not return an error for optional simple mappings when the input key is missing' do
91
+ optional_simple_mapping.process_input({}, result)
92
+ expect(result.errors?).to be_false
93
+ expect(result.data.empty?).to be_true
94
+ end
95
+ it 'does not return an error for optional simple mappings when the input data is blank' do
96
+ optional_simple_mapping.process_input({'field1' => nil}, result)
97
+ expect(result.errors?).to be_false
98
+ expect(result.data['output_column']).to be_nil
99
+ end
100
+ it 'does not return an error for optional complex mappings when an input key is missing' do
101
+ optional_complex_mapping.process_input({'field1' => 4}, result)
102
+ expect(result.errors?).to be_false
103
+ expect(result.data.empty?).to be_true
104
+ end
105
+ it 'returns an error for optional complex mappings when the mapping block raises an exception' do
106
+ optional_complex_mapping.process_input(invalid_complex_input, result)
107
+ expect(result.errors?).to be_true
108
+ expect(result.errors.values).to include('Field1 MUST BE 4')
109
+ expect(result.data.empty?).to be_true
110
+ end
111
+ end
112
+ it 'maps data when the result of the mapping is a false value' do
113
+ #required_simple_mapping.stub(:apply_mapping).and_return(false)
114
+ required_simple_mapping.process_input({'field1' => false}, result)
115
+ expect(result.errors?).to be_false
116
+ expect(result.data['output_column']).to eql(false)
117
+ end
118
+ it 'should not rescue Exceptions that do not inherit from standard error' do
119
+ mapper = Importer::Mapper::FieldMapping.new(true, 'field1', 'output_column') do |input|
120
+ raise Exception, 'Field1 MUST BE 4'
121
+ end
122
+ expect{ mapper.process_input({'field1' => false}, result) }.to raise_error(Exception)
123
+ end
124
+
125
+ end
126
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe Importer::Mapper::ValidationFieldMapping do
4
+ let(:validation_mapping) do
5
+ Importer::Mapper::ValidationFieldMapping.new('test mapping', ['field1', 'field2', 'field3']) do |input|
6
+ sum = input.values.flatten.inject(0) {|sum, x| sum+x}
7
+ raise 'Sum must be 15' if sum != 15
8
+ end
9
+ end
10
+ let(:valid_input) do
11
+ {'field1' => 4, 'field2' => 5, 'field3' => 6}
12
+ end
13
+ let(:invalid_input) do
14
+ {'field1' => 3, 'field2' => 4, 'field3' => 5}
15
+ end
16
+ let(:result) { Importer::Mapper::Result.new('test') }
17
+ describe '#initialize' do
18
+ it 'should not create a validation mapping without a behavior block' do
19
+ expect { Importer::Mapper::ValidationFieldMapping.new('test mapping', ['field1', 'field2', 'field3']) }.
20
+ to raise_error(Topographer::InvalidMappingError)
21
+ end
22
+ end
23
+ describe '#process_input' do
24
+ it 'should not return an error if the validation block passes' do
25
+ validation_mapping.process_input(valid_input, result)
26
+ expect(result.errors?).to be_false
27
+ expect(result.data.blank?).to be_true
28
+ end
29
+ it 'should return an error if the validation block raises an error' do
30
+ validation_mapping.process_input(invalid_input, result)
31
+ expect(result.errors?).to be_true
32
+ expect(result.errors.values).to include('Sum must be 15')
33
+ end
34
+ it 'should not rescue Exceptions that do not inherit from standard error' do
35
+ mapper = Importer::Mapper::ValidationFieldMapping.new('test mapping', ['field1', 'field2', 'field3']) do |input|
36
+ sum = input.values.flatten.inject(0) {|sum, x| sum+x}
37
+ raise Exception, 'Sum must be 15' if sum != 15
38
+ end
39
+ expect{ mapper.process_input(invalid_input, result) }.to raise_error(Exception)
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,318 @@
1
+ require 'spec_helper'
2
+
3
+ describe Importer::Mapper do
4
+ describe '.build_mapper' do
5
+ describe 'required mappings' do
6
+ it 'can require a one to one field mapping' do
7
+ mapper = Importer::Mapper.build_mapper(Object) do |m|
8
+ m.required_mapping 'Field1', 'field_1'
9
+ end
10
+ mapper.required_columns.should include("Field1")
11
+ mapper.output_fields.should include('field_1')
12
+ end
13
+ it 'can require a many to one field mapping' do
14
+ mapper = Importer::Mapper.build_mapper(Object) do |m|
15
+ m.required_mapping ['Field1', 'Field2'], 'field_1'
16
+ end
17
+ mapper.required_columns.should include("Field1", "Field2")
18
+ mapper.output_fields.should include('field_1')
19
+ end
20
+ it 'cannot require a one to many field mapping' do
21
+ expect { mapper = Importer::Mapper.build_mapper(Object) do |m|
22
+ m.required_mapping 'Field1', ['field_1', 'field_2']
23
+ end
24
+ }.to raise_error(Topographer::InvalidMappingError)
25
+ end
26
+ end
27
+ describe 'optional mappings' do
28
+ it 'can create an optional one to one field mapping' do
29
+ mapper = Importer::Mapper.build_mapper(Object) do |m|
30
+ m.optional_mapping 'Field1', 'field_1'
31
+ end
32
+ mapper.optional_columns.should include("Field1")
33
+ mapper.output_fields.should include('field_1')
34
+ end
35
+ it 'can create an optional many to one field mapping' do
36
+ mapper = Importer::Mapper.build_mapper(Object) do |m|
37
+ m.optional_mapping ['Field1', 'Field2'], 'field_1'
38
+ end
39
+ mapper.optional_columns.should include("Field1", "Field2")
40
+ mapper.output_fields.should include('field_1')
41
+ end
42
+ it 'cannot create an optional one to many field mapping' do
43
+ expect { mapper = Importer::Mapper.build_mapper(Object) do |m|
44
+ m.optional_mapping 'Field1', ['field_1', 'field_2']
45
+ end
46
+ }.to raise_error(Topographer::InvalidMappingError)
47
+ end
48
+ end
49
+ describe 'ignored mappings' do
50
+ it 'can ignore a column' do
51
+ mapper = Importer::Mapper.build_mapper(Object) do |m|
52
+ m.ignored_column 'Field1'
53
+ end
54
+ mapper.ignored_columns.should include('Field1')
55
+ mapper.output_fields.should be_empty
56
+ end
57
+
58
+ it 'raises an error when adding a mapping whose output is already an output of another mapping' do
59
+ expect { Importer::Mapper.build_mapper(Object) do |m|
60
+ m.optional_mapping 'Field1', 'field_1'
61
+ m.required_mapping 'Field2', 'field_1'
62
+ end }.to raise_error(Topographer::InvalidMappingError)
63
+ end
64
+
65
+ it 'raises an error when adding a ignored column, which is already an input of another mapping' do
66
+ expect { Importer::Mapper.build_mapper(Object) do |m|
67
+ m.optional_mapping 'Field1', 'field_1'
68
+ m.ignored_column 'Field1'
69
+ end }.to raise_error(Topographer::InvalidMappingError)
70
+ end
71
+
72
+ it 'raises an error when adding a mapped column which has already been ignored' do
73
+ expect { Importer::Mapper.build_mapper(Object) do |m|
74
+ m.ignored_column 'Field1'
75
+ m.optional_mapping 'Field1', 'field_1'
76
+ end }.to raise_error(Topographer::InvalidMappingError)
77
+ end
78
+ end
79
+
80
+ describe 'validation mappings' do
81
+ it 'can create a single column validation' do
82
+ mapper = Importer::Mapper.build_mapper(Object) do |m|
83
+ m.validation_field 'Field 1 Validation', 'Field1' do |input|
84
+ raise 'No Input' unless input
85
+ end
86
+ end
87
+ expect(mapper.validation_columns).to include('Field1')
88
+ expect(mapper.output_fields.empty?).to be_true
89
+ end
90
+ it 'can create a multicolumn validation' do
91
+ mapper = Importer::Mapper.build_mapper(Object) do |m|
92
+ m.validation_field 'Multicolumn Validation', ['Field1', 'Field2'] do |input|
93
+ raise 'No Input' unless input
94
+ end
95
+ end
96
+ expect(mapper.validation_columns).to eql(['Field1', 'Field2'])
97
+ expect(mapper.output_fields.empty?).to be_true
98
+ end
99
+ it 'raises an error if a validation name is repeated' do
100
+ expect {
101
+ mapper = Importer::Mapper.build_mapper(Object) do |m|
102
+ m.validation_field 'Field 1 Validation', 'Field1' do |input|
103
+ raise 'No Input' unless input
104
+ end
105
+ m.validation_field('Field 1 Validation', 'Field1') { |input| raise 'Test Error' }
106
+ end
107
+ }.to raise_error( Topographer::InvalidMappingError )
108
+ end
109
+ end
110
+
111
+ describe 'static mappings' do
112
+ it 'can create a static mapping' do
113
+ mapper = Importer::Mapper.build_mapper(Object) do |m|
114
+ m.default_value 'Field1' do
115
+ 34
116
+ end
117
+ end
118
+ expect(mapper.default_fields).to eql(['Field1'])
119
+ expect(mapper.output_fields).to eql(['Field1'])
120
+ end
121
+ it 'cannot create a static mapping to many columns' do
122
+ expect { mapper = Importer::Mapper.build_mapper(Object) do |m|
123
+ m.default_value ['Field1', 'Field2'] do
124
+ 34
125
+ end
126
+ end }.to raise_error(Topographer::InvalidMappingError)
127
+ end
128
+ it 'cannot add a static mapping to a field that has already been mapped' do
129
+ expect { mapper = Importer::Mapper.build_mapper(Object) do |m|
130
+ m.required_mapping 'Field1', 'field1'
131
+ m.default_value 'field1' do
132
+ 34
133
+ end
134
+ end }.to raise_error(Topographer::InvalidMappingError)
135
+ end
136
+ end
137
+
138
+ it 'associates the model class with the mapper instance' do
139
+ mapper = Importer::Mapper.build_mapper(Object) do |m|
140
+ m.ignored_column 'Field1'
141
+ m.optional_mapping 'Field2', 'field_1'
142
+ end
143
+ expect(mapper.model_class).to be Object
144
+ end
145
+ end
146
+
147
+ describe '#key_field' do
148
+ let(:mapper) { Importer::Mapper.new(Object) }
149
+ it 'should add a key field to the list of key fields' do
150
+ mapper.key_field 'Field1'
151
+ expect(mapper.key_fields).to eql(['Field1'])
152
+ end
153
+ it 'should not allow multiple key fields at one time' do
154
+ expect {
155
+ mapper.key_field ['Field1', 'Field2']
156
+ }.to raise_error(Topographer::InvalidMappingError)
157
+ end
158
+ it 'should not allow the same key field more than once' do
159
+ expect {
160
+ mapper.key_field 'Field1'
161
+ mapper.key_field 'Field1'
162
+ }.to raise_error(Topographer::InvalidMappingError)
163
+ end
164
+ end
165
+
166
+ describe '#input_structure_valid?' do
167
+ let(:mapper) do
168
+ Importer::Mapper.build_mapper(Object) do |m|
169
+ m.required_mapping 'Field1', 'field1'
170
+ m.required_mapping 'Field2', 'field_4'
171
+ m.optional_mapping 'Field3', 'field_6'
172
+ m.validation_field('test validation', 'Field4') { |input| raise 'FAILURE' if !input }
173
+ m.ignored_column 'Field5'
174
+ end
175
+ end
176
+
177
+ let(:valid_input_structure_with_options) { ['Field1', 'Field2', 'Field3', 'Field4'] }
178
+ let(:valid_input_structure_without_options) { ['Field1', 'Field2', 'Field4'] }
179
+ let(:missing_required_column_structure) { ['Field1', 'Field3'] }
180
+ let(:missing_validation_column_structure) { ['Field1', 'Field2', 'Field3'] }
181
+ let(:bad_column_structure) { ['Field1', 'UnknownField', 'Field2', 'Field4'] }
182
+
183
+ it 'returns false if required fields are missing' do
184
+ expect(mapper.input_structure_valid?(missing_required_column_structure)).to be_false
185
+ end
186
+ it 'returns false if a validation field is missing' do
187
+ expect(mapper.input_structure_valid?(missing_validation_column_structure)).to be_false
188
+ end
189
+ it 'returns true if all of the required fields are present' do
190
+ expect(mapper.input_structure_valid?(valid_input_structure_without_options)).to be_true
191
+ end
192
+ it 'returns true if all the required and optional fields are present' do
193
+ expect(mapper.input_structure_valid?(valid_input_structure_with_options)).to be_true
194
+ end
195
+ it 'returns true regardless of whether ignored fields are present' do
196
+ expect(mapper.input_structure_valid?(valid_input_structure_with_options)).to be_true
197
+ expect(mapper.input_structure_valid?(valid_input_structure_with_options+['Field5'])).to be_true
198
+ end
199
+ it 'returns false if there are any extra fields that have not been ignored' do
200
+ mapper.input_structure_valid?(bad_column_structure).should be_false
201
+ end
202
+ end
203
+
204
+ describe '#bad_columns' do
205
+ let(:mapper) do
206
+ Importer::Mapper.build_mapper(Object) do |m|
207
+ m.required_mapping 'Field1', 'field1'
208
+ m.required_mapping 'Field2', 'field_4'
209
+ m.validation_field('test validation 2', 'Field2') { |input| raise 'FAILURE' if !input }
210
+ m.optional_mapping 'Field3', 'field_6'
211
+ m.validation_field('test validation', 'Field4') { |input| raise 'FAILURE' if !input }
212
+ m.ignored_column 'Field5'
213
+ end
214
+ end
215
+ let(:bad_column_structure) { ['Field1', 'Bad Field', 'Field3'] }
216
+
217
+ it 'should return bad column names' do
218
+ mapper.input_structure_valid?(bad_column_structure)
219
+ expect(mapper.bad_columns).to eql(['Bad Field'])
220
+ end
221
+ end
222
+
223
+ describe '#missing_columns' do
224
+ let(:mapper) do
225
+ Importer::Mapper.build_mapper(Object) do |m|
226
+ m.required_mapping 'Field1', 'field1'
227
+ m.required_mapping 'Field2', 'field_4'
228
+ m.optional_mapping 'Field3', 'field_6'
229
+ m.validation_field('test validation', 'Field4') { |input| raise 'FAILURE' if !input }
230
+ m.ignored_column 'Field5'
231
+ end
232
+ end
233
+ let(:missing_column_structure) { ['Field1'] }
234
+
235
+ it 'should return missing column names' do
236
+ mapper.input_structure_valid?(missing_column_structure)
237
+ expect(mapper.missing_columns).to eql(['Field2', 'Field4'])
238
+ end
239
+ end
240
+
241
+ describe '#map_input' do
242
+ let(:mapper) do
243
+ Importer::Mapper.build_mapper(Object) do |m|
244
+ m.required_mapping 'Field1', 'field_1'
245
+ m.required_mapping ['Field1', 'Field2'], 'field_2'
246
+ m.validation_field('Field2 Validation', 'Field2') { |input| raise 'FAILURE' if input['Field2'] != 'datum2'}
247
+ m.required_mapping('Field1', 'field_3') { |x| x['Field1'].length }
248
+ m.required_mapping(['Field1', 'Field3'], 'field_4') { |x| x['Field1'].length * x['Field3'] }
249
+ m.default_value('static_field') { 34 }
250
+ m.optional_mapping('Field4', 'field_5')
251
+ end
252
+ end
253
+ let(:valid_input) do
254
+ double 'SourceData',
255
+ source_identifier: 'row1',
256
+ data: {'Field1' => 'datum1',
257
+ 'Field2' => 'datum2',
258
+ 'Field3' => 6}
259
+ end
260
+
261
+ let(:invalid_data_input) do
262
+ double 'SourceData',
263
+ source_identifier: 'row1',
264
+ data: {'Field1' => 'datum1',
265
+ 'Field2' => 'bad_field2_data',
266
+ 'Field3' => 6}
267
+ end
268
+
269
+ let(:missing_field_input) do
270
+ double 'SourceData',
271
+ source_identifier: 'row1',
272
+ data: {'Field1' => 'datum1',
273
+ 'Field2' => 'datum2'}
274
+ end
275
+
276
+ let(:result) do
277
+ mapper.map_input(valid_input)
278
+ end
279
+
280
+
281
+ it 'maps one to one field information' do
282
+ expect(result.data['field_1']).to eql('datum1')
283
+ end
284
+
285
+ it 'maps many to one field information' do
286
+ expect(result.data['field_2']).to eql('datum1, datum2')
287
+ end
288
+
289
+ it 'maps one to one field information with complex behavior' do
290
+ expect(result.data['field_3']).to eql(6)
291
+ end
292
+
293
+ it 'maps many to one field information with complex behavior' do
294
+ expect(result.data['field_4']).to eql(36)
295
+ end
296
+
297
+ it 'maps static fields' do
298
+ expect(result.data['static_field']).to eql(34)
299
+ end
300
+
301
+ it 'returns an error if required field is missing in input data' do
302
+ invalid_field_result = mapper.map_input(missing_field_input)
303
+ expect(invalid_field_result.errors?).to be_true
304
+ expect(invalid_field_result.errors['field_4']).to include('Missing required input(s): `Field3` for `field_4`')
305
+ end
306
+
307
+ it 'returns an error if a validation does not pass' do
308
+ invalid_data_result = mapper.map_input(invalid_data_input)
309
+ expect(invalid_data_result.errors?).to be_true
310
+ expect(invalid_data_result.errors['Field2 Validation']).to include('FAILURE')
311
+
312
+ end
313
+
314
+ it 'does not return an error if an optional field is missing in the input data' do
315
+ expect(result.errors?).to be_false
316
+ end
317
+ end
318
+ end