shopify-money 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.shared_examples_for "an object supporting to_money" do
4
+ it "supports to_money" do
5
+ expect(@value.to_money).to eq(@money)
6
+ expect(@value.to_money('CAD').currency).to eq(Money::Currency.find!('CAD'))
7
+ end
8
+ end
9
+
10
+ RSpec.describe Integer do
11
+ before(:each) do
12
+ @value = 1
13
+ @money = Money.new("1.00")
14
+ end
15
+
16
+ it_should_behave_like "an object supporting to_money"
17
+ end
18
+
19
+ RSpec.describe Float do
20
+ before(:each) do
21
+ @value = 1.23
22
+ @money = Money.new("1.23")
23
+ end
24
+
25
+ it_should_behave_like "an object supporting to_money"
26
+ end
27
+
28
+ RSpec.describe String do
29
+ before(:each) do
30
+ @value = "1.23"
31
+ @money = Money.new(@value)
32
+ end
33
+
34
+ it_should_behave_like "an object supporting to_money"
35
+ end
36
+
37
+ RSpec.describe BigDecimal do
38
+ before(:each) do
39
+ @value = BigDecimal.new("1.23")
40
+ @money = Money.new("1.23")
41
+ end
42
+
43
+ it_should_behave_like "an object supporting to_money"
44
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Money::Currency::Loader do
4
+
5
+ describe 'load_currencies' do
6
+ it 'loads the iso currency file' do
7
+ expect(subject.load_currencies['usd']['iso_code']).to eq('USD')
8
+ expect(subject.load_currencies['usd']['symbol']).to eq('$')
9
+ expect(subject.load_currencies['usd']['subunit_to_unit']).to eq(100)
10
+ expect(subject.load_currencies['usd']['smallest_denomination']).to eq(1)
11
+ end
12
+
13
+ it 'loads the non iso currency file' do
14
+ expect(subject.load_currencies['jep']['iso_code']).to eq('JEP')
15
+ end
16
+
17
+ it 'loads the historic iso currency file' do
18
+ expect(subject.load_currencies['eek']['iso_code']).to eq('EEK')
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,113 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe "Currency" do
4
+ CURRENCY_DATA = {
5
+ "iso_code": "USD",
6
+ "name": "United States Dollar",
7
+ "subunit_to_unit": 100,
8
+ "iso_numeric": "840",
9
+ "smallest_denomination": 1,
10
+ "minor_units": 2,
11
+ "symbol": '$',
12
+ "disambiguate_symbol": "US$",
13
+ "subunit_symbol": "¢",
14
+ "decimal_mark": ".",
15
+ }
16
+
17
+ let(:currency) { Money::Currency.new('usd') }
18
+
19
+ describe ".new" do
20
+ it "is constructable with a uppercase string" do
21
+ expect(Money::Currency.new('USD').iso_code).to eq('USD')
22
+ end
23
+
24
+ it "is constructable with a symbol" do
25
+ expect(Money::Currency.new(:usd).iso_code).to eq('USD')
26
+ end
27
+
28
+ it "is constructable with a lowercase string" do
29
+ expect(Money::Currency.new('usd').iso_code).to eq('USD')
30
+ end
31
+
32
+ it "raises when the currency is invalid" do
33
+ expect { Money::Currency.new('yyy') }.to raise_error(Money::Currency::UnknownCurrency)
34
+ end
35
+
36
+ it "raises when the currency is nil" do
37
+ expect { Money::Currency.new(nil) }.to raise_error(Money::Currency::UnknownCurrency)
38
+ end
39
+ end
40
+
41
+ describe ".find" do
42
+ it "returns nil when the currency is invalid" do
43
+ expect(Money::Currency.find('yyy')).to eq(nil)
44
+ end
45
+
46
+ it "returns a valid currency" do
47
+ expect(Money::Currency.find('usd')).to eq(Money::Currency.new('usd'))
48
+ end
49
+ end
50
+
51
+ describe ".find!" do
52
+ it "raises when the currency is invalid" do
53
+ expect { Money::Currency.find!('yyy') }.to raise_error(Money::Currency::UnknownCurrency)
54
+ end
55
+
56
+ it "returns a valid currency" do
57
+ expect(Money::Currency.find!('CAD')).to eq(Money::Currency.new('CAD'))
58
+ end
59
+ end
60
+
61
+ CURRENCY_DATA.each do |attribute, value|
62
+ describe "##{attribute}" do
63
+ it 'returns the correct value' do
64
+ expect(currency.public_send(attribute)).to eq(value)
65
+ end
66
+ end
67
+ end
68
+
69
+ describe "#eql?" do
70
+ it "returns true when both objects represent the same currency" do
71
+ expect(currency.eql?(Money.new(1, 'USD').currency)).to eq(true)
72
+ end
73
+
74
+ it "returns false when the currency iso is different" do
75
+ expect(currency.eql?(Money.new(1, 'CAD').currency)).to eq(false)
76
+ end
77
+ end
78
+
79
+ describe "==" do
80
+ it "returns true when both objects have the same currency" do
81
+ expect(currency == Money.new(1, 'USD').currency).to eq(true)
82
+ end
83
+
84
+ it "returns false when the currency iso is different" do
85
+ expect(currency == Money.new(1, 'CAD').currency).to eq(false)
86
+ end
87
+ end
88
+
89
+ describe "#to_s" do
90
+ it "to return the iso code string" do
91
+ expect(currency.to_s).to eq('USD')
92
+ end
93
+ end
94
+
95
+ describe "#compatible" do
96
+
97
+ it "returns true for the same currency" do
98
+ expect(currency.compatible?(Money::Currency.new('USD'))).to eq(true)
99
+ end
100
+
101
+ it "returns true for null_currency" do
102
+ expect(currency.compatible?(Money::NULL_CURRENCY)).to eq(true)
103
+ end
104
+
105
+ it "returns false for nil" do
106
+ expect(currency.compatible?(nil)).to eq(false)
107
+ end
108
+
109
+ it "returns false for a different currency" do
110
+ expect(currency.compatible?(Money::Currency.new('JPY'))).to eq(false)
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe Money::Helpers do
4
+
5
+ describe 'value_to_decimal' do
6
+ let (:amount) { BigDecimal.new('1.23') }
7
+ let (:money) { Money.new(amount) }
8
+
9
+ it 'returns the value of a money object' do
10
+ expect(subject.value_to_decimal(money)).to eq(amount)
11
+ end
12
+
13
+ it 'returns itself if it is already a big decimal' do
14
+ expect(subject.value_to_decimal(BigDecimal.new('1.23'))).to eq(amount)
15
+ end
16
+
17
+ it 'returns zero when nil' do
18
+ expect(subject.value_to_decimal(nil)).to eq(0)
19
+ end
20
+
21
+ it 'returns zero when empty' do
22
+ expect(subject.value_to_decimal('')).to eq(0)
23
+ end
24
+
25
+ it 'returns the bigdecimal version of a integer' do
26
+ expect(subject.value_to_decimal(1)).to eq(BigDecimal.new('1'))
27
+ end
28
+
29
+ it 'returns the bigdecimal version of a float' do
30
+ expect(subject.value_to_decimal(1.23)).to eq(amount)
31
+ end
32
+
33
+ it 'returns the bigdecimal version of a rational' do
34
+ expect(subject.value_to_decimal(amount.to_r)).to eq(amount)
35
+ end
36
+
37
+ it 'returns the bigdecimal version of a ruby number string' do
38
+ expect(subject.value_to_decimal('1.23')).to eq(amount)
39
+ end
40
+
41
+ it 'returns the bigdecimal version of a ruby number string with whitespace padding' do
42
+ expect(subject.value_to_decimal(' 1.23 ')).to eq(amount)
43
+ expect(subject.value_to_decimal("1.23\n")).to eq(amount)
44
+ expect(subject.value_to_decimal(' -1.23 ')).to eq(-amount)
45
+ end
46
+
47
+ it 'invalid string returns zero' do
48
+ expect(Money).to receive(:deprecate).once
49
+ expect(subject.value_to_decimal('invalid')).to eq(0)
50
+ end
51
+
52
+ it 'returns the bigdecimal representation of numbers while they are deprecated' do
53
+ expect(Money).to receive(:deprecate).exactly(2).times
54
+ expect(subject.value_to_decimal('1.23abc')).to eq(amount)
55
+ expect(subject.value_to_decimal("1.23\n23")).to eq(amount)
56
+ end
57
+
58
+ it 'raises on invalid object' do
59
+ expect { subject.value_to_decimal(OpenStruct.new(amount: 1)) }.to raise_error(ArgumentError)
60
+ end
61
+
62
+ it 'returns regular zero for a negative zero value' do
63
+ expect(subject.value_to_decimal(-BigDecimal.new(0))).to eq(BigDecimal.new(0))
64
+ end
65
+ end
66
+
67
+ describe 'subject.value_to_currency' do
68
+ it 'returns itself if it is already a currency' do
69
+ expect(subject.value_to_currency(Money::Currency.new('usd'))).to eq(Money::Currency.find!('usd'))
70
+ expect(subject.value_to_currency(Money::NULL_CURRENCY)).to be_a(Money::NullCurrency)
71
+ end
72
+
73
+ it 'returns the default currency when value is nil' do
74
+ expect(subject.value_to_currency(nil)).to eq(Money.default_currency)
75
+ end
76
+
77
+ it 'returns the default currency when value is empty' do
78
+ expect(subject.value_to_currency('')).to eq(Money.default_currency)
79
+ end
80
+
81
+ it 'returns the default currency when value is xxx' do
82
+ expect(subject.value_to_currency('xxx')).to eq(Money::NULL_CURRENCY)
83
+ end
84
+
85
+ it 'returns the current currency when value is set' do
86
+ expect(Money.with_currency('USD') { subject.value_to_currency(nil) }).to eq(Money::Currency.find!('usd'))
87
+ end
88
+
89
+ it 'returns the matching currency' do
90
+ expect(subject.value_to_currency('usd')).to eq(Money::Currency.new('USD'))
91
+ end
92
+
93
+ it 'returns the null currency when invalid iso is passed' do
94
+ expect(Money).to receive(:deprecate).once
95
+ expect(subject.value_to_currency('invalid')).to eq(Money::NULL_CURRENCY)
96
+ end
97
+
98
+ it 'raises on invalid object' do
99
+ expect { subject.value_to_currency(OpenStruct.new(amount: 1)) }.to raise_error(ArgumentError)
100
+ expect { subject.value_to_currency(1) }.to raise_error(ArgumentError)
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+
3
+ class NormalObject
4
+ include MoneyAccessor
5
+
6
+ money_accessor :price
7
+
8
+ def initialize(price)
9
+ @price = price
10
+ end
11
+ end
12
+
13
+ class StructObject < Struct.new(:price)
14
+ include MoneyAccessor
15
+
16
+ money_accessor :price
17
+ end
18
+
19
+ RSpec.shared_examples_for "an object with a money accessor" do
20
+ it "generates an attribute reader that returns a money object" do
21
+ object = described_class.new(100)
22
+
23
+ expect(object.price).to eq(Money.new(100))
24
+ end
25
+
26
+ it "generates an attribute reader that returns a nil object if the value was nil" do
27
+ object = described_class.new(nil)
28
+
29
+ expect(object.price).to eq(nil)
30
+ end
31
+
32
+ it "generates an attribute reader that returns a nil object if the value was blank" do
33
+ object = described_class.new('')
34
+
35
+ expect(object.price).to eq(nil)
36
+ end
37
+
38
+ it "generates an attribute writer that allow setting a money object" do
39
+ object = described_class.new(0)
40
+ object.price = Money.new(10)
41
+
42
+ expect(object.price).to eq(Money.new(10))
43
+ end
44
+
45
+ it "generates an attribute writer that allow setting a integer value" do
46
+ object = described_class.new(0)
47
+ object.price = 10
48
+
49
+ expect(object.price).to eq(Money.new(10))
50
+ end
51
+
52
+ it "generates an attribute writer that allow setting a float value" do
53
+ object = described_class.new(0)
54
+ object.price = 10.12
55
+
56
+ expect(object.price).to eq(Money.new(10.12))
57
+ end
58
+
59
+ it "generates an attribute writer that allow setting a nil value" do
60
+ object = described_class.new(0)
61
+ object.price = nil
62
+
63
+ expect(object.price).to eq(nil)
64
+ end
65
+
66
+ it "generates an attribute writer that allow setting a blank value" do
67
+ object = described_class.new(0)
68
+ object.price = ''
69
+
70
+ expect(object.price).to eq(nil)
71
+ end
72
+ end
73
+
74
+ RSpec.describe NormalObject do
75
+ it_behaves_like "an object with a money accessor"
76
+ end
77
+
78
+ RSpec.describe StructObject do
79
+ it_behaves_like "an object with a money accessor"
80
+
81
+ it 'does not generate an ivar to store the price value' do
82
+ object = described_class.new(10.00)
83
+
84
+ expect(object.instance_variable_get(:@price)).to eq(nil)
85
+ end
86
+ end
@@ -0,0 +1,298 @@
1
+ require 'spec_helper'
2
+
3
+ class MoneyRecord < ActiveRecord::Base
4
+ RATE = 1.17
5
+ before_validation do
6
+ self.price_usd = Money.new(self["price"] * RATE, 'USD') if self["price"]
7
+ end
8
+ money_column :price, currency_column: 'currency'
9
+ money_column :prix, currency_column: :devise
10
+ money_column :price_usd, currency: 'USD'
11
+ end
12
+
13
+ class MoneyWithValidation < ActiveRecord::Base
14
+ self.table_name = 'money_records'
15
+ validates :price, :currency, presence: true
16
+ money_column :price, currency_column: 'currency'
17
+ end
18
+
19
+ class MoneyWithReadOnlyCurrency < ActiveRecord::Base
20
+ self.table_name = 'money_records'
21
+ money_column :price, currency_column: 'currency', currency_read_only: true
22
+ end
23
+
24
+ class MoneyRecordCoerceNull < ActiveRecord::Base
25
+ self.table_name = 'money_records'
26
+ money_column :price, currency_column: 'currency', coerce_null: true
27
+ money_column :price_usd, currency: 'USD', coerce_null: true
28
+ end
29
+
30
+ class MoneyWithDelegatedCurrency < ActiveRecord::Base
31
+ self.table_name = 'money_records'
32
+ attr_accessor :delegated_record
33
+ delegate :currency, to: :delegated_record
34
+ money_column :price, currency_column: 'currency', currency_read_only: true
35
+ money_column :prix, currency_column: 'currency2', currency_read_only: true
36
+ def currency2
37
+ delegated_record.currency
38
+ end
39
+ end
40
+
41
+ RSpec.describe 'MoneyColumn' do
42
+ let(:amount) { 1.23 }
43
+ let(:currency) { 'EUR' }
44
+ let(:money) { Money.new(amount, currency) }
45
+ let(:toonie) { Money.new(2.00, 'CAD') }
46
+ let(:subject) { MoneyRecord.new(price: money, prix: toonie) }
47
+ let(:record) do
48
+ subject.devise = 'CAD'
49
+ subject.save
50
+ subject.reload
51
+ end
52
+
53
+ it 'returns money with currency from the default column' do
54
+ expect(record.price).to eq(Money.new(1.23, 'EUR'))
55
+ end
56
+
57
+ it 'writes the currency to the db' do
58
+ record.update(currency: nil)
59
+ record.update(price: Money.new(4, 'JPY'))
60
+ record.reload
61
+ expect(record.price.value).to eq(4)
62
+ expect(record.price.currency.to_s).to eq('JPY')
63
+ end
64
+
65
+ it 'duplicating the record keeps the money values' do
66
+ expect(MoneyRecord.new(price: money).clone.price).to eq(money)
67
+ expect(MoneyRecord.new(price: money).dup.price).to eq(money)
68
+ end
69
+
70
+ it 'returns money with currency from the specified column' do
71
+ expect(record.prix).to eq(Money.new(2.00, 'CAD'))
72
+ end
73
+
74
+ it 'returns money with the hardcoded currency' do
75
+ expect(record.price_usd).to eq(Money.new(1.44, 'USD'))
76
+ end
77
+
78
+ it 'returns money with null currency when the currency in the DB is invalid' do
79
+ expect(Money).to receive(:deprecate).once
80
+ record.update_columns(currency: 'invalid')
81
+ record.reload
82
+ expect(record.price.currency).to be_a(Money::NullCurrency)
83
+ expect(record.price.value).to eq(1.23)
84
+ end
85
+
86
+ it 'handles legacy support for saving floats' do
87
+ record.update(price: 3.21, prix: 3.21)
88
+ expect(record.price.value).to eq(3.21)
89
+ expect(record.price.currency.to_s).to eq(currency)
90
+ expect(record.price_usd.value).to eq(3.76)
91
+ expect(record.price_usd.currency.to_s).to eq('USD')
92
+ expect(record.prix.value).to eq(3.21)
93
+ expect(record.prix.currency.to_s).to eq('CAD')
94
+ end
95
+
96
+ it 'handles legacy support for saving floats with correct currency rounding' do
97
+ record.update(price: 3.2112, prix: 3.2156)
98
+ expect(record.attributes['price']).to eq(3.21)
99
+ expect(record.price.value).to eq(3.21)
100
+ expect(record.price.currency.to_s).to eq(currency)
101
+ expect(record.attributes['prix']).to eq(3.22)
102
+ expect(record.prix.value).to eq(3.22)
103
+ expect(record.prix.currency.to_s).to eq('CAD')
104
+ end
105
+
106
+ it 'handles legacy support for saving string' do
107
+ record.update(price: '3.21', prix: '3.21')
108
+ expect(record.price.value).to eq(3.21)
109
+ expect(record.price.currency.to_s).to eq(currency)
110
+ expect(record.price_usd.value).to eq(3.76)
111
+ expect(record.price_usd.currency.to_s).to eq('USD')
112
+ expect(record.prix.value).to eq(3.21)
113
+ expect(record.prix.currency.to_s).to eq('CAD')
114
+ end
115
+
116
+ it 'does not overwrite a currency column with a default currency when saving zero' do
117
+ expect(record.currency.to_s).to eq('EUR')
118
+ record.update(price: Money.zero)
119
+ expect(record.currency.to_s).to eq('EUR')
120
+ end
121
+
122
+ it 'does overwrite a currency if changed but will show a deprecation notice' do
123
+ expect(record.currency.to_s).to eq('EUR')
124
+ expect(Money).to receive(:deprecate).once
125
+ record.update(price: Money.new(4, 'JPY'))
126
+ expect(record.currency.to_s).to eq('JPY')
127
+ end
128
+
129
+ describe 'non-fractional-currencies' do
130
+ let(:money) { Money.new(1, 'JPY') }
131
+
132
+ it 'returns money with currency from the default column' do
133
+ expect(record.price).to eq(Money.new(1, 'JPY'))
134
+ end
135
+ end
136
+
137
+ describe 'three-decimal currencies' do
138
+ let(:money) { Money.new(1.234, 'JOD') }
139
+
140
+ it 'returns money with currency from the default column' do
141
+ expect(record.price).to eq(Money.new(1.234, 'JOD'))
142
+ end
143
+ end
144
+
145
+ describe 'garbage amount' do
146
+ let(:amount) { 'foo' }
147
+
148
+ it 'raises a deprecation warning' do
149
+ expect { subject }.to raise_error(ActiveSupport::DeprecationException)
150
+ end
151
+ end
152
+
153
+ describe 'garbage currency' do
154
+ let(:currency) { 'foo' }
155
+
156
+ it 'raises an UnknownCurrency error' do
157
+ expect { subject }.to raise_error(ActiveSupport::DeprecationException)
158
+ end
159
+ end
160
+
161
+ describe 'wrong money_column currency arguments' do
162
+ let(:subject) do
163
+ class MoneyWithWrongCurrencyArguments < ActiveRecord::Base
164
+ self.table_name = 'money_records'
165
+ money_column :price, currency_column: :currency, currency: 'USD'
166
+ end
167
+ end
168
+
169
+ it 'raises an ArgumentError' do
170
+ expect { subject }.to raise_error(ArgumentError, 'cannot set both currency_column and a fixed currency')
171
+ end
172
+ end
173
+
174
+ describe 'missing money_column currency arguments' do
175
+ let(:subject) do
176
+ class MoneyWithWrongCurrencyArguments < ActiveRecord::Base
177
+ self.table_name = 'money_records'
178
+ money_column :price
179
+ end
180
+ end
181
+
182
+ it 'raises an ArgumentError' do
183
+ expect { subject }.to raise_error(ArgumentError, 'must set one of :currency_column or :currency options')
184
+ end
185
+ end
186
+
187
+ describe 'null currency and validations' do
188
+ let(:currency) { Money::NULL_CURRENCY }
189
+ let(:subject) { MoneyWithValidation.new(price: money) }
190
+
191
+ it 'is not allowed to be saved because `to_s` returns a blank string' do
192
+ subject.valid?
193
+ expect(subject.errors[:currency]).to include("can't be blank")
194
+ end
195
+ end
196
+
197
+ describe 'read_only_currency true' do
198
+ it 'does not write the currency to the db' do
199
+ record = MoneyWithReadOnlyCurrency.create
200
+ record.update_columns(price: 1, currency: 'USD')
201
+ expect(Money).to receive(:deprecate).once
202
+ record.update(price: Money.new(4, 'CAD'))
203
+ expect(record.price.value).to eq(4)
204
+ expect(record.price.currency.to_s).to eq('USD')
205
+ end
206
+
207
+ it 'reads the currency that is already in the db' do
208
+ record = MoneyWithReadOnlyCurrency.create
209
+ record.update_columns(currency: 'USD', price: 1)
210
+ record.reload
211
+ expect(record.price.value).to eq(1)
212
+ expect(record.price.currency.to_s).to eq('USD')
213
+ end
214
+
215
+ it 'reads an invalid currency from the db and generates a no currency object' do
216
+ expect(Money).to receive(:deprecate).once
217
+ record = MoneyWithReadOnlyCurrency.create
218
+ record.update_columns(currency: 'invalid', price: 1)
219
+ record.reload
220
+ expect(record.price.value).to eq(1)
221
+ expect(record.price.currency.to_s).to eq('')
222
+ end
223
+
224
+ it 'sets the currency correctly when the currency is changed' do
225
+ record = MoneyWithReadOnlyCurrency.create(currency: 'CAD', price: 1)
226
+ record.currency = 'USD'
227
+ expect(record.price.currency.to_s).to eq('USD')
228
+ end
229
+
230
+ it 'handle cases where the delegate allow_nil is false' do
231
+ record = MoneyWithDelegatedCurrency.new(price: Money.new(10, 'USD'), delegated_record: MoneyRecord.new(currency: 'USD'))
232
+ expect(record.price.currency.to_s).to eq('USD')
233
+ end
234
+
235
+ it 'handle cases where a manual delegate does not allow nil' do
236
+ record = MoneyWithDelegatedCurrency.new(prix: Money.new(10, 'USD'), delegated_record: MoneyRecord.new(currency: 'USD'))
237
+ expect(record.price.currency.to_s).to eq('USD')
238
+ end
239
+ end
240
+
241
+ describe 'coerce_null' do
242
+ it 'returns nil when money value have not been set and coerce_null is false' do
243
+ record = MoneyRecord.new(price: nil)
244
+ expect(record.price).to eq(nil)
245
+ expect(record.price_usd).to eq(nil)
246
+ end
247
+
248
+ it 'returns 0$ when money value have not been set and coerce_null is true' do
249
+ record = MoneyRecordCoerceNull.new(price: nil)
250
+ expect(record.price.value).to eq(0)
251
+ expect(record.price_usd.value).to eq(0)
252
+ end
253
+ end
254
+
255
+ describe 'memoization' do
256
+ it 'correctly memoizes the read value' do
257
+ expect(record.instance_variable_get(:@money_column_cache)["price"]).to eq(nil)
258
+ price = Money.new(1, 'USD')
259
+ record = MoneyRecord.new(price: price)
260
+ expect(record.price).to eq(price)
261
+ expect(record.instance_variable_get(:@money_column_cache)["price"]).to eq(price)
262
+ end
263
+
264
+ it 'memoizes values get reset when writing a new value' do
265
+ price = Money.new(1, 'USD')
266
+ record = MoneyRecord.new(price: price)
267
+ expect(record.price).to eq(price)
268
+ price = Money.new(2, 'USD')
269
+ record.update!(price: price)
270
+ expect(record.price).to eq(price)
271
+ expect(record.instance_variable_get(:@money_column_cache)["price"]).to eq(price)
272
+ end
273
+
274
+ it 'reload will clear memoizes money values' do
275
+ price = Money.new(1, 'USD')
276
+ record = MoneyRecord.create(price: price)
277
+ expect(record.price).to eq(price)
278
+ expect(record.instance_variable_get(:@money_column_cache)["price"]).to eq(price)
279
+ record.reload
280
+ expect(record.instance_variable_get(:@money_column_cache)["price"]).to eq(nil)
281
+ record.price
282
+ expect(record.instance_variable_get(:@money_column_cache)["price"]).to eq(price)
283
+ end
284
+ end
285
+
286
+ describe 'ActiveRecord querying' do
287
+ it 'can be serialized for querying on the value' do
288
+ price = Money.new(1, 'USD')
289
+ record = MoneyRecord.create!(price: price)
290
+ expect(MoneyRecord.find_by(price: price)).to eq(record)
291
+ end
292
+
293
+ it 'nil value persist in the DB' do
294
+ record = MoneyRecord.create!(price: nil)
295
+ expect(MoneyRecord.find_by(price: nil)).to eq(record)
296
+ end
297
+ end
298
+ end