money-rails 0.12.0 → 1.0.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: 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: