money-rails 0.7.1 → 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.md +13 -0
- data/README.md +111 -25
- data/Rakefile +3 -3
- data/lib/generators/templates/money.rb +8 -2
- data/lib/money-rails.rb +1 -0
- data/lib/money-rails/active_model/validator.rb +12 -9
- data/lib/money-rails/active_record/monetizable.rb +13 -6
- data/lib/money-rails/configuration.rb +23 -0
- data/lib/money-rails/helpers/action_view_extension.rb +27 -12
- data/lib/money-rails/hooks.rb +2 -4
- data/lib/money-rails/money.rb +23 -0
- data/lib/money-rails/mongoid/{three.rb → money.rb} +1 -1
- data/lib/money-rails/test_helpers.rb +49 -0
- data/lib/money-rails/version.rb +1 -1
- data/money-rails.gemspec +9 -3
- data/spec/active_record/migration_extensions/schema_statements_spec.rb +3 -1
- data/spec/active_record/migration_extensions/table_spec.rb +3 -1
- data/spec/active_record/monetizable_spec.rb +46 -4
- data/spec/configuration_spec.rb +63 -0
- data/spec/dummy/app/models/product.rb +7 -0
- data/spec/dummy/db/migrate/20130124023419_add_price_in_a_range_cents_to_products.rb +5 -0
- data/spec/dummy/db/schema.rb +5 -1
- data/spec/dummy/db/structure.sql +4 -2
- data/spec/helpers/action_view_extension_spec.rb +25 -2
- data/spec/mongoid/three_spec.rb +14 -0
- data/spec/test_helpers_spec.rb +31 -0
- metadata +99 -101
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +0 -1
- data/spec/dummy/log/test.log +0 -17903
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## master
|
4
|
+
|
5
|
+
## 0.8.0
|
6
|
+
- Added defaults for amount and currency columns in database schema, based on the default currency.
|
7
|
+
- Use a better default subunit_unit name (choose the value of column.postfix set in the config).
|
8
|
+
- Began support of Rails 4.
|
9
|
+
- Added global settings for money object formatted output (:no_cents_if_whole, :symbol options).
|
10
|
+
- Enhanced money validator.
|
11
|
+
- Added ability to use numericality validations on monetize (GH-70).
|
12
|
+
- Fixed error caused by ActiveSupport::HashWithIndifferentAccess
|
13
|
+
(GH-62).
|
14
|
+
- Added money-rails test helper (rspec matcher).
|
15
|
+
|
3
16
|
## 0.7.1
|
4
17
|
- Fix error when instantiating new model in mongoid extension (GH-60)
|
5
18
|
|
data/README.md
CHANGED
@@ -44,29 +44,6 @@ configuration parameters for the rails app.
|
|
44
44
|
|
45
45
|
### ActiveRecord
|
46
46
|
|
47
|
-
#### Migration helpers
|
48
|
-
|
49
|
-
If you want to add money field to product model you may use ```add_money``` helper. That
|
50
|
-
helper might be customized inside ```MoneyRails.configure``` block. You should customize
|
51
|
-
```add_money``` helper to match the most common use case and utilize it across all migrations.
|
52
|
-
|
53
|
-
```ruby
|
54
|
-
class MonetizeProduct < ActiveRecord::Migration
|
55
|
-
def change
|
56
|
-
add_money :products, :price
|
57
|
-
|
58
|
-
# OR
|
59
|
-
|
60
|
-
change_table :products do |t|
|
61
|
-
t.money :price
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
```
|
66
|
-
|
67
|
-
```add_money``` helper is revertable, so you may use it inside ```change``` migrations.
|
68
|
-
If you writing separate ```up``` and ```down``` methods, you may use ```remove_money``` helper.
|
69
|
-
|
70
47
|
#### Usage example
|
71
48
|
|
72
49
|
For example, we create a Product model which has an integer price_cents column
|
@@ -98,6 +75,39 @@ Now the model objects will have a ```discount``` attribute which
|
|
98
75
|
is a Money object, wrapping the value of ```discount_subunit``` column to a
|
99
76
|
Money instance.
|
100
77
|
|
78
|
+
#### Migration helpers
|
79
|
+
|
80
|
+
If you want to add money field to product model you may use ```add_money``` helper. That
|
81
|
+
helper might be customized inside ```MoneyRails.configure``` block. You should customize
|
82
|
+
```add_money``` helper to match the most common use case and utilize it across all migrations.
|
83
|
+
|
84
|
+
```ruby
|
85
|
+
class MonetizeProduct < ActiveRecord::Migration
|
86
|
+
def change
|
87
|
+
add_money :products, :price
|
88
|
+
|
89
|
+
# OR
|
90
|
+
|
91
|
+
change_table :products do |t|
|
92
|
+
t.money :price
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
Another example where the currency column is not including:
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
class MonetizeItem < ActiveRecord::Migration
|
102
|
+
def change
|
103
|
+
add_money :items, :price, currency: { present: false }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
```
|
107
|
+
|
108
|
+
```add_money``` helper is revertable, so you may use it inside ```change``` migrations.
|
109
|
+
If you writing separate ```up``` and ```down``` methods, you may use ```remove_money``` helper.
|
110
|
+
|
101
111
|
#### Allow nil values
|
102
112
|
|
103
113
|
If you want to allow the assignment of nil and/or blank values to a specific
|
@@ -114,6 +124,20 @@ product.optional_price # => nil
|
|
114
124
|
product.optional_price_cents # => nil
|
115
125
|
```
|
116
126
|
|
127
|
+
#### Numericality validation options
|
128
|
+
|
129
|
+
You can also pass along
|
130
|
+
[numericality validation options](http://guides.rubyonrails.org/active_record_validations_callbacks.html#numericality)
|
131
|
+
such as this:
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
monetize :price_in_a_range_cents, :allow_nil => true,
|
135
|
+
:numericality => {
|
136
|
+
:greater_than_or_equal_to => 0,
|
137
|
+
:less_than_or_equal_to => 10000
|
138
|
+
}
|
139
|
+
```
|
140
|
+
|
117
141
|
### Mongoid 2.x and 3.x
|
118
142
|
|
119
143
|
`Money` is available as a field type to supply during a field definition:
|
@@ -290,6 +314,26 @@ MoneyRails.configure do |config|
|
|
290
314
|
#
|
291
315
|
config.include_validations = true
|
292
316
|
|
317
|
+
# Default ActiveRecord migration configuration values for columns:
|
318
|
+
#
|
319
|
+
# config.amount_column = { prefix: '', # column name prefix
|
320
|
+
# postfix: '_cents', # column name postfix
|
321
|
+
# column_name: nil, # full column name (overrides prefix, postfix and accessor name)
|
322
|
+
# type: :integer, # column type
|
323
|
+
# present: true, # column will be created
|
324
|
+
# null: false, # other options will be treated as column options
|
325
|
+
# default: 0
|
326
|
+
# }
|
327
|
+
#
|
328
|
+
# config.currency_column = { prefix: '',
|
329
|
+
# postfix: '_currency',
|
330
|
+
# column_name: nil,
|
331
|
+
# type: :string,
|
332
|
+
# present: true,
|
333
|
+
# null: false,
|
334
|
+
# default: 'USD'
|
335
|
+
# }
|
336
|
+
|
293
337
|
# Register a custom currency
|
294
338
|
#
|
295
339
|
# config.register_currency = {
|
@@ -303,10 +347,17 @@ MoneyRails.configure do |config|
|
|
303
347
|
# :thousands_separator => ".",
|
304
348
|
# :decimal_mark => ","
|
305
349
|
# }
|
350
|
+
|
351
|
+
# Set money formatted output globally.
|
352
|
+
# Default value is nil meaning "ignore this option".
|
353
|
+
# Options are nil, true, false.
|
354
|
+
#
|
355
|
+
# config.no_cents_if_whole = nil
|
356
|
+
# config.symbol = nil
|
306
357
|
end
|
307
358
|
```
|
308
359
|
|
309
|
-
* ```
|
360
|
+
* ```default_currency```: Set the default (application wide) currency (USD is the default)
|
310
361
|
* ```include_validations```: Permit the inclusion of a ```validates_numericality_of```
|
311
362
|
validation for each monetized field (the default is true)
|
312
363
|
* ```register_currency```: Register one custom currency. This option can be
|
@@ -318,6 +369,11 @@ end
|
|
318
369
|
only! This rate is added to the attached bank object.
|
319
370
|
* ```default_bank```: The default bank object holding exchange rates etc.
|
320
371
|
(https://github.com/RubyMoney/money#currency-exchange)
|
372
|
+
* ```no_cents_if_whole```: Force `Money#format` method to use its value as the default for ```no_cents_if_whole``` key.
|
373
|
+
* ```symbol```: Use its value as the default for ```symbol``` key in
|
374
|
+
`Money#format` method.
|
375
|
+
* ```amount_column```: Provide values for the amount column (holding the fractional part of a money object).
|
376
|
+
* ```currency_column```: Provide default values or even disable (`present: false`) the currency column.
|
321
377
|
|
322
378
|
### Helpers
|
323
379
|
|
@@ -335,7 +391,7 @@ This will render a `span` dom element with the default currency symbol.
|
|
335
391
|
```
|
336
392
|
This will render a formatted money value without the currency symbol and
|
337
393
|
without the cents part if it contains only zeros (uses
|
338
|
-
`:
|
394
|
+
`:no_cents_if_whole flag`).
|
339
395
|
|
340
396
|
* humanize with symbol helper
|
341
397
|
|
@@ -362,6 +418,36 @@ without the cents part.
|
|
362
418
|
This will render a formatted money value including the currency symbol and
|
363
419
|
without the cents part.
|
364
420
|
|
421
|
+
### Testing
|
422
|
+
|
423
|
+
If you use Rspec there is an test helper implementation.
|
424
|
+
Just write `require "money-rails/test_helpers"` in spec_helper.rb and
|
425
|
+
`include MoneyRails::TestHelpers` inside a describe block you want to
|
426
|
+
use the helper.
|
427
|
+
|
428
|
+
* the `monetize` matcher
|
429
|
+
|
430
|
+
```
|
431
|
+
monetize(:price_cents).should be_true
|
432
|
+
```
|
433
|
+
This will ensure that a column called `price_cents` is being monetized.
|
434
|
+
|
435
|
+
```
|
436
|
+
monetize(:price_cents).as(:discount_value).should be_true
|
437
|
+
```
|
438
|
+
By using `as` chain you can specify the exact name to which a monetized
|
439
|
+
column is being mapped.
|
440
|
+
|
441
|
+
```
|
442
|
+
monetize(:price_cents).with_currency(:gbp).should be_true
|
443
|
+
```
|
444
|
+
|
445
|
+
By using the `with_currency` chain you can specify the expected currency
|
446
|
+
for the chosen money attribute. (You can also combine all the chains.)
|
447
|
+
|
448
|
+
For examples on using the test_helpers look at
|
449
|
+
[test_helpers_spec.rb](https://github.com/RubyMoney/money-rails/blob/master/spec/test_helpers_spec.rb)
|
450
|
+
|
365
451
|
## Supported ORMs/ODMs
|
366
452
|
|
367
453
|
* ActiveRecord (>= 3.x)
|
data/Rakefile
CHANGED
@@ -54,7 +54,7 @@ namespace :spec do
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
-
desc "Update
|
58
|
-
task :
|
59
|
-
sh "git shortlog -s | awk '{ print $2 \" \" $3 }' >
|
57
|
+
desc "Update CONTRIBUTORS file"
|
58
|
+
task :contributors do
|
59
|
+
sh "git shortlog -s | awk '{ print $2 \" \" $3 }' > CONTRIBUTORS"
|
60
60
|
end
|
@@ -4,7 +4,7 @@ MoneyRails.configure do |config|
|
|
4
4
|
|
5
5
|
# To set the default currency
|
6
6
|
#
|
7
|
-
#config.default_currency = :usd
|
7
|
+
# config.default_currency = :usd
|
8
8
|
|
9
9
|
# Set default bank object
|
10
10
|
#
|
@@ -21,7 +21,7 @@ MoneyRails.configure do |config|
|
|
21
21
|
# To handle the inclusion of validations for monetized fields
|
22
22
|
# The default value is true
|
23
23
|
#
|
24
|
-
#config.include_validations = true
|
24
|
+
# config.include_validations = true
|
25
25
|
|
26
26
|
# Default ActiveRecord migration configuration values for columns:
|
27
27
|
#
|
@@ -58,4 +58,10 @@ MoneyRails.configure do |config|
|
|
58
58
|
# :decimal_mark => ","
|
59
59
|
# }
|
60
60
|
|
61
|
+
# Set money formatted output globally.
|
62
|
+
# Default value is nil meaning "ignore this option".
|
63
|
+
# Options are nil, true, false.
|
64
|
+
#
|
65
|
+
# config.no_cents_if_whole = nil
|
66
|
+
# config.symbol = nil
|
61
67
|
end
|
data/lib/money-rails.rb
CHANGED
@@ -23,15 +23,17 @@ module MoneyRails
|
|
23
23
|
thousands_separator = I18n.t('number.currency.format.delimiter', default: currency.thousands_separator)
|
24
24
|
symbol = I18n.t('number.currency.format.unit', default: currency.symbol)
|
25
25
|
|
26
|
-
raw_value = raw_value.to_s.gsub(symbol, "")
|
26
|
+
raw_value = raw_value.to_s.gsub(symbol, "")
|
27
|
+
abs_raw_value = raw_value.gsub(/^-/, "")
|
27
28
|
|
28
|
-
decimal_pieces =
|
29
|
+
decimal_pieces = abs_raw_value.split(decimal_mark)
|
29
30
|
|
30
|
-
# check for numbers like 12.23.45
|
31
|
-
|
31
|
+
# check for numbers like '12.23.45' or '....'
|
32
|
+
unless [1, 2].include? decimal_pieces.length
|
32
33
|
record.errors.add(attr, I18n.t('errors.messages.invalid_currency',
|
33
34
|
{ :thousands => thousands_separator,
|
34
35
|
:decimal => decimal_mark }))
|
36
|
+
return
|
35
37
|
end
|
36
38
|
|
37
39
|
pieces = decimal_pieces[0].split(thousands_separator)
|
@@ -48,11 +50,12 @@ module MoneyRails
|
|
48
50
|
end
|
49
51
|
end
|
50
52
|
|
51
|
-
#
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
53
|
+
# Remove thousands separators, normalize decimal mark,
|
54
|
+
# remove whitespaces and _ (E.g. 99 999 999 or 12_300_200.20)
|
55
|
+
raw_value = raw_value.to_s
|
56
|
+
.gsub(thousands_separator, '')
|
57
|
+
.gsub(decimal_mark, '.')
|
58
|
+
.gsub(/[\s_]/, '')
|
56
59
|
end
|
57
60
|
super(record, attr, raw_value)
|
58
61
|
end
|
@@ -35,12 +35,12 @@ module MoneyRails
|
|
35
35
|
|
36
36
|
# Form target name for the money backed ActiveModel field:
|
37
37
|
# if a target name is provided then use it
|
38
|
-
# if there is a "
|
38
|
+
# if there is a "_{column.postfix}" suffix then just remove it to create the target name
|
39
39
|
# if none of the previous is the case then use a default suffix
|
40
40
|
if name
|
41
41
|
name = name.to_s
|
42
|
-
elsif subunit_name =~
|
43
|
-
name = subunit_name.sub(
|
42
|
+
elsif subunit_name =~ /#{MoneyRails::Configuration.amount_column[:postfix]}$/
|
43
|
+
name = subunit_name.sub(/#{MoneyRails::Configuration.amount_column[:postfix]}$/, "")
|
44
44
|
else
|
45
45
|
# FIXME: provide a better default
|
46
46
|
name = [subunit_name, "money"].join("_")
|
@@ -57,10 +57,17 @@ module MoneyRails
|
|
57
57
|
|
58
58
|
# Include numericality validation if needed
|
59
59
|
if MoneyRails.include_validations
|
60
|
-
|
61
|
-
|
60
|
+
validation_options = {
|
61
|
+
:allow_nil => options[:allow_nil],
|
62
|
+
:numericality => true
|
63
|
+
}
|
64
|
+
validates subunit_name, validation_options
|
65
|
+
|
66
|
+
validation_options = { :allow_nil => options[:allow_nil] }
|
67
|
+
validation_options = options[:numericality].merge(validation_options) if options[:numericality]
|
68
|
+
|
62
69
|
# Allow only Money objects or Numeric values!
|
63
|
-
validates name.to_sym, 'money_rails/active_model/money' =>
|
70
|
+
validates name.to_sym, 'money_rails/active_model/money' => validation_options
|
64
71
|
end
|
65
72
|
|
66
73
|
define_method name do |*args|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'active_support/core_ext/module/delegation'
|
2
2
|
require 'active_support/core_ext/module/attribute_accessors'
|
3
|
+
require 'active_support/core_ext/string/inflections'
|
3
4
|
|
4
5
|
module MoneyRails
|
5
6
|
|
@@ -19,9 +20,15 @@ module MoneyRails
|
|
19
20
|
|
20
21
|
# Configuration parameters
|
21
22
|
|
23
|
+
def default_currency
|
24
|
+
Money.default_currency
|
25
|
+
end
|
26
|
+
|
22
27
|
# Set default currency of money library
|
23
28
|
def default_currency=(currency_name)
|
24
29
|
Money.default_currency = Money::Currency.new(currency_name)
|
30
|
+
set_amount_column_for_default_currency!
|
31
|
+
set_currency_column_for_default_currency!
|
25
32
|
end
|
26
33
|
|
27
34
|
# Register a custom currency
|
@@ -29,6 +36,15 @@ module MoneyRails
|
|
29
36
|
Money::Currency.register(currency_options)
|
30
37
|
end
|
31
38
|
|
39
|
+
def set_amount_column_for_default_currency!
|
40
|
+
amount_column.merge! postfix: "_#{default_currency.subunit.downcase.pluralize}" if default_currency.subunit
|
41
|
+
end
|
42
|
+
|
43
|
+
def set_currency_column_for_default_currency!
|
44
|
+
iso_code = default_currency.iso_code
|
45
|
+
currency_column.merge! default: iso_code
|
46
|
+
end
|
47
|
+
|
32
48
|
# Set default bank object
|
33
49
|
#
|
34
50
|
# example (given that eu_central_bank is in Gemfile):
|
@@ -50,5 +66,12 @@ module MoneyRails
|
|
50
66
|
|
51
67
|
mattr_accessor :currency_column
|
52
68
|
@@currency_column = { postfix: '_currency', type: :string, null: false, default: 'USD', present: true }
|
69
|
+
|
70
|
+
# Use nil values to ignore defaults
|
71
|
+
mattr_accessor :no_cents_if_whole
|
72
|
+
@@no_cents_if_whole = nil
|
73
|
+
|
74
|
+
mattr_accessor :symbol
|
75
|
+
@@symbol = nil
|
53
76
|
end
|
54
77
|
end
|
@@ -5,32 +5,47 @@ module MoneyRails
|
|
5
5
|
content_tag(:span, Money.default_currency.symbol, :class => "currency_symbol")
|
6
6
|
end
|
7
7
|
|
8
|
-
def humanized_money(value,
|
8
|
+
def humanized_money(value, options={})
|
9
|
+
if !options || !options.is_a?(Hash)
|
10
|
+
warn "humanized_money now takes a hash of formatting options, please specify { :symbol => true }"
|
11
|
+
options = { :symbol => options }
|
12
|
+
end
|
13
|
+
|
14
|
+
options = {
|
15
|
+
:no_cents_if_whole => true,
|
16
|
+
:symbol => false
|
17
|
+
}.merge(options)
|
18
|
+
|
9
19
|
if value.is_a?(Money)
|
10
|
-
value.format(
|
20
|
+
value.format(options)
|
11
21
|
elsif value.respond_to?(:to_money)
|
12
|
-
value.to_money.format(
|
22
|
+
value.to_money.format(options)
|
13
23
|
else
|
14
24
|
""
|
15
25
|
end
|
16
26
|
end
|
17
27
|
|
18
28
|
def humanized_money_with_symbol(value)
|
19
|
-
humanized_money(value, true)
|
29
|
+
humanized_money(value, :symbol => true)
|
20
30
|
end
|
21
31
|
|
22
|
-
def money_without_cents(value,
|
23
|
-
if
|
24
|
-
|
25
|
-
|
26
|
-
value.to_money.format(:no_cents => true, :symbol => symbol)
|
27
|
-
else
|
28
|
-
""
|
32
|
+
def money_without_cents(value, options={})
|
33
|
+
if !options || !options.is_a?(Hash)
|
34
|
+
warn "money_without_cents now takes a hash of formatting options, please specify { :symbol => true }"
|
35
|
+
options = { :symbol => options }
|
29
36
|
end
|
37
|
+
|
38
|
+
options = {
|
39
|
+
:no_cents => true,
|
40
|
+
:no_cents_if_whole => false,
|
41
|
+
:symbol => false
|
42
|
+
}.merge(options)
|
43
|
+
|
44
|
+
humanized_money(value, options)
|
30
45
|
end
|
31
46
|
|
32
47
|
def money_without_cents_and_with_symbol(value)
|
33
|
-
money_without_cents(value, true)
|
48
|
+
money_without_cents(value, :symbol => true)
|
34
49
|
end
|
35
50
|
end
|
36
51
|
end
|
data/lib/money-rails/hooks.rb
CHANGED
@@ -18,10 +18,8 @@ module MoneyRails
|
|
18
18
|
if defined? ::Mongoid
|
19
19
|
if ::Mongoid::VERSION =~ /^2(.*)/
|
20
20
|
require 'money-rails/mongoid/two' # Loading the file is enough
|
21
|
-
|
22
|
-
|
23
|
-
if ::Mongoid::VERSION =~ /^3(.*)/
|
24
|
-
require 'money-rails/mongoid/three'
|
21
|
+
else
|
22
|
+
require 'money-rails/mongoid/money'
|
25
23
|
end
|
26
24
|
end
|
27
25
|
|