amountable 0.1.0 → 0.2.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: a50976a37bd54567185d9369a702a40dd5b38795
4
- data.tar.gz: df9bce5b74e8ce773e8b068787be2b80a2184234
3
+ metadata.gz: 70fd4bd67ff8f0f3c0a67ea5701fb035934c3a8f
4
+ data.tar.gz: 3a7f3ce3ff221dcbe13378f3ebe18958385e787d
5
5
  SHA512:
6
- metadata.gz: f644fbc7e790baa2cb469b681e80bd0c1f5dfb461d0364b808fe0a414cb37cbf9513c6c9063de6803eb7dd4b26b97235f382fff620bf9522973f55f1c11cf190
7
- data.tar.gz: 121c40b69da25971c5bcd59a2c7d385d26ea8e19e9d296e4054e24be18c93973cd910a80d2cc52ca736a2599f7bb860c14b9cfe50a74f0c6df119f52c828288b
6
+ metadata.gz: b785e2e1a02a280a3e6125c4e3731b1281eb02760a17405f669e1d1669d089131a3c9e01cc084ffa2bc6673ca3721dcc5060c974e892aaad973d2deb2d3097a8
7
+ data.tar.gz: 3063523fcfa424dc576bdb2ad3cf7a2e2eca9378098cde60a06fbb150d40c8a6313ab7e4cd77f6a6c51e04ed9ecc8b1c0b3d2a38e9a849fc80fb51a3c96b32f3
data/README.md CHANGED
@@ -39,7 +39,6 @@ Setup your model
39
39
 
40
40
  ```ruby
41
41
  class Order < ActiveRecord::Base
42
- include Amountable
43
42
  act_as_amountable
44
43
  amount :subtotal, sets: [:total]
45
44
  amount :delivery_fee, sets: [:total, :fees]
data/lib/amountable.rb CHANGED
@@ -2,7 +2,9 @@
2
2
 
3
3
  module Amountable
4
4
  extend ActiveSupport::Autoload
5
+ autoload :Operations
5
6
  autoload :Amount
7
+ autoload :NilAmount
6
8
  autoload :VERSION
7
9
  autoload :TableMethods
8
10
  autoload :JsonbMethods
@@ -13,46 +15,38 @@ module Amountable
13
15
  ALLOWED_STORAGE = %i(table json).freeze
14
16
 
15
17
  def self.included(base)
16
-
17
18
  base.extend Amountable::ClassMethods
19
+ end
18
20
 
19
- base.class_eval do
20
- validate :validate_amount_names
21
- class_attribute :amount_names
22
- class_attribute :amount_sets
23
- class_attribute :amounts_column_name
24
- self.amount_sets = Hash.new { |h, k| h[k] = Set.new }
25
- self.amount_names = Set.new
26
- self.amounts_column_name = 'amounts'
21
+ module InstanceMethods
27
22
 
28
- def all_amounts
29
- @all_amounts ||= amounts.to_set
30
- end
23
+ def all_amounts
24
+ @all_amounts ||= amounts.to_set
25
+ end
31
26
 
32
- def find_amount(name)
33
- (@amounts_by_name ||= {})[name.to_sym] ||= amounts.to_set.find { |am| am.name == name.to_s }
34
- end
27
+ def find_amount(name)
28
+ (@amounts_by_name ||= {})[name.to_sym] ||= amounts.to_set.find { |am| am.name == name.to_s }
29
+ end
35
30
 
36
- def find_amounts(names)
37
- amounts.to_set.select { |am| names.include?(am.name.to_sym) }
38
- end
31
+ def find_amounts(names)
32
+ amounts.to_set.select { |am| names.include?(am.name.to_sym) }
33
+ end
39
34
 
40
- def validate_amount_names
41
- amounts.each do |amount|
42
- errors.add(:amounts, "#{amount.name} is not an allowed amount name.") unless self.class.allowed_amount_name?(amount.name)
43
- end
35
+ def validate_amount_names
36
+ amounts.each do |amount|
37
+ errors.add(:amounts, "#{amount.name} is not an allowed amount name.") unless self.class.allowed_amount_name?(amount.name)
44
38
  end
39
+ end
45
40
 
46
- def serializable_hash(opts = nil)
47
- opts ||= {}
48
- super(opts).tap do |base|
49
- unless opts[:except].to_a.include?(:amounts)
50
- amounts_json = (self.class.amount_names + self.class.amount_sets.keys).inject({}) do |mem, name|
51
- mem.merge!(name.to_s => send(name).to_f) unless opts[:except].to_a.include?(name.to_sym)
52
- mem
53
- end
54
- base.merge!(amounts_json)
41
+ def serializable_hash(opts = nil)
42
+ opts ||= {}
43
+ super(opts).tap do |base|
44
+ unless opts[:except].to_a.include?(:amounts)
45
+ amounts_json = (self.class.amount_names + self.class.amount_sets.keys).inject({}) do |mem, name|
46
+ mem.merge!(name.to_s => send(name).to_f) unless opts[:except].to_a.include?(name.to_sym)
47
+ mem
55
48
  end
49
+ base.merge!(amounts_json)
56
50
  end
57
51
  end
58
52
  end
@@ -62,9 +56,15 @@ module Amountable
62
56
 
63
57
  # Possible storage values: [:table, :jsonb]
64
58
  def act_as_amountable(options = {})
59
+ class_attribute :amount_names
60
+ class_attribute :amount_sets
61
+ class_attribute :amounts_column_name
62
+ self.amount_sets = Hash.new { |h, k| h[k] = Set.new }
63
+ self.amount_names = Set.new
64
+ self.amounts_column_name = 'amounts'
65
65
  case (options[:storage] || :table).to_sym
66
66
  when :table
67
- has_many :amounts, as: :amountable, dependent: :destroy, autosave: false
67
+ has_many :amounts, class_name: 'Amountable::Amount', as: :amountable, dependent: :destroy, autosave: false
68
68
  include Amountable::TableMethods
69
69
  when :jsonb
70
70
  self.amounts_column_name = options[:column].to_s if options[:column]
@@ -73,6 +73,8 @@ module Amountable
73
73
  else
74
74
  raise ArgumentError.new("Please specify a storage: #{ALLOWED_STORAGE}")
75
75
  end
76
+ validate :validate_amount_names
77
+ include Amountable::InstanceMethods
76
78
  end
77
79
 
78
80
  def amount_set(set_name, component)
@@ -87,7 +89,7 @@ module Amountable
87
89
  (self.amount_names ||= Set.new) << name
88
90
 
89
91
  define_method name do
90
- (find_amount(name) || ::NilAmount.new).value
92
+ (find_amount(name) || Amountable::NilAmount.new).value
91
93
  end
92
94
 
93
95
  define_method "#{name}=" do |value|
@@ -105,3 +107,7 @@ module Amountable
105
107
 
106
108
  end
107
109
  end
110
+
111
+ ActiveSupport.on_load(:active_record) do
112
+ include Amountable
113
+ end
@@ -1,49 +1,23 @@
1
1
  # Copyright 2015-2016, Instacart
2
2
 
3
- class Amount < ActiveRecord::Base
3
+ module Amountable
4
+ class Amount < ActiveRecord::Base
4
5
 
5
- belongs_to :amountable, polymorphic: true
6
+ include Amountable::Operations
6
7
 
7
- monetize :value_cents
8
+ belongs_to :amountable, polymorphic: true
8
9
 
9
- validates :name, presence: true
10
- validates :name, uniqueness: {scope: [:amountable_id, :amountable_type]}
10
+ monetize :value_cents
11
11
 
12
- attr_accessor :persistable
12
+ validates :name, presence: true
13
+ validates :name, uniqueness: {scope: [:amountable_id, :amountable_type]}
13
14
 
14
- def save
15
- raise StandardError.new("Can't persist amount to database") if persistable == false
16
- super
17
- end
18
-
19
- module Operations
20
-
21
- def +(other_value)
22
- value + other_value.to_money
23
- end
24
-
25
- def -(other_value)
26
- value - other_value.to_money
27
- end
28
-
29
- def *(multiplier)
30
- value * multiplier
31
- end
32
-
33
- def /(divisor)
34
- value / divisor
35
- end
15
+ attr_accessor :persistable
36
16
 
37
- def to_money
38
- value
17
+ def save
18
+ raise StandardError.new("Can't persist amount to database") if persistable == false
19
+ super
39
20
  end
40
21
 
41
22
  end
42
- include Operations
43
- end
44
-
45
- class NilAmount
46
- include Amount::Operations
47
- def value; Money.zero; end
48
- def amountable; nil; end
49
- end
23
+ end
@@ -0,0 +1,9 @@
1
+ # Copyright 2015-2016, Instacart
2
+
3
+ module Amountable
4
+ class NilAmount
5
+ include Amountable::Operations
6
+ def value; Money.zero; end
7
+ def amountable; nil; end
8
+ end
9
+ end
@@ -0,0 +1,27 @@
1
+ # Copyright 2015-2016, Instacart
2
+
3
+ module Amountable
4
+ module Operations
5
+
6
+ def +(other_value)
7
+ value + other_value.to_money
8
+ end
9
+
10
+ def -(other_value)
11
+ value - other_value.to_money
12
+ end
13
+
14
+ def *(multiplier)
15
+ value * multiplier
16
+ end
17
+
18
+ def /(divisor)
19
+ value / divisor
20
+ end
21
+
22
+ def to_money
23
+ value
24
+ end
25
+
26
+ end
27
+ end
@@ -1,5 +1,5 @@
1
1
  # Copyright 2015-2016, Instacart
2
2
 
3
3
  module Amountable
4
- VERSION = '0.1.0'
4
+ VERSION = '0.2.0'
5
5
  end
@@ -2,20 +2,20 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe Amount do
5
+ describe Amountable::Amount do
6
6
 
7
7
  it 'should validate name presence' do
8
- Amount.new.tap do |amount|
8
+ subject.tap do |amount|
9
9
  expect(amount.valid?).to be false
10
10
  expect(amount.errors[:name]).not_to be nil
11
11
  end
12
12
  end
13
13
 
14
14
  it 'should validate name uniqueness' do
15
- Amount.new(name: 'test', amountable_id: 1, amountable_type: 'Amountable').tap do |amount|
15
+ Amountable::Amount.new(name: 'test', amountable_id: 1, amountable_type: 'Amountable').tap do |amount|
16
16
  expect(amount.valid?).to be true
17
17
  amount.save
18
- Amount.new(name: 'test', amountable_id: 2, amountable_type: 'Amountable').tap do |other_amount|
18
+ Amountable::Amount.new(name: 'test', amountable_id: 2, amountable_type: 'Amountable').tap do |other_amount|
19
19
  expect(other_amount.valid?).to be true
20
20
  other_amount.amountable_id = amount.amountable_id
21
21
  expect(other_amount.valid?).to be false
@@ -25,17 +25,17 @@ describe Amount do
25
25
  end
26
26
 
27
27
  it 'should have operations' do
28
- expect(Amount.new(value: Money.new(1)) + Amount.new(value: Money.new(2))).to eq(Money.new(3))
29
- expect(Amount.new(value: Money.new(1)) + 0.02).to eq(Money.new(3))
30
- expect(Amount.new(value: Money.new(2)) - Amount.new(value: Money.new(1))).to eq(Money.new(1))
31
- expect(Amount.new(value: Money.new(2)) - 0.01).to eq(Money.new(1))
32
- expect(Amount.new(value: Money.new(2)) * 3).to eq(Money.new(6))
33
- expect(Amount.new(value: Money.new(6)) / 3).to eq(Money.new(2))
34
- expect(Amount.new(value: Money.new(2)).to_money).to eq(Money.new(2))
28
+ expect(Amountable::Amount.new(value: Money.new(1)) + Amountable::Amount.new(value: Money.new(2))).to eq(Money.new(3))
29
+ expect(Amountable::Amount.new(value: Money.new(1)) + 0.02).to eq(Money.new(3))
30
+ expect(Amountable::Amount.new(value: Money.new(2)) - Amountable::Amount.new(value: Money.new(1))).to eq(Money.new(1))
31
+ expect(Amountable::Amount.new(value: Money.new(2)) - 0.01).to eq(Money.new(1))
32
+ expect(Amountable::Amount.new(value: Money.new(2)) * 3).to eq(Money.new(6))
33
+ expect(Amountable::Amount.new(value: Money.new(6)) / 3).to eq(Money.new(2))
34
+ expect(Amountable::Amount.new(value: Money.new(2)).to_money).to eq(Money.new(2))
35
35
  end
36
36
 
37
37
  it 'should not save if not persistable' do
38
- expect { Amount.new(persistable: false).save }.to raise_exception(StandardError)
38
+ expect { subject.new(persistable: false).save }.to raise_exception(StandardError)
39
39
  end
40
40
 
41
41
  end
@@ -7,7 +7,7 @@ describe Amountable do
7
7
  context 'storage == :table' do
8
8
  it 'should' do
9
9
  order = Order.new
10
- expect { order.save }.not_to change { Amount.count }
10
+ expect { order.save }.not_to change { Amountable::Amount.count }
11
11
  %i(sub_total taxes total).each do |name|
12
12
  expect(order.send(name)).to eq(Money.zero)
13
13
  end
@@ -19,12 +19,12 @@ describe Amountable do
19
19
  expect(amount.name).to eq('sub_total')
20
20
  expect(amount.value).to eq(Money.new(100))
21
21
  expect(amount.new_record?).to be true
22
- expect { order.save }.to change { Amount.count }.by(1)
22
+ expect { order.save }.to change { Amountable::Amount.count }.by(1)
23
23
  expect(amount.persisted?).to be true
24
24
  end
25
25
  expect do
26
26
  expect(order.update_attributes(sub_total: Money.new(200)))
27
- end.not_to change { Amount.count }
27
+ end.not_to change { Amountable::Amount.count }
28
28
  end
29
29
 
30
30
  describe 'name=' do
@@ -32,20 +32,20 @@ describe Amountable do
32
32
 
33
33
  it 'should not persist Money.zero' do
34
34
  expect(order.sub_total = Money.zero).to eq(Money.zero)
35
- expect { order.save }.not_to change { Amount.count }
35
+ expect { order.save }.not_to change { Amountable::Amount.count }
36
36
  end
37
37
 
38
38
  it 'should not persist Money.zero if using ActiveRecord persistence' do
39
- expect { order.update(sub_total: Money.zero) }.not_to change { Amount.count }
39
+ expect { order.update(sub_total: Money.zero) }.not_to change { Amountable::Amount.count }
40
40
  end
41
41
 
42
42
  it 'should work with ActiveRecord#update' do
43
- expect { order.update(sub_total: Money.new(1)) }.to change { Amount.count }.by(1)
43
+ expect { order.update(sub_total: Money.new(1)) }.to change { Amountable::Amount.count }.by(1)
44
44
  end
45
45
 
46
46
  it 'should destroy Amount if exist and assigning Money.zero' do
47
47
  order.update(sub_total: Money.new(1))
48
- expect { order.sub_total = Money.zero }.to change { Amount.count }.by(-1)
48
+ expect { order.sub_total = Money.zero }.to change { Amountable::Amount.count }.by(-1)
49
49
  expect(order.amounts.empty?).to be true
50
50
  end
51
51
  end
@@ -61,7 +61,7 @@ describe Amountable do
61
61
  context 'storage == :jsonb' do
62
62
  it 'should' do
63
63
  subscription = Subscription.new
64
- expect { subscription.save }.not_to change { Amount.count }
64
+ expect { subscription.save }.not_to change { Amountable::Amount.count }
65
65
  expect(subscription.amounts).to eq(Set.new)
66
66
  expect(subscription.attributes['amounts']).to be_nil
67
67
  %i(sub_total taxes total).each do |name|
@@ -76,7 +76,7 @@ describe Amountable do
76
76
  expect(amount.name).to eq('sub_total')
77
77
  expect(amount.value).to eq(Money.new(100))
78
78
  expect(amount.new_record?).to be true
79
- expect { subscription.save }.not_to change { Amount.count }
79
+ expect { subscription.save }.not_to change { Amountable::Amount.count }
80
80
  expect(amount.persisted?).to be false
81
81
  end
82
82
  subscription.update_attributes(sub_total: Money.new(200))
@@ -2,24 +2,24 @@
2
2
 
3
3
  require 'spec_helper'
4
4
 
5
- describe NilAmount do
5
+ describe Amountable::NilAmount do
6
6
 
7
7
  it 'should return 0' do
8
- expect(NilAmount.new.value).to eq(Money.zero)
8
+ expect(subject.value).to eq(Money.zero)
9
9
  end
10
10
 
11
11
  it 'should have nil amountable' do
12
- expect(NilAmount.new.amountable).to be nil
12
+ expect(subject.amountable).to be nil
13
13
  end
14
14
 
15
15
  it 'should have operations' do
16
- expect(NilAmount.new + Amount.new(value: Money.new(2))).to eq(Money.new(2))
17
- expect(NilAmount.new + 0.02).to eq(Money.new(2))
18
- expect(NilAmount.new - Amount.new(value: Money.new(1))).to eq(Money.new(-1))
19
- expect(NilAmount.new - 0.01).to eq(Money.new(-1))
20
- expect(NilAmount.new * 3).to eq(Money.zero)
21
- expect(NilAmount.new / 3).to eq(Money.zero)
22
- expect(NilAmount.new.to_money).to eq(Money.zero)
16
+ expect(subject + Amountable::Amount.new(value: Money.new(2))).to eq(Money.new(2))
17
+ expect(subject + 0.02).to eq(Money.new(2))
18
+ expect(subject - Amountable::Amount.new(value: Money.new(1))).to eq(Money.new(-1))
19
+ expect(subject - 0.01).to eq(Money.new(-1))
20
+ expect(subject * 3).to eq(Money.zero)
21
+ expect(subject / 3).to eq(Money.zero)
22
+ expect(subject.to_money).to eq(Money.zero)
23
23
  end
24
24
 
25
25
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amountable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emmanuel Turlay
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-04 00:00:00.000000000 Z
11
+ date: 2016-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -210,6 +210,8 @@ files:
210
210
  - lib/amountable.rb
211
211
  - lib/amountable/amount.rb
212
212
  - lib/amountable/jsonb_methods.rb
213
+ - lib/amountable/nil_amount.rb
214
+ - lib/amountable/operations.rb
213
215
  - lib/amountable/table_methods.rb
214
216
  - lib/amountable/version.rb
215
217
  - spec/amountable/amount_spec.rb