money-rails 1.2.0 → 1.3.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: 84b727182c1db1168ce04b8ff16e96bfe4607b6c
4
- data.tar.gz: e7ad11eca117fa5a7b7a1f8f941c4876d4b898eb
3
+ metadata.gz: f6d1b9abaf2dfb8fa5d9ede9509fe5dd3ef323a5
4
+ data.tar.gz: 34b2f71bba5a7773e2b85571fda0892f643cb0c0
5
5
  SHA512:
6
- metadata.gz: bdccd1bba23586013586dab5460f136eb6fd5fbf72e531c935314cd1c9a243f4358cc8ed18fa5f29c08ebcc9372cc4c6e18eebcd303a957118fb74fb8d12425a
7
- data.tar.gz: e4942ea49fd18149c4c414516a43c2d49ae978cbd32281bd6e4e2bc3a864b7c86e5084fd67e2508cab5ef0c2afb6dcb8e36d0a1e5e266bc347fbf0590fb89493
6
+ metadata.gz: f8038c315b04d1d026b2582c1ac76a1c904e4e9ec5a2462fcbfcdfcca283475e1ee778621a34dbd5acc7d28b608350f4a216cf1805bb669f99134cb340bca4b5
7
+ data.tar.gz: 9109fd349499fe439531743e309a6babd40e7bc1ca3a218a3955e4f87985e4046caeaef5f46ce705293f48a2b0544bb0865e5c9a81f8e072ce852f27f7bfd415
data/README.md CHANGED
@@ -107,10 +107,17 @@ class MonetizeItem < ActiveRecord::Migration
107
107
  end
108
108
  ```
109
109
 
110
+ Notice. Default value of currency field, generated by migration's helper, is USD. To override these defaults, you need change default_currency in money initializer and run migrations.
111
+
110
112
  The ```add_money``` helper is reversible, so you an use it inside ```change```
111
113
  migrations. If you're writing separate ```up``` and ```down``` methods, you
112
114
  can use the ```remove_money``` helper.
113
115
 
116
+ ##### Notice for Rails >= 4.2 and PG adapter
117
+
118
+ Due the adding `money` column type for postgres in Rails 4.2 you will need to use `add_monetize` for add money column,
119
+ `t.monetize` for add column in `create_table` or `change_table` block and `remove_monetize` for removing column.
120
+
114
121
  #### Allow nil values
115
122
 
116
123
  If you want to allow nil and/or blank values to a specific
@@ -377,7 +384,7 @@ MoneyRails.configure do |config|
377
384
 
378
385
  # Specify a rounding mode
379
386
  # Any one of:
380
- #
387
+ #
381
388
  # BigDecimal::ROUND_UP,
382
389
  # BigDecimal::ROUND_DOWN,
383
390
  # BigDecimal::ROUND_HALF_UP,
@@ -385,9 +392,9 @@ MoneyRails.configure do |config|
385
392
  # BigDecimal::ROUND_HALF_EVEN,
386
393
  # BigDecimal::ROUND_CEILING,
387
394
  # BigDecimal::ROUND_FLOOR
388
- #
395
+ #
389
396
  # set to BigDecimal::ROUND_HALF_EVEN by default
390
- #
397
+ #
391
398
  # config.rounding_mode = BigDecimal::ROUND_HALF_UP
392
399
 
393
400
  # Set default money format globally.
@@ -434,6 +441,8 @@ _For examples below, `@money_object == <Money fractional:650 currency:USD>`_
434
441
  #### `no_cents_if_whole`
435
442
 
436
443
  `humanized_money` and `humanized_money_with_symbol` will not render the cents part if it contains only zeros, unless `config.no_cents_if_whole` is set to `false` in the `money.rb` configuration (default: true).
444
+ Note that the `config.default_format` will be overwritten by `config.no_cents_if_whole`.
445
+ So `humanized_money` will ignore `config.default_format = { no_cents_if_whole: false }` if you don't set `config.no_cents_if_whole = false`.
437
446
 
438
447
  ### Testing
439
448
 
@@ -6,21 +6,18 @@ module MoneyRails
6
6
  @record = record
7
7
  @attr = attr
8
8
 
9
- # If subunit is not set then no need to validate as it is an
10
- # indicator that no assignment has been done onto the virtual
11
- # money field.
12
9
  subunit_attr = @record.class.monetized_attributes[@attr.to_sym]
13
- return unless @record.changed_attributes.keys.include? subunit_attr
14
10
 
15
11
  # WARNING: Currently this is only defined in ActiveRecord extension!
16
12
  before_type_cast = :"#{@attr}_money_before_type_cast"
17
13
  @raw_value = @record.try(before_type_cast)
18
14
 
19
15
  # If raw value is nil and changed subunit is nil, then
20
- # nil is a assigned value, elsewhere we should treat the
16
+ # nil is a assigned value, else we should treat the
21
17
  # subunit value as the one assigned.
22
- if @raw_value.nil? && @record.send(subunit_attr)
23
- @raw_value = @record.send(subunit_attr)
18
+ if @raw_value.nil? && @record.public_send(subunit_attr)
19
+ subunit_value = @record.public_send(subunit_attr)
20
+ @raw_value = subunit_value.to_f / currency.subunit_to_unit
24
21
  end
25
22
 
26
23
  return if options[:allow_nil] && @raw_value.nil?
@@ -51,7 +48,7 @@ module MoneyRails
51
48
  end
52
49
 
53
50
  def currency
54
- @_currency ||= @record.send("currency_for_#{@attr}")
51
+ @_currency ||= @record.public_send("currency_for_#{@attr}")
55
52
  end
56
53
 
57
54
  def decimal_mark
@@ -0,0 +1,21 @@
1
+ module MoneyRails
2
+ module ActiveRecord
3
+ module MigrationExtensions
4
+ module SchemaStatements
5
+ def add_monetize(table_name, accessor, options={})
6
+ [:amount, :currency].each do |attribute|
7
+ column_present, *opts = OptionsExtractor.extract attribute, table_name, accessor, options
8
+ add_column *opts if column_present
9
+ end
10
+ end
11
+
12
+ def remove_monetize(table_name, accessor, options={})
13
+ [:amount, :currency].each do |attribute|
14
+ column_present, table_name, column_name, _, _ = OptionsExtractor.extract attribute, table_name, accessor, options
15
+ remove_column table_name, column_name if column_present
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ module MoneyRails
2
+ module ActiveRecord
3
+ module MigrationExtensions
4
+ module Table
5
+ def monetize(accessor, options={})
6
+ [:amount, :currency].each do |attribute|
7
+ column_present, _, *opts = OptionsExtractor.extract attribute, :no_table, accessor, options
8
+ column *opts if column_present
9
+ end
10
+ end
11
+
12
+ def remove_monetize(accessor, options={})
13
+ [:amount, :currency].each do |attribute|
14
+ column_present, _, column_name, _, _ = OptionsExtractor.extract attribute, :no_table, accessor, options
15
+ remove column_name if column_present
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -8,224 +8,232 @@ module MoneyRails
8
8
  extend ActiveSupport::Concern
9
9
 
10
10
  module ClassMethods
11
- def monetize(field, *args)
12
- options = args.extract_options!
13
-
14
- # Stringify model field name
15
- subunit_name = field.to_s
16
-
17
- if options[:field_currency] || options[:target_name] ||
18
- options[:model_currency]
19
- ActiveSupport::Deprecation.warn("You are using the old " \
20
- "argument keys of the monetize command! Instead use :as, " \
21
- ":with_currency or :with_model_currency")
22
- end
11
+ def monetize(*fields)
12
+ options = fields.extract_options!
13
+
14
+ fields.each do |field|
15
+ # Stringify model field name
16
+ subunit_name = field.to_s
17
+
18
+ if options[:field_currency] || options[:target_name] ||
19
+ options[:model_currency]
20
+ ActiveSupport::Deprecation.warn("You are using the old " \
21
+ "argument keys of the monetize command! Instead use :as, " \
22
+ ":with_currency or :with_model_currency")
23
+ end
23
24
 
24
- # Optional accessor to be run on an instance to detect currency
25
- instance_currency_name = options[:with_model_currency] ||
26
- options[:model_currency] ||
27
- MoneyRails::Configuration.currency_column[:column_name]
28
-
29
- instance_currency_name = instance_currency_name &&
30
- instance_currency_name.to_s
31
-
32
- # This attribute allows per column currency values
33
- # Overrides row and default currency
34
- field_currency_name = options[:with_currency] ||
35
- options[:field_currency] || nil
36
-
37
- name = options[:as] || options[:target_name] || nil
38
-
39
- # Form target name for the money backed ActiveModel field:
40
- # if a target name is provided then use it
41
- # if there is a "{column.postfix}" suffix then just remove it to create the target name
42
- # if none of the previous is the case then use a default suffix
43
- if name
44
- name = name.to_s
45
- elsif subunit_name =~ /#{MoneyRails::Configuration.amount_column[:postfix]}$/
46
- name = subunit_name.sub(/#{MoneyRails::Configuration.amount_column[:postfix]}$/, "")
47
- else
48
- # FIXME: provide a better default
49
- name = [subunit_name, "money"].join("_")
50
- end
25
+ # Optional accessor to be run on an instance to detect currency
26
+ instance_currency_name = options[:with_model_currency] ||
27
+ options[:model_currency] ||
28
+ MoneyRails::Configuration.currency_column[:column_name]
29
+
30
+ instance_currency_name = instance_currency_name &&
31
+ instance_currency_name.to_s
32
+
33
+ # This attribute allows per column currency values
34
+ # Overrides row and default currency
35
+ field_currency_name = options[:with_currency] ||
36
+ options[:field_currency] || nil
37
+
38
+ name = options[:as] || options[:target_name] || nil
39
+
40
+ # Form target name for the money backed ActiveModel field:
41
+ # if a target name is provided then use it
42
+ # if there is a "{column.postfix}" suffix then just remove it to create the target name
43
+ # if none of the previous is the case then use a default suffix
44
+ if name
45
+ name = name.to_s
46
+ elsif subunit_name =~ /#{MoneyRails::Configuration.amount_column[:postfix]}$/
47
+ name = subunit_name.sub(/#{MoneyRails::Configuration.amount_column[:postfix]}$/, "")
48
+ else
49
+ # FIXME: provide a better default
50
+ name = [subunit_name, "money"].join("_")
51
+ end
51
52
 
52
- # Create a reverse mapping of the monetized attributes
53
- @monetized_attributes ||= {}.with_indifferent_access
54
- if @monetized_attributes[name].present?
55
- raise ArgumentError, "#{self} already has a monetized attribute called '#{name}'"
56
- end
57
- @monetized_attributes[name] = subunit_name
58
- class << self
59
- def monetized_attributes
60
- @monetized_attributes || superclass.monetized_attributes
53
+ # Create a reverse mapping of the monetized attributes
54
+ @monetized_attributes ||= {}.with_indifferent_access
55
+ if @monetized_attributes[name].present?
56
+ raise ArgumentError, "#{self} already has a monetized attribute called '#{name}'"
61
57
  end
62
- end unless respond_to? :monetized_attributes
63
-
64
- # Include numericality validations if needed.
65
- # There are two validation options:
66
- #
67
- # 1. Subunit field validation (e.g. cents should be > 100)
68
- # 2. Money field validation (e.g. euros should be > 10)
69
- #
70
- # All the options which are available for Rails numericality
71
- # validation, are also available for both types.
72
- # E.g.
73
- # monetize :price_in_a_range_cents, :allow_nil => true,
74
- # :subunit_numericality => {
75
- # :greater_than_or_equal_to => 0,
76
- # :less_than_or_equal_to => 10000,
77
- # },
78
- # :numericality => {
79
- # :greater_than_or_equal_to => 0,
80
- # :less_than_or_equal_to => 100,
81
- # :message => "Must be greater than zero and less than $100"
82
- # }
83
- #
84
- # To disable validation entirely, use :disable_validation, E.g:
85
- # monetize :price_in_a_range_cents, :disable_validation => true
86
- if validation_enabled = MoneyRails.include_validations && !options[:disable_validation]
87
-
88
- subunit_validation_options =
89
- unless options.has_key? :subunit_numericality
90
- true
91
- else
92
- options[:subunit_numericality]
58
+ @monetized_attributes[name] = subunit_name
59
+ class << self
60
+ def monetized_attributes
61
+ @monetized_attributes || superclass.monetized_attributes
93
62
  end
63
+ end unless respond_to? :monetized_attributes
64
+
65
+ # Include numericality validations if needed.
66
+ # There are two validation options:
67
+ #
68
+ # 1. Subunit field validation (e.g. cents should be > 100)
69
+ # 2. Money field validation (e.g. euros should be > 10)
70
+ #
71
+ # All the options which are available for Rails numericality
72
+ # validation, are also available for both types.
73
+ # E.g.
74
+ # monetize :price_in_a_range_cents, :allow_nil => true,
75
+ # :subunit_numericality => {
76
+ # :greater_than_or_equal_to => 0,
77
+ # :less_than_or_equal_to => 10000,
78
+ # },
79
+ # :numericality => {
80
+ # :greater_than_or_equal_to => 0,
81
+ # :less_than_or_equal_to => 100,
82
+ # :message => "Must be greater than zero and less than $100"
83
+ # }
84
+ #
85
+ # To disable validation entirely, use :disable_validation, E.g:
86
+ # monetize :price_in_a_range_cents, :disable_validation => true
87
+ if validation_enabled = MoneyRails.include_validations && !options[:disable_validation]
88
+
89
+ subunit_validation_options =
90
+ unless options.has_key? :subunit_numericality
91
+ true
92
+ else
93
+ options[:subunit_numericality]
94
+ end
94
95
 
95
- money_validation_options =
96
- unless options.has_key? :numericality
97
- true
98
- else
99
- options[:numericality]
100
- end
96
+ money_validation_options =
97
+ unless options.has_key? :numericality
98
+ true
99
+ else
100
+ options[:numericality]
101
+ end
101
102
 
102
- # This is a validation for the subunit
103
- validates subunit_name, {
104
- :allow_nil => options[:allow_nil],
105
- :numericality => subunit_validation_options
106
- }
107
-
108
- # Allow only Money objects or Numeric values!
109
- validates name.to_sym, {
110
- :allow_nil => options[:allow_nil],
111
- 'money_rails/active_model/money' => money_validation_options
112
- }
113
- end
103
+ # This is a validation for the subunit
104
+ validates subunit_name, {
105
+ :allow_nil => options[:allow_nil],
106
+ :numericality => subunit_validation_options
107
+ }
108
+
109
+ # Allow only Money objects or Numeric values!
110
+ validates name.to_sym, {
111
+ :allow_nil => options[:allow_nil],
112
+ 'money_rails/active_model/money' => money_validation_options
113
+ }
114
+ end
114
115
 
115
116
 
116
- define_method name do |*args|
117
+ define_method name do |*args|
117
118
 
118
- # Get the cents
119
- amount = send(subunit_name, *args)
119
+ # Get the cents
120
+ amount = public_send(subunit_name, *args)
120
121
 
121
- # Get the currency object
122
- attr_currency = send("currency_for_#{name}")
122
+ # Get the currency object
123
+ attr_currency = public_send("currency_for_#{name}")
123
124
 
124
- # Get the cached value
125
- memoized = instance_variable_get("@#{name}")
125
+ # Get the cached value
126
+ memoized = instance_variable_get("@#{name}")
126
127
 
127
- # Dont create a new Money instance if the values haven't been changed.
128
- return memoized if memoized && memoized.cents == amount &&
129
- memoized.currency == attr_currency
128
+ # Dont create a new Money instance if the values haven't been changed.
129
+ return memoized if memoized && memoized.cents == amount &&
130
+ memoized.currency == attr_currency
130
131
 
131
- # If amount is NOT nil (or empty string) load the amount in a Money
132
- amount = Money.new(amount, attr_currency) unless amount.blank?
132
+ # If amount is NOT nil (or empty string) load the amount in a Money
133
+ amount = Money.new(amount, attr_currency) unless amount.blank?
133
134
 
134
- # Cache and return the value (it may be nil)
135
- instance_variable_set "@#{name}", amount
136
- end
135
+ # Cache and return the value (it may be nil)
136
+ instance_variable_set "@#{name}", amount
137
+ end
137
138
 
138
- define_method "#{name}=" do |value|
139
+ define_method "#{name}=" do |value|
139
140
 
140
- # Lets keep the before_type_cast value
141
- instance_variable_set "@#{name}_money_before_type_cast", value
141
+ # Lets keep the before_type_cast value
142
+ instance_variable_set "@#{name}_money_before_type_cast", value
142
143
 
143
- # Use nil or get a Money object
144
- if options[:allow_nil] && value.blank?
145
- money = nil
146
- else
147
- if value.is_a?(Money)
148
- money = value
144
+ # Use nil or get a Money object
145
+ if options[:allow_nil] && value.blank?
146
+ money = nil
149
147
  else
150
- begin
151
- money = value.to_money(send("currency_for_#{name}"))
152
- rescue NoMethodError
153
- return nil
154
- rescue ArgumentError
155
- raise if MoneyRails.raise_error_on_money_parsing
156
- return nil
157
- rescue Money::Currency::UnknownCurrency
158
- raise if MoneyRails.raise_error_on_money_parsing
159
- return nil
148
+ if value.is_a?(Money)
149
+ money = value
150
+ else
151
+ begin
152
+ money = value.to_money(public_send("currency_for_#{name}"))
153
+ rescue NoMethodError
154
+ return nil
155
+ rescue ArgumentError
156
+ raise if MoneyRails.raise_error_on_money_parsing
157
+ return nil
158
+ rescue Money::Currency::UnknownCurrency
159
+ raise if MoneyRails.raise_error_on_money_parsing
160
+ return nil
161
+ end
160
162
  end
161
163
  end
162
- end
163
164
 
164
- # Update cents
165
- # If the attribute is aliased, make sure we write to the original
166
- # attribute name or an error will be raised.
167
- # (Note: 'attribute_aliases' doesn't exist in Rails 3.x, so we
168
- # can't tell if the attribute was aliased.)
169
- if self.class.respond_to?(:attribute_aliases) &&
170
- self.class.attribute_aliases.key?(subunit_name)
171
- original_name = self.class.attribute_aliases[subunit_name.to_s]
172
- write_attribute(original_name, money.try(:cents))
173
- else
174
- write_attribute(subunit_name, money.try(:cents))
175
- end
165
+ # Update cents
166
+ if !validation_enabled
167
+ # We haven't defined our own subunit writer, so we can invoke
168
+ # the regular writer, which works with store_accessors
169
+ public_send("#{subunit_name}=", money.try(:cents))
170
+ elsif self.class.respond_to?(:attribute_aliases) &&
171
+ self.class.attribute_aliases.key?(subunit_name)
172
+ # If the attribute is aliased, make sure we write to the original
173
+ # attribute name or an error will be raised.
174
+ # (Note: 'attribute_aliases' doesn't exist in Rails 3.x, so we
175
+ # can't tell if the attribute was aliased.)
176
+ original_name = self.class.attribute_aliases[subunit_name.to_s]
177
+ write_attribute(original_name, money.try(:cents))
178
+ else
179
+ write_attribute(subunit_name, money.try(:cents))
180
+ end
176
181
 
177
- money_currency = money.try(:currency)
182
+ money_currency = money.try(:currency)
178
183
 
179
- # Update currency iso value if there is an instance currency attribute
180
- if instance_currency_name.present? &&
181
- self.respond_to?("#{instance_currency_name}=")
184
+ # Update currency iso value if there is an instance currency attribute
185
+ if instance_currency_name.present? &&
186
+ respond_to?("#{instance_currency_name}=")
182
187
 
183
- send("#{instance_currency_name}=", money_currency.try(:iso_code))
184
- else
185
- current_currency = send("currency_for_#{name}")
186
- if money_currency && current_currency != money_currency.id
187
- raise "Can't change readonly currency '#{current_currency}' to '#{money_currency}' for field '#{name}'"
188
+ public_send("#{instance_currency_name}=", money_currency.try(:iso_code))
189
+ else
190
+ current_currency = public_send("currency_for_#{name}")
191
+ if money_currency && current_currency != money_currency.id
192
+ raise "Can't change readonly currency '#{current_currency}' to '#{money_currency}' for field '#{name}'"
193
+ end
188
194
  end
189
- end
190
195
 
191
- # Save and return the new Money object
192
- instance_variable_set "@#{name}", money
193
- end
196
+ # Save and return the new Money object
197
+ instance_variable_set "@#{name}", money
198
+ end
194
199
 
195
- if validation_enabled
196
- # Ensure that the before_type_cast value is updated when setting
197
- # the subunit value directly
198
- define_method "#{subunit_name}=" do |value|
199
- before_type_cast = value.to_f / send("currency_for_#{name}").subunit_to_unit
200
- instance_variable_set "@#{name}_money_before_type_cast", before_type_cast
201
- write_attribute(subunit_name, value)
200
+ if validation_enabled
201
+ # Ensure that the before_type_cast value is cleared when setting
202
+ # the subunit value directly
203
+ define_method "#{subunit_name}=" do |value|
204
+ instance_variable_set "@#{name}_money_before_type_cast", nil
205
+ write_attribute(subunit_name, value)
206
+ end
202
207
  end
203
- end
204
208
 
205
- define_method "currency_for_#{name}" do
206
- if instance_currency_name.present? &&
207
- self.respond_to?(instance_currency_name) &&
208
- send(instance_currency_name).present? &&
209
- Money::Currency.find(send(instance_currency_name))
210
-
211
- Money::Currency.find(send(instance_currency_name))
212
- elsif field_currency_name
213
- Money::Currency.find(field_currency_name)
214
- elsif self.class.respond_to?(:currency)
215
- self.class.currency
216
- else
217
- Money.default_currency
209
+ define_method "currency_for_#{name}" do
210
+ instance_currency_name_with_postfix = "#{name}#{MoneyRails::Configuration.currency_column[:postfix]}"
211
+
212
+ if instance_currency_name.present? &&
213
+ respond_to?(instance_currency_name) &&
214
+ Money::Currency.find(public_send(instance_currency_name))
215
+
216
+ Money::Currency.find(public_send(instance_currency_name))
217
+ elsif field_currency_name
218
+ Money::Currency.find(field_currency_name)
219
+ elsif respond_to?(instance_currency_name_with_postfix) &&
220
+ Money::Currency.find(public_send(instance_currency_name_with_postfix))
221
+
222
+ Money::Currency.find(public_send(instance_currency_name_with_postfix))
223
+ elsif self.class.respond_to?(:currency)
224
+ self.class.currency
225
+ else
226
+ Money.default_currency
227
+ end
218
228
  end
219
- end
220
229
 
221
- define_method "#{name}_money_before_type_cast" do
222
- instance_variable_get "@#{name}_money_before_type_cast"
223
- end
230
+ attr_reader "#{name}_money_before_type_cast"
224
231
 
225
- # Hook to ensure the reset of before_type_cast attr
226
- # TODO: think of a better way to avoid this
227
- after_save do
228
- instance_variable_set "@#{name}_money_before_type_cast", nil
232
+ # Hook to ensure the reset of before_type_cast attr
233
+ # TODO: think of a better way to avoid this
234
+ after_save do
235
+ instance_variable_set "@#{name}_money_before_type_cast", nil
236
+ end
229
237
  end
230
238
  end
231
239
 
@@ -15,6 +15,7 @@ module MoneyRails
15
15
  :no_cents_if_whole => MoneyRails::Configuration.no_cents_if_whole.nil? ? true : MoneyRails::Configuration.no_cents_if_whole,
16
16
  :symbol => false
17
17
  }.merge(options)
18
+ options.delete(:symbol) if options[:disambiguate]
18
19
 
19
20
  if value.is_a?(Money)
20
21
  value.format(options)
@@ -6,8 +6,26 @@ module MoneyRails
6
6
  require 'money-rails/active_model/validator'
7
7
  require 'money-rails/active_record/monetizable'
8
8
  ::ActiveRecord::Base.send :include, MoneyRails::ActiveRecord::Monetizable
9
+ if ::Rails::VERSION::MAJOR >= 4
10
+ rails42 = case
11
+ when ::Rails::VERSION::MAJOR < 5 && ::Rails::VERSION::MINOR >= 2
12
+ true
13
+ when ::Rails::VERSION::MAJOR >= 5
14
+ true
15
+ else
16
+ false
17
+ end
18
+ postgresql_with_money = rails42 && ['activerecord-jdbcpostgresql-adapter', 'postgresql'].include?(::ActiveRecord::Base.connection.instance_values["config"][:adapter])
19
+ end
9
20
 
10
- %w{options_extractor schema_statements table}.each { |file| require "money-rails/active_record/migration_extensions/#{file}" }
21
+ require "money-rails/active_record/migration_extensions/options_extractor"
22
+ %w{schema_statements table}.each do |file|
23
+ if postgresql_with_money
24
+ require "money-rails/active_record/migration_extensions/#{file}_pg_rails4"
25
+ else
26
+ require "money-rails/active_record/migration_extensions/#{file}"
27
+ end
28
+ end
11
29
  ::ActiveRecord::Migration.send :include, MoneyRails::ActiveRecord::MigrationExtensions::SchemaStatements
12
30
  ::ActiveRecord::ConnectionAdapters::TableDefinition.send :include, MoneyRails::ActiveRecord::MigrationExtensions::Table
13
31
  ::ActiveRecord::ConnectionAdapters::Table.send :include, MoneyRails::ActiveRecord::MigrationExtensions::Table
@@ -14,7 +14,11 @@ class Money
14
14
  # this custom class from it.
15
15
  def demongoize(object)
16
16
  if object.is_a?(Hash)
17
- object = object.deep_symbolize_keys
17
+ if object.respond_to?(:deep_symbolize_keys)
18
+ object = object.deep_symbolize_keys
19
+ else
20
+ object = object.symbolize_keys
21
+ end
18
22
  object.has_key?(:cents) ? ::Money.new(object[:cents], object[:currency_iso]) : nil
19
23
  else
20
24
  nil
@@ -27,7 +31,11 @@ class Money
27
31
  case
28
32
  when object.is_a?(Money) then object.mongoize
29
33
  when object.is_a?(Hash) then
30
- object.deep_symbolize_keys! if object.respond_to?(:deep_symbolize_keys!)
34
+ if object.respond_to?(:deep_symbolize_keys!)
35
+ object.deep_symbolize_keys!
36
+ elsif object.respond_to?(:symbolize_keys!)
37
+ object.symbolize_keys!
38
+ end
31
39
  ::Money.new(object[:cents], object[:currency_iso]).mongoize
32
40
  when object.respond_to?(:to_money) then
33
41
  begin
@@ -1,3 +1,3 @@
1
1
  module MoneyRails
2
- VERSION = "1.2.0"
2
+ VERSION = "1.3.0"
3
3
  end
data/money-rails.gemspec CHANGED
@@ -33,5 +33,5 @@ Gem::Specification.new do |s|
33
33
 
34
34
  s.add_development_dependency "rails", ">= 3.0"
35
35
  s.add_development_dependency "rspec-rails", "~> 3.0"
36
- s.add_development_dependency 'database_cleaner', ['>= 0.8.0']
36
+ s.add_development_dependency 'database_cleaner', ['>= 0.8.0', '< 1.4.0']
37
37
  end
@@ -8,7 +8,9 @@ if defined? ActiveRecord
8
8
  let(:product) do
9
9
  Product.create(:price_cents => 3000, :discount => 150,
10
10
  :bonus_cents => 200, :optional_price => 100,
11
- :sale_price_amount => 1200)
11
+ :sale_price_amount => 1200, :delivery_fee_cents => 100,
12
+ :restock_fee_cents => 2000,
13
+ :reduced_price_cents => 1500, :reduced_price_currency => :lvl)
12
14
  end
13
15
 
14
16
  let(:service) do
@@ -25,6 +27,11 @@ if defined? ActiveRecord
25
27
  expect(product.bonus).to be_an_instance_of(Money)
26
28
  end
27
29
 
30
+ it "attaches Money objects to multiple model fields" do
31
+ expect(product.delivery_fee).to be_an_instance_of(Money)
32
+ expect(product.restock_fee).to be_an_instance_of(Money)
33
+ end
34
+
28
35
  it "returns the expected money amount as a Money object" do
29
36
  expect(product.price).to eq(Money.new(3000, "USD"))
30
37
  end
@@ -75,7 +82,7 @@ if defined? ActiveRecord
75
82
  end
76
83
 
77
84
  it "skips numericality validation when disabled" do
78
- product.invalid_price_cents = 'not_valid'
85
+ product.accessor_price_cents = 'not_valid'
79
86
  expect(product.save).to be_truthy
80
87
  end
81
88
 
@@ -91,13 +98,13 @@ if defined? ActiveRecord
91
98
  after { MoneyRails.raise_error_on_money_parsing = false }
92
99
 
93
100
  it "raises exception when a String value with hyphen is assigned" do
94
- expect { product.invalid_price = "10-235" }.to raise_error
101
+ expect { product.accessor_price = "10-235" }.to raise_error
95
102
  end
96
103
  end
97
104
 
98
105
  context "when MoneyRails.raise_error_on_money_parsing is false (default)" do
99
106
  it "does not raise exception when a String value with hyphen is assigned" do
100
- expect { product.invalid_price = "10-235" }.not_to raise_error
107
+ expect { product.accessor_price = "10-235" }.not_to raise_error
101
108
  end
102
109
  end
103
110
 
@@ -193,6 +200,15 @@ if defined? ActiveRecord
193
200
  expect(product.errors[:price_in_a_range].first).to match(/Must be greater than zero and less than \$100/)
194
201
  end
195
202
 
203
+ it "fails validation if linked attribute changed" do
204
+ product = Product.create(:price => Money.new(3210, "USD"), :discount => 150,
205
+ :validates_method_amount => 100,
206
+ :bonus_cents => 200, :optional_price => 100)
207
+ expect(product.valid?).to be_truthy
208
+ product.optional_price = 50
209
+ expect(product.valid?).to be_falsey
210
+ end
211
+
196
212
  it "fails validation with the proper error message using validates :money" do
197
213
  product.validates_method_amount = "-12"
198
214
  expect(product.valid?).to be_falsey
@@ -297,20 +313,6 @@ if defined? ActiveRecord
297
313
  I18n.locale = old_locale
298
314
  end
299
315
 
300
- it "defaults to Money::Currency format when no I18n information is present" do
301
- old_locale = I18n.locale
302
-
303
- I18n.locale = "zxsw"
304
- Money.default_currency = Money::Currency.find('EUR')
305
- expect("12,00".to_money).to eq(Money.new(1200, :eur))
306
- transaction = Transaction.new(amount: "12,00", tax: "13,00")
307
- expect(transaction.amount_cents).to eq(1200)
308
- expect(transaction.valid?).to be_truthy
309
-
310
- # reset locale setting
311
- I18n.locale = old_locale
312
- end
313
-
314
316
  it "doesn't allow nil by default" do
315
317
  product.price_cents = nil
316
318
  expect(product.save).to be_falsey
@@ -364,6 +366,10 @@ if defined? ActiveRecord
364
366
  expect(product.bonus.currency).to eq(Money::Currency.find(:gbp))
365
367
  end
366
368
 
369
+ it "uses currency postfix to determine attribute that stores currency" do
370
+ expect(product.reduced_price.currency).to eq(Money::Currency.find(:lvl))
371
+ end
372
+
367
373
  it "correctly assigns Money objects to the attribute" do
368
374
  product.price = Money.new(2500, :USD)
369
375
  expect(product.save).to be_truthy
@@ -395,6 +401,13 @@ if defined? ActiveRecord
395
401
  expect(service.discount.currency_as_string).to eq("EUR")
396
402
  end
397
403
 
404
+ it "correctly assigns objects to a accessor attribute" do
405
+ product.accessor_price = 1.23
406
+ expect(product.save).to be_truthy
407
+ expect(product.accessor_price.cents).to eq(123)
408
+ expect(product.accessor_price_cents).to eq(123)
409
+ end
410
+
398
411
  it "overrides default, model currency with the value of :with_currency in fixnum assignments" do
399
412
  product.bonus = 25
400
413
  expect(product.save).to be_truthy
@@ -467,7 +480,6 @@ if defined? ActiveRecord
467
480
  expect(product.optional_price).to be_nil
468
481
  end
469
482
 
470
-
471
483
  context "when the monetized field is an aliased attribute" do
472
484
  it "writes the subunits to the original (unaliased) column" do
473
485
  pending if Rails::VERSION::MAJOR < 4
@@ -5,6 +5,9 @@ class Product < ActiveRecord::Base
5
5
  # Use money-rails macros
6
6
  monetize :price_cents
7
7
 
8
+ # Use money-rails macro with multiple fields
9
+ monetize :delivery_fee_cents, :restock_fee_cents, :allow_nil => true
10
+
8
11
  # Use a custom name for the Money attribute
9
12
  monetize :discount, :as => "discount_value"
10
13
 
@@ -29,18 +32,21 @@ class Product < ActiveRecord::Base
29
32
  :message => "Must be greater than zero and less than $100"
30
33
  }
31
34
 
32
- attr_accessor :invalid_price_cents
33
- monetize :invalid_price_cents, disable_validation: true
35
+ attr_accessor :accessor_price_cents
36
+ monetize :accessor_price_cents, disable_validation: true
34
37
 
35
38
  monetize :validates_method_amount_cents, allow_nil: true
36
39
 
37
40
  validates :validates_method_amount, :money => {
38
41
  :greater_than => 0,
39
- :less_than_or_equal_to => 100,
42
+ :less_than_or_equal_to => ->(product) { product.optional_price.to_f },
40
43
  :message => 'Must be greater than zero and less than $100',
41
- }
44
+ }, allow_nil: true
42
45
 
43
46
  alias_attribute :renamed_cents, :aliased_cents
44
47
 
45
48
  monetize :renamed_cents, allow_nil: true
49
+
50
+ # Using postfix to determine currency column (reduced_price_currency)
51
+ monetize :reduced_price_cents, :allow_nil => true
46
52
  end
@@ -0,0 +1,6 @@
1
+ class AddDeliveryFeeCentsAndRestockFeeCentsToProduct < ActiveRecord::Migration
2
+ def change
3
+ add_column :products, :delivery_fee_cents, :integer
4
+ add_column :products, :restock_fee_cents, :integer
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class AddReducedPriceToProducts < ActiveRecord::Migration
2
+ def change
3
+ add_column :products, :reduced_price_cents, :integer
4
+ add_column :products, :reduced_price_currency, :string
5
+ end
6
+ end
@@ -11,7 +11,7 @@
11
11
  #
12
12
  # It's strongly recommended that you check this file into your version control system.
13
13
 
14
- ActiveRecord::Schema.define(version: 20141005075025) do
14
+ ActiveRecord::Schema.define(version: 20150126231442) do
15
15
 
16
16
  create_table "dummy_products", force: true do |t|
17
17
  t.string "currency"
@@ -32,6 +32,10 @@ ActiveRecord::Schema.define(version: 20141005075025) do
32
32
  t.integer "price_in_a_range_cents"
33
33
  t.integer "validates_method_amount_cents"
34
34
  t.integer "aliased_cents"
35
+ t.integer "delivery_fee_cents"
36
+ t.integer "restock_fee_cents"
37
+ t.integer "reduced_price_cents"
38
+ t.string "reduced_price_currency"
35
39
  end
36
40
 
37
41
  create_table "services", force: true do |t|
@@ -8,8 +8,9 @@ describe 'MoneyRails::ActionViewExtension', :type => :helper do
8
8
  end
9
9
 
10
10
  describe '#humanized_money' do
11
+ let(:money_object){ Money.new(12500) }
11
12
  let(:options) { {} }
12
- subject { helper.humanized_money Money.new(12500), options }
13
+ subject { helper.humanized_money money_object, options }
13
14
  it { is_expected.to be_a String }
14
15
  it { is_expected.not_to include Money.default_currency.symbol }
15
16
  it { is_expected.not_to include Money.default_currency.decimal_mark }
@@ -26,6 +27,20 @@ describe 'MoneyRails::ActionViewExtension', :type => :helper do
26
27
  end
27
28
  it { is_expected.to include Money.default_currency.symbol }
28
29
  end
30
+
31
+ context 'with a currency with an alternate symbol' do
32
+ let(:money_object) { Money.new(125_00, 'SGD') }
33
+
34
+ context 'with symbol options' do
35
+ let(:options) { { :symbol => true } }
36
+ it { is_expected.to include Money::Currency.new(:sgd).symbol }
37
+
38
+ context 'with disambiguate options' do
39
+ let(:options) { { :symbol => true, :disambiguate => true } }
40
+ it { is_expected.to include Money::Currency.new(:sgd).disambiguate_symbol }
41
+ end
42
+ end
43
+ end
29
44
  end
30
45
 
31
46
  describe '#humanized_money_with_symbol' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: money-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Loupasakis
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2014-12-24 00:00:00.000000000 Z
13
+ date: 2015-01-29 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: money
@@ -103,6 +103,9 @@ dependencies:
103
103
  - - ">="
104
104
  - !ruby/object:Gem::Version
105
105
  version: 0.8.0
106
+ - - "<"
107
+ - !ruby/object:Gem::Version
108
+ version: 1.4.0
106
109
  type: :development
107
110
  prerelease: false
108
111
  version_requirements: !ruby/object:Gem::Requirement
@@ -110,6 +113,9 @@ dependencies:
110
113
  - - ">="
111
114
  - !ruby/object:Gem::Version
112
115
  version: 0.8.0
116
+ - - "<"
117
+ - !ruby/object:Gem::Version
118
+ version: 1.4.0
113
119
  description: This library provides integration of RubyMoney - Money gem with Rails
114
120
  email:
115
121
  - alup.rubymoney@gmail.com
@@ -128,7 +134,9 @@ files:
128
134
  - lib/money-rails/active_model/validator.rb
129
135
  - lib/money-rails/active_record/migration_extensions/options_extractor.rb
130
136
  - lib/money-rails/active_record/migration_extensions/schema_statements.rb
137
+ - lib/money-rails/active_record/migration_extensions/schema_statements_pg_rails4.rb
131
138
  - lib/money-rails/active_record/migration_extensions/table.rb
139
+ - lib/money-rails/active_record/migration_extensions/table_pg_rails4.rb
132
140
  - lib/money-rails/active_record/monetizable.rb
133
141
  - lib/money-rails/configuration.rb
134
142
  - lib/money-rails/engine.rb
@@ -187,6 +195,8 @@ files:
187
195
  - spec/dummy/db/migrate/20130124023419_add_price_in_a_range_cents_to_products.rb
188
196
  - spec/dummy/db/migrate/20140110194016_add_validates_method_amount_cents_to_products.rb
189
197
  - spec/dummy/db/migrate/20141005075025_add_aliased_attr_to_products.rb
198
+ - spec/dummy/db/migrate/20150107061030_add_delivery_fee_cents_and_restock_fee_cents_to_product.rb
199
+ - spec/dummy/db/migrate/20150126231442_add_reduced_price_to_products.rb
190
200
  - spec/dummy/db/schema.rb
191
201
  - spec/dummy/db/structure.sql
192
202
  - spec/dummy/public/404.html
@@ -273,6 +283,8 @@ test_files:
273
283
  - spec/dummy/db/migrate/20130124023419_add_price_in_a_range_cents_to_products.rb
274
284
  - spec/dummy/db/migrate/20140110194016_add_validates_method_amount_cents_to_products.rb
275
285
  - spec/dummy/db/migrate/20141005075025_add_aliased_attr_to_products.rb
286
+ - spec/dummy/db/migrate/20150107061030_add_delivery_fee_cents_and_restock_fee_cents_to_product.rb
287
+ - spec/dummy/db/migrate/20150126231442_add_reduced_price_to_products.rb
276
288
  - spec/dummy/db/schema.rb
277
289
  - spec/dummy/db/structure.sql
278
290
  - spec/dummy/public/404.html