money-rails 1.15.0 → 2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -6
- data/LICENSE +1 -1
- data/README.md +121 -102
- data/config/locales/money.pt.yml +4 -0
- data/lib/money-rails/active_job/money_serializer.rb +19 -0
- data/lib/money-rails/active_record/monetizable.rb +17 -7
- data/lib/money-rails/configuration.rb +2 -2
- data/lib/money-rails/helpers/action_view_extension.rb +1 -1
- data/lib/money-rails/hooks.rb +11 -0
- data/lib/money-rails/version.rb +3 -1
- data/money-rails.gemspec +13 -26
- metadata +16 -225
- data/Rakefile +0 -84
- data/spec/active_record/migration_extensions/schema_statements_spec.rb +0 -101
- data/spec/active_record/migration_extensions/table_spec.rb +0 -104
- data/spec/active_record/monetizable_spec.rb +0 -1062
- data/spec/configuration_spec.rb +0 -147
- data/spec/dummy/README.rdoc +0 -261
- data/spec/dummy/Rakefile +0 -7
- data/spec/dummy/app/assets/config/manifest.js +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +0 -15
- data/spec/dummy/app/assets/stylesheets/application.css +0 -13
- data/spec/dummy/app/controllers/application_controller.rb +0 -3
- data/spec/dummy/app/helpers/application_helper.rb +0 -2
- data/spec/dummy/app/models/dummy_product.rb +0 -8
- data/spec/dummy/app/models/priceable.rb +0 -8
- data/spec/dummy/app/models/product.rb +0 -59
- data/spec/dummy/app/models/service.rb +0 -5
- data/spec/dummy/app/models/transaction.rb +0 -13
- data/spec/dummy/app/views/layouts/application.html.erb +0 -14
- data/spec/dummy/config/application.rb +0 -66
- data/spec/dummy/config/boot.rb +0 -13
- data/spec/dummy/config/database.yml +0 -25
- data/spec/dummy/config/environment.rb +0 -5
- data/spec/dummy/config/environments/development.rb +0 -30
- data/spec/dummy/config/environments/production.rb +0 -69
- data/spec/dummy/config/environments/test.rb +0 -36
- data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/spec/dummy/config/initializers/inflections.rb +0 -15
- data/spec/dummy/config/initializers/mime_types.rb +0 -5
- data/spec/dummy/config/initializers/money.rb +0 -31
- data/spec/dummy/config/initializers/secret_token.rb +0 -7
- data/spec/dummy/config/initializers/session_store.rb +0 -8
- data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
- data/spec/dummy/config/locales/en-GB.yml +0 -11
- data/spec/dummy/config/locales/en-US.yml +0 -5
- data/spec/dummy/config/locales/en.yml +0 -5
- data/spec/dummy/config/locales/it.yml +0 -11
- data/spec/dummy/config/mongoid.yml +0 -29
- data/spec/dummy/config/routes.rb +0 -3
- data/spec/dummy/config.ru +0 -4
- data/spec/dummy/db/migrate/20120331190108_create_products.rb +0 -10
- data/spec/dummy/db/migrate/20120402080348_add_bonus_cents_to_product.rb +0 -6
- data/spec/dummy/db/migrate/20120524052716_create_services.rb +0 -10
- data/spec/dummy/db/migrate/20120528181002_create_transactions.rb +0 -11
- data/spec/dummy/db/migrate/20120528210103_create_dummy_products.rb +0 -10
- data/spec/dummy/db/migrate/20120607210247_add_column_that_allows_nil.rb +0 -5
- data/spec/dummy/db/migrate/20120712202655_add_sale_price_cents_to_product.rb +0 -7
- data/spec/dummy/db/migrate/20130124023419_add_price_in_a_range_cents_to_products.rb +0 -5
- data/spec/dummy/db/migrate/20140110194016_add_validates_method_amount_cents_to_products.rb +0 -5
- data/spec/dummy/db/migrate/20141005075025_add_aliased_attr_to_products.rb +0 -5
- data/spec/dummy/db/migrate/20150107061030_add_delivery_fee_cents_and_restock_fee_cents_to_product.rb +0 -6
- data/spec/dummy/db/migrate/20150126231442_add_reduced_price_to_products.rb +0 -6
- data/spec/dummy/db/migrate/20150213234410_add_special_price_to_products.rb +0 -5
- data/spec/dummy/db/migrate/20150217222612_add_lambda_price_to_products.rb +0 -5
- data/spec/dummy/db/migrate/20150303222230_add_skip_validation_price_cents_to_products.rb +0 -5
- data/spec/dummy/db/migrate/20151026220420_add_optional_amount_to_transactions.rb +0 -5
- data/spec/dummy/db/schema.rb +0 -59
- data/spec/dummy/db/structure.sql +0 -21
- data/spec/dummy/public/404.html +0 -26
- data/spec/dummy/public/422.html +0 -26
- data/spec/dummy/public/500.html +0 -25
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +0 -6
- data/spec/helpers/action_view_extension_spec.rb +0 -189
- data/spec/helpers/form_helper_spec.rb +0 -19
- data/spec/money_spec.rb +0 -40
- data/spec/mongoid/mongoid_spec.rb +0 -130
- data/spec/mongoid/two_spec.rb +0 -80
- data/spec/spec_helper.rb +0 -23
- data/spec/support/database_cleaner.rb +0 -14
- data/spec/test_helpers_spec.rb +0 -72
|
@@ -1,1062 +0,0 @@
|
|
|
1
|
-
# encoding: utf-8
|
|
2
|
-
|
|
3
|
-
require 'spec_helper'
|
|
4
|
-
|
|
5
|
-
class Sub < Product; end
|
|
6
|
-
|
|
7
|
-
if defined? ActiveRecord
|
|
8
|
-
describe MoneyRails::ActiveRecord::Monetizable do
|
|
9
|
-
let(:product) do
|
|
10
|
-
Product.create(price_cents: 3000, discount: 150,
|
|
11
|
-
bonus_cents: 200, optional_price: 100,
|
|
12
|
-
sale_price_amount: 1200, delivery_fee_cents: 100,
|
|
13
|
-
restock_fee_cents: 2000,
|
|
14
|
-
reduced_price_cents: 1500, reduced_price_currency: :lvl,
|
|
15
|
-
lambda_price_cents: 4000)
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
describe ".monetize" do
|
|
19
|
-
let(:service) do
|
|
20
|
-
Service.create(charge_cents: 2000, discount_cents: 120)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def update_product(*attributes)
|
|
24
|
-
if defined?(::ActiveRecord::VERSION) && ::ActiveRecord::VERSION::MAJOR >= 5
|
|
25
|
-
product.update(*attributes)
|
|
26
|
-
else
|
|
27
|
-
product.update_attributes(*attributes)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
context ".monetized_attributes" do
|
|
32
|
-
|
|
33
|
-
class InheritedMonetizeProduct < Product
|
|
34
|
-
monetize :special_price_cents
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
it "should be inherited by subclasses" do
|
|
38
|
-
assert_monetized_attributes(Sub.monetized_attributes, Product.monetized_attributes)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
it "should be inherited by subclasses with new monetized attribute" do
|
|
42
|
-
assert_monetized_attributes(InheritedMonetizeProduct.monetized_attributes, Product.monetized_attributes.merge(special_price: "special_price_cents"))
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
def assert_monetized_attributes(monetized_attributes, expected_attributes)
|
|
46
|
-
expect(monetized_attributes).to include expected_attributes
|
|
47
|
-
expect(expected_attributes).to include monetized_attributes
|
|
48
|
-
expect(monetized_attributes.size).to eql expected_attributes.size
|
|
49
|
-
monetized_attributes.keys.each do |key|
|
|
50
|
-
expect(key.is_a? String).to be_truthy
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
it "attaches a Money object to model field" do
|
|
56
|
-
expect(product.price).to be_an_instance_of(Money)
|
|
57
|
-
expect(product.discount_value).to be_an_instance_of(Money)
|
|
58
|
-
expect(product.bonus).to be_an_instance_of(Money)
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
it "attaches Money objects to multiple model fields" do
|
|
62
|
-
expect(product.delivery_fee).to be_an_instance_of(Money)
|
|
63
|
-
expect(product.restock_fee).to be_an_instance_of(Money)
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
it "returns the expected money amount as a Money object" do
|
|
67
|
-
expect(product.price).to eq(Money.new(3000, "USD"))
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
it "assigns the correct value from a Money object" do
|
|
71
|
-
product.price = Money.new(3210, "USD")
|
|
72
|
-
expect(product.save).to be_truthy
|
|
73
|
-
expect(product.price_cents).to eq(3210)
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
it "assigns the correct value from a Money object using create" do
|
|
77
|
-
product = Product.create(price: Money.new(3210, "USD"), discount: 150,
|
|
78
|
-
bonus_cents: 200, optional_price: 100)
|
|
79
|
-
expect(product.valid?).to be_truthy
|
|
80
|
-
expect(product.price_cents).to eq(3210)
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
it "correctly updates from a Money object using update_attributes" do
|
|
84
|
-
expect(update_product(price: Money.new(215, "USD"))).to be_truthy
|
|
85
|
-
expect(product.price_cents).to eq(215)
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
it "assigns the correct value from params" do
|
|
89
|
-
params_clp = { amount: '20000', tax: '1000', currency: 'CLP' }
|
|
90
|
-
product = Transaction.create(params_clp)
|
|
91
|
-
expect(product.valid?).to be_truthy
|
|
92
|
-
expect(product.amount.currency.subunit_to_unit).to eq(1)
|
|
93
|
-
expect(product.amount_cents).to eq(20000)
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
# TODO: This is a slightly controversial example, btu it reflects the current behaviour
|
|
97
|
-
it "re-assigns cents amount when subunit/unit ratio changes preserving amount in units" do
|
|
98
|
-
transaction = Transaction.create(amount: '20000', tax: '1000', currency: 'USD')
|
|
99
|
-
|
|
100
|
-
expect(transaction.amount).to eq(Money.new(20000_00, 'USD'))
|
|
101
|
-
|
|
102
|
-
transaction.currency = 'CLP'
|
|
103
|
-
|
|
104
|
-
expect(transaction.amount).to eq(Money.new(20000, 'CLP'))
|
|
105
|
-
expect(transaction.amount_cents).to eq(20000)
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
it "raises an error if trying to create two attributes with the same name" do
|
|
109
|
-
expect do
|
|
110
|
-
class Product
|
|
111
|
-
monetize :discount, as: :price
|
|
112
|
-
end
|
|
113
|
-
end.to raise_error ArgumentError
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
it "raises an error if Money object has the same attribute name as the monetizable attribute" do
|
|
117
|
-
expect do
|
|
118
|
-
class AnotherProduct < Product
|
|
119
|
-
monetize :price_cents, as: :price_cents
|
|
120
|
-
end
|
|
121
|
-
end.to raise_error ArgumentError
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
it "raises an error when unable to infer attribute name" do
|
|
125
|
-
old_postfix = MoneyRails::Configuration.amount_column[:postfix]
|
|
126
|
-
MoneyRails::Configuration.amount_column[:postfix] = '_pennies'
|
|
127
|
-
|
|
128
|
-
expect do
|
|
129
|
-
class AnotherProduct < Product
|
|
130
|
-
monetize :price_cents
|
|
131
|
-
end
|
|
132
|
-
end.to raise_error ArgumentError
|
|
133
|
-
|
|
134
|
-
MoneyRails::Configuration.amount_column[:postfix] = old_postfix
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
it "allows subclass to redefine attribute with the same name" do
|
|
138
|
-
class SubProduct < Product
|
|
139
|
-
monetize :discount, as: :discount_price, with_currency: :gbp
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
sub_product = SubProduct.new(discount: 100)
|
|
143
|
-
|
|
144
|
-
expect(sub_product.discount_price).to be_an_instance_of(Money)
|
|
145
|
-
expect(sub_product.discount_price.currency.id).to equal :gbp
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
it "respects :as argument" do
|
|
149
|
-
expect(product.discount_value).to eq(Money.new(150, "USD"))
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
it "uses numericality validation" do
|
|
153
|
-
product.price_cents = "foo"
|
|
154
|
-
expect(product.save).to be_falsey
|
|
155
|
-
|
|
156
|
-
product.price_cents = 2000
|
|
157
|
-
expect(product.save).to be_truthy
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
it "skips numericality validation when disabled" do
|
|
161
|
-
product.accessor_price_cents = 'not_valid'
|
|
162
|
-
expect(product.save).to be_truthy
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
it "passes validation after updating fractional attribute which was previously invalid" do
|
|
166
|
-
product.price_in_a_range = -5
|
|
167
|
-
expect(product).not_to be_valid
|
|
168
|
-
product.price_in_a_range_cents = 500
|
|
169
|
-
expect(product).to be_valid
|
|
170
|
-
end
|
|
171
|
-
|
|
172
|
-
context "when MoneyRails.raise_error_on_money_parsing is true" do
|
|
173
|
-
before { MoneyRails.raise_error_on_money_parsing = true }
|
|
174
|
-
after { MoneyRails.raise_error_on_money_parsing = false }
|
|
175
|
-
|
|
176
|
-
it "raises exception when a String value with hyphen is assigned" do
|
|
177
|
-
expect { product.accessor_price = "10-235" }.to raise_error MoneyRails::Error
|
|
178
|
-
end
|
|
179
|
-
|
|
180
|
-
it "raises an exception if it can't change currency" do
|
|
181
|
-
expect {
|
|
182
|
-
Product.new.price = Money.new(10, "RUB")
|
|
183
|
-
}.to raise_error(MoneyRails::ActiveRecord::Monetizable::ReadOnlyCurrencyException, "Can't change readonly currency 'USD' to 'RUB' for field 'price'")
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
context "when MoneyRails.raise_error_on_money_parsing is false (default)" do
|
|
188
|
-
it "does not raise exception when a String value with hyphen is assigned" do
|
|
189
|
-
expect { product.accessor_price = "10-235" }.not_to raise_error
|
|
190
|
-
end
|
|
191
|
-
|
|
192
|
-
it "does not raise exception if it can't change currency" do
|
|
193
|
-
expect { Product.new.price = Money.new(10, "RUB") }.not_to raise_error
|
|
194
|
-
end
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
it "respects numericality validation when using update_attributes" do
|
|
198
|
-
expect(update_product(price_cents: "some text")).to be_falsey
|
|
199
|
-
expect(update_product(price_cents: 2000)).to be_truthy
|
|
200
|
-
end
|
|
201
|
-
|
|
202
|
-
it "uses numericality validation on money attribute" do
|
|
203
|
-
product.price = "some text"
|
|
204
|
-
expect(product.save).to be_falsey
|
|
205
|
-
|
|
206
|
-
product.price = Money.new(320, "USD")
|
|
207
|
-
expect(product.save).to be_truthy
|
|
208
|
-
|
|
209
|
-
product.sale_price = "12.34"
|
|
210
|
-
product.sale_price_currency_code = 'EUR'
|
|
211
|
-
expect(product.valid?).to be_truthy
|
|
212
|
-
end
|
|
213
|
-
|
|
214
|
-
it "separately skips price validations" do
|
|
215
|
-
product.skip_validation_price = 'hundred thousands'
|
|
216
|
-
expect(product.save).to be_truthy
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
it "separately skips subunit validations" do
|
|
220
|
-
product.skip_validation_price_cents = 'ten million'
|
|
221
|
-
expect(product.save).to be_truthy
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
it "shouldn't init empty key in errors" do
|
|
225
|
-
product.price = Money.new(320, "USD")
|
|
226
|
-
product.valid?
|
|
227
|
-
expect(product.errors.has_key?(:price)).to be_falsey
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
it "fails validation with the proper error message if money value is invalid decimal" do
|
|
231
|
-
product.price = "12.23.24"
|
|
232
|
-
expect(product.save).to be_falsey
|
|
233
|
-
expect(product.errors[:price].size).to eq(1)
|
|
234
|
-
expect(product.errors[:price].first).to match(/not a number/)
|
|
235
|
-
end
|
|
236
|
-
|
|
237
|
-
it "fails validation with the proper error message if money value is nothing but periods" do
|
|
238
|
-
product.price = "..."
|
|
239
|
-
expect(product.save).to be_falsey
|
|
240
|
-
expect(product.errors[:price].size).to eq(1)
|
|
241
|
-
expect(product.errors[:price].first).to match(/not a number/)
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
it "fails validation with the proper error message if money value has invalid thousands part" do
|
|
245
|
-
product.price = "12,23.24"
|
|
246
|
-
expect(product.save).to be_falsey
|
|
247
|
-
expect(product.errors[:price].size).to eq(1)
|
|
248
|
-
expect(product.errors[:price].first).to match(/has invalid format/)
|
|
249
|
-
expect(product.errors[:price].first).to match(/Got 12,23.24/)
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
it "fails validation with the proper error message if money value has thousand char after decimal mark" do
|
|
253
|
-
product.price = "1.234,56"
|
|
254
|
-
expect(product.save).to be_falsey
|
|
255
|
-
expect(product.errors[:price].size).to eq(1)
|
|
256
|
-
expect(product.errors[:price].first).to match(/has invalid format/)
|
|
257
|
-
expect(product.errors[:price].first).to match(/Got 1.234,56/)
|
|
258
|
-
end
|
|
259
|
-
|
|
260
|
-
it "allows an empty string as the thousands separator" do
|
|
261
|
-
begin
|
|
262
|
-
I18n.locale = 'en-US'
|
|
263
|
-
product.price = '10.00'
|
|
264
|
-
expect(product).to be_valid
|
|
265
|
-
ensure
|
|
266
|
-
I18n.locale = I18n.default_locale
|
|
267
|
-
end
|
|
268
|
-
end
|
|
269
|
-
|
|
270
|
-
it "passes validation if money value is a Float and the currency decimal mark is not period" do
|
|
271
|
-
# The corresponding String would be "12,34" euros
|
|
272
|
-
service.discount = 12.34
|
|
273
|
-
expect(service.save).to be_truthy
|
|
274
|
-
end
|
|
275
|
-
|
|
276
|
-
it "passes validation if money value is a Float" do
|
|
277
|
-
product.price = 12.34
|
|
278
|
-
expect(product.save).to be_truthy
|
|
279
|
-
end
|
|
280
|
-
|
|
281
|
-
it "passes validation if money value is an Integer" do
|
|
282
|
-
product.price = 12
|
|
283
|
-
expect(product.save).to be_truthy
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
it "fails validation with the proper error message using numericality validations" do
|
|
287
|
-
product.price_in_a_range = "-12"
|
|
288
|
-
expect(product.valid?).to be_falsey
|
|
289
|
-
expect(product.errors[:price_in_a_range].size).to eq(1)
|
|
290
|
-
expect(product.errors[:price_in_a_range].first).to match(/must be greater than zero and less than \$100/)
|
|
291
|
-
|
|
292
|
-
product.price_in_a_range = Money.new(-1200, "USD")
|
|
293
|
-
expect(product.valid?).to be_falsey
|
|
294
|
-
expect(product.errors[:price_in_a_range].size).to eq(1)
|
|
295
|
-
expect(product.errors[:price_in_a_range].first).to match(/must be greater than zero and less than \$100/)
|
|
296
|
-
|
|
297
|
-
product.price_in_a_range = "0"
|
|
298
|
-
expect(product.valid?).to be_falsey
|
|
299
|
-
expect(product.errors[:price_in_a_range].size).to eq(1)
|
|
300
|
-
expect(product.errors[:price_in_a_range].first).to match(/must be greater than zero and less than \$100/)
|
|
301
|
-
|
|
302
|
-
product.price_in_a_range = "12"
|
|
303
|
-
expect(product.valid?).to be_truthy
|
|
304
|
-
|
|
305
|
-
product.price_in_a_range = Money.new(1200, "USD")
|
|
306
|
-
expect(product.valid?).to be_truthy
|
|
307
|
-
|
|
308
|
-
product.price_in_a_range = "101"
|
|
309
|
-
expect(product.valid?).to be_falsey
|
|
310
|
-
expect(product.errors[:price_in_a_range].size).to eq(1)
|
|
311
|
-
expect(product.errors[:price_in_a_range].first).to match(/must be greater than zero and less than \$100/)
|
|
312
|
-
|
|
313
|
-
product.price_in_a_range = Money.new(10100, "USD")
|
|
314
|
-
expect(product.valid?).to be_falsey
|
|
315
|
-
expect(product.errors[:price_in_a_range].size).to eq(1)
|
|
316
|
-
expect(product.errors[:price_in_a_range].first).to match(/must be greater than zero and less than \$100/)
|
|
317
|
-
end
|
|
318
|
-
|
|
319
|
-
it "fails validation if linked attribute changed" do
|
|
320
|
-
product = Product.create(price: Money.new(3210, "USD"), discount: 150,
|
|
321
|
-
validates_method_amount: 100,
|
|
322
|
-
bonus_cents: 200, optional_price: 100)
|
|
323
|
-
expect(product.valid?).to be_truthy
|
|
324
|
-
product.optional_price = 50
|
|
325
|
-
expect(product.valid?).to be_falsey
|
|
326
|
-
end
|
|
327
|
-
|
|
328
|
-
it "fails validation with the proper error message using validates :money" do
|
|
329
|
-
product.validates_method_amount = "-12"
|
|
330
|
-
expect(product.valid?).to be_falsey
|
|
331
|
-
expect(product.errors[:validates_method_amount].size).to eq(1)
|
|
332
|
-
expect(product.errors[:validates_method_amount].first).to match(/must be greater than zero and less than \$100/)
|
|
333
|
-
|
|
334
|
-
product.validates_method_amount = Money.new(-1200, "USD")
|
|
335
|
-
expect(product.valid?).to be_falsey
|
|
336
|
-
expect(product.errors[:validates_method_amount].size).to eq(1)
|
|
337
|
-
expect(product.errors[:validates_method_amount].first).to match(/must be greater than zero and less than \$100/)
|
|
338
|
-
|
|
339
|
-
product.validates_method_amount = "0"
|
|
340
|
-
expect(product.valid?).to be_falsey
|
|
341
|
-
expect(product.errors[:validates_method_amount].size).to eq(1)
|
|
342
|
-
expect(product.errors[:validates_method_amount].first).to match(/must be greater than zero and less than \$100/)
|
|
343
|
-
|
|
344
|
-
product.validates_method_amount = "12"
|
|
345
|
-
expect(product.valid?).to be_truthy
|
|
346
|
-
|
|
347
|
-
product.validates_method_amount = Money.new(1200, "USD")
|
|
348
|
-
expect(product.valid?).to be_truthy
|
|
349
|
-
|
|
350
|
-
product.validates_method_amount = "101"
|
|
351
|
-
expect(product.valid?).to be_falsey
|
|
352
|
-
expect(product.errors[:validates_method_amount].size).to eq(1)
|
|
353
|
-
expect(product.errors[:validates_method_amount].first).to match(/must be greater than zero and less than \$100/)
|
|
354
|
-
|
|
355
|
-
product.validates_method_amount = Money.new(10100, "USD")
|
|
356
|
-
expect(product.valid?).to be_falsey
|
|
357
|
-
expect(product.errors[:validates_method_amount].size).to eq(1)
|
|
358
|
-
expect(product.errors[:validates_method_amount].first).to match(/must be greater than zero and less than \$100/)
|
|
359
|
-
end
|
|
360
|
-
|
|
361
|
-
it "fails validation with the proper error message on the cents field " do
|
|
362
|
-
product.price_in_a_range = "-12"
|
|
363
|
-
expect(product.valid?).to be_falsey
|
|
364
|
-
expect(product.errors[:price_in_a_range_cents].size).to eq(1)
|
|
365
|
-
expect(product.errors[:price_in_a_range_cents].first).to match(/greater than 0/)
|
|
366
|
-
|
|
367
|
-
product.price_in_a_range = "0"
|
|
368
|
-
expect(product.valid?).to be_falsey
|
|
369
|
-
expect(product.errors[:price_in_a_range_cents].size).to eq(1)
|
|
370
|
-
expect(product.errors[:price_in_a_range_cents].first).to match(/greater than 0/)
|
|
371
|
-
|
|
372
|
-
product.price_in_a_range = "12"
|
|
373
|
-
expect(product.valid?).to be_truthy
|
|
374
|
-
|
|
375
|
-
product.price_in_a_range = "101"
|
|
376
|
-
expect(product.valid?).to be_falsey
|
|
377
|
-
expect(product.errors[:price_in_a_range_cents].size).to eq(1)
|
|
378
|
-
expect(product.errors[:price_in_a_range_cents].first).to match(/less than or equal to 10000/)
|
|
379
|
-
end
|
|
380
|
-
|
|
381
|
-
it "fails validation when a non number string is given" do
|
|
382
|
-
product = Product.create(price_in_a_range: "asd")
|
|
383
|
-
expect(product.valid?).to be_falsey
|
|
384
|
-
expect(product.errors[:price_in_a_range].size).to eq(1)
|
|
385
|
-
expect(product.errors[:price_in_a_range].first).to match(/greater than zero/)
|
|
386
|
-
|
|
387
|
-
product = Product.create(price_in_a_range: "asd23")
|
|
388
|
-
expect(product.valid?).to be_falsey
|
|
389
|
-
expect(product.errors[:price_in_a_range].size).to eq(1)
|
|
390
|
-
expect(product.errors[:price_in_a_range].first).to match(/greater than zero/)
|
|
391
|
-
|
|
392
|
-
product = Product.create(price: "asd")
|
|
393
|
-
expect(product.valid?).to be_falsey
|
|
394
|
-
expect(product.errors[:price].size).to eq(1)
|
|
395
|
-
expect(product.errors[:price].first).to match(/is not a number/)
|
|
396
|
-
|
|
397
|
-
product = Product.create(price: "asd23")
|
|
398
|
-
expect(product.valid?).to be_falsey
|
|
399
|
-
expect(product.errors[:price].size).to eq(1)
|
|
400
|
-
expect(product.errors[:price].first).to match(/is not a number/)
|
|
401
|
-
end
|
|
402
|
-
|
|
403
|
-
it "passes validation when amount contains spaces (999 999.99)" do
|
|
404
|
-
product.price = "999 999.99"
|
|
405
|
-
expect(product).to be_valid
|
|
406
|
-
expect(product.price_cents).to eq(99999999)
|
|
407
|
-
end
|
|
408
|
-
|
|
409
|
-
it "passes validation when amount contains underscores (999_999.99)" do
|
|
410
|
-
product.price = "999_999.99"
|
|
411
|
-
expect(product).to be_valid
|
|
412
|
-
expect(product.price_cents).to eq(99999999)
|
|
413
|
-
end
|
|
414
|
-
|
|
415
|
-
it "passes validation if money value has correct format" do
|
|
416
|
-
product.price = "12,230.24"
|
|
417
|
-
expect(product.save).to be_truthy
|
|
418
|
-
end
|
|
419
|
-
|
|
420
|
-
it "passes validation if there is a whitespace between the currency symbol and amount" do
|
|
421
|
-
product.price = "$ 123,456.78"
|
|
422
|
-
expect(product.save).to be_truthy
|
|
423
|
-
end
|
|
424
|
-
|
|
425
|
-
it "respects numericality validation when using update_attributes on money attribute" do
|
|
426
|
-
expect(update_product(price: "some text")).to be_falsey
|
|
427
|
-
expect(update_product(price: Money.new(320, 'USD'))).to be_truthy
|
|
428
|
-
end
|
|
429
|
-
|
|
430
|
-
it "uses i18n currency format when validating" do
|
|
431
|
-
old_locale = I18n.locale
|
|
432
|
-
|
|
433
|
-
I18n.locale = "en-GB"
|
|
434
|
-
Money.default_currency = Money::Currency.find('EUR')
|
|
435
|
-
expect("12.00".to_money).to eq(Money.new(1200, :eur))
|
|
436
|
-
transaction = Transaction.new(amount: "12.00", tax: "13.00")
|
|
437
|
-
expect(transaction.amount_cents).to eq(1200)
|
|
438
|
-
expect(transaction.valid?).to be_truthy
|
|
439
|
-
|
|
440
|
-
# reset locale setting
|
|
441
|
-
I18n.locale = old_locale
|
|
442
|
-
end
|
|
443
|
-
|
|
444
|
-
it "doesn't allow nil by default" do
|
|
445
|
-
product.price_cents = nil
|
|
446
|
-
expect(product.save).to be_falsey
|
|
447
|
-
end
|
|
448
|
-
|
|
449
|
-
it "allows nil if optioned" do
|
|
450
|
-
product.optional_price = nil
|
|
451
|
-
expect(product.save).to be_truthy
|
|
452
|
-
expect(product.optional_price).to be_nil
|
|
453
|
-
end
|
|
454
|
-
|
|
455
|
-
it "doesn't raise exception if validation is used and nil is not allowed" do
|
|
456
|
-
expect { product.price = nil }.to_not raise_error
|
|
457
|
-
end
|
|
458
|
-
|
|
459
|
-
it "doesn't save nil values if validation is used and nil is not allowed" do
|
|
460
|
-
product.price = nil
|
|
461
|
-
product.save
|
|
462
|
-
expect(product.price_cents).not_to be_nil
|
|
463
|
-
end
|
|
464
|
-
|
|
465
|
-
it "resets money_before_type_cast attr every time a save operation occurs" do
|
|
466
|
-
v = Money.new(100, :usd)
|
|
467
|
-
product.price = v
|
|
468
|
-
expect(product.price_money_before_type_cast).to eq(v)
|
|
469
|
-
product.save
|
|
470
|
-
expect(product.price_money_before_type_cast).to be_nil
|
|
471
|
-
product.price = 10
|
|
472
|
-
expect(product.price_money_before_type_cast).to eq(10)
|
|
473
|
-
product.save
|
|
474
|
-
expect(product.price_money_before_type_cast).to be_nil
|
|
475
|
-
end
|
|
476
|
-
|
|
477
|
-
it "does not reset money_before_type_cast attr if save operation fails" do
|
|
478
|
-
product.bonus = ""
|
|
479
|
-
expect(product.bonus_money_before_type_cast).to eq("")
|
|
480
|
-
expect(product.save).to be_falsey
|
|
481
|
-
expect(product.bonus_money_before_type_cast).to eq("")
|
|
482
|
-
end
|
|
483
|
-
|
|
484
|
-
it "uses Money default currency if :with_currency has not been used" do
|
|
485
|
-
expect(service.discount.currency).to eq(Money::Currency.find(:eur))
|
|
486
|
-
end
|
|
487
|
-
|
|
488
|
-
it "overrides default currency with the currency registered for the model" do
|
|
489
|
-
expect(product.price.currency).to eq(Money::Currency.find(:usd))
|
|
490
|
-
end
|
|
491
|
-
|
|
492
|
-
it "overrides default currency with the value of :with_currency argument" do
|
|
493
|
-
expect(service.charge.currency).to eq(Money::Currency.find(:usd))
|
|
494
|
-
expect(product.bonus.currency).to eq(Money::Currency.find(:gbp))
|
|
495
|
-
end
|
|
496
|
-
|
|
497
|
-
it "uses currency postfix to determine attribute that stores currency" do
|
|
498
|
-
expect(product.reduced_price.currency).to eq(Money::Currency.find(:lvl))
|
|
499
|
-
end
|
|
500
|
-
|
|
501
|
-
it "correctly assigns Money objects to the attribute" do
|
|
502
|
-
product.price = Money.new(2500, :USD)
|
|
503
|
-
expect(product.save).to be_truthy
|
|
504
|
-
expect(product.price.cents).to eq(2500)
|
|
505
|
-
expect(product.price.currency.to_s).to eq("USD")
|
|
506
|
-
end
|
|
507
|
-
|
|
508
|
-
it "correctly assigns Fixnum objects to the attribute" do
|
|
509
|
-
product.price = 25
|
|
510
|
-
expect(product.save).to be_truthy
|
|
511
|
-
expect(product.price.cents).to eq(2500)
|
|
512
|
-
expect(product.price.currency.to_s).to eq("USD")
|
|
513
|
-
|
|
514
|
-
service.discount = 2
|
|
515
|
-
expect(service.save).to be_truthy
|
|
516
|
-
expect(service.discount.cents).to eq(200)
|
|
517
|
-
expect(service.discount.currency.to_s).to eq("EUR")
|
|
518
|
-
end
|
|
519
|
-
|
|
520
|
-
it "correctly assigns String objects to the attribute" do
|
|
521
|
-
product.price = "25"
|
|
522
|
-
expect(product.save).to be_truthy
|
|
523
|
-
expect(product.price.cents).to eq(2500)
|
|
524
|
-
expect(product.price.currency.to_s).to eq("USD")
|
|
525
|
-
|
|
526
|
-
service.discount = "2"
|
|
527
|
-
expect(service.save).to be_truthy
|
|
528
|
-
expect(service.discount.cents).to eq(200)
|
|
529
|
-
expect(service.discount.currency.to_s).to eq("EUR")
|
|
530
|
-
end
|
|
531
|
-
|
|
532
|
-
it "correctly assigns objects to a accessor attribute" do
|
|
533
|
-
product.accessor_price = 1.23
|
|
534
|
-
expect(product.save).to be_truthy
|
|
535
|
-
expect(product.accessor_price.cents).to eq(123)
|
|
536
|
-
expect(product.accessor_price_cents).to eq(123)
|
|
537
|
-
end
|
|
538
|
-
|
|
539
|
-
it "overrides default, model currency with the value of :with_currency in fixnum assignments" do
|
|
540
|
-
product.bonus = 25
|
|
541
|
-
expect(product.save).to be_truthy
|
|
542
|
-
expect(product.bonus.cents).to eq(2500)
|
|
543
|
-
expect(product.bonus.currency.to_s).to eq("GBP")
|
|
544
|
-
|
|
545
|
-
service.charge = 2
|
|
546
|
-
expect(service.save).to be_truthy
|
|
547
|
-
expect(service.charge.cents).to eq(200)
|
|
548
|
-
expect(service.charge.currency.to_s).to eq("USD")
|
|
549
|
-
end
|
|
550
|
-
|
|
551
|
-
it "overrides default, model currency with the value of :with_currency in string assignments" do
|
|
552
|
-
product.bonus = "25"
|
|
553
|
-
expect(product.save).to be_truthy
|
|
554
|
-
expect(product.bonus.cents).to eq(2500)
|
|
555
|
-
expect(product.bonus.currency.to_s).to eq("GBP")
|
|
556
|
-
|
|
557
|
-
service.charge = "2"
|
|
558
|
-
expect(service.save).to be_truthy
|
|
559
|
-
expect(service.charge.cents).to eq(200)
|
|
560
|
-
expect(service.charge.currency.to_s).to eq("USD")
|
|
561
|
-
|
|
562
|
-
product.lambda_price = "32"
|
|
563
|
-
expect(product.save).to be_truthy
|
|
564
|
-
expect(product.lambda_price.cents).to eq(3200)
|
|
565
|
-
expect(product.lambda_price.currency.to_s).to eq("CAD")
|
|
566
|
-
end
|
|
567
|
-
|
|
568
|
-
it "overrides default currency with model currency, in fixnum assignments" do
|
|
569
|
-
product.discount_value = 5
|
|
570
|
-
expect(product.save).to be_truthy
|
|
571
|
-
expect(product.discount_value.cents).to eq(500)
|
|
572
|
-
expect(product.discount_value.currency.to_s).to eq("USD")
|
|
573
|
-
end
|
|
574
|
-
|
|
575
|
-
it "overrides default currency with model currency, in string assignments" do
|
|
576
|
-
product.discount_value = "5"
|
|
577
|
-
expect(product.save).to be_truthy
|
|
578
|
-
expect(product.discount_value.cents).to eq(500)
|
|
579
|
-
expect(product.discount_value.currency.to_s).to eq("USD")
|
|
580
|
-
end
|
|
581
|
-
|
|
582
|
-
it "falls back to default currency, in fixnum assignments" do
|
|
583
|
-
service.discount = 5
|
|
584
|
-
expect(service.save).to be_truthy
|
|
585
|
-
expect(service.discount.cents).to eq(500)
|
|
586
|
-
expect(service.discount.currency.to_s).to eq("EUR")
|
|
587
|
-
end
|
|
588
|
-
|
|
589
|
-
it "falls back to default currency, in string assignments" do
|
|
590
|
-
service.discount = "5"
|
|
591
|
-
expect(service.save).to be_truthy
|
|
592
|
-
expect(service.discount.cents).to eq(500)
|
|
593
|
-
expect(service.discount.currency.to_s).to eq("EUR")
|
|
594
|
-
end
|
|
595
|
-
|
|
596
|
-
it "sets field to nil, in nil assignments if allow_nil is set" do
|
|
597
|
-
product.optional_price = nil
|
|
598
|
-
expect(product.save).to be_truthy
|
|
599
|
-
expect(product.optional_price).to be_nil
|
|
600
|
-
end
|
|
601
|
-
|
|
602
|
-
it "sets field to nil, in instantiation if allow_nil is set" do
|
|
603
|
-
pr = Product.new(optional_price: nil, price_cents: 5320,
|
|
604
|
-
discount: 350, bonus_cents: 320)
|
|
605
|
-
expect(pr.optional_price).to be_nil
|
|
606
|
-
expect(pr.save).to be_truthy
|
|
607
|
-
expect(pr.optional_price).to be_nil
|
|
608
|
-
end
|
|
609
|
-
|
|
610
|
-
it "sets field to nil, in blank assignments if allow_nil is set" do
|
|
611
|
-
product.optional_price = ""
|
|
612
|
-
expect(product.save).to be_truthy
|
|
613
|
-
expect(product.optional_price).to be_nil
|
|
614
|
-
end
|
|
615
|
-
|
|
616
|
-
context "when the monetized field is an aliased attribute" do
|
|
617
|
-
it "writes the subunits to the original (unaliased) column" do
|
|
618
|
-
pending if Rails::VERSION::MAJOR < 4
|
|
619
|
-
product.renamed = "$10.00"
|
|
620
|
-
expect(product.aliased_cents).to eq 10_00
|
|
621
|
-
end
|
|
622
|
-
end
|
|
623
|
-
|
|
624
|
-
context "for column with model currency:" do
|
|
625
|
-
it "has default currency if not specified" do
|
|
626
|
-
product = Product.create(sale_price_amount: 1234)
|
|
627
|
-
product.sale_price.currency.to_s == 'USD'
|
|
628
|
-
end
|
|
629
|
-
|
|
630
|
-
it "is overridden by instance currency column" do
|
|
631
|
-
product = Product.create(sale_price_amount: 1234,
|
|
632
|
-
sale_price_currency_code: 'CAD')
|
|
633
|
-
expect(product.sale_price.currency.to_s).to eq('CAD')
|
|
634
|
-
end
|
|
635
|
-
|
|
636
|
-
it 'can change currency of custom column' do
|
|
637
|
-
product = Product.create!(
|
|
638
|
-
price: Money.new(10,'USD'),
|
|
639
|
-
bonus: Money.new(10,'GBP'),
|
|
640
|
-
discount: 10,
|
|
641
|
-
sale_price_amount: 1234,
|
|
642
|
-
sale_price_currency_code: 'USD'
|
|
643
|
-
)
|
|
644
|
-
|
|
645
|
-
expect(product.sale_price.currency.to_s).to eq('USD')
|
|
646
|
-
|
|
647
|
-
product.sale_price = Money.new 456, 'CAD'
|
|
648
|
-
product.save
|
|
649
|
-
product.reload
|
|
650
|
-
|
|
651
|
-
expect(product.sale_price.currency.to_s).to eq('CAD')
|
|
652
|
-
expect(product.discount_value.currency.to_s).to eq('USD')
|
|
653
|
-
end
|
|
654
|
-
end
|
|
655
|
-
|
|
656
|
-
context "for model with currency column:" do
|
|
657
|
-
let(:transaction) do
|
|
658
|
-
Transaction.create(amount_cents: 2400, tax_cents: 600,
|
|
659
|
-
currency: :usd)
|
|
660
|
-
end
|
|
661
|
-
|
|
662
|
-
let(:dummy_product) do
|
|
663
|
-
DummyProduct.create(price_cents: 2400, currency: :usd)
|
|
664
|
-
end
|
|
665
|
-
|
|
666
|
-
let(:dummy_product_with_nil_currency) do
|
|
667
|
-
DummyProduct.create(price_cents: 2600) # nil currency
|
|
668
|
-
end
|
|
669
|
-
|
|
670
|
-
let(:dummy_product_with_invalid_currency) do
|
|
671
|
-
# invalid currency
|
|
672
|
-
DummyProduct.create(price_cents: 2600, currency: :foo)
|
|
673
|
-
end
|
|
674
|
-
|
|
675
|
-
it "correctly serializes the currency to a new instance of model" do
|
|
676
|
-
d = DummyProduct.new
|
|
677
|
-
d.price = Money.new(10, "EUR")
|
|
678
|
-
d.save!
|
|
679
|
-
d.reload
|
|
680
|
-
expect(d.currency).to eq("EUR")
|
|
681
|
-
end
|
|
682
|
-
|
|
683
|
-
it "overrides default currency with the value of row currency" do
|
|
684
|
-
expect(transaction.amount.currency).to eq(Money::Currency.find(:usd))
|
|
685
|
-
end
|
|
686
|
-
|
|
687
|
-
it "overrides default currency with the currency registered for the model" do
|
|
688
|
-
expect(dummy_product_with_nil_currency.price.currency).to eq(
|
|
689
|
-
Money::Currency.find(:gbp)
|
|
690
|
-
)
|
|
691
|
-
end
|
|
692
|
-
|
|
693
|
-
it "overrides default currency with the currency registered for the model if currency is invalid" do
|
|
694
|
-
expect(dummy_product_with_invalid_currency.price.currency).to eq(
|
|
695
|
-
Money::Currency.find(:gbp)
|
|
696
|
-
)
|
|
697
|
-
end
|
|
698
|
-
|
|
699
|
-
it "overrides default and model currency with the row currency" do
|
|
700
|
-
expect(dummy_product.price.currency).to eq(Money::Currency.find(:usd))
|
|
701
|
-
end
|
|
702
|
-
|
|
703
|
-
it "constructs the money attribute from the stored mapped attribute values" do
|
|
704
|
-
expect(transaction.amount).to eq(Money.new(2400, :usd))
|
|
705
|
-
end
|
|
706
|
-
|
|
707
|
-
it "correctly instantiates Money objects from the mapped attributes" do
|
|
708
|
-
t = Transaction.new(amount_cents: 2500, currency: "CAD")
|
|
709
|
-
expect(t.amount).to eq(Money.new(2500, "CAD"))
|
|
710
|
-
end
|
|
711
|
-
|
|
712
|
-
it "correctly assigns Money objects to the attribute" do
|
|
713
|
-
transaction.amount = Money.new(2500, :eur)
|
|
714
|
-
expect(transaction.save).to be_truthy
|
|
715
|
-
expect(transaction.amount.cents).to eq(Money.new(2500, :eur).cents)
|
|
716
|
-
expect(transaction.amount.currency.to_s).to eq("EUR")
|
|
717
|
-
end
|
|
718
|
-
|
|
719
|
-
it "uses default currency if a non Money object is assigned to the attribute" do
|
|
720
|
-
transaction.amount = 234
|
|
721
|
-
expect(transaction.amount.currency.to_s).to eq("USD")
|
|
722
|
-
end
|
|
723
|
-
|
|
724
|
-
it "constructs the money object from the mapped method value" do
|
|
725
|
-
expect(transaction.total).to eq(Money.new(3000, :usd))
|
|
726
|
-
end
|
|
727
|
-
|
|
728
|
-
it "allows currency column postfix to be blank" do
|
|
729
|
-
allow(MoneyRails::Configuration).to receive(:currency_column) { { postfix: nil, column_name: 'currency' } }
|
|
730
|
-
expect(dummy_product_with_nil_currency.price.currency).to eq(Money::Currency.find(:gbp))
|
|
731
|
-
end
|
|
732
|
-
|
|
733
|
-
it "updates inferred currency column based on currency column postfix" do
|
|
734
|
-
product.reduced_price = Money.new(999_00, 'CAD')
|
|
735
|
-
product.save
|
|
736
|
-
|
|
737
|
-
expect(product.reduced_price_cents).to eq(999_00)
|
|
738
|
-
expect(product.reduced_price_currency).to eq('CAD')
|
|
739
|
-
end
|
|
740
|
-
|
|
741
|
-
context "and field with allow_nil: true" do
|
|
742
|
-
it "doesn't set currency to nil when setting the field to nil" do
|
|
743
|
-
t = Transaction.new(amount_cents: 2500, currency: "CAD")
|
|
744
|
-
t.optional_amount = nil
|
|
745
|
-
expect(t.currency).to eq("CAD")
|
|
746
|
-
end
|
|
747
|
-
end
|
|
748
|
-
|
|
749
|
-
# TODO: these specs should mock locale_backend with expected values
|
|
750
|
-
# instead of manipulating it directly
|
|
751
|
-
context "and an Italian locale" do
|
|
752
|
-
around(:each) do |example|
|
|
753
|
-
I18n.with_locale(:it) do
|
|
754
|
-
example.run
|
|
755
|
-
end
|
|
756
|
-
end
|
|
757
|
-
|
|
758
|
-
context "when using :i18n locale backend" do
|
|
759
|
-
it "validates with the locale's decimal mark" do
|
|
760
|
-
transaction.amount = "123,45"
|
|
761
|
-
expect(transaction.valid?).to be_truthy
|
|
762
|
-
end
|
|
763
|
-
|
|
764
|
-
it "does not validate with the currency's decimal mark" do
|
|
765
|
-
transaction.amount = "123.45"
|
|
766
|
-
expect(transaction.valid?).to be_falsey
|
|
767
|
-
end
|
|
768
|
-
|
|
769
|
-
it "validates with the locale's currency symbol" do
|
|
770
|
-
transaction.amount = "€123"
|
|
771
|
-
expect(transaction.valid?).to be_truthy
|
|
772
|
-
end
|
|
773
|
-
|
|
774
|
-
it "does not validate with the transaction's currency symbol" do
|
|
775
|
-
transaction.amount = "$123.45"
|
|
776
|
-
expect(transaction.valid?).to be_falsey
|
|
777
|
-
end
|
|
778
|
-
end
|
|
779
|
-
|
|
780
|
-
context "when using :currency locale backend" do
|
|
781
|
-
around(:each) do |example|
|
|
782
|
-
begin
|
|
783
|
-
Money.locale_backend = :currency
|
|
784
|
-
example.run
|
|
785
|
-
ensure
|
|
786
|
-
Money.locale_backend = :i18n
|
|
787
|
-
end
|
|
788
|
-
end
|
|
789
|
-
|
|
790
|
-
it "does not validate with the locale's decimal mark" do
|
|
791
|
-
transaction.amount = "123,45"
|
|
792
|
-
expect(transaction.valid?).to be_falsey
|
|
793
|
-
end
|
|
794
|
-
|
|
795
|
-
it "validates with the currency's decimal mark" do
|
|
796
|
-
transaction.amount = "123.45"
|
|
797
|
-
expect(transaction.valid?).to be_truthy
|
|
798
|
-
end
|
|
799
|
-
|
|
800
|
-
it "does not validate with the locale's currency symbol" do
|
|
801
|
-
transaction.amount = "€123"
|
|
802
|
-
expect(transaction.valid?).to be_falsey
|
|
803
|
-
end
|
|
804
|
-
|
|
805
|
-
it "validates with the transaction's currency symbol" do
|
|
806
|
-
transaction.amount = "$123"
|
|
807
|
-
expect(transaction.valid?).to be_truthy
|
|
808
|
-
end
|
|
809
|
-
end
|
|
810
|
-
end
|
|
811
|
-
end
|
|
812
|
-
end
|
|
813
|
-
|
|
814
|
-
describe ".register_currency" do
|
|
815
|
-
it "attaches currency at model level" do
|
|
816
|
-
expect(Product.currency).to eq(Money::Currency.find(:usd))
|
|
817
|
-
expect(DummyProduct.currency).to eq(Money::Currency.find(:gbp))
|
|
818
|
-
end
|
|
819
|
-
end
|
|
820
|
-
|
|
821
|
-
describe "#read_monetized" do
|
|
822
|
-
it "returns monetized attribute's value" do
|
|
823
|
-
reduced_price = product.read_monetized(:reduced_price, :reduced_price_cents)
|
|
824
|
-
|
|
825
|
-
expect(reduced_price).to be_an_instance_of(Money)
|
|
826
|
-
expect(reduced_price).to eq(Money.new(product.reduced_price_cents, product.reduced_price_currency))
|
|
827
|
-
end
|
|
828
|
-
|
|
829
|
-
context "memoize" do
|
|
830
|
-
it "memoizes monetized attribute's value" do
|
|
831
|
-
product.instance_variable_set '@reduced_price', nil
|
|
832
|
-
reduced_price = product.read_monetized(:reduced_price, :reduced_price_cents)
|
|
833
|
-
|
|
834
|
-
expect(product.instance_variable_get('@reduced_price')).to eq(reduced_price)
|
|
835
|
-
end
|
|
836
|
-
|
|
837
|
-
it "resets memoized attribute's value if amount has changed" do
|
|
838
|
-
reduced_price = product.read_monetized(:reduced_price, :reduced_price_cents)
|
|
839
|
-
product.reduced_price_cents = 100
|
|
840
|
-
|
|
841
|
-
expect(product.read_monetized(:reduced_price, :reduced_price_cents)).not_to eq(reduced_price)
|
|
842
|
-
end
|
|
843
|
-
|
|
844
|
-
it "resets memoized attribute's value if currency has changed" do
|
|
845
|
-
reduced_price = product.read_monetized(:reduced_price, :reduced_price_cents)
|
|
846
|
-
product.reduced_price_currency = 'CAD'
|
|
847
|
-
|
|
848
|
-
expect(product.read_monetized(:reduced_price, :reduced_price_cents)).not_to eq(reduced_price)
|
|
849
|
-
end
|
|
850
|
-
end
|
|
851
|
-
|
|
852
|
-
context "with preserve_user_input set" do
|
|
853
|
-
around(:each) do |example|
|
|
854
|
-
MoneyRails::Configuration.preserve_user_input = true
|
|
855
|
-
example.run
|
|
856
|
-
MoneyRails::Configuration.preserve_user_input = false
|
|
857
|
-
end
|
|
858
|
-
|
|
859
|
-
it "has no effect if validation passes" do
|
|
860
|
-
product.price = '14'
|
|
861
|
-
|
|
862
|
-
expect(product.save).to be_truthy
|
|
863
|
-
expect(product.read_monetized(:price, :price_cents).to_s).to eq('14.00')
|
|
864
|
-
end
|
|
865
|
-
|
|
866
|
-
it "preserves user input if validation fails" do
|
|
867
|
-
product.price = '14,0'
|
|
868
|
-
|
|
869
|
-
expect(product.save).to be_falsy
|
|
870
|
-
expect(product.read_monetized(:price, :price_cents).to_s).to eq('14,0')
|
|
871
|
-
end
|
|
872
|
-
end
|
|
873
|
-
|
|
874
|
-
context "with a monetized attribute that is nil" do
|
|
875
|
-
let(:service) do
|
|
876
|
-
Service.create(discount_cents: nil)
|
|
877
|
-
end
|
|
878
|
-
let(:default_currency_lambda) { double("Default Currency Fallback") }
|
|
879
|
-
subject { service.read_monetized(:discount, :discount_cents, options) }
|
|
880
|
-
|
|
881
|
-
around(:each) do |example|
|
|
882
|
-
service # Instantiate instance which relies on Money.default_currency
|
|
883
|
-
original_default_currency = Money.default_currency
|
|
884
|
-
Money.default_currency = -> { default_currency_lambda.read_currency }
|
|
885
|
-
example.run
|
|
886
|
-
Money.default_currency = original_default_currency
|
|
887
|
-
end
|
|
888
|
-
|
|
889
|
-
context "when allow_nil options is set" do
|
|
890
|
-
let(:options) { { allow_nil: true } }
|
|
891
|
-
it "does not attempt to read the fallback default currency" do
|
|
892
|
-
expect(default_currency_lambda).not_to receive(:read_currency)
|
|
893
|
-
subject
|
|
894
|
-
end
|
|
895
|
-
end
|
|
896
|
-
end
|
|
897
|
-
end
|
|
898
|
-
|
|
899
|
-
describe "#write_monetized" do
|
|
900
|
-
let(:value) { Money.new(1_000, 'LVL') }
|
|
901
|
-
|
|
902
|
-
it "sets monetized attribute's value to Money object" do
|
|
903
|
-
product.write_monetized :price, :price_cents, value, false, nil, {}
|
|
904
|
-
|
|
905
|
-
expect(product.price).to be_an_instance_of(Money)
|
|
906
|
-
expect(product.price_cents).to eq(value.cents)
|
|
907
|
-
# Because :price does not have a column for currency
|
|
908
|
-
expect(product.price.currency).to eq(Product.currency)
|
|
909
|
-
end
|
|
910
|
-
|
|
911
|
-
it "sets monetized attribute's value from a given Fixnum" do
|
|
912
|
-
product.write_monetized :price, :price_cents, 10, false, nil, {}
|
|
913
|
-
|
|
914
|
-
expect(product.price).to be_an_instance_of(Money)
|
|
915
|
-
expect(product.price_cents).to eq(1000)
|
|
916
|
-
end
|
|
917
|
-
|
|
918
|
-
it "sets monetized attribute's value from a given Float" do
|
|
919
|
-
product.write_monetized :price, :price_cents, 10.5, false, nil, {}
|
|
920
|
-
|
|
921
|
-
expect(product.price).to be_an_instance_of(Money)
|
|
922
|
-
expect(product.price_cents).to eq(1050)
|
|
923
|
-
end
|
|
924
|
-
|
|
925
|
-
it "resets monetized attribute when given blank input" do
|
|
926
|
-
product.write_monetized :price, :price_cents, nil, false, nil, { allow_nil: true }
|
|
927
|
-
|
|
928
|
-
expect(product.price).to eq(nil)
|
|
929
|
-
end
|
|
930
|
-
|
|
931
|
-
it "sets monetized attribute to 0 when given a blank value" do
|
|
932
|
-
currency = product.price.currency
|
|
933
|
-
product.write_monetized :price, :price_cents, nil, false, nil, {}
|
|
934
|
-
|
|
935
|
-
expect(product.price.amount).to eq(0)
|
|
936
|
-
expect(product.price.currency).to eq(currency)
|
|
937
|
-
end
|
|
938
|
-
|
|
939
|
-
it "does not memoize monetized attribute's value if currency is read-only" do
|
|
940
|
-
product.write_monetized :price, :price_cents, value, false, nil, {}
|
|
941
|
-
|
|
942
|
-
price = product.instance_variable_get('@price')
|
|
943
|
-
|
|
944
|
-
expect(price).to be_an_instance_of(Money)
|
|
945
|
-
expect(price.amount).not_to eq(value.amount)
|
|
946
|
-
end
|
|
947
|
-
|
|
948
|
-
describe "instance_currency_name" do
|
|
949
|
-
it "updates instance_currency_name attribute" do
|
|
950
|
-
product.write_monetized :sale_price, :sale_price_amount, value, false, :sale_price_currency_code, {}
|
|
951
|
-
|
|
952
|
-
expect(product.sale_price).to eq(value)
|
|
953
|
-
expect(product.sale_price_currency_code).to eq('LVL')
|
|
954
|
-
end
|
|
955
|
-
|
|
956
|
-
it "memoizes monetized attribute's value with currency" do
|
|
957
|
-
product.write_monetized :sale_price, :sale_price_amount, value, false, :sale_price_currency_code, {}
|
|
958
|
-
|
|
959
|
-
expect(product.instance_variable_get('@sale_price')).to eq(value)
|
|
960
|
-
end
|
|
961
|
-
|
|
962
|
-
it "ignores empty instance_currency_name" do
|
|
963
|
-
product.write_monetized :sale_price, :sale_price_amount, value, false, '', {}
|
|
964
|
-
|
|
965
|
-
expect(product.sale_price.amount).to eq(value.amount)
|
|
966
|
-
expect(product.sale_price.currency).to eq(Product.currency)
|
|
967
|
-
end
|
|
968
|
-
|
|
969
|
-
it "ignores instance_currency_name that model does not respond to" do
|
|
970
|
-
product.write_monetized :sale_price, :sale_price_amount, value, false, :non_existing_currency, {}
|
|
971
|
-
|
|
972
|
-
expect(product.sale_price.amount).to eq(value.amount)
|
|
973
|
-
expect(product.sale_price.currency).to eq(Product.currency)
|
|
974
|
-
end
|
|
975
|
-
end
|
|
976
|
-
|
|
977
|
-
describe "error handling" do
|
|
978
|
-
let!(:old_price_value) { product.price }
|
|
979
|
-
|
|
980
|
-
it "ignores values that do not implement to_money method" do
|
|
981
|
-
product.write_monetized :price, :price_cents, [10], false, nil, {}
|
|
982
|
-
|
|
983
|
-
expect(product.price).to eq(old_price_value)
|
|
984
|
-
end
|
|
985
|
-
|
|
986
|
-
context "raise_error_on_money_parsing enabled" do
|
|
987
|
-
before { MoneyRails.raise_error_on_money_parsing = true }
|
|
988
|
-
after { MoneyRails.raise_error_on_money_parsing = false }
|
|
989
|
-
|
|
990
|
-
it "raises a MoneyRails::Error when given an invalid value" do
|
|
991
|
-
expect {
|
|
992
|
-
product.write_monetized :price, :price_cents, '10-50', false, nil, {}
|
|
993
|
-
}.to raise_error(MoneyRails::Error)
|
|
994
|
-
end
|
|
995
|
-
|
|
996
|
-
it "raises a MoneyRails::Error error when trying to set invalid currency" do
|
|
997
|
-
allow(product).to receive(:currency_for_price).and_return('INVALID_CURRENCY')
|
|
998
|
-
expect {
|
|
999
|
-
product.write_monetized :price, :price_cents, 10, false, nil, {}
|
|
1000
|
-
}.to raise_error(MoneyRails::Error)
|
|
1001
|
-
end
|
|
1002
|
-
end
|
|
1003
|
-
|
|
1004
|
-
context "raise_error_on_money_parsing disabled" do
|
|
1005
|
-
it "ignores when given invalid value" do
|
|
1006
|
-
product.write_monetized :price, :price_cents, '10-50', false, nil, {}
|
|
1007
|
-
|
|
1008
|
-
expect(product.price).to eq(old_price_value)
|
|
1009
|
-
end
|
|
1010
|
-
|
|
1011
|
-
it "raises a MoneyRails::Error error when trying to set invalid currency" do
|
|
1012
|
-
allow(product).to receive(:currency_for_price).and_return('INVALID_CURRENCY')
|
|
1013
|
-
product.write_monetized :price, :price_cents, 10, false, nil, {}
|
|
1014
|
-
|
|
1015
|
-
# Can not use public accessor here because currency_for_price is stubbed
|
|
1016
|
-
expect(product.instance_variable_get('@price')).to eq(old_price_value)
|
|
1017
|
-
end
|
|
1018
|
-
end
|
|
1019
|
-
end
|
|
1020
|
-
end
|
|
1021
|
-
|
|
1022
|
-
describe "#currency_for" do
|
|
1023
|
-
it "detects currency based on instance currency name" do
|
|
1024
|
-
product = Product.new(sale_price_currency_code: 'CAD')
|
|
1025
|
-
currency = product.send(:currency_for, :sale_price, :sale_price_currency_code, nil)
|
|
1026
|
-
|
|
1027
|
-
expect(currency).to be_an_instance_of(Money::Currency)
|
|
1028
|
-
expect(currency.iso_code).to eq('CAD')
|
|
1029
|
-
end
|
|
1030
|
-
|
|
1031
|
-
it "detects currency based on currency passed as a block" do
|
|
1032
|
-
product = Product.new
|
|
1033
|
-
currency = product.send(:currency_for, :lambda_price, nil, ->(_) { 'CAD' })
|
|
1034
|
-
|
|
1035
|
-
expect(currency).to be_an_instance_of(Money::Currency)
|
|
1036
|
-
expect(currency.iso_code).to eq('CAD')
|
|
1037
|
-
end
|
|
1038
|
-
|
|
1039
|
-
it "detects currency based on currency passed explicitly" do
|
|
1040
|
-
product = Product.new
|
|
1041
|
-
currency = product.send(:currency_for, :bonus, nil, 'CAD')
|
|
1042
|
-
|
|
1043
|
-
expect(currency).to be_an_instance_of(Money::Currency)
|
|
1044
|
-
expect(currency.iso_code).to eq('CAD')
|
|
1045
|
-
end
|
|
1046
|
-
|
|
1047
|
-
it "falls back to a registered currency" do
|
|
1048
|
-
product = Product.new
|
|
1049
|
-
currency = product.send(:currency_for, :amount, nil, nil)
|
|
1050
|
-
|
|
1051
|
-
expect(currency).to eq(Product.currency)
|
|
1052
|
-
end
|
|
1053
|
-
|
|
1054
|
-
it "falls back to a default currency" do
|
|
1055
|
-
transaction = Transaction.new
|
|
1056
|
-
currency = transaction.send(:currency_for, :amount, nil, nil)
|
|
1057
|
-
|
|
1058
|
-
expect(currency).to eq(Money.default_currency)
|
|
1059
|
-
end
|
|
1060
|
-
end
|
|
1061
|
-
end
|
|
1062
|
-
end
|