amountable 0.0.2
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 +7 -0
- data/.rspec +1 -0
- data/.travis.yml +5 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +157 -0
- data/LICENSE +13 -0
- data/README.md +75 -0
- data/amountable.gemspec +34 -0
- data/db/migrate/0_create_amounts.rb +22 -0
- data/lib/amountable.rb +122 -0
- data/lib/amountable/amount.rb +42 -0
- data/lib/amountable/version.rb +5 -0
- data/spec/amountable/amount_spec.rb +37 -0
- data/spec/amountable/amountable_spec.rb +45 -0
- data/spec/amountable/nil_amount_spec.rb +25 -0
- data/spec/internal/app/models/order.rb +10 -0
- data/spec/internal/config/database.yml +7 -0
- data/spec/internal/config/database.yml.sample +19 -0
- data/spec/internal/db/schema.rb +21 -0
- data/spec/spec_helper.rb +27 -0
- data/spec/support/database.rb +36 -0
- metadata +227 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: eaab8de5d539d970c93a407222cddfbdcb0b3cef
|
|
4
|
+
data.tar.gz: ef1f96155420b3adddbfab6ce97fbaffd7bafe8f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 67aa3b1d6d456f2c3c3e2005ef6a009be518be79492cc2edbdfae92a2b011661e8ae51d7cd959d5794d5044e9ec80ad8132245acceb1832ce910f5607ff280d3
|
|
7
|
+
data.tar.gz: 38ffecf5763a50f826ca60c4304cdf721e91814a6bdb08aacddf5fdb2da21ddffb965243c07cd396d16a2d47c862d218fd06efecc0789a5b6467c756f5de34a4
|
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--colour
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
amountable (0.0.1)
|
|
5
|
+
activerecord-import (= 0.10.0)
|
|
6
|
+
monetize
|
|
7
|
+
money-rails
|
|
8
|
+
rails (~> 4.2)
|
|
9
|
+
|
|
10
|
+
GEM
|
|
11
|
+
remote: https://rubygems.org/
|
|
12
|
+
specs:
|
|
13
|
+
actionmailer (4.2.5)
|
|
14
|
+
actionpack (= 4.2.5)
|
|
15
|
+
actionview (= 4.2.5)
|
|
16
|
+
activejob (= 4.2.5)
|
|
17
|
+
mail (~> 2.5, >= 2.5.4)
|
|
18
|
+
rails-dom-testing (~> 1.0, >= 1.0.5)
|
|
19
|
+
actionpack (4.2.5)
|
|
20
|
+
actionview (= 4.2.5)
|
|
21
|
+
activesupport (= 4.2.5)
|
|
22
|
+
rack (~> 1.6)
|
|
23
|
+
rack-test (~> 0.6.2)
|
|
24
|
+
rails-dom-testing (~> 1.0, >= 1.0.5)
|
|
25
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
|
26
|
+
actionview (4.2.5)
|
|
27
|
+
activesupport (= 4.2.5)
|
|
28
|
+
builder (~> 3.1)
|
|
29
|
+
erubis (~> 2.7.0)
|
|
30
|
+
rails-dom-testing (~> 1.0, >= 1.0.5)
|
|
31
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
|
32
|
+
activejob (4.2.5)
|
|
33
|
+
activesupport (= 4.2.5)
|
|
34
|
+
globalid (>= 0.3.0)
|
|
35
|
+
activemodel (4.2.5)
|
|
36
|
+
activesupport (= 4.2.5)
|
|
37
|
+
builder (~> 3.1)
|
|
38
|
+
activerecord (4.2.5)
|
|
39
|
+
activemodel (= 4.2.5)
|
|
40
|
+
activesupport (= 4.2.5)
|
|
41
|
+
arel (~> 6.0)
|
|
42
|
+
activerecord-import (0.10.0)
|
|
43
|
+
activerecord (>= 3.0)
|
|
44
|
+
activesupport (4.2.5)
|
|
45
|
+
i18n (~> 0.7)
|
|
46
|
+
json (~> 1.7, >= 1.7.7)
|
|
47
|
+
minitest (~> 5.1)
|
|
48
|
+
thread_safe (~> 0.3, >= 0.3.4)
|
|
49
|
+
tzinfo (~> 1.1)
|
|
50
|
+
arel (6.0.3)
|
|
51
|
+
builder (3.2.2)
|
|
52
|
+
concurrent-ruby (1.0.0)
|
|
53
|
+
database_cleaner (1.5.1)
|
|
54
|
+
db-query-matchers (0.3.0)
|
|
55
|
+
diff-lcs (1.2.5)
|
|
56
|
+
erubis (2.7.0)
|
|
57
|
+
globalid (0.3.6)
|
|
58
|
+
activesupport (>= 4.1.0)
|
|
59
|
+
i18n (0.7.0)
|
|
60
|
+
json (1.8.3)
|
|
61
|
+
loofah (2.0.3)
|
|
62
|
+
nokogiri (>= 1.5.9)
|
|
63
|
+
mail (2.6.3)
|
|
64
|
+
mime-types (>= 1.16, < 3)
|
|
65
|
+
mime-types (2.99)
|
|
66
|
+
mini_portile (0.6.2)
|
|
67
|
+
minitest (5.8.3)
|
|
68
|
+
monetize (1.1.0)
|
|
69
|
+
money (~> 6.5.0)
|
|
70
|
+
money (6.5.1)
|
|
71
|
+
i18n (>= 0.6.4, <= 0.7.0)
|
|
72
|
+
money-rails (1.4.1)
|
|
73
|
+
activesupport (>= 3.0)
|
|
74
|
+
monetize (~> 1.1.0)
|
|
75
|
+
money (~> 6.5.0)
|
|
76
|
+
railties (>= 3.0)
|
|
77
|
+
mysql2 (0.3.15)
|
|
78
|
+
nokogiri (1.6.6.2)
|
|
79
|
+
mini_portile (~> 0.6.0)
|
|
80
|
+
pg (0.18.1)
|
|
81
|
+
rack (1.6.4)
|
|
82
|
+
rack-test (0.6.3)
|
|
83
|
+
rack (>= 1.0)
|
|
84
|
+
rails (4.2.5)
|
|
85
|
+
actionmailer (= 4.2.5)
|
|
86
|
+
actionpack (= 4.2.5)
|
|
87
|
+
actionview (= 4.2.5)
|
|
88
|
+
activejob (= 4.2.5)
|
|
89
|
+
activemodel (= 4.2.5)
|
|
90
|
+
activerecord (= 4.2.5)
|
|
91
|
+
activesupport (= 4.2.5)
|
|
92
|
+
bundler (>= 1.3.0, < 2.0)
|
|
93
|
+
railties (= 4.2.5)
|
|
94
|
+
sprockets-rails
|
|
95
|
+
rails-deprecated_sanitizer (1.0.3)
|
|
96
|
+
activesupport (>= 4.2.0.alpha)
|
|
97
|
+
rails-dom-testing (1.0.7)
|
|
98
|
+
activesupport (>= 4.2.0.beta, < 5.0)
|
|
99
|
+
nokogiri (~> 1.6.0)
|
|
100
|
+
rails-deprecated_sanitizer (>= 1.0.1)
|
|
101
|
+
rails-html-sanitizer (1.0.2)
|
|
102
|
+
loofah (~> 2.0)
|
|
103
|
+
railties (4.2.5)
|
|
104
|
+
actionpack (= 4.2.5)
|
|
105
|
+
activesupport (= 4.2.5)
|
|
106
|
+
rake (>= 0.8.7)
|
|
107
|
+
thor (>= 0.18.1, < 2.0)
|
|
108
|
+
rake (10.4.2)
|
|
109
|
+
rspec (3.4.0)
|
|
110
|
+
rspec-core (~> 3.4.0)
|
|
111
|
+
rspec-expectations (~> 3.4.0)
|
|
112
|
+
rspec-mocks (~> 3.4.0)
|
|
113
|
+
rspec-core (3.4.1)
|
|
114
|
+
rspec-support (~> 3.4.0)
|
|
115
|
+
rspec-expectations (3.4.0)
|
|
116
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
117
|
+
rspec-support (~> 3.4.0)
|
|
118
|
+
rspec-mocks (3.4.0)
|
|
119
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
120
|
+
rspec-support (~> 3.4.0)
|
|
121
|
+
rspec-rails (3.4.0)
|
|
122
|
+
actionpack (>= 3.0, < 4.3)
|
|
123
|
+
activesupport (>= 3.0, < 4.3)
|
|
124
|
+
railties (>= 3.0, < 4.3)
|
|
125
|
+
rspec-core (~> 3.4.0)
|
|
126
|
+
rspec-expectations (~> 3.4.0)
|
|
127
|
+
rspec-mocks (~> 3.4.0)
|
|
128
|
+
rspec-support (~> 3.4.0)
|
|
129
|
+
rspec-support (3.4.1)
|
|
130
|
+
sprockets (3.5.2)
|
|
131
|
+
concurrent-ruby (~> 1.0)
|
|
132
|
+
rack (> 1, < 3)
|
|
133
|
+
sprockets-rails (2.3.3)
|
|
134
|
+
actionpack (>= 3.0)
|
|
135
|
+
activesupport (>= 3.0)
|
|
136
|
+
sprockets (>= 2.8, < 4.0)
|
|
137
|
+
sqlite3 (1.3.11)
|
|
138
|
+
thor (0.19.1)
|
|
139
|
+
thread_safe (0.3.5)
|
|
140
|
+
tzinfo (1.2.2)
|
|
141
|
+
thread_safe (~> 0.1)
|
|
142
|
+
|
|
143
|
+
PLATFORMS
|
|
144
|
+
ruby
|
|
145
|
+
|
|
146
|
+
DEPENDENCIES
|
|
147
|
+
amountable!
|
|
148
|
+
database_cleaner
|
|
149
|
+
db-query-matchers
|
|
150
|
+
mysql2
|
|
151
|
+
pg
|
|
152
|
+
rspec
|
|
153
|
+
rspec-rails
|
|
154
|
+
sqlite3
|
|
155
|
+
|
|
156
|
+
BUNDLED WITH
|
|
157
|
+
1.10.6
|
data/LICENSE
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Copyright © 2015, Instacart
|
|
2
|
+
|
|
3
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
4
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
5
|
+
copyright notice and this permission notice appear in all copies.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
8
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
9
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
10
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
11
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
12
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
13
|
+
PERFORMANCE OF THIS SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Amountable
|
|
2
|
+
|
|
3
|
+
[](https://travis-ci.org/instacart/amountable)
|
|
4
|
+
|
|
5
|
+
This gem helps you integrate Money fields into your ActiveRecord models without having to add new columns each time.
|
|
6
|
+
|
|
7
|
+
It also helps manage and sum various components of your models to keep amount definitions consistent across your application.
|
|
8
|
+
|
|
9
|
+
## Installation
|
|
10
|
+
|
|
11
|
+
Add
|
|
12
|
+
|
|
13
|
+
```ruby
|
|
14
|
+
gem 'amountable', github: 'instacart/amountable'
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
to your `Gemfile`. Then run
|
|
18
|
+
|
|
19
|
+
```shell
|
|
20
|
+
bundle
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
and
|
|
24
|
+
|
|
25
|
+
```shell
|
|
26
|
+
rake amountable:install:migrations
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
and finally
|
|
30
|
+
|
|
31
|
+
```shell
|
|
32
|
+
rake db:migrate
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
Setup your model
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
class Order < ActiveRecord::Base
|
|
41
|
+
include Amountable
|
|
42
|
+
amount :subtotal, sets: [:total]
|
|
43
|
+
amount :delivery_fee, sets: [:total, :fees]
|
|
44
|
+
amount :bags_fee, sets: [:total, :fees]
|
|
45
|
+
amount :sales_tax, sets: [:total, :taxes]
|
|
46
|
+
amount :local_tax, sets: [:total, :taxes]
|
|
47
|
+
end
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
then create it
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
order = Order.create(
|
|
54
|
+
subtotal: Money.new(123),
|
|
55
|
+
delivery_fee: Money.new(100),
|
|
56
|
+
bags_fee: Money.new(10),
|
|
57
|
+
sales_tax: Money.new(56)
|
|
58
|
+
)
|
|
59
|
+
order.subtotal
|
|
60
|
+
# => #<Money fractional:123 currency:USD>
|
|
61
|
+
order.total
|
|
62
|
+
# => #<Money fractional:289 currency:USD>
|
|
63
|
+
order.fees
|
|
64
|
+
# => #<Money fractional:110 currency:USD>
|
|
65
|
+
order.taxes
|
|
66
|
+
# => #<Money fractional:56 currency:USD>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Implementation
|
|
70
|
+
|
|
71
|
+
When you run the migration, an `amounts` table will be created with a polymorphic relationship to an `amountable`, which in the above example would be your orders.
|
|
72
|
+
|
|
73
|
+
When the an order is created with some amounts, the associated `Amount` objects are persisted.
|
|
74
|
+
|
|
75
|
+
`Amount` objects are persisted in bulk, so there are no N + 1 queries. If an amount is zero, no `Amount` model is created.
|
data/amountable.gemspec
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
+
require 'amountable/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |gem|
|
|
6
|
+
gem.name = 'amountable'
|
|
7
|
+
gem.version = Amountable::VERSION
|
|
8
|
+
gem.authors = ['Emmanuel Turlay']
|
|
9
|
+
gem.email = %w(emmanuel@instacart.com)
|
|
10
|
+
gem.description = %q{With Amountable, you can easily attach, organize and sum Ruby money fields to your models without migrating.}
|
|
11
|
+
gem.summary = 'Easy Money fields for your Rails models.'
|
|
12
|
+
gem.homepage = 'https://github.com/instacart/amountable'
|
|
13
|
+
# gem.license = 'MIT'
|
|
14
|
+
|
|
15
|
+
gem.files = `git ls-files`.split($/)
|
|
16
|
+
gem.test_files = gem.files.grep(%r{^spec/})
|
|
17
|
+
gem.require_paths = ['lib']
|
|
18
|
+
gem.required_ruby_version = '>= 2.1.1'
|
|
19
|
+
|
|
20
|
+
gem.add_dependency 'rails', '~> 4.2'
|
|
21
|
+
#gem.add_dependency 'activerecord', ['>= 4.2', '< 5']
|
|
22
|
+
gem.add_dependency 'activerecord-import', '0.10.0'
|
|
23
|
+
gem.add_dependency 'money-rails'
|
|
24
|
+
gem.add_dependency 'monetize'
|
|
25
|
+
|
|
26
|
+
gem.add_development_dependency 'sqlite3'
|
|
27
|
+
gem.add_development_dependency 'mysql2'
|
|
28
|
+
gem.add_development_dependency 'pg'
|
|
29
|
+
|
|
30
|
+
gem.add_development_dependency 'rspec-rails'
|
|
31
|
+
gem.add_development_dependency 'rspec'
|
|
32
|
+
gem.add_development_dependency 'database_cleaner'
|
|
33
|
+
gem.add_development_dependency 'db-query-matchers'
|
|
34
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Copyright 2015, Instacart
|
|
2
|
+
|
|
3
|
+
class CreateAmounts < ActiveRecord::Migration
|
|
4
|
+
def change
|
|
5
|
+
create_table :amounts do |t|
|
|
6
|
+
t.integer :amountable_id, null: false
|
|
7
|
+
t.string :amountable_type, null: false
|
|
8
|
+
t.string :name, null: false
|
|
9
|
+
t.timestamps
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
add_monetize :amounts, :value
|
|
13
|
+
|
|
14
|
+
add_index :amounts, [:amountable_id, :amountable_type]
|
|
15
|
+
|
|
16
|
+
ActiveRecord::Base.connection.execute("CLUSTER amounts USING index_amounts_on_amountable_id_and_amountable_type") if is_pg?
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def is_pg?
|
|
20
|
+
ActiveRecord::Base.connection.instance_values["config"][:adapter].include?('postgresql')
|
|
21
|
+
end
|
|
22
|
+
end
|
data/lib/amountable.rb
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Copyright 2015, Instacart
|
|
2
|
+
|
|
3
|
+
module Amountable
|
|
4
|
+
extend ActiveSupport::Autoload
|
|
5
|
+
autoload :Amount
|
|
6
|
+
autoload :VERSION
|
|
7
|
+
|
|
8
|
+
class InvalidAmountName < StandardError; end
|
|
9
|
+
|
|
10
|
+
def self.included(base)
|
|
11
|
+
|
|
12
|
+
base.extend Amountable::ClassMethods
|
|
13
|
+
|
|
14
|
+
base.class_eval do
|
|
15
|
+
has_many :amounts, as: :amountable, dependent: :destroy, autosave: false
|
|
16
|
+
validate :validate_amount_names
|
|
17
|
+
class_attribute :amount_names
|
|
18
|
+
class_attribute :amount_sets
|
|
19
|
+
self.amount_sets = Hash.new { |h, k| h[k] = Set.new }
|
|
20
|
+
self.amount_names = Set.new
|
|
21
|
+
|
|
22
|
+
def all_amounts
|
|
23
|
+
@all_amounts ||= amounts.to_set
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def find_amount(name)
|
|
27
|
+
(@amounts_by_name ||= {})[name.to_sym] ||= all_amounts.find { |am| am.name == name.to_s }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def find_amounts(names)
|
|
31
|
+
all_amounts.select { |am| names.include?(am.name.to_sym) }
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def validate_amount_names
|
|
35
|
+
amounts.each do |amount|
|
|
36
|
+
errors.add(:amounts, "#{amount.name} is not an allowed amount name.") unless self.class.allowed_amount_name?(amount.name)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def serializable_hash(opts = nil)
|
|
41
|
+
opts ||= {}
|
|
42
|
+
super(opts).tap do |base|
|
|
43
|
+
unless opts[:except].to_a.include?(:amounts)
|
|
44
|
+
amounts_json = (self.class.amount_names + self.class.amount_sets.keys).inject({}) do |mem, name|
|
|
45
|
+
mem.merge!(name.to_s => send(name).to_f) unless opts[:except].to_a.include?(name.to_sym)
|
|
46
|
+
mem
|
|
47
|
+
end
|
|
48
|
+
base.merge!(amounts_json)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def save(args = {})
|
|
54
|
+
ActiveRecord::Base.transaction do
|
|
55
|
+
save_amounts if super(args)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def save!(args = {})
|
|
60
|
+
ActiveRecord::Base.transaction do
|
|
61
|
+
save_amounts! if super(args)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def save_amounts(bang: false)
|
|
66
|
+
amounts_to_insert = []
|
|
67
|
+
amounts.each do |amount|
|
|
68
|
+
if amount.new_record?
|
|
69
|
+
amount.amountable_id = self.id
|
|
70
|
+
amounts_to_insert << amount
|
|
71
|
+
else
|
|
72
|
+
bang ? amount.save! : amount.save
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
Amount.import(amounts_to_insert, timestamps: true, validate: false)
|
|
76
|
+
amounts_to_insert.each do |amount|
|
|
77
|
+
amount.instance_variable_set(:@new_record, false)
|
|
78
|
+
end
|
|
79
|
+
true
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def save_amounts!; save_amounts(bang: true); end
|
|
83
|
+
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
module ClassMethods
|
|
88
|
+
|
|
89
|
+
def amount_set(set_name, component)
|
|
90
|
+
self.amount_sets[set_name.to_sym] << component.to_sym
|
|
91
|
+
|
|
92
|
+
define_method set_name do
|
|
93
|
+
find_amounts(self.amount_sets[set_name.to_sym]).sum(Money.zero, &:value)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def amount(name, options = {})
|
|
98
|
+
(self.amount_names ||= Set.new) << name
|
|
99
|
+
|
|
100
|
+
define_method name do
|
|
101
|
+
(find_amount(name) || NilAmount.new).value
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
define_method "#{name}=" do |value|
|
|
105
|
+
return Money.zero if value.zero?
|
|
106
|
+
amount = find_amount(name) || amounts.build(name: name)
|
|
107
|
+
amount.value = value.to_money
|
|
108
|
+
all_amounts << amount if amount.new_record?
|
|
109
|
+
(@amounts_by_name ||= {})[name.to_sym] = amount
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
Array(options[:summable] || options[:summables] || options[:set] || options[:sets] || options[:amount_set] || options[:amount_sets]).each do |set|
|
|
113
|
+
amount_set(set, name)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def allowed_amount_name?(name)
|
|
118
|
+
self.amount_names.include?(name.to_sym)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
end
|
|
122
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# Copyright 2015, Instacart
|
|
2
|
+
|
|
3
|
+
class Amount < ActiveRecord::Base
|
|
4
|
+
|
|
5
|
+
belongs_to :amountable, polymorphic: true
|
|
6
|
+
|
|
7
|
+
monetize :value_cents
|
|
8
|
+
|
|
9
|
+
validates :name, presence: true
|
|
10
|
+
validates :name, uniqueness: {scope: [:amountable_id, :amountable_type]}
|
|
11
|
+
|
|
12
|
+
module Operations
|
|
13
|
+
|
|
14
|
+
def +(other_value)
|
|
15
|
+
value + other_value.to_money
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def -(other_value)
|
|
19
|
+
value - other_value.to_money
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def *(multiplier)
|
|
23
|
+
value * multiplier
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def /(divisor)
|
|
27
|
+
value / divisor
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def to_money
|
|
31
|
+
value
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
end
|
|
35
|
+
include Operations
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class NilAmount
|
|
39
|
+
include Amount::Operations
|
|
40
|
+
def value; Money.zero; end
|
|
41
|
+
def amountable; nil; end
|
|
42
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Copyright 2015, Instacart
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Amount do
|
|
6
|
+
|
|
7
|
+
it 'should validate name presence' do
|
|
8
|
+
Amount.new.tap do |amount|
|
|
9
|
+
expect(amount.valid?).to be false
|
|
10
|
+
expect(amount.errors[:name]).not_to be nil
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it 'should validate name uniqueness' do
|
|
15
|
+
Amount.new(name: 'test', amountable_id: 1, amountable_type: 'Amountable').tap do |amount|
|
|
16
|
+
expect(amount.valid?).to be true
|
|
17
|
+
amount.save
|
|
18
|
+
Amount.new(name: 'test', amountable_id: 2, amountable_type: 'Amountable').tap do |other_amount|
|
|
19
|
+
expect(other_amount.valid?).to be true
|
|
20
|
+
other_amount.amountable_id = amount.amountable_id
|
|
21
|
+
expect(other_amount.valid?).to be false
|
|
22
|
+
expect(amount.errors[:name]).not_to be nil
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
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))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Copyright 2015, Instacart
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe Amountable do
|
|
6
|
+
|
|
7
|
+
it 'should' do
|
|
8
|
+
order = Order.new
|
|
9
|
+
expect {
|
|
10
|
+
order.save
|
|
11
|
+
}.not_to change {
|
|
12
|
+
Amount.count
|
|
13
|
+
}
|
|
14
|
+
%i(sub_total taxes total).each do |name|
|
|
15
|
+
expect(order.send(name)).to eq(Money.zero)
|
|
16
|
+
end
|
|
17
|
+
order.sub_total = Money.new(100)
|
|
18
|
+
expect(order.sub_total).to eq(Money.new(100))
|
|
19
|
+
expect(order.total).to eq(Money.new(100))
|
|
20
|
+
expect(order.all_amounts.size).to eq(1)
|
|
21
|
+
order.all_amounts.first.tap do |amount|
|
|
22
|
+
expect(amount.name).to eq('sub_total')
|
|
23
|
+
expect(amount.value).to eq(Money.new(100))
|
|
24
|
+
expect(amount.new_record?).to be true
|
|
25
|
+
expect {
|
|
26
|
+
order.save
|
|
27
|
+
}.to change {
|
|
28
|
+
Amount.count
|
|
29
|
+
}.by(1)
|
|
30
|
+
expect(amount.persisted?).to be true
|
|
31
|
+
end
|
|
32
|
+
expect{
|
|
33
|
+
expect(order.update_attributes(sub_total: Money.new(200)))
|
|
34
|
+
}.not_to change {
|
|
35
|
+
Amount.count
|
|
36
|
+
}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it 'should insert amounts in bulk' do
|
|
40
|
+
order = Order.create
|
|
41
|
+
expect {
|
|
42
|
+
order.update(sub_total: Money.new(100), taxes: Money.new(200))
|
|
43
|
+
}.to make_database_queries(count: 1, manipulative: true)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Copyright 2015, Instacart
|
|
2
|
+
|
|
3
|
+
require 'spec_helper'
|
|
4
|
+
|
|
5
|
+
describe NilAmount do
|
|
6
|
+
|
|
7
|
+
it 'should return 0' do
|
|
8
|
+
expect(NilAmount.new.value).to eq(Money.zero)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
it 'should have nil amountable' do
|
|
12
|
+
expect(NilAmount.new.amountable).to be nil
|
|
13
|
+
end
|
|
14
|
+
|
|
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)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
sqlite3:
|
|
2
|
+
adapter: sqlite3
|
|
3
|
+
database: ':memory:'
|
|
4
|
+
|
|
5
|
+
mysql:
|
|
6
|
+
adapter: mysql2
|
|
7
|
+
host: localhost
|
|
8
|
+
username: root
|
|
9
|
+
password:
|
|
10
|
+
database: amountable
|
|
11
|
+
charset: utf8
|
|
12
|
+
|
|
13
|
+
postgresql:
|
|
14
|
+
adapter: postgresql
|
|
15
|
+
hostname: localhost
|
|
16
|
+
username: postgres
|
|
17
|
+
password:
|
|
18
|
+
database: amountable
|
|
19
|
+
encoding: utf8
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Copyright 2015, Instacart
|
|
2
|
+
|
|
3
|
+
ActiveRecord::Schema.define version: 1 do
|
|
4
|
+
|
|
5
|
+
create_table "amounts", force: :cascade do |t|
|
|
6
|
+
t.integer "amountable_id", null: false
|
|
7
|
+
t.string "amountable_type", null: false
|
|
8
|
+
t.datetime "created_at"
|
|
9
|
+
t.string "name", null: false
|
|
10
|
+
t.datetime "updated_at"
|
|
11
|
+
t.integer "value_cents", default: 0, null: false
|
|
12
|
+
t.string "value_currency", default: "USD", null: false
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
add_index "amounts", ["amountable_id", "amountable_type"], name: "index_amounts_on_amountable_id_and_amountable_type", using: :btree
|
|
16
|
+
|
|
17
|
+
create_table "orders" do |t|
|
|
18
|
+
t.datetime "created_at"
|
|
19
|
+
t.datetime "updated_at"
|
|
20
|
+
end
|
|
21
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Copyright 2015, Instacart
|
|
2
|
+
|
|
3
|
+
ENV['RAILS_ENV'] = 'test'
|
|
4
|
+
require 'rails'
|
|
5
|
+
require 'money-rails'
|
|
6
|
+
require 'active_record'
|
|
7
|
+
require 'activerecord-import'
|
|
8
|
+
require 'amountable'
|
|
9
|
+
Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
|
|
10
|
+
MoneyRails::Hooks.init
|
|
11
|
+
require 'amountable/amount'
|
|
12
|
+
require 'database_cleaner'
|
|
13
|
+
require 'db-query-matchers'
|
|
14
|
+
|
|
15
|
+
RSpec.configure do |config|
|
|
16
|
+
|
|
17
|
+
config.before(:suite) do
|
|
18
|
+
DatabaseCleaner.strategy = :transaction
|
|
19
|
+
DatabaseCleaner.clean_with(:truncation)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
config.around(:each) do |example|
|
|
23
|
+
DatabaseCleaner.cleaning do
|
|
24
|
+
example.run
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Copyright 2015, Instacart
|
|
2
|
+
|
|
3
|
+
db_name = ENV['DB'] || 'postgresql'
|
|
4
|
+
database_yml = File.expand_path('../../internal/config/database.yml', __FILE__)
|
|
5
|
+
|
|
6
|
+
if File.exist?(database_yml)
|
|
7
|
+
|
|
8
|
+
ActiveRecord::Migration.verbose = false
|
|
9
|
+
ActiveRecord::Base.default_timezone = :utc
|
|
10
|
+
ActiveRecord::Base.configurations = YAML.load_file(database_yml)
|
|
11
|
+
ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__), '../debug.log'))
|
|
12
|
+
ActiveRecord::Base.logger.level = ENV['TRAVIS'] ? ::Logger::ERROR : ::Logger::DEBUG
|
|
13
|
+
config = ActiveRecord::Base.configurations[db_name]
|
|
14
|
+
|
|
15
|
+
begin
|
|
16
|
+
ActiveRecord::Base.establish_connection(db_name.to_sym)
|
|
17
|
+
ActiveRecord::Base.connection
|
|
18
|
+
rescue
|
|
19
|
+
case db_name
|
|
20
|
+
when /mysql/
|
|
21
|
+
ActiveRecord::Base.establish_connection(config.merge('database' => nil))
|
|
22
|
+
ActiveRecord::Base.connection.create_database(config['database'], {charset: 'utf8', collation: 'utf8_unicode_ci'})
|
|
23
|
+
when 'postgresql'
|
|
24
|
+
ActiveRecord::Base.establish_connection(config.merge('database' => 'postgres', 'schema_search_path' => 'public'))
|
|
25
|
+
ActiveRecord::Base.connection.create_database(config['database'], config.merge('encoding' => 'utf8'))
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
ActiveRecord::Base.establish_connection(config)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
load(File.dirname(__FILE__) + '/../internal/db/schema.rb')
|
|
32
|
+
load(File.dirname(__FILE__) + '/../internal/app/models/order.rb')
|
|
33
|
+
|
|
34
|
+
else
|
|
35
|
+
fail "Please create #{database_yml} first to configure your database. Take a look at: #{database_yml}.sample"
|
|
36
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: amountable
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.2
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Emmanuel Turlay
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2015-12-17 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rails
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '4.2'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '4.2'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: activerecord-import
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - '='
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 0.10.0
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - '='
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 0.10.0
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: money-rails
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :runtime
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: monetize
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :runtime
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: sqlite3
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: mysql2
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: pg
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: rspec-rails
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - ">="
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '0'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - ">="
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '0'
|
|
125
|
+
- !ruby/object:Gem::Dependency
|
|
126
|
+
name: rspec
|
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
|
128
|
+
requirements:
|
|
129
|
+
- - ">="
|
|
130
|
+
- !ruby/object:Gem::Version
|
|
131
|
+
version: '0'
|
|
132
|
+
type: :development
|
|
133
|
+
prerelease: false
|
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - ">="
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: '0'
|
|
139
|
+
- !ruby/object:Gem::Dependency
|
|
140
|
+
name: database_cleaner
|
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
|
142
|
+
requirements:
|
|
143
|
+
- - ">="
|
|
144
|
+
- !ruby/object:Gem::Version
|
|
145
|
+
version: '0'
|
|
146
|
+
type: :development
|
|
147
|
+
prerelease: false
|
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
149
|
+
requirements:
|
|
150
|
+
- - ">="
|
|
151
|
+
- !ruby/object:Gem::Version
|
|
152
|
+
version: '0'
|
|
153
|
+
- !ruby/object:Gem::Dependency
|
|
154
|
+
name: db-query-matchers
|
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
|
156
|
+
requirements:
|
|
157
|
+
- - ">="
|
|
158
|
+
- !ruby/object:Gem::Version
|
|
159
|
+
version: '0'
|
|
160
|
+
type: :development
|
|
161
|
+
prerelease: false
|
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
163
|
+
requirements:
|
|
164
|
+
- - ">="
|
|
165
|
+
- !ruby/object:Gem::Version
|
|
166
|
+
version: '0'
|
|
167
|
+
description: With Amountable, you can easily attach, organize and sum Ruby money fields
|
|
168
|
+
to your models without migrating.
|
|
169
|
+
email:
|
|
170
|
+
- emmanuel@instacart.com
|
|
171
|
+
executables: []
|
|
172
|
+
extensions: []
|
|
173
|
+
extra_rdoc_files: []
|
|
174
|
+
files:
|
|
175
|
+
- ".rspec"
|
|
176
|
+
- ".travis.yml"
|
|
177
|
+
- Gemfile
|
|
178
|
+
- Gemfile.lock
|
|
179
|
+
- LICENSE
|
|
180
|
+
- README.md
|
|
181
|
+
- amountable.gemspec
|
|
182
|
+
- db/migrate/0_create_amounts.rb
|
|
183
|
+
- lib/amountable.rb
|
|
184
|
+
- lib/amountable/amount.rb
|
|
185
|
+
- lib/amountable/version.rb
|
|
186
|
+
- spec/amountable/amount_spec.rb
|
|
187
|
+
- spec/amountable/amountable_spec.rb
|
|
188
|
+
- spec/amountable/nil_amount_spec.rb
|
|
189
|
+
- spec/internal/app/models/order.rb
|
|
190
|
+
- spec/internal/config/database.yml
|
|
191
|
+
- spec/internal/config/database.yml.sample
|
|
192
|
+
- spec/internal/db/schema.rb
|
|
193
|
+
- spec/spec_helper.rb
|
|
194
|
+
- spec/support/database.rb
|
|
195
|
+
homepage: https://github.com/instacart/amountable
|
|
196
|
+
licenses: []
|
|
197
|
+
metadata: {}
|
|
198
|
+
post_install_message:
|
|
199
|
+
rdoc_options: []
|
|
200
|
+
require_paths:
|
|
201
|
+
- lib
|
|
202
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
203
|
+
requirements:
|
|
204
|
+
- - ">="
|
|
205
|
+
- !ruby/object:Gem::Version
|
|
206
|
+
version: 2.1.1
|
|
207
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
208
|
+
requirements:
|
|
209
|
+
- - ">="
|
|
210
|
+
- !ruby/object:Gem::Version
|
|
211
|
+
version: '0'
|
|
212
|
+
requirements: []
|
|
213
|
+
rubyforge_project:
|
|
214
|
+
rubygems_version: 2.4.5
|
|
215
|
+
signing_key:
|
|
216
|
+
specification_version: 4
|
|
217
|
+
summary: Easy Money fields for your Rails models.
|
|
218
|
+
test_files:
|
|
219
|
+
- spec/amountable/amount_spec.rb
|
|
220
|
+
- spec/amountable/amountable_spec.rb
|
|
221
|
+
- spec/amountable/nil_amount_spec.rb
|
|
222
|
+
- spec/internal/app/models/order.rb
|
|
223
|
+
- spec/internal/config/database.yml
|
|
224
|
+
- spec/internal/config/database.yml.sample
|
|
225
|
+
- spec/internal/db/schema.rb
|
|
226
|
+
- spec/spec_helper.rb
|
|
227
|
+
- spec/support/database.rb
|