money-rails 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 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