money-rails 0.12.0 → 1.0.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: 6c8b379f2b72435fdb82e4b5022a947ac6470ddb
4
- data.tar.gz: 1a4286158053e24e3eac5da3475b014b6251518e
3
+ metadata.gz: 1045d1d93a0e6e7f720ee329933a3f1821210f39
4
+ data.tar.gz: 9059b8e4f70988725a8009ec68fb7ac2548b0335
5
5
  SHA512:
6
- metadata.gz: f82ac9627b0e16100aa8d09fae383f86bc2398f632ff32e30af6f326636da2d183f863286d62b2ece83322eecc89f311cec63649d2b061191a7a1336b0ff182b
7
- data.tar.gz: 79a411fa93a7a6a9ccda5ad9132b32b5973a9b2f991e116b1d05a6d86f0ba3358765f1b70375aa117f795fae0d9d5fc1d95abd2398c9af9e35457810dec02af1
6
+ metadata.gz: c7b9b0c7492769ffcfac56971877b9c0a58821613c4a9401f0440ecb8358276b729a303678a9f1e3ba644d674fdec6e18cd3ee6dce6a6601998b9a6e2bd7059d
7
+ data.tar.gz: 02b1bd7f8bd5ee35cc6365f77bc2d1b571f117a261c7e2aa7416ad012964fd956acfc588751076ee83b7eae8072289afbeac254fe3aa52c640c0a058fd5cadb1
data/CHANGELOG.md CHANGED
@@ -1,7 +1,31 @@
1
1
  # Changelog
2
2
 
3
3
  ## master (next release)
4
+
5
+ ## 0.12.0
6
+ - Add allow_nil chain for monetize test helper
7
+ - Add testing tasks for rails 4.1
8
+
9
+ ## 0.11.0
10
+ - Helpers respect no_cents_if_whole configuration option (GH-157)
11
+
12
+ ## 0.10.0
13
+ - Depend on Money gem version ~> 6.1.1
14
+ - Depend on monetize gem version ~> 0.3.0
15
+ - Set mongoized value of cents to float
16
+ - Fix validation error with whitespace between currency symbol and amount
17
+ - Add raise_error_on_money_parsing configuration option (default is false)
18
+ - Add rounding mode configuration option (default is ROUND_HALF_UP)
19
+ - Rescue ArgumentError with invalid values in mongoid (GH-114)
20
+ - Compatiblity between ActiveModel::Validations and MoneyValidator
4
21
  - Raise error when trying to change model based currency
22
+
23
+ ## 0.9.0
24
+ - Depend on Money gem version ~> 6.0.0
25
+ - Add disable_validation option for skipping validations on monetized attributes
26
+ - Remove implicit usage of 'currency' as a default currency column
27
+ - Options to humanized_money_with_symbol are passed to humanized_money
28
+ - Fix mongoization of Money when using infinite_precision
5
29
  - Add testing tasks for rails 4.x
6
30
  - Fix issue with Numeric values in MoneyValidator (GH-83).
7
31
  - Fix test helper
@@ -48,11 +72,6 @@
48
72
  - Fixed issue related to symbolized keys in Mongoid (GH-40)
49
73
  - Added add_money/remove_money & t.money/t.remove_money methods for ActiveRecord migrations
50
74
 
51
- TODOs (for upcoming releases):
52
- - decouple validator from active_record
53
- - enhance validator to cover every possible case
54
- - update documentation
55
-
56
75
  ## 0.6.0
57
76
  - Added basic support for Mongoid >= 3.0.
58
77
  - Allow class methods to be monetized (ActiveRecord only - GH-34)
data/README.md CHANGED
@@ -8,14 +8,14 @@
8
8
 
9
9
  ## Introduction
10
10
 
11
- This library provides integration of [money](http://github.com/Rubymoney/money) gem with Rails.
11
+ This library provides integration of the [money](http://github.com/Rubymoney/money) gem with Rails.
12
12
 
13
13
  Use 'monetize' to specify which fields you want to be backed by
14
14
  Money objects and helpers provided by the [money](http://github.com/Rubymoney/money)
15
15
  gem.
16
16
 
17
17
  Currently, this library is in active development mode, so if you would
18
- like to have a new feature feel free to open a new issue
18
+ like to have a new feature, feel free to open a new issue
19
19
  [here](https://github.com/RubyMoney/money-rails/issues). You are also
20
20
  welcome to contribute to the project.
21
21
 
@@ -29,11 +29,11 @@ And then execute:
29
29
 
30
30
  $ bundle
31
31
 
32
- Or install it yourself as:
32
+ Or install it yourself using:
33
33
 
34
34
  $ gem install money-rails
35
35
 
36
- You may also install money configuration initializer:
36
+ You can also use the money configuration initializer:
37
37
 
38
38
  ```
39
39
  $ rails g money_rails:initializer
@@ -48,8 +48,8 @@ configuration parameters for the rails app.
48
48
 
49
49
  #### Usage example
50
50
 
51
- For example, we create a Product model which has an integer price_cents column
52
- and we want to handle it by using a Money object instead:
51
+ For example, we create a Product model which has an integer column called
52
+ `price_cents` and we want to handle it using a `Money` object instead:
53
53
 
54
54
  ```ruby
55
55
  class Product < ActiveRecord::Base
@@ -60,27 +60,27 @@ end
60
60
  ```
61
61
 
62
62
  Now each Product object will also have an attribute called ```price``` which
63
- is a Money object and can be used for money comparisons, conversions etc.
63
+ is a `Money` object, and can be used for money comparisons, conversions etc.
64
64
 
65
65
  In this case the name of the money attribute is created automagically by removing the
66
- ```_cents``` suffix of the column name.
66
+ ```_cents``` suffix from the column name.
67
67
 
68
- If you are using another db column name or you prefer another name for the
69
- money attribute, then you can provide ```as``` argument with a string
68
+ If you are using another db column name, or you prefer another name for the
69
+ money attribute, then you can provide an ```as``` argument with a string
70
70
  value to the ```monetize``` macro:
71
71
 
72
72
  ```ruby
73
73
  monetize :discount_subunit, :as => "discount"
74
74
  ```
75
75
 
76
- Now the model objects will have a ```discount``` attribute which
77
- is a Money object, wrapping the value of ```discount_subunit``` column to a
78
- Money instance.
76
+ Now the model objects will have a ```discount``` attribute which is a `Money`
77
+ object, wrapping the value of the ```discount_subunit``` column with a Money
78
+ instance.
79
79
 
80
80
  #### Migration helpers
81
81
 
82
- If you want to add money field to product model you may use ```add_money``` helper. That
83
- helper might be customized inside ```MoneyRails.configure``` block. You should customize
82
+ If you want to add a money field to a product model you can use the ```add_money``` helper. This
83
+ helper can be customized inside a ```MoneyRails.configure``` block. You should customize the
84
84
  ```add_money``` helper to match the most common use case and utilize it across all migrations.
85
85
 
86
86
  ```ruby
@@ -97,7 +97,7 @@ class MonetizeProduct < ActiveRecord::Migration
97
97
  end
98
98
  ```
99
99
 
100
- Another example where the currency column is not including:
100
+ Another example, where the currency column is not included:
101
101
 
102
102
  ```ruby
103
103
  class MonetizeItem < ActiveRecord::Migration
@@ -107,13 +107,14 @@ class MonetizeItem < ActiveRecord::Migration
107
107
  end
108
108
  ```
109
109
 
110
- ```add_money``` helper is revertable, so you may use it inside ```change``` migrations.
111
- If you writing separate ```up``` and ```down``` methods, you may use ```remove_money``` helper.
110
+ The ```add_money``` helper is reversible, so you an use it inside ```change```
111
+ migrations. If you're writing separate ```up``` and ```down``` methods, you
112
+ can use the ```remove_money``` helper.
112
113
 
113
114
  #### Allow nil values
114
115
 
115
- If you want to allow the assignment of nil and/or blank values to a specific
116
- monetized field, you can use the `:allow_nil` parameter like this:
116
+ If you want to allow nil and/or blank values to a specific
117
+ monetized field, you can use the `:allow_nil` parameter:
117
118
 
118
119
  ```
119
120
  # in Product model
@@ -124,7 +125,7 @@ def change
124
125
  add_money :products, :optional_price, amount: { null: true, default: nil }
125
126
  end
126
127
 
127
- # then blank assignments are permitted
128
+ # now blank assignments are permitted
128
129
  product.optional_price = nil
129
130
  product.save # returns without errors
130
131
  product.optional_price # => nil
@@ -184,7 +185,7 @@ obj
184
185
  obj.price
185
186
  #=> #<Money cents:100 currency:EUR>
186
187
 
187
- ## You can access the money hash too :
188
+ ## You can access the money hash too:
188
189
  obj[:price]
189
190
  # => {:cents=>100, :currency_iso=>"EUR"}
190
191
  ```
@@ -193,7 +194,7 @@ The usual options on `field` as `index`, `default`, ..., are available.
193
194
 
194
195
  ### Method conversion
195
196
 
196
- Method return values can be converted in the same way attributes are converted. For example:
197
+ Method return values can be monetized in the same way attributes are monetized. For example:
197
198
 
198
199
  ```ruby
199
200
  class Transaction < ActiveRecord::Base
@@ -208,13 +209,13 @@ class Transaction < ActiveRecord::Base
208
209
  end
209
210
  ```
210
211
 
211
- Now each Transaction object has a method called `total` which returns a Money object.
212
+ Now each Transaction object has a method called `total` which returns a `Money` object.
212
213
 
213
214
  ### Currencies
214
215
 
215
- Money-rails supports a set of options to handle currencies for your
216
+ money-rails supports a set of options to handle currencies for your
216
217
  monetized fields. The default option for every conversion is to use
217
- the global default currency of Money library, as given in the configuration
218
+ the global default currency of the Money library, as given in the configuration
218
219
  initializer of money-rails:
219
220
 
220
221
  ```ruby
@@ -228,15 +229,12 @@ end
228
229
  ```
229
230
 
230
231
  In many cases this is not enough, so there are some other options to
231
- satisfy your needs.
232
+ meet your needs.
232
233
 
233
234
  #### Model Currency
234
235
 
235
- You can define a specific currency for an activerecord model (not for mongoid).
236
- This currency is used for the creation and conversions of the Money objects
237
- referring to every monetized attributes of the specific model.
238
- This means it overrides the global default currency of Money library.
239
- To attach a currency to a model use the ```register_currency``` macro:
236
+ You can override the global default currency within a specific ActiveRecord
237
+ model using the ```register_currency``` macro:
240
238
 
241
239
  ```ruby
242
240
  # app/models/product.rb
@@ -251,22 +249,26 @@ class Product < ActiveRecord::Base
251
249
  end
252
250
  ```
253
251
 
254
- Now ```product.discount``` and ```product.bonus``` will return a Money
255
- object using EUR as currency, instead of the default USD.
252
+ Now ```product.discount``` and ```product.bonus``` will return a `Money`
253
+ object using EUR as their currency, instead of the default USD.
254
+
255
+ (This is not availabe in Mongoid).
256
256
 
257
257
  #### Attribute Currency (:with_currency)
258
258
 
259
- By using the key ```:with_currency``` with a currency symbol value in
260
- the ```monetize``` macro call, you can define a currency in a more
261
- granular way. This way you attach a currency only to the specific monetized
262
- model attribute. It also allows to override both the model level
263
- and the global default currency:
259
+ By passing the option ```:with_currency``` to the ```monetize``` macro call,
260
+ with a currency code as its value, you can define a currency in a more granular
261
+ way. This will you attach the given currency only to the specified monetized model
262
+ attribute (allowing you tom for example, monetize different attributes of the same model with different currencyies.).
263
+
264
+ This allows you to override both the model level and the global
265
+ default currencies:
264
266
 
265
267
  ```ruby
266
268
  # app/models/product.rb
267
269
  class Product < ActiveRecord::Base
268
270
 
269
- # Use EUR as model level currency
271
+ # Use EUR as the model level currency
270
272
  register_currency :eur
271
273
 
272
274
  monetize :discount_subunit, :as => "discount"
@@ -275,17 +277,18 @@ class Product < ActiveRecord::Base
275
277
  end
276
278
  ```
277
279
 
278
- In this case the ```product.bonus``` will return a Money object of GBP
280
+ In this case ```product.bonus``` will return a Money object with GBP as its
279
281
  currency, whereas ```product.discount.currency_as_string # => EUR ```
280
282
 
281
283
  #### Instance Currencies
282
284
 
283
- All the previous options do not require any extra model field to hold
284
- currency values. If you need to provide differrent currency per model
285
- instance, then you need to add a column with the name ```currency```
286
- in your db table. You should specify ```with_model_currency``` as an argument
285
+ All the previous options do not require any extra model fields to hold
286
+ the currency values. If the currency of a field with vary from
287
+ once model instance to another, then you should add a column called ```currency```
288
+ to your database table and pass the option ```with_model_currency```
287
289
  to the ```monetize``` macro.
288
- Money-rails will use this knowledge to override the model level and global
290
+
291
+ money-rails will use this knowledge to override the model level and global
289
292
  default values. Non-nil instance currency values also override attribute
290
293
  currency values, so they have the highest precedence.
291
294
 
data/Rakefile CHANGED
@@ -38,6 +38,9 @@ end
38
38
 
39
39
  namespace :spec do
40
40
 
41
+ desc "Run Tests against mongoid (version 4)"
42
+ task(:mongoid4) { run_with_gemfile 'gemfiles/mongoid4.gemfile' }
43
+
41
44
  desc "Run Tests against mongoid (version 3)"
42
45
  task(:mongoid3) { run_with_gemfile 'gemfiles/mongoid3.gemfile' }
43
46
 
@@ -53,8 +56,8 @@ namespace :spec do
53
56
  desc "Run Tests against rails 3"
54
57
  task(:rails3) { run_with_gemfile 'gemfiles/rails3.gemfile' }
55
58
 
56
- desc "Run Tests against mongoid 2 & 3"
57
- task :mongoid => [:mongoid2, :mongoid3]
59
+ desc "Run Tests against mongoid 2 & 3 & 4"
60
+ task :mongoid => [:mongoid2, :mongoid3, :mongoid4]
58
61
 
59
62
  desc "Run Tests against rails 3 & 4 & 4.1"
60
63
  task :rails => [:rails3, :rails4, :rails41]
@@ -1,5 +1,5 @@
1
1
  en:
2
2
  errors:
3
3
  messages:
4
- invalid_currency: Must be a valid currency (eg. '100', '5%{decimal}24', or '123%{thousands}456%{decimal}78')
4
+ invalid_currency: Must be a valid currency (eg. '100', '5%{decimal}24', or '123%{thousands}456%{decimal}78'). Got %{currency}
5
5
 
@@ -2,78 +2,112 @@ module MoneyRails
2
2
  module ActiveModel
3
3
  class MoneyValidator < ::ActiveModel::Validations::NumericalityValidator
4
4
  def validate_each(record, attr, value)
5
+ reset_memoized_variables!
6
+ @record = record
7
+ @attr = attr
5
8
 
6
9
  # If subunit is not set then no need to validate as it is an
7
10
  # indicator that no assignment has been done onto the virtual
8
11
  # money field.
9
- subunit_attr = record.class.monetized_attributes[attr.to_sym]
10
- return unless record.changed_attributes.keys.include? subunit_attr
11
-
12
- raw_value = nil
12
+ subunit_attr = @record.class.monetized_attributes[@attr.to_sym]
13
+ return unless @record.changed_attributes.keys.include? subunit_attr
13
14
 
14
15
  # WARNING: Currently this is only defined in ActiveRecord extension!
15
- before_type_cast = "#{attr}_money_before_type_cast"
16
- raw_value = record.send(before_type_cast) if record.respond_to?(
17
- before_type_cast.to_sym)
16
+ before_type_cast = :"#{@attr}_money_before_type_cast"
17
+ @raw_value = @record.try(before_type_cast)
18
18
 
19
19
  # If raw value is nil and changed subunit is nil, then
20
20
  # nil is a assigned value, elsewhere we should treat the
21
21
  # subunit value as the one assigned.
22
- if raw_value.nil?
23
- if record.send(subunit_attr)
24
- raw_value = record.send(subunit_attr)
25
- end
22
+ if @raw_value.nil? && @record.send(subunit_attr)
23
+ @raw_value = @record.send(subunit_attr)
26
24
  end
27
25
 
28
- return if options[:allow_nil] && raw_value.nil?
26
+ return if options[:allow_nil] && @raw_value.nil?
29
27
 
30
28
  # Skip normalization for Numeric values
31
29
  # which can directly be handled by NumericalityValidator
32
- if raw_value.present? && !raw_value.is_a?(Numeric)
30
+ if raw_value_is_non_numeric
33
31
  # remove currency symbol, and negative sign
34
- currency = record.send("currency_for_#{attr}")
35
- decimal_mark = I18n.t('number.currency.format.separator',
36
- default: currency.decimal_mark)
37
- thousands_separator = I18n.t('number.currency.format.delimiter',
38
- default: currency.thousands_separator)
39
- symbol = I18n.t('number.currency.format.unit', default: currency.symbol)
40
-
41
- raw_value = raw_value.to_s.strip.gsub(symbol, "")
42
- abs_raw_value = raw_value.strip.gsub(/^-/, "")
43
-
44
- decimal_pieces = abs_raw_value.split(decimal_mark)
45
-
46
- # check for numbers like '12.23.45' or '....'
47
- unless [1, 2].include? decimal_pieces.length
48
- record.errors.add(attr, I18n.t('errors.messages.invalid_currency',
49
- { :thousands => thousands_separator,
50
- :decimal => decimal_mark }))
51
- return
52
- end
53
-
54
- pieces = decimal_pieces[0].split(thousands_separator)
55
-
56
- # check for valid thousands separation
57
- if pieces.length > 1
58
- record.errors.add(attr, I18n.t('errors.messages.invalid_currency',
59
- { :thousands => thousands_separator,
60
- :decimal => decimal_mark })) if pieces[0].length > 3
61
- (1..pieces.length-1).each do |index|
62
- record.errors.add(attr, I18n.t('errors.messages.invalid_currency',
63
- { :thousands => thousands_separator,
64
- :decimal => decimal_mark })) if pieces[index].length != 3
65
- end
66
- end
67
-
68
- # Remove thousands separators, normalize decimal mark,
69
- # remove whitespaces and _ (E.g. 99 999 999 or 12_300_200.20)
70
- raw_value = raw_value.to_s
71
- .gsub(thousands_separator, '')
72
- .gsub(decimal_mark, '.')
73
- .gsub(/[\s_]/, '')
32
+ @raw_value = @raw_value.to_s.strip.gsub(symbol, "")
33
+
34
+ add_error and return if value_has_too_many_decimal_points
35
+ add_error if invalid_thousands_separation
36
+
37
+ end
38
+
39
+ normalize_raw_value!
40
+ super(@record, @attr, @raw_value)
41
+ end
42
+
43
+ private
44
+
45
+ def reset_memoized_variables!
46
+ [:currency, :decimal_mark, :thousands_separator, :symbol,
47
+ :abs_raw_value, :decimal_pieces, :pieces_array].each do |var_name|
48
+ ivar_name = :"@_#{var_name}"
49
+ remove_instance_variable(ivar_name) if instance_variable_get(ivar_name)
50
+ end
51
+ end
52
+
53
+ def currency
54
+ @_currency ||= @record.send("currency_for_#{@attr}")
55
+ end
56
+
57
+ def decimal_mark
58
+ @_decimal_mark ||= I18n.t('number.currency.format.separator', default: currency.decimal_mark)
59
+ end
60
+
61
+ def thousands_separator
62
+ @_thousands_separator ||= I18n.t('number.currency.format.delimiter', default: currency.thousands_separator)
63
+ end
64
+
65
+ def symbol
66
+ @_symbol ||= I18n.t('number.currency.format.unit', default: currency.symbol)
67
+ end
68
+
69
+ def raw_value_is_non_numeric
70
+ @raw_value.present? && !@raw_value.is_a?(Numeric)
71
+ end
72
+
73
+ def abs_raw_value
74
+ @_abs_raw_value ||= @raw_value.strip.sub(/^-/, "")
75
+ end
76
+
77
+ def add_error
78
+ @record.errors.add(@attr, I18n.t('errors.messages.invalid_currency',
79
+ { :thousands => thousands_separator,
80
+ :decimal => decimal_mark,
81
+ :currency => abs_raw_value }))
82
+ end
83
+
84
+ def decimal_pieces
85
+ @_decimal_pieces ||= abs_raw_value.split(decimal_mark)
86
+ end
87
+
88
+ def value_has_too_many_decimal_points
89
+ ![1, 2].include?(decimal_pieces.length)
90
+ end
91
+
92
+ def pieces_array
93
+ @_pieces_array ||= decimal_pieces[0].split(thousands_separator)
94
+ end
95
+
96
+ def invalid_thousands_separation
97
+ return false if pieces_array.length <= 1
98
+ return true if pieces_array[0].length > 3
99
+ pieces_array[1..-1].any? do |thousands_group|
100
+ thousands_group.length != 3
74
101
  end
102
+ end
75
103
 
76
- super(record, attr, raw_value)
104
+ # Remove thousands separators, normalize decimal mark,
105
+ # remove whitespaces and _ (E.g. 99 999 999 or 12_300_200.20)
106
+ def normalize_raw_value!
107
+ @raw_value = @raw_value.to_s
108
+ .gsub(thousands_separator, '')
109
+ .gsub(decimal_mark, '.')
110
+ .gsub(/[\s_]/, '')
77
111
  end
78
112
  end
79
113
  end
@@ -80,7 +80,7 @@ module MoneyRails
80
80
  #
81
81
  # To disable validation entirely, use :disable_validation, E.g:
82
82
  # monetize :price_in_a_range_cents, :disable_validation => true
83
- if MoneyRails.include_validations && !options[:disable_validation]
83
+ if validation_enabled = MoneyRails.include_validations && !options[:disable_validation]
84
84
 
85
85
  subunit_validation_options =
86
86
  unless options.has_key? :subunit_numericality
@@ -109,6 +109,7 @@ module MoneyRails
109
109
  }
110
110
  end
111
111
 
112
+
112
113
  define_method name do |*args|
113
114
 
114
115
  # Get the cents
@@ -155,7 +156,7 @@ module MoneyRails
155
156
  end
156
157
 
157
158
  # Update cents
158
- send("#{subunit_name}=", money.try(:cents))
159
+ write_attribute(subunit_name, money.try(:cents))
159
160
 
160
161
  money_currency = money.try(:currency)
161
162
 
@@ -175,6 +176,16 @@ module MoneyRails
175
176
  instance_variable_set "@#{name}", money
176
177
  end
177
178
 
179
+ if validation_enabled
180
+ # Ensure that the before_type_cast value is updated when settin
181
+ # the subunit value directly
182
+ define_method "#{subunit_name}=" do |value|
183
+ before_type_cast = value.to_f / send("currency_for_#{name}").subunit_to_unit
184
+ instance_variable_set "@#{name}_money_before_type_cast", before_type_cast
185
+ write_attribute(subunit_name, value)
186
+ end
187
+ end
188
+
178
189
  define_method "currency_for_#{name}" do
179
190
  if instance_currency_name.present? &&
180
191
  self.respond_to?(instance_currency_name) &&
@@ -32,7 +32,7 @@ class Money
32
32
  when object.respond_to?(:to_money) then
33
33
  begin
34
34
  object.to_money.mongoize
35
- rescue ArgumentError
35
+ rescue ArgumentError, Money::Currency::UnknownCurrency
36
36
  raise if MoneyRails.raise_error_on_money_parsing
37
37
  nil
38
38
  end
@@ -1,3 +1,3 @@
1
1
  module MoneyRails
2
- VERSION = "0.12.0"
2
+ VERSION = "1.0.0"
3
3
  end
data/money-rails.gemspec CHANGED
@@ -26,8 +26,8 @@ Gem::Specification.new do |s|
26
26
 
27
27
  s.require_path = "lib"
28
28
 
29
- s.add_dependency "money", "~> 6.1.1"
30
- s.add_dependency "monetize", "~> 0.3.0"
29
+ s.add_dependency "money", "~> 6.4.0"
30
+ s.add_dependency "monetize", "~> 1.0.0"
31
31
  s.add_dependency "activesupport", ">= 3.0"
32
32
  s.add_dependency "railties", ">= 3.0"
33
33
 
@@ -42,7 +42,7 @@ if defined? ActiveRecord
42
42
  product.price_cents.should == 3210
43
43
  end
44
44
 
45
- it "updates correctly from a Money object using update_attributes" do
45
+ it "correctly updates from a Money object using update_attributes" do
46
46
  product.update_attributes(:price => Money.new(215, "USD")).should eq(true)
47
47
  product.price_cents.should == 215
48
48
  end
@@ -71,6 +71,13 @@ if defined? ActiveRecord
71
71
  product.save.should eq(true)
72
72
  end
73
73
 
74
+ it "passes validation after updating fractional attribute which was previously invalid" do
75
+ product.price_in_a_range = -5
76
+ product.should_not be_valid
77
+ product.price_in_a_range_cents = 500
78
+ product.should be_valid
79
+ end
80
+
74
81
  context "when MoneyRails.raise_error_on_money_parsing is true" do
75
82
  before { MoneyRails.raise_error_on_money_parsing = true }
76
83
  after { MoneyRails.raise_error_on_money_parsing = false }
@@ -107,18 +114,21 @@ if defined? ActiveRecord
107
114
  product.price = "12.23.24"
108
115
  product.save.should eq(false)
109
116
  product.errors[:price].first.should match(/Must be a valid/)
117
+ product.errors[:price].first.should match(/Got 12.23.24/)
110
118
  end
111
119
 
112
120
  it "fails validation with the proper error message if money value is nothing but periods" do
113
121
  product.price = "..."
114
122
  product.save.should eq(false)
115
123
  product.errors[:price].first.should match(/Must be a valid/)
124
+ product.errors[:price].first.should match(/Got .../)
116
125
  end
117
126
 
118
127
  it "fails validation with the proper error message if money value has invalid thousands part" do
119
128
  product.price = "12,23.24"
120
129
  product.save.should eq(false)
121
130
  product.errors[:price].first.should match(/Must be a valid/)
131
+ product.errors[:price].first.should match(/Got 12,23.24/)
122
132
  end
123
133
 
124
134
  it "passes validation if money value is a Float and the currency decimal mark is not period" do
@@ -336,14 +346,14 @@ if defined? ActiveRecord
336
346
  product.bonus.currency.should == Money::Currency.find(:gbp)
337
347
  end
338
348
 
339
- it "assigns correctly Money objects to the attribute" do
349
+ it "correctly assigns Money objects to the attribute" do
340
350
  product.price = Money.new(2500, :USD)
341
351
  product.save.should eq(true)
342
352
  product.price.cents.should == 2500
343
353
  product.price.currency_as_string.should == "USD"
344
354
  end
345
355
 
346
- it "assigns correctly Fixnum objects to the attribute" do
356
+ it "correctly assigns Fixnum objects to the attribute" do
347
357
  product.price = 25
348
358
  product.save.should eq(true)
349
359
  product.price.cents.should == 2500
@@ -355,7 +365,7 @@ if defined? ActiveRecord
355
365
  service.discount.currency_as_string.should == "EUR"
356
366
  end
357
367
 
358
- it "assigns correctly String objects to the attribute" do
368
+ it "correctly assigns String objects to the attribute" do
359
369
  product.price = "25"
360
370
  product.save.should eq(true)
361
371
  product.price.cents.should == 2500
@@ -489,7 +499,7 @@ if defined? ActiveRecord
489
499
  DummyProduct.create(:price_cents => 2600, :currency => :foo)
490
500
  end
491
501
 
492
- it "serializes correctly the currency to a new instance of model" do
502
+ it "correctly serializes the currency to a new instance of model" do
493
503
  d = DummyProduct.new
494
504
  d.price = Money.new(10, "EUR")
495
505
  d.save!
@@ -519,12 +529,12 @@ if defined? ActiveRecord
519
529
  transaction.amount.should == Money.new(2400, :usd)
520
530
  end
521
531
 
522
- it "instantiates correctly Money objects from the mapped attributes" do
532
+ it "correctly instantiates Money objects from the mapped attributes" do
523
533
  t = Transaction.new(:amount_cents => 2500, :currency => "CAD")
524
534
  t.amount.should == Money.new(2500, "CAD")
525
535
  end
526
536
 
527
- it "assigns correctly Money objects to the attribute" do
537
+ it "correctly assigns Money objects to the attribute" do
528
538
  transaction.amount = Money.new(2500, :eur)
529
539
  transaction.save.should eq(true)
530
540
  transaction.amount.cents.should == Money.new(2500, :eur).cents
@@ -9,43 +9,43 @@
9
9
  # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10
10
  # you'll amass, the slower it'll run and the greater likelihood for issues).
11
11
  #
12
- # It's strongly recommended to check this file into your version control system.
12
+ # It's strongly recommended that you check this file into your version control system.
13
13
 
14
- ActiveRecord::Schema.define(:version => 20140110194016) do
14
+ ActiveRecord::Schema.define(version: 20140110194016) do
15
15
 
16
- create_table "dummy_products", :force => true do |t|
16
+ create_table "dummy_products", force: true do |t|
17
17
  t.string "currency"
18
18
  t.integer "price_cents"
19
- t.datetime "created_at", :null => false
20
- t.datetime "updated_at", :null => false
19
+ t.datetime "created_at"
20
+ t.datetime "updated_at"
21
21
  end
22
22
 
23
- create_table "products", :force => true do |t|
23
+ create_table "products", force: true do |t|
24
24
  t.integer "price_cents"
25
25
  t.integer "discount"
26
- t.datetime "created_at", :null => false
27
- t.datetime "updated_at", :null => false
26
+ t.datetime "created_at"
27
+ t.datetime "updated_at"
28
28
  t.integer "bonus_cents"
29
29
  t.integer "optional_price_cents"
30
- t.integer "sale_price_amount", :default => 0, :null => false
30
+ t.integer "sale_price_amount", default: 0, null: false
31
31
  t.string "sale_price_currency_code"
32
32
  t.integer "price_in_a_range_cents"
33
33
  t.integer "validates_method_amount_cents"
34
34
  end
35
35
 
36
- create_table "services", :force => true do |t|
36
+ create_table "services", force: true do |t|
37
37
  t.integer "charge_cents"
38
38
  t.integer "discount_cents"
39
- t.datetime "created_at", :null => false
40
- t.datetime "updated_at", :null => false
39
+ t.datetime "created_at"
40
+ t.datetime "updated_at"
41
41
  end
42
42
 
43
- create_table "transactions", :force => true do |t|
43
+ create_table "transactions", force: true do |t|
44
44
  t.integer "amount_cents"
45
45
  t.integer "tax_cents"
46
46
  t.string "currency"
47
- t.datetime "created_at", :null => false
48
- t.datetime "updated_at", :null => false
47
+ t.datetime "created_at"
48
+ t.datetime "updated_at"
49
49
  end
50
50
 
51
51
  end
@@ -0,0 +1,115 @@
1
+ require 'spec_helper'
2
+
3
+ if defined?(Mongoid) && ::Mongoid::VERSION =~ /^4(.*)/
4
+
5
+ describe Money do
6
+ let!(:priceable) { Priceable.create(:price => Money.new(100, 'EUR')) }
7
+ let!(:priceable_from_num) { Priceable.create(:price => 1) }
8
+ let!(:priceable_from_string) { Priceable.create(:price => '1 EUR' )}
9
+ let!(:priceable_from_hash) { Priceable.create(:price => {:cents=>100, :currency_iso=>"EUR"} )}
10
+ let!(:priceable_from_hash_with_indifferent_access) {
11
+ Priceable.create(:price => {:cents=>100, :currency_iso=>"EUR"}.with_indifferent_access)
12
+ }
13
+ let(:priceable_from_string_with_hyphen) { Priceable.create(:price => '1-2 EUR' )}
14
+ let(:priceable_from_string_with_unknown_currency) { Priceable.create(:price => '1 TLDR') }
15
+ let(:priceable_with_infinite_precision) { Priceable.create(:price => Money.new(BigDecimal.new('100.1'), 'EUR')) }
16
+ let(:priceable_with_hash_field) {
17
+ Priceable.create(:price_hash => {
18
+ :key1 => Money.new(100, "EUR"),
19
+ :key2 => Money.new(200, "USD")
20
+ })
21
+ }
22
+
23
+ context "mongoize" do
24
+ it "correctly mongoizes a Money object to a hash of cents and currency" do
25
+ priceable.price.cents.should == 100
26
+ priceable.price.currency.should == Money::Currency.find('EUR')
27
+ end
28
+
29
+ it "correctly mongoizes a Numeric object to a hash of cents and currency" do
30
+ priceable_from_num.price.cents.should == 100
31
+ priceable_from_num.price.currency.should == Money.default_currency
32
+ end
33
+
34
+ it "correctly mongoizes a String object to a hash of cents and currency" do
35
+ priceable_from_string.price.cents.should == 100
36
+ priceable_from_string.price.currency.should == Money::Currency.find('EUR')
37
+ end
38
+
39
+ context "when MoneyRails.raise_error_on_money_parsing is true" do
40
+ before { MoneyRails.raise_error_on_money_parsing = true }
41
+ after { MoneyRails.raise_error_on_money_parsing = false }
42
+
43
+ it "raises exception if the mongoized value is a String with a hyphen" do
44
+ expect { priceable_from_string_with_hyphen }.to raise_error
45
+ end
46
+
47
+ it "raises exception if the mongoized value is a String with an unknown currency" do
48
+ expect { priceable_from_string_with_unknown_currency }.to raise_error
49
+ end
50
+ end
51
+
52
+ context "when MoneyRails.raise_error_on_money_parsing is false" do
53
+ it "does not correctly mongoize a String with a hyphen in its middle" do
54
+ priceable_from_string_with_hyphen.price.should == nil
55
+ end
56
+
57
+ it "does not correctly mongoize a String with an unknown currency" do
58
+ priceable_from_string_with_unknown_currency.price.should == nil
59
+ end
60
+ end
61
+
62
+ it "correctly mongoizes a hash of cents and currency" do
63
+ priceable_from_hash.price.cents.should == 100
64
+ priceable_from_hash.price.currency.should == Money::Currency.find('EUR')
65
+ end
66
+
67
+ it "correctly mongoizes a HashWithIndifferentAccess of cents and currency" do
68
+ priceable_from_hash_with_indifferent_access.price.cents.should == 100
69
+ priceable_from_hash_with_indifferent_access.price.currency.should == Money::Currency.find('EUR')
70
+ end
71
+
72
+ context "infinite_precision = true" do
73
+ before do
74
+ Money.infinite_precision = true
75
+ end
76
+
77
+ after do
78
+ Money.infinite_precision = false
79
+ end
80
+
81
+ it "correctly mongoizes a Money object to a hash of cents and currency" do
82
+ priceable_with_infinite_precision.price.cents.should == BigDecimal.new('100.1')
83
+ priceable_with_infinite_precision.price.currency.should == Money::Currency.find('EUR')
84
+ end
85
+ end
86
+ end
87
+
88
+ it "correctly serializes a Hash field containing Money objects" do
89
+ priceable_with_hash_field.price_hash[:key1][:cents].should == 100
90
+ priceable_with_hash_field.price_hash[:key2][:cents].should == 200
91
+ priceable_with_hash_field.price_hash[:key1][:currency_iso].should == 'EUR'
92
+ priceable_with_hash_field.price_hash[:key2][:currency_iso].should == 'USD'
93
+ end
94
+
95
+ context "demongoize" do
96
+ subject { Priceable.first.price }
97
+ it { should be_an_instance_of(Money) }
98
+ it { should == Money.new(100, 'EUR') }
99
+ it "returns nil if a nil value was stored" do
100
+ nil_priceable = Priceable.create(:price => nil)
101
+ nil_priceable.price.should be_nil
102
+ end
103
+ it 'returns nil if an unknown value was stored' do
104
+ zero_priceable = Priceable.create(:price => [])
105
+ zero_priceable.price.should be_nil
106
+ end
107
+ end
108
+
109
+ context "evolve" do
110
+ it "correctly transforms a Money object into a Mongo friendly value" do
111
+ Priceable.where(:price => Money.new(100, 'EUR')).first.should == priceable
112
+ end
113
+ end
114
+ end
115
+ end
@@ -11,6 +11,7 @@ if defined?(Mongoid) && ::Mongoid::VERSION =~ /^3(.*)/
11
11
  Priceable.create(:price => {:cents=>100, :currency_iso=>"EUR"}.with_indifferent_access)
12
12
  }
13
13
  let(:priceable_from_string_with_hyphen) { Priceable.create(:price => '1-2 EUR' )}
14
+ let(:priceable_from_string_with_unknown_currency) { Priceable.create(:price => '1 TLDR') }
14
15
  let(:priceable_with_infinite_precision) { Priceable.create(:price => Money.new(BigDecimal.new('100.1'), 'EUR')) }
15
16
  let(:priceable_with_hash_field) {
16
17
  Priceable.create(:price_hash => {
@@ -42,12 +43,20 @@ if defined?(Mongoid) && ::Mongoid::VERSION =~ /^3(.*)/
42
43
  it "raises exception if the mongoized value is a String with a hyphen" do
43
44
  expect { priceable_from_string_with_hyphen }.to raise_error
44
45
  end
46
+
47
+ it "raises exception if the mongoized value is a String with an unknown currency" do
48
+ expect { priceable_from_string_with_unknown_currency }.to raise_error
49
+ end
45
50
  end
46
51
 
47
52
  context "when MoneyRails.raise_error_on_money_parsing is false" do
48
53
  it "does not mongoizes correctly a String with hyphen in its middle" do
49
54
  priceable_from_string_with_hyphen.price.should == nil
50
55
  end
56
+
57
+ it "does not mongoize correctly a String with an unknown currency" do
58
+ priceable_from_string_with_unknown_currency.price.should == nil
59
+ end
51
60
  end
52
61
 
53
62
  it "mongoizes correctly a hash of cents and currency" 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: 0.12.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Loupasakis
@@ -10,106 +10,106 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2014-06-16 00:00:00.000000000 Z
13
+ date: 2014-10-31 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
- prerelease: false
17
16
  name: money
18
- version_requirements: !ruby/object:Gem::Requirement
19
- requirements:
20
- - - ~>
21
- - !ruby/object:Gem::Version
22
- version: 6.1.1
23
17
  requirement: !ruby/object:Gem::Requirement
24
18
  requirements:
25
- - - ~>
19
+ - - "~>"
26
20
  - !ruby/object:Gem::Version
27
- version: 6.1.1
21
+ version: 6.4.0
28
22
  type: :runtime
29
- - !ruby/object:Gem::Dependency
30
23
  prerelease: false
31
- name: monetize
32
24
  version_requirements: !ruby/object:Gem::Requirement
33
25
  requirements:
34
- - - ~>
26
+ - - "~>"
35
27
  - !ruby/object:Gem::Version
36
- version: 0.3.0
28
+ version: 6.4.0
29
+ - !ruby/object:Gem::Dependency
30
+ name: monetize
37
31
  requirement: !ruby/object:Gem::Requirement
38
32
  requirements:
39
- - - ~>
33
+ - - "~>"
40
34
  - !ruby/object:Gem::Version
41
- version: 0.3.0
35
+ version: 1.0.0
42
36
  type: :runtime
43
- - !ruby/object:Gem::Dependency
44
37
  prerelease: false
45
- name: activesupport
46
38
  version_requirements: !ruby/object:Gem::Requirement
47
39
  requirements:
48
- - - '>='
40
+ - - "~>"
49
41
  - !ruby/object:Gem::Version
50
- version: '3.0'
42
+ version: 1.0.0
43
+ - !ruby/object:Gem::Dependency
44
+ name: activesupport
51
45
  requirement: !ruby/object:Gem::Requirement
52
46
  requirements:
53
- - - '>='
47
+ - - ">="
54
48
  - !ruby/object:Gem::Version
55
49
  version: '3.0'
56
50
  type: :runtime
57
- - !ruby/object:Gem::Dependency
58
51
  prerelease: false
59
- name: railties
60
52
  version_requirements: !ruby/object:Gem::Requirement
61
53
  requirements:
62
- - - '>='
54
+ - - ">="
63
55
  - !ruby/object:Gem::Version
64
56
  version: '3.0'
57
+ - !ruby/object:Gem::Dependency
58
+ name: railties
65
59
  requirement: !ruby/object:Gem::Requirement
66
60
  requirements:
67
- - - '>='
61
+ - - ">="
68
62
  - !ruby/object:Gem::Version
69
63
  version: '3.0'
70
64
  type: :runtime
71
- - !ruby/object:Gem::Dependency
72
65
  prerelease: false
73
- name: rails
74
66
  version_requirements: !ruby/object:Gem::Requirement
75
67
  requirements:
76
- - - '>='
68
+ - - ">="
77
69
  - !ruby/object:Gem::Version
78
70
  version: '3.0'
71
+ - !ruby/object:Gem::Dependency
72
+ name: rails
79
73
  requirement: !ruby/object:Gem::Requirement
80
74
  requirements:
81
- - - '>='
75
+ - - ">="
82
76
  - !ruby/object:Gem::Version
83
77
  version: '3.0'
84
78
  type: :development
85
- - !ruby/object:Gem::Dependency
86
79
  prerelease: false
87
- name: rspec-rails
88
80
  version_requirements: !ruby/object:Gem::Requirement
89
81
  requirements:
90
- - - ~>
82
+ - - ">="
91
83
  - !ruby/object:Gem::Version
92
- version: '2.10'
84
+ version: '3.0'
85
+ - !ruby/object:Gem::Dependency
86
+ name: rspec-rails
93
87
  requirement: !ruby/object:Gem::Requirement
94
88
  requirements:
95
- - - ~>
89
+ - - "~>"
96
90
  - !ruby/object:Gem::Version
97
91
  version: '2.10'
98
92
  type: :development
99
- - !ruby/object:Gem::Dependency
100
93
  prerelease: false
101
- name: database_cleaner
102
94
  version_requirements: !ruby/object:Gem::Requirement
103
95
  requirements:
104
- - - '>='
96
+ - - "~>"
105
97
  - !ruby/object:Gem::Version
106
- version: 0.8.0
98
+ version: '2.10'
99
+ - !ruby/object:Gem::Dependency
100
+ name: database_cleaner
107
101
  requirement: !ruby/object:Gem::Requirement
108
102
  requirements:
109
- - - '>='
103
+ - - ">="
110
104
  - !ruby/object:Gem::Version
111
105
  version: 0.8.0
112
106
  type: :development
107
+ prerelease: false
108
+ version_requirements: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: 0.8.0
113
113
  description: This library provides integration of RubyMoney - Money gem with Rails
114
114
  email:
115
115
  - alup.rubymoney@gmail.com
@@ -194,6 +194,7 @@ files:
194
194
  - spec/dummy/script/rails
195
195
  - spec/helpers/action_view_extension_spec.rb
196
196
  - spec/helpers/form_helper_spec.rb
197
+ - spec/mongoid/four_spec.rb
197
198
  - spec/mongoid/three_spec.rb
198
199
  - spec/mongoid/two_spec.rb
199
200
  - spec/spec_helper.rb
@@ -209,12 +210,12 @@ require_paths:
209
210
  - lib
210
211
  required_ruby_version: !ruby/object:Gem::Requirement
211
212
  requirements:
212
- - - '>='
213
+ - - ">="
213
214
  - !ruby/object:Gem::Version
214
215
  version: '0'
215
216
  required_rubygems_version: !ruby/object:Gem::Requirement
216
217
  requirements:
217
- - - '>='
218
+ - - ">="
218
219
  - !ruby/object:Gem::Version
219
220
  version: '0'
220
221
  requirements: []
@@ -277,9 +278,9 @@ test_files:
277
278
  - spec/dummy/script/rails
278
279
  - spec/helpers/action_view_extension_spec.rb
279
280
  - spec/helpers/form_helper_spec.rb
281
+ - spec/mongoid/four_spec.rb
280
282
  - spec/mongoid/three_spec.rb
281
283
  - spec/mongoid/two_spec.rb
282
284
  - spec/spec_helper.rb
283
285
  - spec/support/database_cleaner.rb
284
286
  - spec/test_helpers_spec.rb
285
- has_rdoc: