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,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