topographer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe Importer::Strategy::Base do
4
+ let(:mapper) do
5
+ double('Mapper')
6
+ end
7
+ let(:strategy) { Importer::Strategy::Base.new(mapper) }
8
+ let(:status) do
9
+ double 'Status',
10
+ errors?: false
11
+ end
12
+ let(:bad_status) do
13
+ double 'Status',
14
+ errors?: true
15
+ end
16
+ describe '#initialize' do
17
+ it 'creates a new Strategy instance with the given mapper' do
18
+ strategy = Importer::Strategy::Base.new(mapper)
19
+ strategy.instance_variable_get(:@mapper).should be(mapper)
20
+ end
21
+ end
22
+ describe '#import_record' do
23
+ it 'should raise NotImplementedError' do
24
+ expect { strategy.import_record(nil) }.to raise_error(NotImplementedError)
25
+ end
26
+ end
27
+
28
+ describe '#should_persist_import?' do
29
+ it 'returns true if the status has no errors and the import is not a dry run' do
30
+ expect(strategy.should_persist_import?(status)).to be_true
31
+ end
32
+ it 'returns false if the status has errors regardless of whether the import is a dry run or not' do
33
+ expect(strategy.should_persist_import?(bad_status)).to be_false
34
+ strategy.dry_run = true
35
+ expect(strategy.should_persist_import?(bad_status)).to be_false
36
+ end
37
+ it 'returns false if the status has no errors and the import is a dry run' do
38
+ strategy.dry_run = true
39
+ expect(strategy.should_persist_import?(status)).to be_false
40
+ end
41
+ end
42
+
43
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+ require_relative 'mapped_model'
4
+
5
+ describe Importer::Strategy::CreateOrUpdateRecord do
6
+
7
+ let(:strategy) { Importer::Strategy::CreateOrUpdateRecord.new(MappedModel.get_mapper) }
8
+ let(:input) do
9
+ double 'Data',
10
+ source_identifier: 'record',
11
+ data: {'Field1' => 'datum1',
12
+ 'Field2' => 'datum2'}
13
+ end
14
+ let(:invalid_input) do
15
+ double 'Data',
16
+ source_identifier: 'bad record',
17
+ data: {'Field1' => 'datum1'}
18
+ end
19
+
20
+
21
+ describe '#import_record' do
22
+ it 'should return an ImportStatus object' do
23
+ expect(strategy.import_record(input)).to be_a Importer::Strategy::ImportStatus
24
+ end
25
+ it 'should import a record from valid input' do
26
+ MappedModel.any_instance.should_receive(:save).once
27
+ import_status = strategy.import_record(input)
28
+ expect(import_status.errors?).to be false
29
+ end
30
+ it 'should not import a record from invalid input' do
31
+
32
+ MappedModel.any_instance.should_not_receive(:save)
33
+ import_status = strategy.import_record(invalid_input)
34
+ expect(import_status.errors?).to be true
35
+ expect(import_status.errors[:mapping]).to include('Missing required input(s): `Field2` for `field_2`')
36
+ expect(import_status.errors[:validation]).to include('Field 2 is not datum2')
37
+ end
38
+ it 'should not save a record on a dry run' do
39
+ MappedModel.any_instance.should_not_receive(:save)
40
+ strategy.dry_run = true
41
+ import_status = strategy.import_record(input)
42
+ expect(import_status.errors?).to be false
43
+ end
44
+ end
45
+
46
+ end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+ require_relative 'mapped_model'
4
+
5
+ describe Importer::Strategy::ImportNewRecord do
6
+ let(:valid_status) do
7
+ double 'Result',
8
+ source_identifier: 'row1',
9
+ data: {'field_1' => 'datum1', 'field_2' => 'datum2'},
10
+ errors?: false,
11
+ errors: {}
12
+ end
13
+ let(:invalid_result) do
14
+ double 'Result',
15
+ source_identifier: 'row1',
16
+ data: {'field_1' => 'dataum1'},
17
+ errors?: true,
18
+ errors: {'field_2' => 'Missing input(s): `Field2` for `field_2`'}
19
+
20
+ end
21
+ let(:mapper) do
22
+ double('Mapper', map_input: valid_status, model_class: MappedModel)
23
+ end
24
+
25
+ let(:strategy) { Importer::Strategy::ImportNewRecord.new(MappedModel.get_mapper) }
26
+ let(:input) do
27
+ double 'Data',
28
+ source_identifier: 'record',
29
+ data: {'Field1' => 'datum1',
30
+ 'Field2' => 'datum2'}
31
+ end
32
+
33
+ describe '#initialize' do
34
+ it 'creates a new Strategy instance with the given mapper' do
35
+ strategy = Importer::Strategy::ImportNewRecord.new(mapper)
36
+ strategy.instance_variable_get(:@mapper).should be(mapper)
37
+ end
38
+ end
39
+
40
+ describe '#import_record' do
41
+ it 'should return an ImportStatus object' do
42
+ expect(strategy.import_record(input)).to be_a Importer::Strategy::ImportStatus
43
+ end
44
+ it 'should import a record from valid input' do
45
+ MappedModel.any_instance.should_receive(:save).once
46
+ result = strategy.import_record(input)
47
+ expect(result.errors?).to be false
48
+ end
49
+ it 'should not import a record from invalid input' do
50
+ mapper.stub(:map_input).and_return(invalid_result)
51
+ strategy = Importer::Strategy::ImportNewRecord.new(mapper)
52
+ MappedModel.any_instance.should_not_receive(:save)
53
+ import_status = strategy.import_record(input)
54
+ expect(import_status.errors?).to be true
55
+ expect(import_status.errors[:mapping]).to include('Missing input(s): `Field2` for `field_2`')
56
+ expect(import_status.errors[:validation]).to include('Field 2 is not datum2')
57
+ end
58
+ it 'should not save a record on a dry run' do
59
+ MappedModel.any_instance.should_not_receive(:save)
60
+ strategy.dry_run = true
61
+ import_status = strategy.import_record(input)
62
+ expect(import_status.errors?).to be false
63
+ end
64
+ end
65
+
66
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe Importer::Strategy::ImportStatus do
4
+ let(:status) {Importer::Strategy::ImportStatus.new('row1')}
5
+ describe '#add_error' do
6
+ it 'should add errors' do
7
+ status.add_error(:validation, 'ERROR')
8
+ expect(status.error_count).to be 1
9
+ end
10
+ end
11
+ describe '#set_timestamp' do
12
+ it 'should set the timestamp the first time it is called' do
13
+ expect(status.timestamp).to be_nil
14
+ status.set_timestamp
15
+ expect(status.timestamp).to be_a DateTime
16
+ end
17
+ it 'should not change the timestamp after it has been called' do
18
+ status.set_timestamp
19
+ timestamp = status.timestamp
20
+ status.set_timestamp
21
+ expect(timestamp).to eql(status.timestamp)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,41 @@
1
+ class MappedModel < OpenStruct
2
+ def self.create(params)
3
+ self.new(params)
4
+ end
5
+
6
+ def first
7
+ self
8
+ end
9
+
10
+ def valid?
11
+ self.errors = OpenStruct.new(full_messages: [])
12
+ if field_2 == 'datum2'
13
+ true
14
+ else
15
+ self.errors = OpenStruct.new(full_messages: ['Field 2 is not datum2'])
16
+ false
17
+ end
18
+ end
19
+
20
+ def save
21
+ true
22
+ end
23
+
24
+ def self.get_mapper
25
+ Importer::Mapper.build_mapper(MappedModel) do |mapping|
26
+ mapping.required_mapping 'Field1', 'field_1'
27
+ mapping.required_mapping 'Field2', 'field_2'
28
+ mapping.optional_mapping 'Field3', 'field_3'
29
+ mapping.key_field 'field_1'
30
+ mapping.key_field 'field_2'
31
+ end
32
+ end
33
+
34
+ def self.where(params)
35
+ return self.new(params)
36
+ end
37
+
38
+ def self.find_or_initialize_by(params)
39
+ return self.new(params)
40
+ end
41
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+ require 'ostruct'
3
+ require_relative 'mapped_model'
4
+
5
+ describe Importer::Strategy::UpdateRecord do
6
+
7
+ let(:strategy) { Importer::Strategy::UpdateRecord.new(MappedModel.get_mapper) }
8
+ let(:input) do
9
+ double 'Data',
10
+ source_identifier: 'record',
11
+ data: {'Field1' => 'datum1',
12
+ 'Field2' => 'datum2'}
13
+ end
14
+ let(:invalid_input) do
15
+ double 'Data',
16
+ source_identifier: 'bad record',
17
+ data: {'Field1' => 'datum1'}
18
+ end
19
+
20
+ describe '#import_record' do
21
+ it 'should return an ImportStatus object' do
22
+ expect(strategy.import_record(input)).to be_a Importer::Strategy::ImportStatus
23
+ end
24
+ it 'should import a record from valid input' do
25
+ MappedModel.any_instance.should_receive(:save).once
26
+ result = strategy.import_record(input)
27
+ expect(result.errors?).to be false
28
+ end
29
+ it 'should not import a record from invalid input' do
30
+
31
+ MappedModel.any_instance.should_not_receive(:save)
32
+ import_status = strategy.import_record(invalid_input)
33
+ expect(import_status.errors?).to be true
34
+ expect(import_status.errors[:mapping]).to include('Missing required input(s): `Field2` for `field_2`')
35
+ expect(import_status.errors[:validation]).to include('Field 2 is not datum2')
36
+ end
37
+ it 'should not save a record on a dry run' do
38
+ MappedModel.any_instance.should_not_receive(:save)
39
+ strategy.dry_run = true
40
+ import_status = strategy.import_record(input)
41
+ expect(import_status.errors?).to be false
42
+ end
43
+ end
44
+
45
+ end
@@ -0,0 +1 @@
1
+ require 'topographer'
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'Topographer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'topographer'
8
+ spec.version = Topographer::VERSION
9
+ spec.authors = ['Mike Simpson', 'Emerson Huitt']
10
+ spec.email = ['mjs2600@gmail.com', 'ehuitt@gmail.com']
11
+ spec.description = %q{Topographer enables importing of models from various input sources.}
12
+ spec.summary = %q{Topographer allows the mapping of columnar input data to fields for active record models. This facilitates importing from a variety of sources.}
13
+ spec.homepage = ""
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_runtime_dependency('activesupport', ['>= 3'])
22
+
23
+ spec.add_development_dependency('bundler', ['~> 1.3'])
24
+ spec.add_development_dependency('rake')
25
+ spec.add_development_dependency('rspec')
26
+ end
metadata ADDED
@@ -0,0 +1,175 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: topographer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Mike Simpson
8
+ - Emerson Huitt
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-02-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - '>='
19
+ - !ruby/object:Gem::Version
20
+ version: '3'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - '>='
26
+ - !ruby/object:Gem::Version
27
+ version: '3'
28
+ - !ruby/object:Gem::Dependency
29
+ name: bundler
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ~>
33
+ - !ruby/object:Gem::Version
34
+ version: '1.3'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ~>
40
+ - !ruby/object:Gem::Version
41
+ version: '1.3'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rake
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - '>='
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rspec
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ description: Topographer enables importing of models from various input sources.
71
+ email:
72
+ - mjs2600@gmail.com
73
+ - ehuitt@gmail.com
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - .gitignore
79
+ - Gemfile
80
+ - LICENSE
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - lib/Topographer/exceptions.rb
85
+ - lib/Topographer/importer.rb
86
+ - lib/Topographer/importer/helpers.rb
87
+ - lib/Topographer/importer/helpers/write_log_to_csv.rb
88
+ - lib/Topographer/importer/importable.rb
89
+ - lib/Topographer/importer/input.rb
90
+ - lib/Topographer/importer/input/base.rb
91
+ - lib/Topographer/importer/input/roo.rb
92
+ - lib/Topographer/importer/input/source_data.rb
93
+ - lib/Topographer/importer/logger.rb
94
+ - lib/Topographer/importer/logger/base.rb
95
+ - lib/Topographer/importer/logger/fatal_error_entry.rb
96
+ - lib/Topographer/importer/logger/file.rb
97
+ - lib/Topographer/importer/logger/log_entry.rb
98
+ - lib/Topographer/importer/logger/simple.rb
99
+ - lib/Topographer/importer/mapper.rb
100
+ - lib/Topographer/importer/mapper/default_field_mapping.rb
101
+ - lib/Topographer/importer/mapper/field_mapping.rb
102
+ - lib/Topographer/importer/mapper/ignored_field_mapping.rb
103
+ - lib/Topographer/importer/mapper/result.rb
104
+ - lib/Topographer/importer/mapper/validation_field_mapping.rb
105
+ - lib/Topographer/importer/strategy.rb
106
+ - lib/Topographer/importer/strategy/base.rb
107
+ - lib/Topographer/importer/strategy/create_or_update_record.rb
108
+ - lib/Topographer/importer/strategy/import_new_record.rb
109
+ - lib/Topographer/importer/strategy/import_status.rb
110
+ - lib/Topographer/importer/strategy/update_record.rb
111
+ - lib/Topographer/version.rb
112
+ - lib/topographer.rb
113
+ - spec/Cartographer/importer/helpers/write_log_to_csv_spec.rb
114
+ - spec/Cartographer/importer/helpers_spec.rb
115
+ - spec/Cartographer/importer/importable_spec.rb
116
+ - spec/Cartographer/importer/importer_spec.rb
117
+ - spec/Cartographer/importer/logger/base_spec.rb
118
+ - spec/Cartographer/importer/logger/fatal_error_entry_spec.rb
119
+ - spec/Cartographer/importer/logger/simple_spec.rb
120
+ - spec/Cartographer/importer/mapper/default_field_mapping_spec.rb
121
+ - spec/Cartographer/importer/mapper/field_mapping_spec.rb
122
+ - spec/Cartographer/importer/mapper/validation_field_mapping_spec.rb
123
+ - spec/Cartographer/importer/mapper_spec.rb
124
+ - spec/Cartographer/importer/strategy/base_spec.rb
125
+ - spec/Cartographer/importer/strategy/create_or_update_record_spec.rb
126
+ - spec/Cartographer/importer/strategy/import_new_records_spec.rb
127
+ - spec/Cartographer/importer/strategy/import_status_spec.rb
128
+ - spec/Cartographer/importer/strategy/mapped_model.rb
129
+ - spec/Cartographer/importer/strategy/update_record_spec.rb
130
+ - spec/spec_helper.rb
131
+ - topographer.gemspec
132
+ homepage: ''
133
+ licenses:
134
+ - MIT
135
+ metadata: {}
136
+ post_install_message:
137
+ rdoc_options: []
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - '>='
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ required_rubygems_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ requirements: []
151
+ rubyforge_project:
152
+ rubygems_version: 2.1.11
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: Topographer allows the mapping of columnar input data to fields for active
156
+ record models. This facilitates importing from a variety of sources.
157
+ test_files:
158
+ - spec/Cartographer/importer/helpers/write_log_to_csv_spec.rb
159
+ - spec/Cartographer/importer/helpers_spec.rb
160
+ - spec/Cartographer/importer/importable_spec.rb
161
+ - spec/Cartographer/importer/importer_spec.rb
162
+ - spec/Cartographer/importer/logger/base_spec.rb
163
+ - spec/Cartographer/importer/logger/fatal_error_entry_spec.rb
164
+ - spec/Cartographer/importer/logger/simple_spec.rb
165
+ - spec/Cartographer/importer/mapper/default_field_mapping_spec.rb
166
+ - spec/Cartographer/importer/mapper/field_mapping_spec.rb
167
+ - spec/Cartographer/importer/mapper/validation_field_mapping_spec.rb
168
+ - spec/Cartographer/importer/mapper_spec.rb
169
+ - spec/Cartographer/importer/strategy/base_spec.rb
170
+ - spec/Cartographer/importer/strategy/create_or_update_record_spec.rb
171
+ - spec/Cartographer/importer/strategy/import_new_records_spec.rb
172
+ - spec/Cartographer/importer/strategy/import_status_spec.rb
173
+ - spec/Cartographer/importer/strategy/mapped_model.rb
174
+ - spec/Cartographer/importer/strategy/update_record_spec.rb
175
+ - spec/spec_helper.rb