money-rails 1.6.2 → 1.7.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 +7 -0
- data/Rakefile +21 -6
- data/lib/money-rails/active_record/monetizable.rb +121 -116
- data/lib/money-rails/mongoid/money.rb +1 -0
- data/lib/money-rails/mongoid/two.rb +1 -0
- data/lib/money-rails/version.rb +1 -1
- data/spec/active_record/monetizable_spec.rb +228 -26
- data/spec/dummy/config/application.rb +1 -1
- data/spec/dummy/config/mongoid.yml +6 -0
- data/spec/mongoid/five_spec.rb +117 -0
- data/spec/mongoid/four_spec.rb +8 -3
- data/spec/mongoid/three_spec.rb +9 -3
- data/spec/mongoid/two_spec.rb +9 -3
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 248e9756e9e93e82ed71a92426635e8864412a66
|
4
|
+
data.tar.gz: b985a0e0926babc02c77bd3ece23927c261ccaab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea054a49148d15a90b373c61df52d73e2598cd67d06e9ffc79dea67e515e6038a365d45f924b09133b90c804311cd9050432e39add9259354823f1f6e0950171
|
7
|
+
data.tar.gz: b53220b8d3e692f983f556a264a9045b5b09d5361a578fa12ce500df96853d58f71e01b927250528b7447a57b5d60be60fbc0d896eec4b3fc440efb2d839aee4
|
data/CHANGELOG.md
CHANGED
data/Rakefile
CHANGED
@@ -28,17 +28,29 @@ task :spec => :prepare_test_env
|
|
28
28
|
desc "Prepare money-rails engine test environment"
|
29
29
|
task :prepare_test_env do
|
30
30
|
Rake.application['app:db:drop:all'].invoke
|
31
|
+
Rake.application['app:db:create'].invoke if Rails::VERSION::MAJOR >= 5
|
31
32
|
Rake.application['app:db:migrate'].invoke
|
32
33
|
Rake.application['app:db:test:prepare'].invoke
|
33
34
|
end
|
34
35
|
|
35
36
|
def run_with_gemfile(gemfile)
|
36
|
-
|
37
|
-
|
37
|
+
Bundler.with_clean_env do
|
38
|
+
begin
|
39
|
+
sh "BUNDLE_GEMFILE='#{gemfile}' bundle install --quiet"
|
40
|
+
Rake.application['app:db:create'].invoke
|
41
|
+
Rake.application['app:db:test:prepare'].invoke
|
42
|
+
sh "BUNDLE_GEMFILE='#{gemfile}' bundle exec rake spec"
|
43
|
+
ensure
|
44
|
+
Rake.application['app:db:drop:all'].execute
|
45
|
+
end
|
46
|
+
end
|
38
47
|
end
|
39
48
|
|
40
49
|
namespace :spec do
|
41
50
|
|
51
|
+
desc "Run Tests against mongoid (version 5)"
|
52
|
+
task(:mongoid5) { run_with_gemfile 'gemfiles/mongoid5.gemfile' }
|
53
|
+
|
42
54
|
desc "Run Tests against mongoid (version 4)"
|
43
55
|
task(:mongoid4) { run_with_gemfile 'gemfiles/mongoid4.gemfile' }
|
44
56
|
|
@@ -48,6 +60,9 @@ namespace :spec do
|
|
48
60
|
desc "Run Tests against mongoid (version 2)"
|
49
61
|
task(:mongoid2) { run_with_gemfile 'gemfiles/mongoid2.gemfile' }
|
50
62
|
|
63
|
+
desc "Run Tests against rails 5.0"
|
64
|
+
task(:rails50) { run_with_gemfile 'gemfiles/rails50.gemfile' }
|
65
|
+
|
51
66
|
desc "Run Tests against rails 4.2"
|
52
67
|
task(:rails42) { run_with_gemfile 'gemfiles/rails42.gemfile' }
|
53
68
|
|
@@ -60,11 +75,11 @@ namespace :spec do
|
|
60
75
|
desc "Run Tests against rails 3"
|
61
76
|
task(:rails3) { run_with_gemfile 'gemfiles/rails3.gemfile' }
|
62
77
|
|
63
|
-
desc "Run Tests against mongoid 2 & 3 & 4"
|
64
|
-
task :mongoid => [:mongoid2, :mongoid3, :mongoid4]
|
78
|
+
desc "Run Tests against mongoid 2 & 3 & 4, 5"
|
79
|
+
task :mongoid => [:mongoid2, :mongoid3, :mongoid4, :mongoid5]
|
65
80
|
|
66
|
-
desc "Run Tests against rails 3 & 4 & 4.1 & 4.2"
|
67
|
-
task :rails => [:rails3, :rails4, :rails41, :rails42]
|
81
|
+
desc "Run Tests against rails 3 & 4 & 4.1 & 4.2 & 5.0"
|
82
|
+
task :rails => [:rails3, :rails4, :rails41, :rails42, :rails50]
|
68
83
|
|
69
84
|
desc "Run Tests against all ORMs"
|
70
85
|
task :all => [:rails, :mongoid]
|
@@ -116,104 +116,15 @@ module MoneyRails
|
|
116
116
|
end
|
117
117
|
end
|
118
118
|
|
119
|
-
define_method name do |*args|
|
120
|
-
|
121
|
-
# Get the cents
|
122
|
-
amount = public_send(subunit_name, *args)
|
123
|
-
|
124
|
-
# Get the currency object
|
125
|
-
attr_currency = public_send("currency_for_#{name}")
|
126
|
-
|
127
|
-
# Get the cached value
|
128
|
-
memoized = instance_variable_get("@#{name}")
|
129
|
-
|
130
|
-
# Dont create a new Money instance if the values haven't been changed.
|
131
|
-
if memoized && memoized.cents == amount
|
132
|
-
if memoized.currency == attr_currency
|
133
|
-
result = memoized
|
134
|
-
else
|
135
|
-
memoized_amount = memoized.amount.to_money(attr_currency)
|
136
|
-
write_attribute subunit_name, memoized_amount.cents
|
137
|
-
# Cache the value (it may be nil)
|
138
|
-
result = instance_variable_set "@#{name}", memoized_amount
|
139
|
-
end
|
140
|
-
elsif amount.present?
|
141
|
-
# If amount is NOT nil (or empty string) load the amount in a Money
|
142
|
-
amount = Money.new(amount, attr_currency)
|
143
|
-
|
144
|
-
# Cache the value (it may be nil)
|
145
|
-
result = instance_variable_set "@#{name}", amount
|
146
|
-
end
|
147
119
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
result.define_singleton_method(:to_s) { value_before_type_cast }
|
152
|
-
result.define_singleton_method(:format) { |_| value_before_type_cast }
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
result
|
120
|
+
# Getter for monetized attribute
|
121
|
+
define_method name do |*args|
|
122
|
+
read_monetized name, subunit_name, *args
|
157
123
|
end
|
158
124
|
|
125
|
+
# Setter for monetized attribute
|
159
126
|
define_method "#{name}=" do |value|
|
160
|
-
|
161
|
-
# Lets keep the before_type_cast value
|
162
|
-
instance_variable_set "@#{name}_money_before_type_cast", value
|
163
|
-
|
164
|
-
# Use nil or get a Money object
|
165
|
-
if options[:allow_nil] && value.blank?
|
166
|
-
money = nil
|
167
|
-
else
|
168
|
-
if value.is_a?(Money)
|
169
|
-
money = value
|
170
|
-
else
|
171
|
-
begin
|
172
|
-
money = value.to_money(public_send("currency_for_#{name}"))
|
173
|
-
rescue NoMethodError
|
174
|
-
return nil
|
175
|
-
rescue ArgumentError
|
176
|
-
raise if MoneyRails.raise_error_on_money_parsing
|
177
|
-
return nil
|
178
|
-
rescue Money::Currency::UnknownCurrency
|
179
|
-
raise if MoneyRails.raise_error_on_money_parsing
|
180
|
-
return nil
|
181
|
-
end
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
# Update cents
|
186
|
-
if !validation_enabled
|
187
|
-
# We haven't defined our own subunit writer, so we can invoke
|
188
|
-
# the regular writer, which works with store_accessors
|
189
|
-
public_send("#{subunit_name}=", money.try(:cents))
|
190
|
-
elsif self.class.respond_to?(:attribute_aliases) &&
|
191
|
-
self.class.attribute_aliases.key?(subunit_name)
|
192
|
-
# If the attribute is aliased, make sure we write to the original
|
193
|
-
# attribute name or an error will be raised.
|
194
|
-
# (Note: 'attribute_aliases' doesn't exist in Rails 3.x, so we
|
195
|
-
# can't tell if the attribute was aliased.)
|
196
|
-
original_name = self.class.attribute_aliases[subunit_name.to_s]
|
197
|
-
write_attribute(original_name, money.try(:cents))
|
198
|
-
else
|
199
|
-
write_attribute(subunit_name, money.try(:cents))
|
200
|
-
end
|
201
|
-
|
202
|
-
money_currency = money.try(:currency)
|
203
|
-
|
204
|
-
# Update currency iso value if there is an instance currency attribute
|
205
|
-
if instance_currency_name.present? && respond_to?("#{instance_currency_name}=") && money_currency
|
206
|
-
public_send("#{instance_currency_name}=", money_currency.try(:iso_code))
|
207
|
-
else
|
208
|
-
current_currency = public_send("currency_for_#{name}")
|
209
|
-
if money_currency && current_currency != money_currency.id
|
210
|
-
raise ReadOnlyCurrencyException.new("Can't change readonly currency '#{current_currency}' to '#{money_currency}' for field '#{name}'") if MoneyRails.raise_error_on_money_parsing
|
211
|
-
return nil
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
# Save and return the new Money object
|
216
|
-
instance_variable_set "@#{name}", money
|
127
|
+
write_monetized name, subunit_name, value, validation_enabled, instance_currency_name, options
|
217
128
|
end
|
218
129
|
|
219
130
|
if validation_enabled
|
@@ -225,29 +136,9 @@ module MoneyRails
|
|
225
136
|
end
|
226
137
|
end
|
227
138
|
|
139
|
+
# Currency getter
|
228
140
|
define_method "currency_for_#{name}" do
|
229
|
-
|
230
|
-
instance_currency_name_with_postfix = "#{name}#{MoneyRails::Configuration.currency_column[:postfix]}"
|
231
|
-
end
|
232
|
-
|
233
|
-
if instance_currency_name.present? && respond_to?(instance_currency_name) &&
|
234
|
-
Money::Currency.find(public_send(instance_currency_name))
|
235
|
-
|
236
|
-
Money::Currency.find(public_send(instance_currency_name))
|
237
|
-
elsif field_currency_name.respond_to?(:call)
|
238
|
-
Money::Currency.find(field_currency_name.call(self))
|
239
|
-
elsif field_currency_name
|
240
|
-
Money::Currency.find(field_currency_name)
|
241
|
-
elsif instance_currency_name_with_postfix.present? &&
|
242
|
-
respond_to?(instance_currency_name_with_postfix) &&
|
243
|
-
Money::Currency.find(public_send(instance_currency_name_with_postfix))
|
244
|
-
|
245
|
-
Money::Currency.find(public_send(instance_currency_name_with_postfix))
|
246
|
-
elsif self.class.respond_to?(:currency)
|
247
|
-
self.class.currency
|
248
|
-
else
|
249
|
-
Money.default_currency
|
250
|
-
end
|
141
|
+
currency_for name, instance_currency_name, field_currency_name
|
251
142
|
end
|
252
143
|
|
253
144
|
attr_reader "#{name}_money_before_type_cast"
|
@@ -286,6 +177,120 @@ module MoneyRails
|
|
286
177
|
@monetized_attributes[name] = value
|
287
178
|
end
|
288
179
|
end
|
180
|
+
|
181
|
+
def read_monetized(name, subunit_name, *args)
|
182
|
+
# Get the cents
|
183
|
+
amount = public_send(subunit_name, *args)
|
184
|
+
|
185
|
+
# Get the currency object
|
186
|
+
attr_currency = public_send("currency_for_#{name}")
|
187
|
+
|
188
|
+
# Get the cached value
|
189
|
+
memoized = instance_variable_get("@#{name}")
|
190
|
+
|
191
|
+
# Dont create a new Money instance if the values haven't been changed.
|
192
|
+
if memoized && memoized.cents == amount
|
193
|
+
if memoized.currency == attr_currency
|
194
|
+
result = memoized
|
195
|
+
else
|
196
|
+
memoized_amount = memoized.amount.to_money(attr_currency)
|
197
|
+
write_attribute subunit_name, memoized_amount.cents
|
198
|
+
# Cache the value (it may be nil)
|
199
|
+
result = instance_variable_set("@#{name}", memoized_amount)
|
200
|
+
end
|
201
|
+
elsif amount.present?
|
202
|
+
# If amount is NOT nil (or empty string) load the amount in a Money
|
203
|
+
amount = Money.new(amount, attr_currency)
|
204
|
+
|
205
|
+
# Cache the value (it may be nil)
|
206
|
+
result = instance_variable_set("@#{name}", amount)
|
207
|
+
end
|
208
|
+
|
209
|
+
if MoneyRails::Configuration.preserve_user_input
|
210
|
+
value_before_type_cast = instance_variable_get "@#{name}_money_before_type_cast"
|
211
|
+
if errors[name.to_sym].present?
|
212
|
+
result.define_singleton_method(:to_s) { value_before_type_cast }
|
213
|
+
result.define_singleton_method(:format) { |_| value_before_type_cast }
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
result
|
218
|
+
end
|
219
|
+
|
220
|
+
def write_monetized(name, subunit_name, value, validation_enabled, instance_currency_name, options)
|
221
|
+
# Keep before_type_cast value as a reference to original input
|
222
|
+
instance_variable_set "@#{name}_money_before_type_cast", value
|
223
|
+
|
224
|
+
# Use nil or get a Money object
|
225
|
+
if options[:allow_nil] && value.blank?
|
226
|
+
money = nil
|
227
|
+
else
|
228
|
+
if value.is_a?(Money)
|
229
|
+
money = value
|
230
|
+
else
|
231
|
+
begin
|
232
|
+
money = value.to_money(public_send("currency_for_#{name}"))
|
233
|
+
rescue NoMethodError
|
234
|
+
return nil
|
235
|
+
rescue ArgumentError
|
236
|
+
raise if MoneyRails.raise_error_on_money_parsing
|
237
|
+
return nil
|
238
|
+
rescue Money::Currency::UnknownCurrency
|
239
|
+
raise if MoneyRails.raise_error_on_money_parsing
|
240
|
+
return nil
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
# Update cents
|
246
|
+
if !validation_enabled
|
247
|
+
# We haven't defined our own subunit writer, so we can invoke
|
248
|
+
# the regular writer, which works with store_accessors
|
249
|
+
public_send("#{subunit_name}=", money.try(:cents))
|
250
|
+
elsif self.class.respond_to?(:attribute_aliases) &&
|
251
|
+
self.class.attribute_aliases.key?(subunit_name)
|
252
|
+
# If the attribute is aliased, make sure we write to the original
|
253
|
+
# attribute name or an error will be raised.
|
254
|
+
# (Note: 'attribute_aliases' doesn't exist in Rails 3.x, so we
|
255
|
+
# can't tell if the attribute was aliased.)
|
256
|
+
original_name = self.class.attribute_aliases[subunit_name.to_s]
|
257
|
+
write_attribute(original_name, money.try(:cents))
|
258
|
+
else
|
259
|
+
write_attribute(subunit_name, money.try(:cents))
|
260
|
+
end
|
261
|
+
|
262
|
+
if money_currency = money.try(:currency)
|
263
|
+
# Update currency iso value if there is an instance currency attribute
|
264
|
+
if instance_currency_name.present? && respond_to?("#{instance_currency_name}=")
|
265
|
+
public_send("#{instance_currency_name}=", money_currency.iso_code)
|
266
|
+
else
|
267
|
+
current_currency = public_send("currency_for_#{name}")
|
268
|
+
if current_currency != money_currency.id
|
269
|
+
raise ReadOnlyCurrencyException.new("Can't change readonly currency '#{current_currency}' to '#{money_currency}' for field '#{name}'") if MoneyRails.raise_error_on_money_parsing
|
270
|
+
return nil
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
# Save and return the new Money object
|
276
|
+
instance_variable_set "@#{name}", money
|
277
|
+
end
|
278
|
+
|
279
|
+
def currency_for(name, instance_currency_name, field_currency_name)
|
280
|
+
if instance_currency_name.present? && respond_to?(instance_currency_name) &&
|
281
|
+
Money::Currency.find(public_send(instance_currency_name))
|
282
|
+
|
283
|
+
Money::Currency.find(public_send(instance_currency_name))
|
284
|
+
elsif field_currency_name.respond_to?(:call)
|
285
|
+
Money::Currency.find(field_currency_name.call(self))
|
286
|
+
elsif field_currency_name
|
287
|
+
Money::Currency.find(field_currency_name)
|
288
|
+
elsif self.class.respond_to?(:currency)
|
289
|
+
self.class.currency
|
290
|
+
else
|
291
|
+
Money.default_currency
|
292
|
+
end
|
293
|
+
end
|
289
294
|
end
|
290
295
|
end
|
291
296
|
end
|
data/lib/money-rails/version.rb
CHANGED
@@ -6,21 +6,21 @@ class Sub < Product; end
|
|
6
6
|
|
7
7
|
if defined? ActiveRecord
|
8
8
|
describe MoneyRails::ActiveRecord::Monetizable do
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
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
|
18
17
|
|
18
|
+
describe ".monetize" do
|
19
19
|
let(:service) do
|
20
20
|
Service.create(:charge_cents => 2000, :discount_cents => 120)
|
21
21
|
end
|
22
22
|
|
23
|
-
context
|
23
|
+
context ".monetized_attributes" do
|
24
24
|
|
25
25
|
class InheritedMonetizeProduct < Product
|
26
26
|
monetize :special_price_cents
|
@@ -396,21 +396,6 @@ if defined? ActiveRecord
|
|
396
396
|
expect(product.save).to be_truthy
|
397
397
|
end
|
398
398
|
|
399
|
-
describe "with preserve_user_input set" do
|
400
|
-
around(:each) do |example|
|
401
|
-
old_value = MoneyRails::Configuration.preserve_user_input
|
402
|
-
MoneyRails::Configuration.preserve_user_input = true
|
403
|
-
example.run
|
404
|
-
MoneyRails::Configuration.preserve_user_input = false
|
405
|
-
end
|
406
|
-
|
407
|
-
it "preserves user input if validation fails" do
|
408
|
-
product.price = "14,0"
|
409
|
-
expect(product.save).to be_falsy
|
410
|
-
expect(product.price.to_s).to eq("14,0")
|
411
|
-
end
|
412
|
-
end
|
413
|
-
|
414
399
|
it "respects numericality validation when using update_attributes on money attribute" do
|
415
400
|
expect(product.update_attributes(:price => "some text")).to be_falsey
|
416
401
|
expect(product.update_attributes(:price => Money.new(320, 'USD'))).to be_truthy
|
@@ -798,11 +783,228 @@ if defined? ActiveRecord
|
|
798
783
|
end
|
799
784
|
end
|
800
785
|
|
801
|
-
describe "register_currency" do
|
786
|
+
describe ".register_currency" do
|
802
787
|
it "attaches currency at model level" do
|
803
788
|
expect(Product.currency).to eq(Money::Currency.find(:usd))
|
804
789
|
expect(DummyProduct.currency).to eq(Money::Currency.find(:gbp))
|
805
790
|
end
|
806
791
|
end
|
792
|
+
|
793
|
+
describe "#read_monetized" do
|
794
|
+
it "returns monetized attribute's value" do
|
795
|
+
reduced_price = product.read_monetized(:reduced_price, :reduced_price_cents)
|
796
|
+
|
797
|
+
expect(reduced_price).to be_an_instance_of(Money)
|
798
|
+
expect(reduced_price).to eq(Money.new(product.reduced_price_cents, product.reduced_price_currency))
|
799
|
+
end
|
800
|
+
|
801
|
+
context "memoize" do
|
802
|
+
it "memoizes monetized attribute's value" do
|
803
|
+
product.instance_variable_set '@reduced_price', nil
|
804
|
+
reduced_price = product.read_monetized(:reduced_price, :reduced_price_cents)
|
805
|
+
|
806
|
+
expect(product.instance_variable_get('@reduced_price')).to eq(reduced_price)
|
807
|
+
end
|
808
|
+
|
809
|
+
it "resets memoized attribute's value if amount has changed" do
|
810
|
+
reduced_price = product.read_monetized(:reduced_price, :reduced_price_cents)
|
811
|
+
product.reduced_price_cents = 100
|
812
|
+
|
813
|
+
expect(product.read_monetized(:reduced_price, :reduced_price_cents)).not_to eq(reduced_price)
|
814
|
+
end
|
815
|
+
|
816
|
+
it "resets memoized attribute's value if currency has changed" do
|
817
|
+
reduced_price = product.read_monetized(:reduced_price, :reduced_price_cents)
|
818
|
+
product.reduced_price_currency = 'CAD'
|
819
|
+
|
820
|
+
expect(product.read_monetized(:reduced_price, :reduced_price_cents)).not_to eq(reduced_price)
|
821
|
+
end
|
822
|
+
end
|
823
|
+
|
824
|
+
context "with preserve_user_input set" do
|
825
|
+
around(:each) do |example|
|
826
|
+
MoneyRails::Configuration.preserve_user_input = true
|
827
|
+
example.run
|
828
|
+
MoneyRails::Configuration.preserve_user_input = false
|
829
|
+
end
|
830
|
+
|
831
|
+
it "has no effect if validation passes" do
|
832
|
+
product.price = '14'
|
833
|
+
|
834
|
+
expect(product.save).to be_truthy
|
835
|
+
expect(product.read_monetized(:price, :price_cents).to_s).to eq('14.00')
|
836
|
+
end
|
837
|
+
|
838
|
+
it "preserves user input if validation fails" do
|
839
|
+
product.price = '14,0'
|
840
|
+
|
841
|
+
expect(product.save).to be_falsy
|
842
|
+
expect(product.read_monetized(:price, :price_cents).to_s).to eq('14,0')
|
843
|
+
end
|
844
|
+
end
|
845
|
+
end
|
846
|
+
|
847
|
+
describe "#write_monetized" do
|
848
|
+
let(:value) { Money.new(1_000, 'LVL') }
|
849
|
+
|
850
|
+
it "sets monetized attribute's value to Money object" do
|
851
|
+
product.write_monetized :price, :price_cents, value, false, nil, {}
|
852
|
+
|
853
|
+
expect(product.price).to be_an_instance_of(Money)
|
854
|
+
expect(product.price_cents).to eq(value.cents)
|
855
|
+
# Because :price does not have a column for currency
|
856
|
+
expect(product.price.currency).to eq(Product.currency)
|
857
|
+
end
|
858
|
+
|
859
|
+
it "sets monetized attribute's value from a given Fixnum" do
|
860
|
+
product.write_monetized :price, :price_cents, 10, false, nil, {}
|
861
|
+
|
862
|
+
expect(product.price).to be_an_instance_of(Money)
|
863
|
+
expect(product.price_cents).to eq(1000)
|
864
|
+
end
|
865
|
+
|
866
|
+
it "sets monetized attribute's value from a given Float" do
|
867
|
+
product.write_monetized :price, :price_cents, 10.5, false, nil, {}
|
868
|
+
|
869
|
+
expect(product.price).to be_an_instance_of(Money)
|
870
|
+
expect(product.price_cents).to eq(1050)
|
871
|
+
end
|
872
|
+
|
873
|
+
it "resets monetized attribute when given blank input" do
|
874
|
+
product.write_monetized :price, :price_cents, nil, false, nil, { :allow_nil => true }
|
875
|
+
|
876
|
+
expect(product.price).to eq(nil)
|
877
|
+
end
|
878
|
+
|
879
|
+
it "sets monetized attribute to 0 when given a blank value" do
|
880
|
+
currency = product.price.currency
|
881
|
+
product.write_monetized :price, :price_cents, nil, false, nil, {}
|
882
|
+
|
883
|
+
expect(product.price.amount).to eq(0)
|
884
|
+
expect(product.price.currency).to eq(currency)
|
885
|
+
end
|
886
|
+
|
887
|
+
it "does not memoize monetized attribute's value if currency is read-only" do
|
888
|
+
product.write_monetized :price, :price_cents, value, false, nil, {}
|
889
|
+
|
890
|
+
price = product.instance_variable_get('@price')
|
891
|
+
|
892
|
+
expect(price).to be_an_instance_of(Money)
|
893
|
+
expect(price.amount).not_to eq(value.amount)
|
894
|
+
end
|
895
|
+
|
896
|
+
describe "instance_currency_name" do
|
897
|
+
it "updates instance_currency_name attribute" do
|
898
|
+
product.write_monetized :sale_price, :sale_price_amount, value, false, :sale_price_currency_code, {}
|
899
|
+
|
900
|
+
expect(product.sale_price).to eq(value)
|
901
|
+
expect(product.sale_price_currency_code).to eq('LVL')
|
902
|
+
end
|
903
|
+
|
904
|
+
it "memoizes monetized attribute's value with currency" do
|
905
|
+
product.write_monetized :sale_price, :sale_price_amount, value, false, :sale_price_currency_code, {}
|
906
|
+
|
907
|
+
expect(product.instance_variable_get('@sale_price')).to eq(value)
|
908
|
+
end
|
909
|
+
|
910
|
+
it "ignores empty instance_currency_name" do
|
911
|
+
product.write_monetized :sale_price, :sale_price_amount, value, false, '', {}
|
912
|
+
|
913
|
+
expect(product.sale_price.amount).to eq(value.amount)
|
914
|
+
expect(product.sale_price.currency).to eq(Product.currency)
|
915
|
+
end
|
916
|
+
|
917
|
+
it "ignores instance_currency_name that model does not respond to" do
|
918
|
+
product.write_monetized :sale_price, :sale_price_amount, value, false, :non_existing_currency, {}
|
919
|
+
|
920
|
+
expect(product.sale_price.amount).to eq(value.amount)
|
921
|
+
expect(product.sale_price.currency).to eq(Product.currency)
|
922
|
+
end
|
923
|
+
end
|
924
|
+
|
925
|
+
describe "error handling" do
|
926
|
+
let!(:old_price_value) { product.price }
|
927
|
+
|
928
|
+
it "ignores values that do not implement to_money method" do
|
929
|
+
product.write_monetized :price, :price_cents, [10], false, nil, {}
|
930
|
+
|
931
|
+
expect(product.price).to eq(old_price_value)
|
932
|
+
end
|
933
|
+
|
934
|
+
context "raise_error_on_money_parsing enabled" do
|
935
|
+
before { MoneyRails.raise_error_on_money_parsing = true }
|
936
|
+
after { MoneyRails.raise_error_on_money_parsing = false }
|
937
|
+
|
938
|
+
it "raises an ArgumentError when given an invalid value" do
|
939
|
+
expect {
|
940
|
+
product.write_monetized :price, :price_cents, '10-50', false, nil, {}
|
941
|
+
}.to raise_error(ArgumentError)
|
942
|
+
end
|
943
|
+
|
944
|
+
it "raises a Money::Currency::UnknownCurrency error when trying to set invalid currency" do
|
945
|
+
allow(product).to receive(:currency_for_price).and_return('INVALID_CURRENCY')
|
946
|
+
expect {
|
947
|
+
product.write_monetized :price, :price_cents, 10, false, nil, {}
|
948
|
+
}.to raise_error(Money::Currency::UnknownCurrency)
|
949
|
+
end
|
950
|
+
end
|
951
|
+
|
952
|
+
context "raise_error_on_money_parsing disabled" do
|
953
|
+
it "ignores when given invalid value" do
|
954
|
+
product.write_monetized :price, :price_cents, '10-50', false, nil, {}
|
955
|
+
|
956
|
+
expect(product.price).to eq(old_price_value)
|
957
|
+
end
|
958
|
+
|
959
|
+
it "raises a Money::Currency::UnknownCurrency error when trying to set invalid currency" do
|
960
|
+
allow(product).to receive(:currency_for_price).and_return('INVALID_CURRENCY')
|
961
|
+
product.write_monetized :price, :price_cents, 10, false, nil, {}
|
962
|
+
|
963
|
+
# Can not use public accessor here because currency_for_price is stubbed
|
964
|
+
expect(product.instance_variable_get('@price')).to eq(old_price_value)
|
965
|
+
end
|
966
|
+
end
|
967
|
+
end
|
968
|
+
end
|
969
|
+
|
970
|
+
describe "#currency_for" do
|
971
|
+
it "detects currency based on instance currency name" do
|
972
|
+
product = Product.new(:sale_price_currency_code => 'CAD')
|
973
|
+
currency = product.send(:currency_for, :sale_price, :sale_price_currency_code, nil)
|
974
|
+
|
975
|
+
expect(currency).to be_an_instance_of(Money::Currency)
|
976
|
+
expect(currency.iso_code).to eq('CAD')
|
977
|
+
end
|
978
|
+
|
979
|
+
it "detects currency based on currency passed as a block" do
|
980
|
+
product = Product.new
|
981
|
+
currency = product.send(:currency_for, :lambda_price, nil, ->(_) { 'CAD' })
|
982
|
+
|
983
|
+
expect(currency).to be_an_instance_of(Money::Currency)
|
984
|
+
expect(currency.iso_code).to eq('CAD')
|
985
|
+
end
|
986
|
+
|
987
|
+
it "detects currency based on currency passed explicitly" do
|
988
|
+
product = Product.new
|
989
|
+
currency = product.send(:currency_for, :bonus, nil, 'CAD')
|
990
|
+
|
991
|
+
expect(currency).to be_an_instance_of(Money::Currency)
|
992
|
+
expect(currency.iso_code).to eq('CAD')
|
993
|
+
end
|
994
|
+
|
995
|
+
it "falls back to a registered currency" do
|
996
|
+
product = Product.new
|
997
|
+
currency = product.send(:currency_for, :amount, nil, nil)
|
998
|
+
|
999
|
+
expect(currency).to eq(Product.currency)
|
1000
|
+
end
|
1001
|
+
|
1002
|
+
it "falls back to a default currency" do
|
1003
|
+
transaction = Transaction.new
|
1004
|
+
currency = transaction.send(:currency_for, :amount, nil, nil)
|
1005
|
+
|
1006
|
+
expect(currency).to eq(Money.default_currency)
|
1007
|
+
end
|
1008
|
+
end
|
807
1009
|
end
|
808
1010
|
end
|
@@ -4,7 +4,7 @@ require File.expand_path('../boot', __FILE__)
|
|
4
4
|
require "active_record/railtie"
|
5
5
|
require "action_controller/railtie"
|
6
6
|
require "action_mailer/railtie"
|
7
|
-
unless Rails::VERSION::MAJOR
|
7
|
+
unless Rails::VERSION::MAJOR >= 4
|
8
8
|
require "active_resource/railtie"
|
9
9
|
end
|
10
10
|
require "sprockets/railtie"
|
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
if defined?(Mongoid) && ::Mongoid::VERSION =~ /^5(.*)/
|
4
|
+
|
5
|
+
describe Money do
|
6
|
+
let!(:priceable) { Priceable.create(:price => Money.new(100, 'EUR')) }
|
7
|
+
let!(:priceable_from_num) { Priceable.create(:price => 1) }
|
8
|
+
let!(:priceable_from_string) { Priceable.create(:price => '1 EUR' )}
|
9
|
+
let!(:priceable_from_hash) { Priceable.create(:price => {:cents=>100, :currency_iso=>"EUR"} )}
|
10
|
+
let!(:priceable_from_hash_with_indifferent_access) {
|
11
|
+
Priceable.create(:price => {:cents=>100, :currency_iso=>"EUR"}.with_indifferent_access)
|
12
|
+
}
|
13
|
+
let(:priceable_from_string_with_hyphen) { Priceable.create(:price => '1-2 EUR' )}
|
14
|
+
let(:priceable_from_string_with_unknown_currency) { Priceable.create(:price => '1 TLDR') }
|
15
|
+
let(:priceable_with_infinite_precision) { Priceable.create(:price => Money.new(BigDecimal.new('100.1'), 'EUR')) }
|
16
|
+
let(:priceable_with_hash_field) {
|
17
|
+
Priceable.create(:price_hash => {
|
18
|
+
:key1 => Money.new(100, "EUR"),
|
19
|
+
:key2 => Money.new(200, "USD")
|
20
|
+
})
|
21
|
+
}
|
22
|
+
|
23
|
+
context "mongoize" do
|
24
|
+
it "correctly mongoizes a Money object to a hash of cents and currency" do
|
25
|
+
expect(priceable.price.cents).to eq(100)
|
26
|
+
expect(priceable.price.currency).to eq(Money::Currency.find('EUR'))
|
27
|
+
end
|
28
|
+
|
29
|
+
it "correctly mongoizes a Numeric object to a hash of cents and currency" do
|
30
|
+
expect(priceable_from_num.price.cents).to eq(100)
|
31
|
+
expect(priceable_from_num.price.currency).to eq(Money.default_currency)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "correctly mongoizes a String object to a hash of cents and currency" do
|
35
|
+
expect(priceable_from_string.price.cents).to eq(100)
|
36
|
+
expect(priceable_from_string.price.currency).to eq(Money::Currency.find('EUR'))
|
37
|
+
end
|
38
|
+
|
39
|
+
context "when MoneyRails.raise_error_on_money_parsing is true" do
|
40
|
+
before { MoneyRails.raise_error_on_money_parsing = true }
|
41
|
+
after { MoneyRails.raise_error_on_money_parsing = false }
|
42
|
+
|
43
|
+
it "raises exception if the mongoized value is a String with a hyphen" do
|
44
|
+
expect { priceable_from_string_with_hyphen }.to raise_error ArgumentError
|
45
|
+
end
|
46
|
+
|
47
|
+
it "raises exception if the mongoized value is a String with an unknown currency" do
|
48
|
+
expect { priceable_from_string_with_unknown_currency }.to raise_error ArgumentError
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "when MoneyRails.raise_error_on_money_parsing is false" do
|
53
|
+
it "does not correctly mongoize a String with a hyphen in its middle" do
|
54
|
+
expect(priceable_from_string_with_hyphen.price).to eq(nil)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "does not correctly mongoize a String with an unknown currency" do
|
58
|
+
expect(priceable_from_string_with_unknown_currency.price).to eq(nil)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
it "correctly mongoizes a hash of cents and currency" do
|
63
|
+
expect(priceable_from_hash.price.cents).to eq(100)
|
64
|
+
expect(priceable_from_hash.price.currency).to eq(Money::Currency.find('EUR'))
|
65
|
+
end
|
66
|
+
|
67
|
+
it "correctly mongoizes a HashWithIndifferentAccess of cents and currency" do
|
68
|
+
expect(priceable_from_hash_with_indifferent_access.price.cents).to eq(100)
|
69
|
+
expect(priceable_from_hash_with_indifferent_access.price.currency).to eq(Money::Currency.find('EUR'))
|
70
|
+
end
|
71
|
+
|
72
|
+
context "infinite_precision = true" do
|
73
|
+
before do
|
74
|
+
Money.infinite_precision = true
|
75
|
+
end
|
76
|
+
|
77
|
+
after do
|
78
|
+
Money.infinite_precision = false
|
79
|
+
end
|
80
|
+
|
81
|
+
it "correctly mongoizes a Money object to a hash of cents and currency" do
|
82
|
+
expect(priceable_with_infinite_precision.price.cents).to eq(BigDecimal.new('100.1'))
|
83
|
+
expect(priceable_with_infinite_precision.price.currency).to eq(Money::Currency.find('EUR'))
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it "correctly serializes a Hash field containing Money objects" do
|
89
|
+
expect(priceable_with_hash_field.price_hash[:key1][:cents]).to eq(100)
|
90
|
+
expect(priceable_with_hash_field.price_hash[:key2][:cents]).to eq(200)
|
91
|
+
expect(priceable_with_hash_field.price_hash[:key1][:currency_iso]).to eq('EUR')
|
92
|
+
expect(priceable_with_hash_field.price_hash[:key2][:currency_iso]).to eq('USD')
|
93
|
+
end
|
94
|
+
|
95
|
+
context "demongoize" do
|
96
|
+
subject { Priceable.first.price }
|
97
|
+
it { is_expected.to be_an_instance_of(Money) }
|
98
|
+
it { is_expected.to eq(Money.new(100, 'EUR')) }
|
99
|
+
|
100
|
+
it "returns 0 cents in default_currency if a nil value was stored" do
|
101
|
+
nil_priceable = Priceable.create(price: nil)
|
102
|
+
expect(nil_priceable.price.cents).to eq(0)
|
103
|
+
expect(nil_priceable.price.currency).to eq(Money.default_currency)
|
104
|
+
end
|
105
|
+
it 'returns nil if an unknown value was stored' do
|
106
|
+
zero_priceable = Priceable.create(:price => [])
|
107
|
+
expect(zero_priceable.price).to be_nil
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
context "evolve" do
|
112
|
+
it "correctly transforms a Money object into a Mongo friendly value" do
|
113
|
+
expect(Priceable.where(:price => Money.new(100, 'EUR')).first).to eq(priceable)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
data/spec/mongoid/four_spec.rb
CHANGED
@@ -4,6 +4,7 @@ if defined?(Mongoid) && ::Mongoid::VERSION =~ /^4(.*)/
|
|
4
4
|
|
5
5
|
describe Money do
|
6
6
|
let!(:priceable) { Priceable.create(:price => Money.new(100, 'EUR')) }
|
7
|
+
let!(:priceable_from_nil) { Priceable.create(:price => nil) }
|
7
8
|
let!(:priceable_from_num) { Priceable.create(:price => 1) }
|
8
9
|
let!(:priceable_from_string) { Priceable.create(:price => '1 EUR' )}
|
9
10
|
let!(:priceable_from_hash) { Priceable.create(:price => {:cents=>100, :currency_iso=>"EUR"} )}
|
@@ -21,6 +22,10 @@ if defined?(Mongoid) && ::Mongoid::VERSION =~ /^4(.*)/
|
|
21
22
|
}
|
22
23
|
|
23
24
|
context "mongoize" do
|
25
|
+
it "correctly mongoizes nil to nil" do
|
26
|
+
expect(priceable_from_nil.price).to be_nil
|
27
|
+
end
|
28
|
+
|
24
29
|
it "correctly mongoizes a Money object to a hash of cents and currency" do
|
25
30
|
expect(priceable.price.cents).to eq(100)
|
26
31
|
expect(priceable.price.currency).to eq(Money::Currency.find('EUR'))
|
@@ -97,11 +102,11 @@ if defined?(Mongoid) && ::Mongoid::VERSION =~ /^4(.*)/
|
|
97
102
|
it { is_expected.to be_an_instance_of(Money) }
|
98
103
|
it { is_expected.to eq(Money.new(100, 'EUR')) }
|
99
104
|
|
100
|
-
it "returns
|
105
|
+
it "returns nil if a nil value was stored" do
|
101
106
|
nil_priceable = Priceable.create(price: nil)
|
102
|
-
expect(nil_priceable.price
|
103
|
-
expect(nil_priceable.price.currency).to eq(Money.default_currency)
|
107
|
+
expect(nil_priceable.price).to be_nil
|
104
108
|
end
|
109
|
+
|
105
110
|
it 'returns nil if an unknown value was stored' do
|
106
111
|
zero_priceable = Priceable.create(:price => [])
|
107
112
|
expect(zero_priceable.price).to be_nil
|
data/spec/mongoid/three_spec.rb
CHANGED
@@ -4,6 +4,7 @@ if defined?(Mongoid) && ::Mongoid::VERSION =~ /^3(.*)/
|
|
4
4
|
|
5
5
|
describe Money do
|
6
6
|
let!(:priceable) { Priceable.create(:price => Money.new(100, 'EUR')) }
|
7
|
+
let!(:priceable_from_nil) { Priceable.create(:price => nil) }
|
7
8
|
let!(:priceable_from_num) { Priceable.create(:price => 1) }
|
8
9
|
let!(:priceable_from_string) { Priceable.create(:price => '1 EUR' )}
|
9
10
|
let!(:priceable_from_hash) { Priceable.create(:price => {:cents=>100, :currency_iso=>"EUR"} )}
|
@@ -21,6 +22,10 @@ if defined?(Mongoid) && ::Mongoid::VERSION =~ /^3(.*)/
|
|
21
22
|
}
|
22
23
|
|
23
24
|
context "mongoize" do
|
25
|
+
it "mongoizes correctly nil to nil" do
|
26
|
+
expect(priceable_from_nil.price).to be_nil
|
27
|
+
end
|
28
|
+
|
24
29
|
it "mongoizes correctly a Money object to a hash of cents and currency" do
|
25
30
|
expect(priceable.price.cents).to eq(100)
|
26
31
|
expect(priceable.price.currency).to eq(Money::Currency.find('EUR'))
|
@@ -96,11 +101,12 @@ if defined?(Mongoid) && ::Mongoid::VERSION =~ /^3(.*)/
|
|
96
101
|
subject { Priceable.first.price }
|
97
102
|
it { is_expected.to be_an_instance_of(Money) }
|
98
103
|
it { is_expected.to eq(Money.new(100, 'EUR')) }
|
99
|
-
|
104
|
+
|
105
|
+
it "returns nil if a nil value was stored" do
|
100
106
|
nil_priceable = Priceable.create(price: nil)
|
101
|
-
expect(nil_priceable.price
|
102
|
-
expect(nil_priceable.price.currency).to eq(Money.default_currency)
|
107
|
+
expect(nil_priceable.price).to be_nil
|
103
108
|
end
|
109
|
+
|
104
110
|
it 'returns nil if an unknown value was stored' do
|
105
111
|
zero_priceable = Priceable.create(:price => [])
|
106
112
|
expect(zero_priceable.price).to be_nil
|
data/spec/mongoid/two_spec.rb
CHANGED
@@ -4,12 +4,17 @@ if defined?(Mongoid) && ::Mongoid::VERSION =~ /^2(.*)/
|
|
4
4
|
|
5
5
|
describe Money do
|
6
6
|
let(:priceable) { Priceable.create(:price => Money.new(100, 'EUR')) }
|
7
|
+
let(:priceable_from_nil) { Priceable.create(:price => nil) }
|
7
8
|
let(:priceable_from_num) { Priceable.create(:price => 1) }
|
8
9
|
let(:priceable_from_string) { Priceable.create(:price => '1 EUR' )}
|
9
10
|
let(:priceable_with_infinite_precision) { Priceable.create(:price => Money.new(BigDecimal.new('100.1'), 'EUR')) }
|
10
11
|
let(:priceable_from_string_with_hyphen) { Priceable.create(:price => '1-2 EUR' )}
|
11
12
|
|
12
13
|
context "serialize" do
|
14
|
+
it "mongoizes correctly nil to nil" do
|
15
|
+
expect(priceable_from_nil.price).to be_nil
|
16
|
+
end
|
17
|
+
|
13
18
|
it "serializes correctly a Money object to a hash of cents and currency" do
|
14
19
|
expect(priceable.price.cents).to eq(100)
|
15
20
|
expect(priceable.price.currency).to eq(Money::Currency.find('EUR'))
|
@@ -60,11 +65,12 @@ if defined?(Mongoid) && ::Mongoid::VERSION =~ /^2(.*)/
|
|
60
65
|
subject { priceable.price }
|
61
66
|
it { is_expected.to be_an_instance_of(Money) }
|
62
67
|
it { is_expected.to eq(Money.new(100, 'EUR')) }
|
63
|
-
|
68
|
+
|
69
|
+
it "returns nil if a nil value was stored" do
|
64
70
|
nil_priceable = Priceable.create(price: nil)
|
65
|
-
expect(nil_priceable.price
|
66
|
-
expect(nil_priceable.price.currency).to eq(Money.default_currency)
|
71
|
+
expect(nil_priceable.price).to be_nil
|
67
72
|
end
|
73
|
+
|
68
74
|
it 'returns nil if an unknown value was stored' do
|
69
75
|
zero_priceable = Priceable.create(:price => [])
|
70
76
|
expect(zero_priceable.price).to be_nil
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: money-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andreas Loupasakis
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2016-
|
13
|
+
date: 2016-08-31 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: money
|
@@ -226,6 +226,7 @@ files:
|
|
226
226
|
- spec/dummy/script/rails
|
227
227
|
- spec/helpers/action_view_extension_spec.rb
|
228
228
|
- spec/helpers/form_helper_spec.rb
|
229
|
+
- spec/mongoid/five_spec.rb
|
229
230
|
- spec/mongoid/four_spec.rb
|
230
231
|
- spec/mongoid/three_spec.rb
|
231
232
|
- spec/mongoid/two_spec.rb
|
@@ -319,6 +320,7 @@ test_files:
|
|
319
320
|
- spec/dummy/script/rails
|
320
321
|
- spec/helpers/action_view_extension_spec.rb
|
321
322
|
- spec/helpers/form_helper_spec.rb
|
323
|
+
- spec/mongoid/five_spec.rb
|
322
324
|
- spec/mongoid/four_spec.rb
|
323
325
|
- spec/mongoid/three_spec.rb
|
324
326
|
- spec/mongoid/two_spec.rb
|