importu 0.1.0 → 0.2.0

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 (110) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +15 -0
  3. data/.github/workflows/ci.yml +48 -0
  4. data/.gitignore +4 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +311 -0
  7. data/.simplecov +14 -0
  8. data/.yardstick.yml +36 -0
  9. data/Appraisals +22 -0
  10. data/CHANGELOG.md +51 -0
  11. data/CONTRIBUTING.md +86 -0
  12. data/Gemfile +5 -1
  13. data/LICENSE +21 -0
  14. data/README.md +435 -52
  15. data/Rakefile +71 -0
  16. data/UPGRADING.md +188 -0
  17. data/gemfiles/rails_7_2.gemfile +11 -0
  18. data/gemfiles/rails_7_2.gemfile.lock +268 -0
  19. data/gemfiles/rails_8_0.gemfile +11 -0
  20. data/gemfiles/rails_8_0.gemfile.lock +271 -0
  21. data/gemfiles/rails_8_1.gemfile +11 -0
  22. data/gemfiles/rails_8_1.gemfile.lock +269 -0
  23. data/gemfiles/standalone.gemfile +8 -0
  24. data/gemfiles/standalone.gemfile.lock +197 -0
  25. data/importu.gemspec +41 -22
  26. data/lib/importu/backends/active_record.rb +171 -0
  27. data/lib/importu/backends/middleware/duplicate_manager_proxy.rb +41 -0
  28. data/lib/importu/backends/middleware/enforce_allowed_actions.rb +52 -0
  29. data/lib/importu/backends/middleware.rb +11 -0
  30. data/lib/importu/backends.rb +103 -0
  31. data/lib/importu/config_dsl.rb +381 -0
  32. data/lib/importu/converter_context.rb +94 -0
  33. data/lib/importu/converters.rb +119 -64
  34. data/lib/importu/definition.rb +23 -0
  35. data/lib/importu/duplicate_manager.rb +88 -0
  36. data/lib/importu/exceptions.rb +135 -4
  37. data/lib/importu/importer.rb +183 -96
  38. data/lib/importu/record.rb +138 -102
  39. data/lib/importu/sources/csv.rb +122 -0
  40. data/lib/importu/sources/json.rb +106 -0
  41. data/lib/importu/sources/ruby.rb +46 -0
  42. data/lib/importu/sources/xml.rb +133 -0
  43. data/lib/importu/sources.rb +13 -0
  44. data/lib/importu/summary.rb +277 -0
  45. data/lib/importu/version.rb +3 -1
  46. data/lib/importu.rb +45 -9
  47. data/spec/fixtures/books-duplicates/README.md +7 -0
  48. data/spec/fixtures/books-duplicates/infile.csv +7 -0
  49. data/spec/fixtures/books-duplicates/model.json +23 -0
  50. data/spec/fixtures/books-duplicates/summary.json +10 -0
  51. data/spec/fixtures/books-valid/README.md +13 -0
  52. data/spec/fixtures/books-valid/infile.csv +4 -0
  53. data/spec/fixtures/books-valid/infile.json +23 -0
  54. data/spec/fixtures/books-valid/infile.xml +21 -0
  55. data/spec/fixtures/books-valid/model.json +23 -0
  56. data/spec/fixtures/books-valid/record.json +26 -0
  57. data/spec/fixtures/books-valid/summary.json +8 -0
  58. data/spec/fixtures/source-empty-file/infile.csv +0 -0
  59. data/spec/fixtures/source-empty-file/infile.json +0 -0
  60. data/spec/fixtures/source-empty-file/infile.xml +0 -0
  61. data/spec/fixtures/source-empty-records/infile.csv +3 -0
  62. data/spec/fixtures/source-empty-records/infile.json +1 -0
  63. data/spec/fixtures/source-empty-records/infile.xml +6 -0
  64. data/spec/fixtures/source-malformed/infile.csv +1 -0
  65. data/spec/fixtures/source-malformed/infile.json +1 -0
  66. data/spec/fixtures/source-malformed/infile.xml +3 -0
  67. data/spec/fixtures/source-no-records/infile.csv +1 -0
  68. data/spec/fixtures/source-no-records/infile.json +1 -0
  69. data/spec/fixtures/source-no-records/infile.xml +3 -0
  70. data/spec/lib/importu/backends/active_record_spec.rb +150 -0
  71. data/spec/lib/importu/backends/middleware/duplicate_manager_proxy_spec.rb +70 -0
  72. data/spec/lib/importu/backends/middleware/enforce_allowed_actions_spec.rb +70 -0
  73. data/spec/lib/importu/backends_spec.rb +170 -0
  74. data/spec/lib/importu/converters_spec.rb +184 -141
  75. data/spec/lib/importu/definition_spec.rb +248 -0
  76. data/spec/lib/importu/duplicate_manager_spec.rb +92 -0
  77. data/spec/lib/importu/exceptions_spec.rb +69 -16
  78. data/spec/lib/importu/import_context_spec.rb +199 -0
  79. data/spec/lib/importu/importer_spec.rb +95 -0
  80. data/spec/lib/importu/integration_spec.rb +221 -0
  81. data/spec/lib/importu/record_spec.rb +130 -80
  82. data/spec/lib/importu/sources/csv_spec.rb +29 -0
  83. data/spec/lib/importu/sources/importer_source_examples.rb +175 -0
  84. data/spec/lib/importu/sources/json_spec.rb +29 -0
  85. data/spec/lib/importu/sources/ruby_spec.rb +102 -0
  86. data/spec/lib/importu/sources/xml_spec.rb +70 -0
  87. data/spec/lib/importu/summary_spec.rb +186 -0
  88. data/spec/spec_helper.rb +91 -7
  89. data/spec/support/active_record.rb +20 -0
  90. data/spec/support/book_importer.rb +31 -0
  91. data/spec/support/dummy_backend.rb +50 -0
  92. data/spec/support/fixtures_helper.rb +43 -0
  93. data/spec/support/matchers/delegate_matcher.rb +14 -8
  94. metadata +173 -100
  95. data/lib/importu/core_ext/array/deep_freeze.rb +0 -7
  96. data/lib/importu/core_ext/deep_freeze.rb +0 -3
  97. data/lib/importu/core_ext/hash/deep_freeze.rb +0 -7
  98. data/lib/importu/core_ext/object/deep_freeze.rb +0 -6
  99. data/lib/importu/core_ext.rb +0 -3
  100. data/lib/importu/dsl.rb +0 -127
  101. data/lib/importu/importer/csv.rb +0 -52
  102. data/lib/importu/importer/json.rb +0 -45
  103. data/lib/importu/importer/xml.rb +0 -55
  104. data/spec/factories/importer.rb +0 -12
  105. data/spec/factories/importer_record.rb +0 -13
  106. data/spec/factories/json_importer.rb +0 -14
  107. data/spec/factories/xml_importer.rb +0 -12
  108. data/spec/lib/importu/dsl_spec.rb +0 -26
  109. data/spec/lib/importu/importer/json_spec.rb +0 -37
  110. data/spec/lib/importu/importer/xml_spec.rb +0 -14
@@ -1,274 +1,317 @@
1
- require 'spec_helper'
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
2
3
 
3
- require 'active_support/core_ext/time/calculations'
4
+ require "importu/converter_context"
5
+ require "importu/definition"
4
6
 
5
- describe Importu::Importer do
6
- subject(:record) { build(:importer_record) }
7
+ RSpec.describe "Importu Converters" do
8
+ subject(:context) { Importu::ConverterContext.with_config(**definition.config).new(data) }
9
+ let(:definition) { Class.new(Importu::Definition) }
10
+
11
+ let(:data) { { "field1" => " value1 ", "field2" => "value2" } }
7
12
 
8
13
  describe ":raw converter" do
14
+ let(:definition) { Class.new(super()) { fields :field1, :field3 } }
15
+ let(:data) { { "field1" => " value1 ", "field2" => "value2" } }
16
+
9
17
  it "uses definition's label as key when looking up data" do
10
- record.stub(:definitions => { :field1 => { :label => "field1", :required => true } })
11
- record.stub(:data => { "field1" => " value1 ", "field2" => "value2" })
12
- record.convert(:field1, :raw).should eq " value1 "
18
+ expect(context.raw(:field1)).to eq " value1 "
13
19
  end
14
20
 
15
21
  it "raises an exception if field definition is not defined" do
16
- expect { record.convert(:field1, :raw) }.to raise_error
17
- end
18
-
19
- it "raises MissingField if field data not defined" do
20
- record.stub(:definitions => { :field1 => { :required => true } })
21
- expect { record.convert(:field1, :raw) }.to raise_error(Importu::MissingField)
22
+ expect { context.raw(:field2) }.to raise_error(Importu::InvalidDefinition)
22
23
  end
23
24
  end
24
25
 
25
- describe ":clean converter" do
26
- it "returns nil if raw data is nil" do
27
- record.converters[:raw] = proc { nil }
28
- record.convert(:field1, :clean).should be nil
26
+ describe ":trimmed converter" do
27
+ it "returns nil if raw value is nil" do
28
+ allow(context).to receive(:raw_value).and_return(nil)
29
+ expect(context.trimmed(:field1)).to be nil
29
30
  end
30
31
 
31
32
  it "returns nil if a blank string" do
32
- record.converters[:raw] = proc { "" }
33
- record.convert(:field1, :clean).should be nil
33
+ allow(context).to receive(:raw_value).and_return("")
34
+ expect(context.trimmed(:field1)).to be nil
34
35
  end
35
36
 
36
37
  it "returns stripped value if a string" do
37
- record.converters[:raw] = proc { " abc 123 " }
38
- record.convert(:field1, :clean).should eq "abc 123"
38
+ allow(context).to receive(:raw_value).and_return(" abc 123 ")
39
+ expect(context.trimmed(:field1)).to eq "abc 123"
39
40
  end
40
41
 
41
42
  it "returns original value if not a string" do
42
43
  raw_val = Time.now
43
- record.converters[:raw] = proc { raw_val }
44
- record.convert(:field1, :clean).should eq raw_val
44
+ allow(context).to receive(:raw_value).and_return(raw_val)
45
+ expect(context.trimmed(:field1)).to eq raw_val
46
+ end
47
+
48
+ it "handles unicode strings with leading/trailing whitespace" do
49
+ allow(context).to receive(:raw_value).and_return(" \u00e9l\u00e8ve ")
50
+ expect(context.trimmed(:field1)).to eq "\u00e9l\u00e8ve"
51
+ end
52
+
53
+ # NOTE: Ruby's String#strip only handles ASCII whitespace. Unicode whitespace
54
+ # characters like ideographic space (U+3000) or non-breaking space (U+00A0)
55
+ # are not stripped. This documents the current behavior.
56
+ it "does not strip unicode whitespace characters (uses String#strip)" do
57
+ allow(context).to receive(:raw_value).and_return("\u00a0test\u3000")
58
+ expect(context.trimmed(:field1)).to eq "\u00a0test\u3000"
45
59
  end
46
60
  end
47
61
 
48
62
  describe ":string converter" do
49
- it "returns nil if clean value is nil" do
50
- record.converters[:clean] = proc { nil }
51
- record.convert(:field1, :string).should be nil
63
+ it "returns nil if trimmed value is nil" do
64
+ definition.converter(:trimmed) {|*| nil }
65
+ expect(context.string(:field1)).to be nil
66
+ end
67
+
68
+ it "returns string if trimmed value is a string" do
69
+ definition.converter(:trimmed) {|*| "six pence" }
70
+ expect(context.string(:field1)).to eq "six pence"
52
71
  end
53
72
 
54
- it "returns string if clean value is a string" do
55
- record.converters[:clean] = proc { "six pence" }
56
- record.convert(:field1, :string).should eq "six pence"
73
+ it "converts trimmed value to string if not a string" do
74
+ trimmed_val = Time.now
75
+ definition.converter(:trimmed) {|*| trimmed_val }
76
+ expect(context.string(:field1)).to eq trimmed_val.to_s
57
77
  end
58
78
 
59
- it "converts clean value to string if not a string" do
60
- clean_val = Time.now
61
- record.converters[:clean] = proc { clean_val }
62
- record.convert(:field1, :string).should eq clean_val.to_s
79
+ it "preserves unicode characters in string" do
80
+ definition.converter(:trimmed) {|*| "\u00e9l\u00e8ve \u4e2d\u6587" }
81
+ expect(context.string(:field1)).to eq "\u00e9l\u00e8ve \u4e2d\u6587"
63
82
  end
64
83
  end
65
84
 
66
85
  describe ":integer converter" do
67
- it "returns nil if clean value is nil" do
68
- record.converters[:clean] = proc { nil }
69
- record.convert(:field1, :integer).should be nil
86
+ it "returns nil if trimmed value is nil" do
87
+ definition.converter(:trimmed) {|*| nil }
88
+ expect(context.integer(:field1)).to be nil
89
+ end
90
+
91
+ it "returns integer if trimmed value returns an integer" do
92
+ definition.converter(:trimmed) {|*| 92 }
93
+ expect(context.integer(:field1)).to eq 92
70
94
  end
71
95
 
72
- it "returns integer if clean value returns an integer" do
73
- record.converters[:clean] = proc { 92 }
74
- record.convert(:field1, :integer).should eq 92
96
+ it "converts trimmed value to integer if not an integer type" do
97
+ definition.converter(:trimmed) {|*| "29" }
98
+ expect(context.integer(:field1)).to eq 29
75
99
  end
76
100
 
77
- it "converts clean value to integer if not an integer type" do
78
- record.converters[:clean] = proc { "29" }
79
- record.convert(:field1, :integer).should eq 29
101
+ it "handles values with a leading 0 as a decimal (not octal)" do
102
+ definition.converter(:trimmed) {|*| "044" }
103
+ expect(context.integer(:field1)).to eq 44
80
104
  end
81
105
 
82
- it "raises an exception if clean value is not a valid integer" do
83
- record.converters[:clean] = proc { "4.25" }
84
- expect { record.convert(:field1, :integer) }.to raise_error(Importu::FieldParseError)
106
+ it "raises an ArgumentError if trimmed value is not a valid integer" do
107
+ definition.converter(:trimmed) {|*| "4.25" }
108
+ expect { context.integer(:field1) }.to raise_error(ArgumentError)
85
109
  end
86
110
  end
87
111
 
88
112
  describe ":float converter" do
89
- it "returns nil if clean value is nil" do
90
- record.converters[:clean] = proc { nil }
91
- record.convert(:field1, :float).should be nil
113
+ it "returns nil if trimmed value is nil" do
114
+ definition.converter(:trimmed) {|*| nil }
115
+ expect(context.float(:field1)).to be nil
92
116
  end
93
117
 
94
- it "returns float if clean value returns an float" do
95
- record.converters[:clean] = proc { 92.25 }
96
- record.convert(:field1, :float).should eq 92.25
118
+ it "returns float if trimmed value returns an float" do
119
+ definition.converter(:trimmed) {|*| 92.25 }
120
+ expect(context.float(:field1)).to eq 92.25
97
121
  end
98
122
 
99
- it "converts clean value to float if not an float type" do
100
- record.converters[:clean] = proc { "29.4" }
101
- record.convert(:field1, :float).should eq 29.4
123
+ it "converts trimmed value to float if not an float type" do
124
+ definition.converter(:trimmed) {|*| "29.4" }
125
+ expect(context.float(:field1)).to eq 29.4
102
126
  end
103
127
 
104
128
  it "converts whole values to float" do
105
- record.converters[:clean] = proc { "77" }
106
- record.convert(:field1, :float).should eq 77.0
129
+ definition.converter(:trimmed) {|*| "77" }
130
+ expect(context.float(:field1)).to eq 77.0
107
131
  end
108
132
 
109
- it "raises an exception if clean value is not a valid float" do
110
- record.converters[:clean] = proc { "4d6point3" }
111
- expect { record.convert(:field1, :float) }.to raise_error(Importu::FieldParseError)
133
+ it "raises an ArgumentError if trimmed value is not a valid float" do
134
+ definition.converter(:trimmed) {|*| "4d6point3" }
135
+ expect { context.float(:field1) }.to raise_error(ArgumentError)
112
136
  end
113
137
  end
114
138
 
115
139
  describe ":decimal converter" do
116
- it "returns nil if clean value is nil" do
117
- record.converters[:clean] = proc { nil }
118
- record.convert(:field1, :decimal).should be nil
140
+ it "returns nil if trimmed value is nil" do
141
+ definition.converter(:trimmed) {|*| nil }
142
+ expect(context.decimal(:field1)).to be nil
119
143
  end
120
144
 
121
- it "returns decimal if clean value returns an decimal" do
122
- clean_val = BigDecimal("92.25")
123
- record.converters[:clean] = proc { clean_val }
124
- record.convert(:field1, :decimal).should eq clean_val
145
+ it "returns decimal if trimmed value returns an decimal" do
146
+ trimmed_val = BigDecimal("92.25")
147
+ definition.converter(:trimmed) {|*| trimmed_val }
148
+ expect(context.decimal(:field1)).to eq trimmed_val
125
149
  end
126
150
 
127
- it "converts clean value to decimal if not an decimal type" do
128
- record.converters[:clean] = proc { "29.4" }
129
- record.convert(:field1, :decimal).should eq BigDecimal("29.4")
151
+ it "converts trimmed value to decimal if not an decimal type" do
152
+ definition.converter(:trimmed) {|*| "29.4" }
153
+ expect(context.decimal(:field1)).to eq BigDecimal("29.4")
130
154
  end
131
155
 
132
156
  it "converts whole values to decimal" do
133
- record.converters[:clean] = proc { "77" }
134
- record.convert(:field1, :decimal).should eq BigDecimal("77.0")
157
+ definition.converter(:trimmed) {|*| "77" }
158
+ expect(context.decimal(:field1)).to eq BigDecimal("77.0")
135
159
  end
136
160
 
137
- it "raises an exception if clean value is not a valid decimal" do
138
- record.converters[:clean] = proc { "4d6point3" }
139
- expect { record.convert(:field1, :decimal) }.to raise_error(Importu::FieldParseError)
161
+ it "raises an ArgumentError if trimmed value is not a valid decimal" do
162
+ definition.converter(:trimmed) {|*| "4d6point3" }
163
+ expect { context.decimal(:field1) }.to raise_error(ArgumentError)
140
164
  end
141
165
  end
142
166
 
143
167
  describe ":boolean converter" do
144
- it "returns nil if clean value is nil" do
145
- record.converters[:clean] = proc { nil }
146
- record.convert(:field1, :boolean).should be nil
168
+ it "returns nil if trimmed value is nil" do
169
+ definition.converter(:trimmed) {|*| nil }
170
+ expect(context.boolean(:field1)).to be nil
171
+ end
172
+
173
+ it "returns true if trimmed value is true" do
174
+ definition.converter(:trimmed) {|*| true }
175
+ expect(context.boolean(:field1)).to eq true
176
+ end
177
+
178
+ it "returns true if trimmed value is 'true'" do
179
+ definition.converter(:trimmed) {|*| "true" }
180
+ expect(context.boolean(:field1)).to eq true
181
+ end
182
+
183
+ it "returns true if trimmed value is 'yes'" do
184
+ definition.converter(:trimmed) {|*| "yes" }
185
+ expect(context.boolean(:field1)).to eq true
147
186
  end
148
187
 
149
- it "returns true if clean value is true" do
150
- record.converters[:clean] = proc { true }
151
- record.convert(:field1, :boolean).should eq true
188
+ it "returns true if trimmed value is '1'" do
189
+ definition.converter(:trimmed) {|*| "1" }
190
+ expect(context.boolean(:field1)).to eq true
152
191
  end
153
192
 
154
- it "returns true if clean value is 'true'" do
155
- record.converters[:clean] = proc { "true" }
156
- record.convert(:field1, :boolean).should eq true
193
+ it "returns true if trimmed value is 1" do
194
+ definition.converter(:trimmed) {|*| 1 }
195
+ expect(context.boolean(:field1)).to eq true
157
196
  end
158
197
 
159
- it "returns true if clean value is 'yes'" do
160
- record.converters[:clean] = proc { "yes" }
161
- record.convert(:field1, :boolean).should eq true
198
+ it "returns false if trimmed value is false" do
199
+ definition.converter(:trimmed) {|*| false }
200
+ expect(context.boolean(:field1)).to eq false
162
201
  end
163
202
 
164
- it "returns true if clean value is '1'" do
165
- record.converters[:clean] = proc { "1" }
166
- record.convert(:field1, :boolean).should eq true
203
+ it "returns false if trimmed value is 'false'" do
204
+ definition.converter(:trimmed) {|*| "false" }
205
+ expect(context.boolean(:field1)).to eq false
167
206
  end
168
207
 
169
- it "returns true if clean value is 1" do
170
- record.converters[:clean] = proc { 1 }
171
- record.convert(:field1, :boolean).should eq true
208
+ it "returns false if trimmed value is 'no'" do
209
+ definition.converter(:trimmed) {|*| "no" }
210
+ expect(context.boolean(:field1)).to eq false
172
211
  end
173
212
 
174
- it "returns false if clean value is false" do
175
- record.converters[:clean] = proc { false }
176
- record.convert(:field1, :boolean).should eq false
213
+ it "returns false if trimmed value is '0'" do
214
+ definition.converter(:trimmed) {|*| "0" }
215
+ expect(context.boolean(:field1)).to eq false
177
216
  end
178
217
 
179
- it "returns false if clean value is 'false'" do
180
- record.converters[:clean] = proc { "false" }
181
- record.convert(:field1, :boolean).should eq false
218
+ it "returns false if trimmed value is 0" do
219
+ definition.converter(:trimmed) {|*| 0 }
220
+ expect(context.boolean(:field1)).to eq false
182
221
  end
183
222
 
184
- it "returns false if clean value is 'no'" do
185
- record.converters[:clean] = proc { "no" }
186
- record.convert(:field1, :boolean).should eq false
223
+ it "raises an ArgumentError if the value is invalid" do
224
+ definition.converter(:trimmed) {|*| "why-not-both" }
225
+ expect { context.boolean(:field1) }.to raise_error(ArgumentError)
187
226
  end
188
227
 
189
- it "returns false if clean value is '0'" do
190
- record.converters[:clean] = proc { "0" }
191
- record.convert(:field1, :boolean).should eq false
228
+ it "treats values as case-insensitive" do
229
+ definition.converter(:trimmed) {|*| "TrUE" }
230
+ expect(context.boolean(:field1)).to eq true
192
231
  end
193
232
 
194
- it "returns false if clean value is 0" do
195
- record.converters[:clean] = proc { 0 }
196
- record.convert(:field1, :boolean).should eq false
233
+ # NOTE: The boolean converter receives values AFTER trimming, so whitespace
234
+ # around boolean values should already be stripped. If raw values with
235
+ # whitespace need to work, they should use the :trimmed converter first.
236
+ # This test documents that behavior - " true " without trimming will fail.
237
+ it "does not handle whitespace (expects trimmed input)" do
238
+ definition.converter(:trimmed) {|*| " true " }
239
+ expect { context.boolean(:field1) }.to raise_error(ArgumentError)
197
240
  end
198
241
  end
199
242
 
200
243
  describe ":date converter" do
201
- it "returns nil if clean value is nil" do
202
- record.converters[:clean] = proc { nil }
203
- record.convert(:field1, :date).should be nil
244
+ it "returns nil if trimmed value is nil" do
245
+ definition.converter(:trimmed) {|*| nil }
246
+ expect(context.date(:field1)).to be nil
204
247
  end
205
248
 
206
249
  context "when a format is not specified" do
207
250
  it "tries to guess the date format (DD/MM/YYYY is default?)" do
208
251
  expected = Date.new(2012, 10, 3)
209
- record.converters[:clean] = proc { "03/10/2012" }
210
- record.convert(:field1, :date).should eq expected
252
+ definition.converter(:trimmed) {|*| "03/10/2012" }
253
+ expect(context.date(:field1)).to eq expected
211
254
  end
212
255
 
213
- it "raises an exception if the date is invaild" do
214
- record.converters[:clean] = proc { "2012-04-32" }
215
- expect { record.convert(:field1, :date) }.to raise_error(Importu::FieldParseError)
256
+ it "raises an ArgumentError if the date is invaild" do
257
+ definition.converter(:trimmed) {|*| "2012-04-32" }
258
+ expect { context.date(:field1) }.to raise_error(ArgumentError)
216
259
  end
217
260
  end
218
261
 
219
262
  context "when a format is specified" do
220
263
  it "parses dates that match the format" do
221
264
  expected = Date.new(2012, 4, 18)
222
- record.converters[:clean] = proc { "04/18/2012" }
223
- record.convert(:field1, :date, :format => "%m/%d/%Y").should eq expected
265
+ definition.converter(:trimmed) {|*| "04/18/2012" }
266
+ expect(context.date(:field1, format: "%m/%d/%Y")).to eq expected
224
267
  end
225
268
 
226
- it "raises exception if date doesn't match the format" do
227
- record.converters[:clean] = proc { "04-18-2012" }
228
- expect { record.convert(:field1, :date) }.to raise_error(Importu::FieldParseError)
269
+ it "raises an ArgumentError if date doesn't match the format" do
270
+ definition.converter(:trimmed) {|*| "04-18-2012" }
271
+ expect { context.date(:field1) }.to raise_error(ArgumentError)
229
272
  end
230
273
 
231
- it "raises an exception if the date is invalid" do
232
- record.converters[:clean] = proc { "04/32/2012" }
233
- expect { record.convert(:field1, :date) }.to raise_error(Importu::FieldParseError)
274
+ it "raises an ArgumentError if the date is invalid" do
275
+ definition.converter(:trimmed) {|*| "04/32/2012" }
276
+ expect { context.date(:field1) }.to raise_error(ArgumentError)
234
277
  end
235
278
  end
236
279
  end
237
280
 
238
281
  describe ":datetime converter" do
239
- it "returns nil if clean value is nil" do
240
- record.converters[:clean] = proc { nil }
241
- record.convert(:field1, :datetime).should be nil
282
+ it "returns nil if trimmed value is nil" do
283
+ definition.converter(:trimmed) {|*| nil }
284
+ expect(context.datetime(:field1)).to be nil
242
285
  end
243
286
 
244
287
  context "when a format is not specified" do
245
288
  it "tries to guess the date format (DD/MM/YYYY is default?)" do
246
- expected = DateTime.parse("2012-10-03 04:37:29")
247
- record.converters[:clean] = proc { "03/10/2012 04:37:29" }
248
- record.convert(:field1, :datetime).should eq expected
289
+ expected = Time.parse("2012-10-03 04:37:29Z")
290
+ definition.converter(:trimmed) {|*| "03/10/2012 04:37:29" }
291
+ expect(context.datetime(:field1)).to eq expected
249
292
  end
250
293
 
251
- it "raises an exception if the datetime is invaild" do
252
- record.converters[:clean] = proc { "2012-04-32 15:41:22" }
253
- expect { record.convert(:field1, :datetime) }.to raise_error(Importu::FieldParseError)
294
+ it "raises an ArgumentError if the datetime is invaild" do
295
+ definition.converter(:trimmed) {|*| "2012-04-32 15:41:22" }
296
+ expect { context.datetime(:field1) }.to raise_error(ArgumentError)
254
297
  end
255
298
  end
256
299
 
257
300
  context "when a format is specified" do
258
301
  it "parses datetimes that match the format" do
259
- expected = DateTime.parse("2012-04-18 16:37:00")
260
- record.converters[:clean] = proc { "04/18/2012 16:37" }
261
- record.convert(:field1, :datetime, :format => "%m/%d/%Y %H:%M").should eq expected
302
+ expected = Time.parse("2012-04-18 16:37:00Z")
303
+ definition.converter(:trimmed) {|*| "04/18/2012 16:37" }
304
+ expect(context.datetime(:field1, format: "%m/%d/%Y %H:%M")).to eq expected
262
305
  end
263
306
 
264
- it "raises exception if datetime doesn't match the format" do
265
- record.converters[:clean] = proc { "04-18-2012 15:22:19" }
266
- expect { record.convert(:field1, :datetime) }.to raise_error(Importu::FieldParseError)
307
+ it "raises an ArgumentError if datetime doesn't match the format" do
308
+ definition.converter(:trimmed) {|*| "04-18-2012 15:22:19" }
309
+ expect { context.datetime(:field1) }.to raise_error(ArgumentError)
267
310
  end
268
311
 
269
- it "raises an exception if the datetime is invalid" do
270
- record.converters[:clean] = proc { "04/32/2012 00:00:00" }
271
- expect { record.convert(:field1, :datetime) }.to raise_error(Importu::FieldParseError)
312
+ it "raises an ArgumentError if the datetime is invalid" do
313
+ definition.converter(:trimmed) {|*| "04/32/2012 00:00:00" }
314
+ expect { context.datetime(:field1) }.to raise_error(ArgumentError)
272
315
  end
273
316
  end
274
317
  end