shopify-money 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e9f1cbeeb932d4f9f9d1abb4ef49ae366e9f5467
4
- data.tar.gz: a257d7dd55acb578b89690f153a8a613bc334089
3
+ metadata.gz: 964eb834ae37a4f224f6ce79f5dcd75ae544d8a6
4
+ data.tar.gz: 400e4c5737f8960c44ebb8d89b78f8fcc7506db2
5
5
  SHA512:
6
- metadata.gz: ed81f64a2a63282fa961088381634c8d2d414ea30120f2e39049afb6cb6e5f85a36069d550f6ba956636481b9ccbde8eae8428347e5b105ecfd16b22146b2b5e
7
- data.tar.gz: 7edd2b3d5dd0ec6d0f695a0959fd55f25e4c1bfefdd7c96171825b77085a353892d954d6edb00a49a7a224b57e019982f981554b3c8896b1ab02c443e4682115
6
+ metadata.gz: 11c8e67a85e4709618df2f7443bd6102929e12883339fa8ad55ea7c9a2b2c3b951fe980250ef421b0b7d8066770eac6bad9fe6ea74493095722b00d610ae2b28
7
+ data.tar.gz: 2b4abc9631217467ac967254cd41e5921635acacc351fd69d949fb51e342a6fe8bcf437a0c75d01b078dc5182bd598c157e5c7a1b3089df128832f648e93d4a6
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ sudo: false
2
+ language: ruby
3
+ cache: bundler
4
+ branches:
5
+ only:
6
+ - master
7
+ rvm:
8
+ - 2.5
9
+ - 2.4
10
+ - 2.3
11
+ before_install:
12
+ # https://github.com/travis-ci/travis-ci/issues/8978#issuecomment-354036443
13
+ - gem update --system
14
+ - gem install bundler
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # money
2
2
 
3
- [![Build Status](https://circleci.com/gh/Shopify/money.png?circle-token=88060f61185838446bb65723d96658bdd74ebb3c)](https://circleci.com/gh/Shopify/money) [![codecov](https://codecov.io/gh/Shopify/money/branch/master/graph/badge.svg)](https://codecov.io/gh/Shopify/money)
3
+ [![Build Status](https://travis-ci.org/Shopify/money.svg?branch=master)](https://travis-ci.org/Shopify/money) [![codecov](https://codecov.io/gh/Shopify/money/branch/master/graph/badge.svg)](https://codecov.io/gh/Shopify/money)
4
4
 
5
5
 
6
6
  money_column expects a decimal 8,3 database field.
data/lib/money/helpers.rb CHANGED
@@ -6,7 +6,7 @@ class Money
6
6
  module_function
7
7
 
8
8
  NUMERIC_REGEX = /\A\s*[\+\-]?\d*(\.\d+)?\s*\z/
9
- DECIMAL_ZERO = BigDecimal.new(0).freeze
9
+ DECIMAL_ZERO = BigDecimal(0).freeze
10
10
  MAX_DECIMAL = 21
11
11
 
12
12
  def value_to_decimal(num)
@@ -19,11 +19,11 @@ class Money
19
19
  when nil, 0, ''
20
20
  DECIMAL_ZERO
21
21
  when Integer
22
- BigDecimal.new(num)
22
+ BigDecimal(num)
23
23
  when Float
24
- BigDecimal.new(num, Float::DIG)
24
+ BigDecimal(num, Float::DIG)
25
25
  when Rational
26
- BigDecimal.new(num, MAX_DECIMAL)
26
+ BigDecimal(num, MAX_DECIMAL)
27
27
  when String
28
28
  string_to_decimal(num)
29
29
  else
@@ -57,12 +57,12 @@ class Money
57
57
 
58
58
  def string_to_decimal(num)
59
59
  if num =~ NUMERIC_REGEX
60
- return BigDecimal.new(num)
60
+ return BigDecimal(num)
61
61
  end
62
62
 
63
63
  Money.deprecate("using Money.new('#{num}') is deprecated and will raise an ArgumentError in the next major release")
64
64
  begin
65
- BigDecimal.new(num)
65
+ BigDecimal(num)
66
66
  rescue ArgumentError
67
67
  DECIMAL_ZERO
68
68
  end
data/lib/money/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  class Money
2
- VERSION = "0.10.0"
2
+ VERSION = "0.11.0"
3
3
  end
@@ -5,7 +5,7 @@ module MoneyColumn
5
5
  end
6
6
 
7
7
  def reload(*)
8
- clear_money_column_cache
8
+ clear_money_column_cache if persisted?
9
9
  super
10
10
  end
11
11
 
@@ -17,7 +17,7 @@ module MoneyColumn
17
17
  private
18
18
 
19
19
  def clear_money_column_cache
20
- @money_column_cache.clear if persisted?
20
+ @money_column_cache.clear
21
21
  end
22
22
 
23
23
  def init_internals
@@ -25,69 +25,101 @@ module MoneyColumn
25
25
  super
26
26
  end
27
27
 
28
+ def read_money_attribute(column)
29
+ column = column.to_s
30
+ options = self.class.money_column_options[column]
31
+
32
+ return @money_column_cache[column] if @money_column_cache[column]
33
+
34
+ value = self[column]
35
+
36
+ return if value.nil? && !options[:coerce_null]
37
+
38
+ @money_column_cache[column] = Money.new(value, options[:currency] || send(options[:currency_column]))
39
+ end
40
+
41
+ def write_money_attribute(column, money)
42
+ column = column.to_s
43
+ options = self.class.money_column_options[column]
44
+
45
+ @money_column_cache[column] = nil
46
+
47
+ if money.blank?
48
+ return self[column] = nil
49
+ end
50
+
51
+ currency_raw_source = options[:currency] || (send(options[:currency_column]) rescue nil)
52
+ currency_source = Money::Helpers.value_to_currency(currency_raw_source)
53
+
54
+ if !money.is_a?(Money)
55
+ return self[column] = Money.new(money, currency_source).value
56
+ end
57
+
58
+ if currency_raw_source && !currency_source.compatible?(money.currency)
59
+ Money.deprecate("[money_column] currency mismatch between #{currency_source} and #{money.currency}.")
60
+ end
61
+
62
+ self[column] = money.value
63
+ self[options[:currency_column]] = money.currency.to_s unless options[:currency_read_only] || money.no_currency?
64
+ end
65
+
28
66
  module ClassMethods
67
+ attr_reader :money_column_options
68
+
29
69
  def money_column(*columns, currency_column: nil, currency: nil, currency_read_only: false, coerce_null: false)
30
- raise ArgumentError, 'cannot set both currency_column and a fixed currency' if currency && currency_column
31
-
32
- if currency
33
- currency_iso = Money::Currency.find!(currency).to_s
34
- currency_read_only = true
35
- elsif currency_column
36
- clear_cache_on_currency_change(currency_column)
37
- else
38
- raise ArgumentError, 'must set one of :currency_column or :currency options'
70
+ @money_column_options ||= {}
71
+
72
+ options = normalize_money_column_options(
73
+ currency_column: currency_column,
74
+ currency: currency,
75
+ currency_read_only: currency_read_only,
76
+ coerce_null: coerce_null
77
+ )
78
+
79
+ if options[:currency_column]
80
+ clear_cache_on_currency_change(options[:currency_column])
39
81
  end
40
- currency_column = currency_column.to_s.freeze
41
82
 
42
83
  columns.flatten.each do |column|
43
84
  column_string = column.to_s.freeze
85
+
86
+ @money_column_options[column_string] = options
87
+
44
88
  attribute(column_string, MoneyColumn::ActiveRecordType.new)
45
- money_column_reader(column_string, currency_column, currency_iso, coerce_null)
46
- money_column_writer(column_string, currency_column, currency_iso, currency_read_only)
89
+
90
+ define_method column do
91
+ read_money_attribute(column_string)
92
+ end
93
+
94
+ define_method "#{column}=" do |money|
95
+ write_money_attribute(column_string, money)
96
+ end
47
97
  end
48
98
  end
49
99
 
50
100
  private
51
101
 
52
- def clear_cache_on_currency_change(currency_column)
53
- define_method "#{currency_column}=" do |value|
54
- @money_column_cache.clear
55
- super(value)
102
+ def normalize_money_column_options(options)
103
+ raise ArgumentError, 'cannot set both :currency_column and :currency options' if options[:currency] && options[:currency_column]
104
+ raise ArgumentError, 'must set one of :currency_column or :currency options' unless options[:currency] || options[:currency_column]
105
+
106
+ if options[:currency]
107
+ options[:currency] = Money::Currency.find!(options[:currency]).to_s.freeze
108
+ options[:currency_read_only] = true
56
109
  end
57
- end
58
110
 
59
- def money_column_reader(column, currency_column, currency_iso, coerce_null)
60
- define_method column do
61
- return @money_column_cache[column] if @money_column_cache[column]
62
- value = read_attribute(column)
63
- return if value.nil? && !coerce_null
64
- iso = currency_iso || send(currency_column)
65
- @money_column_cache[column] = Money.new(value, iso)
111
+ if options[:currency_column]
112
+ options[:currency_column] = options[:currency_column].to_s.freeze
66
113
  end
114
+ options
67
115
  end
68
116
 
69
- def money_column_writer(column, currency_column, currency_iso, currency_read_only)
70
- define_method "#{column}=" do |money|
71
- @money_column_cache[column] = nil
72
-
73
- if money.blank?
74
- write_attribute(column, nil)
75
- return nil
76
- end
77
-
78
- currency_raw_source = currency_iso || (send(currency_column) rescue nil)
79
- currency_source = Money::Helpers.value_to_currency(currency_raw_source)
80
-
81
- if !money.is_a?(Money)
82
- return write_attribute(column, Money.new(money, currency_source).value)
83
- end
84
-
85
- if currency_raw_source && !currency_source.compatible?(money.currency)
86
- Money.deprecate("[money_column] currency mismatch between #{currency_source} and #{money.currency}.")
87
- end
117
+ def clear_cache_on_currency_change(currency_column)
118
+ return if money_column_options.any? { |_, opt| opt[:currency_column] == currency_column }
88
119
 
89
- write_attribute(column, money.value)
90
- write_attribute(currency_column, money.currency.to_s) unless currency_read_only || money.no_currency?
120
+ define_method "#{currency_column}=" do |value|
121
+ clear_money_column_cache
122
+ super(value)
91
123
  end
92
124
  end
93
125
  end
@@ -36,7 +36,7 @@ end
36
36
 
37
37
  RSpec.describe BigDecimal do
38
38
  before(:each) do
39
- @value = BigDecimal.new("1.23")
39
+ @value = BigDecimal("1.23")
40
40
  @money = Money.new("1.23")
41
41
  end
42
42
 
data/spec/helpers_spec.rb CHANGED
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  RSpec.describe Money::Helpers do
4
4
 
5
5
  describe 'value_to_decimal' do
6
- let (:amount) { BigDecimal.new('1.23') }
6
+ let (:amount) { BigDecimal('1.23') }
7
7
  let (:money) { Money.new(amount) }
8
8
 
9
9
  it 'returns the value of a money object' do
@@ -11,7 +11,7 @@ RSpec.describe Money::Helpers do
11
11
  end
12
12
 
13
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)
14
+ expect(subject.value_to_decimal(BigDecimal('1.23'))).to eq(amount)
15
15
  end
16
16
 
17
17
  it 'returns zero when nil' do
@@ -23,7 +23,7 @@ RSpec.describe Money::Helpers do
23
23
  end
24
24
 
25
25
  it 'returns the bigdecimal version of a integer' do
26
- expect(subject.value_to_decimal(1)).to eq(BigDecimal.new('1'))
26
+ expect(subject.value_to_decimal(1)).to eq(BigDecimal('1'))
27
27
  end
28
28
 
29
29
  it 'returns the bigdecimal version of a float' do
@@ -60,7 +60,7 @@ RSpec.describe Money::Helpers do
60
60
  end
61
61
 
62
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))
63
+ expect(subject.value_to_decimal(-BigDecimal(0))).to eq(BigDecimal(0))
64
64
  end
65
65
  end
66
66
 
@@ -38,6 +38,17 @@ class MoneyWithDelegatedCurrency < ActiveRecord::Base
38
38
  end
39
39
  end
40
40
 
41
+ class MoneyWithCustomAccessors < ActiveRecord::Base
42
+ self.table_name = 'money_records'
43
+ money_column :price, currency_column: 'currency'
44
+ def price
45
+ read_money_attribute(:price)
46
+ end
47
+ def price=(value)
48
+ write_money_attribute(:price, value + 1)
49
+ end
50
+ end
51
+
41
52
  RSpec.describe 'MoneyColumn' do
42
53
  let(:amount) { 1.23 }
43
54
  let(:currency) { 'EUR' }
@@ -167,7 +178,7 @@ RSpec.describe 'MoneyColumn' do
167
178
  end
168
179
 
169
180
  it 'raises an ArgumentError' do
170
- expect { subject }.to raise_error(ArgumentError, 'cannot set both currency_column and a fixed currency')
181
+ expect { subject }.to raise_error(ArgumentError, 'cannot set both :currency_column and :currency options')
171
182
  end
172
183
  end
173
184
 
@@ -295,4 +306,19 @@ RSpec.describe 'MoneyColumn' do
295
306
  expect(MoneyRecord.find_by(price: nil)).to eq(record)
296
307
  end
297
308
  end
309
+
310
+ describe 'money column attribute accessors' do
311
+ it 'allows to overwrite the setter' do
312
+ amount = Money.new(1, 'USD')
313
+ expect(MoneyWithCustomAccessors.new(price: amount).price).to eq(MoneyRecord.new(price: amount).price + 1)
314
+ end
315
+
316
+ it 'correctly assigns the money_column_cache' do
317
+ amount = Money.new(1, 'USD')
318
+ object = MoneyWithCustomAccessors.new(price: amount)
319
+ expect(object.instance_variable_get(:@money_column_cache)['price']).to eql(nil)
320
+ expect(object.price).to eql(amount + 1)
321
+ expect(object.instance_variable_get(:@money_column_cache)['price']).to eql(amount + 1)
322
+ end
323
+ end
298
324
  end
@@ -315,7 +315,7 @@ RSpec.describe MoneyParser do
315
315
  end
316
316
  end
317
317
 
318
- describe "parsing of fixnum" do
318
+ describe "parsing of integer" do
319
319
  it "parses 1" do
320
320
  expect(@parser.parse(1)).to eq(Money.new(1))
321
321
  end
data/spec/money_spec.rb CHANGED
@@ -71,15 +71,15 @@ RSpec.describe "Money" do
71
71
  end
72
72
 
73
73
  it "is constructable with a BigDecimal" do
74
- expect(Money.new(BigDecimal.new("1.23"))).to eq(Money.new(1.23))
74
+ expect(Money.new(BigDecimal("1.23"))).to eq(Money.new(1.23))
75
75
  end
76
76
 
77
- it "is constructable with a Fixnum" do
77
+ it "is constructable with an Integer" do
78
78
  expect(Money.new(3)).to eq(Money.new(3.00))
79
79
  end
80
80
 
81
81
  it "is construcatable with a Float" do
82
- expect(Money.new(3.00)).to eq(Money.new(BigDecimal.new('3.00')))
82
+ expect(Money.new(3.00)).to eq(Money.new(BigDecimal('3.00')))
83
83
  end
84
84
 
85
85
  it "is construcatable with a String" do
@@ -282,7 +282,7 @@ RSpec.describe "Money" do
282
282
  end
283
283
 
284
284
  it "supports to_d" do
285
- expect(Money.new(1.29).to_d).to eq(BigDecimal.new('1.29'))
285
+ expect(Money.new(1.29).to_d).to eq(BigDecimal('1.29'))
286
286
  end
287
287
 
288
288
  it "supports to_f" do
@@ -434,7 +434,7 @@ RSpec.describe "Money" do
434
434
  end
435
435
 
436
436
  it "returns cents as a decimal value = 1.00" do
437
- expect(money.value).to eq(BigDecimal.new("1.00"))
437
+ expect(money.value).to eq(BigDecimal("1.00"))
438
438
  end
439
439
 
440
440
  it "returns cents as 100 cents" do
@@ -445,10 +445,6 @@ RSpec.describe "Money" do
445
445
  expect(money.subunits).to eq(100)
446
446
  end
447
447
 
448
- it "returns cents as a Fixnum" do
449
- expect(money.subunits).to be_an_instance_of(Fixnum)
450
- end
451
-
452
448
  it "is greater than $0" do
453
449
  expect(money).to be > Money.new(0.00)
454
450
  end
@@ -625,7 +621,7 @@ RSpec.describe "Money" do
625
621
  let (:money) { Money.new(1.125) }
626
622
 
627
623
  it "rounds 3rd decimal place" do
628
- expect(money.value).to eq(BigDecimal.new("1.13"))
624
+ expect(money.value).to eq(BigDecimal("1.13"))
629
625
  end
630
626
  end
631
627
 
@@ -673,7 +669,7 @@ RSpec.describe "Money" do
673
669
  it "accepts numeric values" do
674
670
  expect(Money.from_amount(1)).to eq Money.from_cents(1_00)
675
671
  expect(Money.from_amount(1.0)).to eq Money.from_cents(1_00)
676
- expect(Money.from_amount(BigDecimal.new("1"))).to eq Money.from_cents(1_00)
672
+ expect(Money.from_amount(BigDecimal("1"))).to eq Money.from_cents(1_00)
677
673
  end
678
674
 
679
675
  it "accepts string values" do
@@ -688,7 +684,7 @@ RSpec.describe "Money" do
688
684
  expect { Money.from_amount(1, "CAD") }.to_not raise_error
689
685
  end
690
686
 
691
- it "accepts Rationla number" do
687
+ it "accepts Rational number" do
692
688
  expect(Money.from_amount(Rational("999999999999999999.999")).value).to eql(BigDecimal.new("1000000000000000000", Money::Helpers::MAX_DECIMAL))
693
689
  expect(Money.from_amount(Rational("999999999999999999.99")).value).to eql(BigDecimal.new("999999999999999999.99", Money::Helpers::MAX_DECIMAL))
694
690
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shopify-money
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify Inc
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-04-04 00:00:00.000000000 Z
11
+ date: 2018-04-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -118,12 +118,12 @@ files:
118
118
  - ".document"
119
119
  - ".gitignore"
120
120
  - ".rspec"
121
+ - ".travis.yml"
121
122
  - Gemfile
122
123
  - LICENSE.txt
123
124
  - README.md
124
125
  - Rakefile
125
126
  - bin/console
126
- - circle.yml
127
127
  - config/currency_historic.json
128
128
  - config/currency_iso.json
129
129
  - config/currency_non_iso.json
data/circle.yml DELETED
@@ -1,13 +0,0 @@
1
- machine:
2
- ruby:
3
- version: 2.3.3
4
-
5
- database:
6
- override:
7
- - echo "Skipping database setup"
8
-
9
- test:
10
- override:
11
- - bundle exec rake
12
- post:
13
- - bash <(curl -s https://codecov.io/bash)