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.
- checksums.yaml +7 -0
- data/.editorconfig +15 -0
- data/.github/workflows/ci.yml +48 -0
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/.rubocop.yml +311 -0
- data/.simplecov +14 -0
- data/.yardstick.yml +36 -0
- data/Appraisals +22 -0
- data/CHANGELOG.md +51 -0
- data/CONTRIBUTING.md +86 -0
- data/Gemfile +5 -1
- data/LICENSE +21 -0
- data/README.md +435 -52
- data/Rakefile +71 -0
- data/UPGRADING.md +188 -0
- data/gemfiles/rails_7_2.gemfile +11 -0
- data/gemfiles/rails_7_2.gemfile.lock +268 -0
- data/gemfiles/rails_8_0.gemfile +11 -0
- data/gemfiles/rails_8_0.gemfile.lock +271 -0
- data/gemfiles/rails_8_1.gemfile +11 -0
- data/gemfiles/rails_8_1.gemfile.lock +269 -0
- data/gemfiles/standalone.gemfile +8 -0
- data/gemfiles/standalone.gemfile.lock +197 -0
- data/importu.gemspec +41 -22
- data/lib/importu/backends/active_record.rb +171 -0
- data/lib/importu/backends/middleware/duplicate_manager_proxy.rb +41 -0
- data/lib/importu/backends/middleware/enforce_allowed_actions.rb +52 -0
- data/lib/importu/backends/middleware.rb +11 -0
- data/lib/importu/backends.rb +103 -0
- data/lib/importu/config_dsl.rb +381 -0
- data/lib/importu/converter_context.rb +94 -0
- data/lib/importu/converters.rb +119 -64
- data/lib/importu/definition.rb +23 -0
- data/lib/importu/duplicate_manager.rb +88 -0
- data/lib/importu/exceptions.rb +135 -4
- data/lib/importu/importer.rb +183 -96
- data/lib/importu/record.rb +138 -102
- data/lib/importu/sources/csv.rb +122 -0
- data/lib/importu/sources/json.rb +106 -0
- data/lib/importu/sources/ruby.rb +46 -0
- data/lib/importu/sources/xml.rb +133 -0
- data/lib/importu/sources.rb +13 -0
- data/lib/importu/summary.rb +277 -0
- data/lib/importu/version.rb +3 -1
- data/lib/importu.rb +45 -9
- data/spec/fixtures/books-duplicates/README.md +7 -0
- data/spec/fixtures/books-duplicates/infile.csv +7 -0
- data/spec/fixtures/books-duplicates/model.json +23 -0
- data/spec/fixtures/books-duplicates/summary.json +10 -0
- data/spec/fixtures/books-valid/README.md +13 -0
- data/spec/fixtures/books-valid/infile.csv +4 -0
- data/spec/fixtures/books-valid/infile.json +23 -0
- data/spec/fixtures/books-valid/infile.xml +21 -0
- data/spec/fixtures/books-valid/model.json +23 -0
- data/spec/fixtures/books-valid/record.json +26 -0
- data/spec/fixtures/books-valid/summary.json +8 -0
- data/spec/fixtures/source-empty-file/infile.csv +0 -0
- data/spec/fixtures/source-empty-file/infile.json +0 -0
- data/spec/fixtures/source-empty-file/infile.xml +0 -0
- data/spec/fixtures/source-empty-records/infile.csv +3 -0
- data/spec/fixtures/source-empty-records/infile.json +1 -0
- data/spec/fixtures/source-empty-records/infile.xml +6 -0
- data/spec/fixtures/source-malformed/infile.csv +1 -0
- data/spec/fixtures/source-malformed/infile.json +1 -0
- data/spec/fixtures/source-malformed/infile.xml +3 -0
- data/spec/fixtures/source-no-records/infile.csv +1 -0
- data/spec/fixtures/source-no-records/infile.json +1 -0
- data/spec/fixtures/source-no-records/infile.xml +3 -0
- data/spec/lib/importu/backends/active_record_spec.rb +150 -0
- data/spec/lib/importu/backends/middleware/duplicate_manager_proxy_spec.rb +70 -0
- data/spec/lib/importu/backends/middleware/enforce_allowed_actions_spec.rb +70 -0
- data/spec/lib/importu/backends_spec.rb +170 -0
- data/spec/lib/importu/converters_spec.rb +184 -141
- data/spec/lib/importu/definition_spec.rb +248 -0
- data/spec/lib/importu/duplicate_manager_spec.rb +92 -0
- data/spec/lib/importu/exceptions_spec.rb +69 -16
- data/spec/lib/importu/import_context_spec.rb +199 -0
- data/spec/lib/importu/importer_spec.rb +95 -0
- data/spec/lib/importu/integration_spec.rb +221 -0
- data/spec/lib/importu/record_spec.rb +130 -80
- data/spec/lib/importu/sources/csv_spec.rb +29 -0
- data/spec/lib/importu/sources/importer_source_examples.rb +175 -0
- data/spec/lib/importu/sources/json_spec.rb +29 -0
- data/spec/lib/importu/sources/ruby_spec.rb +102 -0
- data/spec/lib/importu/sources/xml_spec.rb +70 -0
- data/spec/lib/importu/summary_spec.rb +186 -0
- data/spec/spec_helper.rb +91 -7
- data/spec/support/active_record.rb +20 -0
- data/spec/support/book_importer.rb +31 -0
- data/spec/support/dummy_backend.rb +50 -0
- data/spec/support/fixtures_helper.rb +43 -0
- data/spec/support/matchers/delegate_matcher.rb +14 -8
- metadata +173 -100
- data/lib/importu/core_ext/array/deep_freeze.rb +0 -7
- data/lib/importu/core_ext/deep_freeze.rb +0 -3
- data/lib/importu/core_ext/hash/deep_freeze.rb +0 -7
- data/lib/importu/core_ext/object/deep_freeze.rb +0 -6
- data/lib/importu/core_ext.rb +0 -3
- data/lib/importu/dsl.rb +0 -127
- data/lib/importu/importer/csv.rb +0 -52
- data/lib/importu/importer/json.rb +0 -45
- data/lib/importu/importer/xml.rb +0 -55
- data/spec/factories/importer.rb +0 -12
- data/spec/factories/importer_record.rb +0 -13
- data/spec/factories/json_importer.rb +0 -14
- data/spec/factories/xml_importer.rb +0 -12
- data/spec/lib/importu/dsl_spec.rb +0 -26
- data/spec/lib/importu/importer/json_spec.rb +0 -37
- data/spec/lib/importu/importer/xml_spec.rb +0 -14
|
@@ -1,274 +1,317 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "spec_helper"
|
|
2
3
|
|
|
3
|
-
require
|
|
4
|
+
require "importu/converter_context"
|
|
5
|
+
require "importu/definition"
|
|
4
6
|
|
|
5
|
-
describe Importu
|
|
6
|
-
subject(:
|
|
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
|
-
|
|
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 {
|
|
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 ":
|
|
26
|
-
it "returns nil if raw
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
33
|
-
|
|
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
|
-
|
|
38
|
-
|
|
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
|
-
|
|
44
|
-
|
|
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
|
|
50
|
-
|
|
51
|
-
|
|
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 "
|
|
55
|
-
|
|
56
|
-
|
|
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 "
|
|
60
|
-
|
|
61
|
-
|
|
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
|
|
68
|
-
|
|
69
|
-
|
|
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 "
|
|
73
|
-
|
|
74
|
-
|
|
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 "
|
|
78
|
-
|
|
79
|
-
|
|
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
|
|
83
|
-
|
|
84
|
-
expect {
|
|
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
|
|
90
|
-
|
|
91
|
-
|
|
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
|
|
95
|
-
|
|
96
|
-
|
|
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
|
|
100
|
-
|
|
101
|
-
|
|
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
|
-
|
|
106
|
-
|
|
129
|
+
definition.converter(:trimmed) {|*| "77" }
|
|
130
|
+
expect(context.float(:field1)).to eq 77.0
|
|
107
131
|
end
|
|
108
132
|
|
|
109
|
-
it "raises an
|
|
110
|
-
|
|
111
|
-
expect {
|
|
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
|
|
117
|
-
|
|
118
|
-
|
|
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
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
134
|
-
|
|
157
|
+
definition.converter(:trimmed) {|*| "77" }
|
|
158
|
+
expect(context.decimal(:field1)).to eq BigDecimal("77.0")
|
|
135
159
|
end
|
|
136
160
|
|
|
137
|
-
it "raises an
|
|
138
|
-
|
|
139
|
-
expect {
|
|
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
|
|
145
|
-
|
|
146
|
-
|
|
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
|
|
150
|
-
|
|
151
|
-
|
|
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
|
|
155
|
-
|
|
156
|
-
|
|
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
|
|
160
|
-
|
|
161
|
-
|
|
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
|
|
165
|
-
|
|
166
|
-
|
|
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
|
|
170
|
-
|
|
171
|
-
|
|
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
|
|
175
|
-
|
|
176
|
-
|
|
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
|
|
180
|
-
|
|
181
|
-
|
|
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 "
|
|
185
|
-
|
|
186
|
-
|
|
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 "
|
|
190
|
-
|
|
191
|
-
|
|
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
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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
|
|
202
|
-
|
|
203
|
-
|
|
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
|
-
|
|
210
|
-
|
|
252
|
+
definition.converter(:trimmed) {|*| "03/10/2012" }
|
|
253
|
+
expect(context.date(:field1)).to eq expected
|
|
211
254
|
end
|
|
212
255
|
|
|
213
|
-
it "raises an
|
|
214
|
-
|
|
215
|
-
expect {
|
|
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
|
-
|
|
223
|
-
|
|
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
|
|
227
|
-
|
|
228
|
-
expect {
|
|
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
|
|
232
|
-
|
|
233
|
-
expect {
|
|
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
|
|
240
|
-
|
|
241
|
-
|
|
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 =
|
|
247
|
-
|
|
248
|
-
|
|
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
|
|
252
|
-
|
|
253
|
-
expect {
|
|
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 =
|
|
260
|
-
|
|
261
|
-
|
|
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
|
|
265
|
-
|
|
266
|
-
expect {
|
|
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
|
|
270
|
-
|
|
271
|
-
expect {
|
|
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
|