amountable 0.1.0 → 0.2.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 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