amount_field_rails3 3.0.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.
@@ -0,0 +1,7 @@
1
+ class TestProduct < ActiveRecord::Base
2
+
3
+ validates_amount_format_of :price
4
+ validates_numericality_of :price, :message => "not numericality"
5
+ validates_presence_of :price
6
+
7
+ end
@@ -0,0 +1,63 @@
1
+ ENV["RAILS_ENV"] = "test"
2
+ require File.expand_path(File.dirname(__FILE__) + "/../../../../config/environment")
3
+ require 'rails/test_help'
4
+
5
+ require File.join(File.dirname(__FILE__), '../init')
6
+ Dir[File.join(File.dirname(__FILE__), 'models/*.rb')].each { |f| require f }
7
+
8
+ ActiveRecord::Base.establish_connection({
9
+ :adapter => 'mysql2',
10
+ :database => 'gem_amount_field_test',
11
+ :host => 'localhost',
12
+ :username => 'root',
13
+ :password => ''
14
+ })
15
+
16
+ ActiveRecord::Migration.verbose = false
17
+ ActiveRecord::Schema.define(:version => 1) do
18
+ create_table "test_products", :force => true do |t|
19
+ t.column "name", :string
20
+ t.column "price", :decimal, :precision => 12, :scale => 2
21
+ t.column "stock_price", :decimal, :precision => 12, :scale => 2
22
+ t.column "float_price", :float
23
+ end
24
+ end
25
+
26
+ class ActiveSupport::TestCase
27
+
28
+ def with_configuration(config)
29
+ begin
30
+ orig_config = AmountField::ActiveRecord::Validations.configuration
31
+ AmountField::ActiveRecord::Validations.configuration = config
32
+ yield
33
+ ensure
34
+ AmountField::ActiveRecord::Validations.configuration = orig_config
35
+ end
36
+ end
37
+
38
+ def with_locale(locale)
39
+ begin
40
+ orig_locale = I18n.locale
41
+ I18n.locale = locale
42
+ yield
43
+ ensure
44
+ I18n.locale = orig_locale
45
+ end
46
+ end
47
+
48
+ def assert_valid_formats(formats, test_clazz)
49
+ formats.each do |format, expected_value|
50
+ product = test_clazz.new(:amount_field_price => format)
51
+ assert product.valid?, "expected '#{format}' to be valid (#{product.errors.full_messages.inspect})"
52
+ assert_in_delta expected_value, product.price.to_f, 0.001
53
+ end
54
+ end
55
+
56
+ def assert_invalid_formats(formats, test_clazz)
57
+ formats.each do |format|
58
+ product = test_clazz.new(:amount_field_price => format)
59
+ assert !product.valid?, "expected '#{format}' to be invalid (#{product.errors.full_messages.inspect})"
60
+ end
61
+ end
62
+
63
+ end
@@ -0,0 +1,389 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'test_helper'
4
+
5
+ class ValidationsTest < ActiveSupport::TestCase
6
+
7
+ # allows stuff like de_test_values.with_minus.to_us
8
+ class ::Hash
9
+ def with_plus
10
+ self.inject({}) { |h, (k, v)| h["+#{k}"] = v; h }
11
+ end
12
+
13
+ def with_minus
14
+ self.inject({}) { |h, (k, v)| h["-#{k}"] = (v*-1.0); h }
15
+ end
16
+
17
+ def to_us
18
+ self.inject({}) { |h, (k, v)| h[k.sub(',', ';').gsub('.', ',').sub(';', '.')] = v; h }
19
+ end
20
+ end
21
+
22
+ def de_test_values
23
+ {
24
+ '0' => 0.0,
25
+ ',0' => 0.0,
26
+ ',00' => 0.0,
27
+ '1' => 1.0,
28
+ '1,' => 1.0,
29
+ '1,0' => 1.0,
30
+ '1,00' => 1.0,
31
+ '12' => 12.0,
32
+ '12,' => 12.0,
33
+ '12,0' => 12.0,
34
+ '12,00' => 12.0,
35
+ '123' => 123.0,
36
+ '123,' => 123.0,
37
+ '123,0' => 123.0,
38
+ '123,00' => 123.0,
39
+ '1234' => 1234.0,
40
+ '1234,' => 1234.0,
41
+ '1234,0' => 1234.0,
42
+ '1234,00' => 1234.0,
43
+ '1234,56' => 1234.56,
44
+ '1.234,56' => 1234.56,
45
+ '12345,67' => 12345.67,
46
+ '12.345,67' => 12345.67,
47
+ '123456,78' => 123456.78,
48
+ '123.456,78' => 123456.78,
49
+ '1234567,89' => 1234567.89,
50
+ '1.234.567,89' => 1234567.89
51
+ }
52
+ end
53
+
54
+
55
+ test "orignal setter accept ruby value if set via new" do
56
+ product = TestProduct.new(:price => 12.34)
57
+ assert_in_delta 12.34, product.price, 0.001
58
+ end
59
+
60
+ test "orignal setter accept ruby value" do
61
+ product = TestProduct.new
62
+ product.price = 12.34
63
+ assert_in_delta 12.34, product.price, 0.001
64
+ end
65
+
66
+ test "price is valid for loaded record" do
67
+ product = TestProduct.create!(:price => 12.34)
68
+ assert_in_delta 12.34, product.price, 0.001
69
+ end
70
+
71
+ test "special setter does not accept a decimal value" do
72
+ # Special case: The value is assign via the special setter as a decimal not as a string.
73
+ # The validation fails because the value is not of the german format '1234,56' and therefore
74
+ # the price should be 0.0 (not assiged) but is 1234.56
75
+ product = TestProduct.new
76
+ product.amount_field_price = 1234.56
77
+ assert !product.valid?, "expect 1234.56 to be invalid (#{product.errors.full_messages.inspect})"
78
+ assert_in_delta 1234.56, product.price.to_f, 0.001
79
+ end
80
+
81
+ test "special setter accept german format value" do
82
+ with_locale('de') do
83
+ product = TestProduct.new
84
+ product.amount_field_price = '1.234,56'
85
+ assert product.valid?, "expect '1.234,56' to be valid (#{product.errors.full_messages.inspect})"
86
+ assert_in_delta 1234.56, product.price, 0.001
87
+ end
88
+ end
89
+
90
+ test "accept valid german formats" do
91
+ class TestProductValidGermanFormat < ActiveRecord::Base
92
+ set_table_name 'test_products'
93
+ validates_amount_format_of :price
94
+ end
95
+
96
+ with_locale('de') do
97
+ assert_valid_formats(de_test_values, TestProductValidGermanFormat)
98
+ assert_valid_formats(de_test_values.with_minus, TestProductValidGermanFormat) # -value
99
+ assert_valid_formats(de_test_values.with_plus, TestProductValidGermanFormat) # +value
100
+ end
101
+ end
102
+
103
+ test "accept valid us formats" do
104
+ with_configuration({ :separator => '.', :delimiter => ',', :precision => 2 }) do
105
+ class TestProductValidUsFormat < ActiveRecord::Base
106
+ set_table_name 'test_products'
107
+ validates_amount_format_of :price
108
+ end
109
+
110
+ with_locale('de') do
111
+ assert_valid_formats(de_test_values.to_us, TestProductValidUsFormat)
112
+ assert_valid_formats(de_test_values.to_us.with_minus, TestProductValidUsFormat) # -value
113
+ assert_valid_formats(de_test_values.to_us.with_plus, TestProductValidUsFormat) # +value
114
+ end
115
+ end
116
+ end
117
+
118
+ test "dont accept invalid formats" do
119
+ class TestProductInValidGermanFormat < ActiveRecord::Base
120
+ set_table_name 'test_products'
121
+ validates_amount_format_of :price
122
+ end
123
+
124
+ with_locale('de') do
125
+ assert_invalid_formats(['1234.567.890,12', '123.4567.890,12', '1,2.34', '1,2,3', '1.23,45',
126
+ '1.23.45,6', '2,1x', '2,x', 'x2', '++1,23', '+-1,23', '--1,23', '-+1,23', nil],
127
+ TestProductInValidGermanFormat)
128
+ end
129
+ end
130
+
131
+ test "accept only values of format with the defined configuration" do
132
+ class TestProductConfiguration < ActiveRecord::Base
133
+ set_table_name 'test_products'
134
+ validates_amount_format_of :price, :separator => '.', :delimiter => ','
135
+ validates_amount_format_of :stock_price, :separator => ',', :delimiter => '.'
136
+ end
137
+
138
+ product = TestProductConfiguration.new(:amount_field_price => '1,234,567.89',
139
+ :amount_field_stock_price => '1.234.567,89')
140
+ assert product.valid?
141
+ assert_in_delta 1234567.89, product.price, 0.001
142
+ assert_in_delta 1234567.89, product.stock_price, 0.001
143
+ end
144
+
145
+ test "accept only values of format with the defined precision" do
146
+ class TestProductPrecision < ActiveRecord::Base
147
+ set_table_name 'test_products'
148
+ validates_amount_format_of :price # default 2
149
+ validates_amount_format_of :stock_price, :precision => 1
150
+ end
151
+
152
+ product = TestProductPrecision.new(:amount_field_price => '1,234.56',
153
+ :amount_field_stock_price => '1,234.5')
154
+ assert product.valid?
155
+ assert_in_delta 1234.56, product.price, 0.001
156
+ assert_in_delta 1234.50, product.stock_price, 0.001
157
+ end
158
+
159
+ test "accept only integer if precision is 0 or nil" do
160
+ class TestProductInteger < ActiveRecord::Base
161
+ set_table_name 'test_products'
162
+ validates_amount_format_of :price, :precision => 0, :separator => nil
163
+ end
164
+
165
+ with_locale('de') do
166
+ assert_valid_formats({'0' => 0.0, '1' => 1.0, '12' => 12.0, '123' => 123.0, '1.234' => 1234.0}, TestProductInteger)
167
+ assert_invalid_formats(['1,', '1,0', '1,2', '1,23'], TestProductInteger)
168
+ end
169
+ end
170
+
171
+ test "accept only values with no delimiter" do
172
+ class TestProductNoDelimiter < ActiveRecord::Base
173
+ set_table_name 'test_products'
174
+ validates_amount_format_of :price, :separator => '.', :delimiter => nil
175
+ end
176
+
177
+ with_locale('de') do
178
+ assert_valid_formats({'.00' => 0.0, '1.00' => 1.0, '12.00' => 12.0, '123.00' => 123.0, '1234.00' => 1234.0, '12345.00' => 12345.0, '123456.00' => 123456.0}, TestProductNoDelimiter)
179
+ assert_invalid_formats(['1,234.56', '123,456.00'], TestProductNoDelimiter)
180
+ end
181
+ end
182
+
183
+ test "use english default message" do
184
+ class TestProductEnglishMessage < ActiveRecord::Base
185
+ set_table_name 'test_products'
186
+ validates_amount_format_of :price
187
+ end
188
+
189
+ with_locale('en') do
190
+ product = TestProductEnglishMessage.new(:amount_field_price => 'x')
191
+ assert !product.valid?
192
+ assert_equal "'x' is not a valid amount format (d,ddd.dd)", product.errors[:price].first
193
+ end
194
+ end
195
+
196
+ test "use german default message" do
197
+ class TestProductGermanMessage < ActiveRecord::Base
198
+ set_table_name 'test_products'
199
+ validates_amount_format_of :price
200
+ end
201
+
202
+ with_locale('de') do
203
+ product = TestProductGermanMessage.new(:amount_field_price => 'x')
204
+ assert !product.valid?
205
+ assert_equal "'x' ist ein ungültiges Format (d.ddd,dd)", product.errors[:price].first
206
+ end
207
+ end
208
+
209
+ test "validates_amount_format_of use given message" do
210
+ class TestProductGivenMessage < ActiveRecord::Base
211
+ set_table_name 'test_products'
212
+ validates_amount_format_of :price, :message => "special message %{value}"
213
+ end
214
+
215
+ product = TestProductGivenMessage.new(:amount_field_price => 'x')
216
+ assert !product.valid?
217
+ assert_equal "special message x", product.errors[:price].first
218
+ end
219
+
220
+ test "matches if precision is right" do
221
+ class TestProductValidPrecision < ActiveRecord::Base
222
+ set_table_name 'test_products'
223
+ validates_amount_format_of :price, :precision => 1
224
+ end
225
+
226
+ with_locale('de') do
227
+ [ ',0', '1,0', '1,0'].each do |value|
228
+ assert TestProductValidPrecision.new(:amount_field_price => value).valid?, "expected '#{value}' to be valid"
229
+ end
230
+ end
231
+ end
232
+
233
+ test "explicit definition overwrites default configuration" do
234
+ with_configuration(default = { :separator => '_', :delimiter => ';', :precision => 2 }) do
235
+ class TestProductOverwriteConfiguration < ActiveRecord::Base
236
+ set_table_name 'test_products'
237
+ validates_amount_format_of :price, :separator => '/', :delimiter => '@'
238
+ validates_amount_format_of :stock_price
239
+ end
240
+
241
+ [ '/00', '1/00', '12/00', '123/00', '1234/00', '1@234/00', '12@345/00', '123@456/00', '1@234@567/00'
242
+ ].each do |param|
243
+ param2 = param.gsub('/', '_').gsub('@', ';')
244
+ product = TestProductOverwriteConfiguration.new(:amount_field_price => param, :amount_field_stock_price => param2)
245
+ assert product.valid?, product.errors.full_messages.inspect
246
+ assert_in_delta Float(param.gsub('@', '').sub('/', '.')), product.price, 0.001
247
+ end
248
+ end
249
+ end
250
+
251
+ test "nil or blank value is not valid by default" do
252
+ class TestProductNilOrBlankNotValidByDefault < ActiveRecord::Base
253
+ set_table_name 'test_products'
254
+ validates_amount_format_of :price
255
+ validates_amount_format_of :stock_price
256
+ end
257
+
258
+ product = TestProductNilOrBlankNotValidByDefault.new(:amount_field_price => nil, :amount_field_stock_price => "")
259
+ assert !product.valid?
260
+ assert_match /format/, product.errors[:price].first
261
+ assert_match /format/, product.errors[:stock_price].first
262
+ end
263
+
264
+ test "nil or blank value is valid if option :allow_nil or :allow_blank is set to true" do
265
+ class TestProductNilOrBlankValidIfAllowed < ActiveRecord::Base
266
+ set_table_name 'test_products'
267
+ validates_amount_format_of :price, :allow_nil => true
268
+ validates_amount_format_of :stock_price, :allow_blank => true
269
+ end
270
+
271
+ product = TestProductNilOrBlankValidIfAllowed.new(:amount_field_price => nil, :amount_field_stock_price => "")
272
+ assert product.valid?, product.errors.full_messages.inspect
273
+ assert_nil product.price
274
+ end
275
+
276
+ test "valid format example returns string depending on given configuration" do
277
+ assert_equal "d.ddd,dd", TestProduct.send(:valid_format_example, { :precision => 2, :delimiter => '.', :separator => ',' })
278
+ assert_equal "d,ddd.dd", TestProduct.send(:valid_format_example, { :precision => 2, :delimiter => ',', :separator => '.' })
279
+ assert_equal "dddd,dd", TestProduct.send(:valid_format_example, { :precision => 2, :delimiter => nil, :separator => ',' })
280
+ assert_equal "dddd,d", TestProduct.send(:valid_format_example, { :precision => 1, :delimiter => nil, :separator => ',' })
281
+ assert_equal "dddd", TestProduct.send(:valid_format_example, { :precision => nil, :delimiter => nil, :separator => nil })
282
+ assert_equal "d.ddd", TestProduct.send(:valid_format_example, { :precision => nil, :delimiter => '.', :separator => nil })
283
+ end
284
+
285
+ test "default prefix is use for special setter name" do
286
+ class TestProductDefaultPrefix < ActiveRecord::Base
287
+ set_table_name 'test_products'
288
+ validates_amount_format_of :price
289
+ end
290
+
291
+ assert TestProductDefaultPrefix.new.respond_to?("#{AmountField::Configuration.prefix}_price=")
292
+ end
293
+
294
+ test "configured prefix is use for special setter name" do
295
+ AmountField::Configuration.prefix = 'my_prefix'
296
+ class TestProductConfigPrefix < ActiveRecord::Base
297
+ set_table_name 'test_products'
298
+ validates_amount_format_of :price
299
+ end
300
+
301
+ assert TestProductConfigPrefix.new.respond_to?('my_prefix_price=')
302
+ AmountField::Configuration.prefix = 'amount_field'
303
+ end
304
+
305
+ test "works with float attribute" do
306
+ class TestProductFloatAttribute < ActiveRecord::Base
307
+ set_table_name 'test_products'
308
+ validates_amount_format_of :float_price
309
+ end
310
+ with_locale('de') do
311
+ assert TestProductFloatAttribute.new(:amount_field_float_price => '1.234,00').valid?
312
+ assert TestProductFloatAttribute.new(:amount_field_float_price => '1234').valid?
313
+ end
314
+ end
315
+
316
+ test "allow separator and delimiter to be optional for german format" do
317
+ class TestProductOptionalSeparatorAndDelimiterForGerman < ActiveRecord::Base
318
+ set_table_name 'test_products'
319
+ validates_amount_format_of :float_price
320
+ end
321
+ with_locale('de') do
322
+ ['1.234,00', '1234,00', '1.234', '1234'].each do |v|
323
+ p = TestProductOptionalSeparatorAndDelimiterForGerman.new(:amount_field_float_price => v)
324
+ assert p.valid?, p.errors.full_messages.inspect
325
+ end
326
+ end
327
+ end
328
+
329
+ test "allow separator and delimiter to be optional for us format" do
330
+ class TestProductOptionalSeparatorAndDelimiterForUs < ActiveRecord::Base
331
+ set_table_name 'test_products'
332
+ validates_amount_format_of :float_price
333
+ end
334
+ with_locale('en') do
335
+ ['1,234.00', '1234.00', '1,234', '1234'].each do |v|
336
+ p = TestProductOptionalSeparatorAndDelimiterForUs.new(:amount_field_float_price => v)
337
+ assert p.valid?, p.errors.full_messages.inspect
338
+ end
339
+ end
340
+ end
341
+
342
+ test "an invalid value is still invalid after multiple validation" do
343
+ class TestProductValidValid < ActiveRecord::Base
344
+ set_table_name 'test_products'
345
+ validates_amount_format_of :price
346
+ end
347
+ p = TestProductValidValid.new(:amount_field_price => "x")
348
+ assert !p.valid?
349
+ assert !p.valid?
350
+ end
351
+
352
+ test "a value is not invalid after multiple validation" do
353
+ with_locale('de') do
354
+ class TestProductValidValid < ActiveRecord::Base
355
+ set_table_name 'test_products'
356
+ validates_amount_format_of :price
357
+ end
358
+ p = TestProductValidValid.new(:amount_field_price => "1.234,56")
359
+ assert p.valid?
360
+ assert p.valid?
361
+ end
362
+ end
363
+
364
+ test "calling create with an invalid value does not save the model" do
365
+ class TestProductViaCreate< ActiveRecord::Base
366
+ set_table_name 'test_products'
367
+ validates_amount_format_of :price
368
+ end
369
+ assert_no_difference 'TestProduct.count' do
370
+ p = TestProductViaCreate.create(:amount_field_price => "x")
371
+ assert p.new_record?
372
+ assert !p.valid?
373
+ end
374
+ end
375
+
376
+ test "another validation macro validates the converted value" do
377
+ class TestProductWithOtherValidationMacros < ActiveRecord::Base
378
+ set_table_name 'test_products'
379
+ validates_amount_format_of :price
380
+ validates_numericality_of :price
381
+ end
382
+ p = TestProductWithOtherValidationMacros.new(:amount_field_price => "x")
383
+ assert !p.valid?
384
+ assert p.errors.full_messages.include?("Price 'x' is not a valid amount format (d,ddd.dd)")
385
+ assert p.errors.full_messages.include?("Price is not a number")
386
+ end
387
+
388
+ end
389
+