money-rails 0.4.0 → 0.5.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.
- data/CHANGELOG.md +12 -0
- data/README.md +55 -17
- data/Rakefile +26 -1
- data/lib/money-rails/active_record/monetizable.rb +34 -53
- data/lib/money-rails/configuration.rb +3 -0
- data/lib/money-rails/hooks.rb +1 -1
- data/lib/money-rails/mongoid/two.rb +25 -0
- data/lib/money-rails/version.rb +1 -1
- data/money-rails.gemspec +1 -0
- data/spec/active_record/monetizable_spec.rb +201 -171
- data/spec/dummy/app/models/priceable.rb +7 -0
- data/spec/dummy/app/models/product.rb +6 -1
- data/spec/dummy/config/mongoid.yml +7 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20120712202655_add_sale_price_cents_to_product.rb +7 -0
- data/spec/dummy/db/schema.rb +5 -3
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +186 -44
- data/spec/dummy/log/test.log +47601 -1617
- data/spec/mongoid/two_spec.rb +25 -0
- data/spec/spec_helper.rb +2 -6
- data/spec/support/database_cleaner.rb +14 -0
- metadata +143 -81
data/CHANGELOG.md
CHANGED
@@ -1,6 +1,18 @@
|
|
1
1
|
# Changelog
|
2
2
|
|
3
|
+
## 0.5.0
|
4
|
+
|
5
|
+
- Refactored instance currency implementation. Now, instance currency
|
6
|
+
is permitted to override the field currency, if they are both non-nil
|
7
|
+
values. (GH-23)
|
8
|
+
- Replaced deprecated composed_of with a custom implementation for
|
9
|
+
activerecord. (GH-20)
|
10
|
+
- Refactored testing structure to support multiple ORMs/ODMS.
|
11
|
+
- Added Mongoid 2.x basic support. It uses serialization
|
12
|
+
(a differrent approach than activerecord for now). (GH-19)
|
13
|
+
|
3
14
|
## 0.4.0
|
15
|
+
|
4
16
|
- Provide ActionView helpers integration.
|
5
17
|
- Map blank value assignments (for monetized fields) to nil.
|
6
18
|
- Allow nil values for monetized fields. (GH-15)
|
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
# RubyMoney - Money-Rails
|
2
2
|
|
3
3
|
[](http://travis-ci.org/RubyMoney/money-rails)
|
4
|
+
[](https://gemnasium.com/RubyMoney/money-rails)
|
5
|
+
[](https://codeclimate.com/github/Robymoney/money-rails)
|
4
6
|
|
5
7
|
## Introduction
|
6
8
|
|
@@ -40,14 +42,16 @@ configuration parameters for the rails app.
|
|
40
42
|
|
41
43
|
## Usage
|
42
44
|
|
45
|
+
### ActiveRecord
|
46
|
+
|
43
47
|
For example, we create a Product model which has an integer price_cents column
|
44
48
|
and we want to handle it by using a Money object instead:
|
45
49
|
|
46
50
|
```ruby
|
47
51
|
class Product < ActiveRecord::Base
|
48
|
-
|
52
|
+
|
49
53
|
monetize :price_cents
|
50
|
-
|
54
|
+
|
51
55
|
end
|
52
56
|
```
|
53
57
|
|
@@ -55,7 +59,7 @@ Now each Product object will also have an attribute called ```price``` which
|
|
55
59
|
is a Money object and can be used for money comparisons, conversions etc.
|
56
60
|
|
57
61
|
In this case the name of the money attribute is created automagically by removing the
|
58
|
-
```_cents``` suffix of the column name.
|
62
|
+
```_cents``` suffix of the column name.
|
59
63
|
|
60
64
|
If you are using another db column name or you prefer another name for the
|
61
65
|
money attribute, then you can provide ```as``` argument with a string
|
@@ -69,7 +73,7 @@ Now the model objects will have a ```discount``` attribute which
|
|
69
73
|
is a Money object, wrapping the value of ```discount_subunit``` column to a
|
70
74
|
Money instance.
|
71
75
|
|
72
|
-
|
76
|
+
#### Allow nil values
|
73
77
|
|
74
78
|
If you want to allow the assignment of nil and/or blank values to a specific
|
75
79
|
monetized field, you can use the `:allow_nil` parameter like this:
|
@@ -85,6 +89,45 @@ product.optional_price # => nil
|
|
85
89
|
product.optional_price_cents # => nil
|
86
90
|
```
|
87
91
|
|
92
|
+
### Mongoid 2.X
|
93
|
+
|
94
|
+
`Money` is available as a field type to supply during a field definition:
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
class Product
|
98
|
+
include Mongoid::Document
|
99
|
+
|
100
|
+
field :price, type: Money
|
101
|
+
end
|
102
|
+
|
103
|
+
obj = Product.new
|
104
|
+
# => #<Product _id: 4fe865699671383656000001, _type: nil, price: nil>
|
105
|
+
|
106
|
+
obj.price
|
107
|
+
# => nil
|
108
|
+
|
109
|
+
obj.price = Money.new(100, 'EUR')
|
110
|
+
# => #<Money cents:100 currency:EUR>
|
111
|
+
|
112
|
+
obj.price
|
113
|
+
#=> #<Money cents:100 currency:EUR>
|
114
|
+
|
115
|
+
obj.save
|
116
|
+
# => true
|
117
|
+
|
118
|
+
obj
|
119
|
+
# => #<Product _id: 4fe865699671383656000001, _type: nil, price: {:cents=>100, :currency_iso=>"EUR"}>
|
120
|
+
|
121
|
+
obj.price
|
122
|
+
#=> #<Money cents:100 currency:EUR>
|
123
|
+
|
124
|
+
## You can access the money hash too :
|
125
|
+
obj[:price]
|
126
|
+
# => {:cents=>100, :currency_iso=>"EUR"}
|
127
|
+
```
|
128
|
+
|
129
|
+
The usual options on `field` as `index`, `default`, ..., are available.
|
130
|
+
|
88
131
|
### Currencies
|
89
132
|
|
90
133
|
Money-rails supports a set of options to handle currencies for your
|
@@ -107,11 +150,11 @@ satisfy your needs.
|
|
107
150
|
|
108
151
|
#### Model Currency
|
109
152
|
|
110
|
-
You can define a specific currency for an activerecord model
|
111
|
-
used for the creation and conversions of the Money objects
|
112
|
-
every monetized attributes of the specific model.
|
113
|
-
the global default currency of Money library.
|
114
|
-
model use the ```register_currency``` macro:
|
153
|
+
You can define a specific currency for an activerecord model (not for mongoid).
|
154
|
+
This currency is used for the creation and conversions of the Money objects
|
155
|
+
referring to every monetized attributes of the specific model.
|
156
|
+
This means it overrides the global default currency of Money library.
|
157
|
+
To attach a currency to a model use the ```register_currency``` macro:
|
115
158
|
|
116
159
|
```ruby
|
117
160
|
# app/models/product.rb
|
@@ -160,8 +203,8 @@ currency values. If you need to provide differrent currency per model
|
|
160
203
|
instance, then you need to add a column with the name ```currency```
|
161
204
|
in your db table. Money-rails will discover this automatically,
|
162
205
|
and will use this knowledge to override the model level and global
|
163
|
-
default values.
|
164
|
-
currency
|
206
|
+
default values. Non-nil instance currency values also override attribute
|
207
|
+
currency values, so they have the highest precedence.
|
165
208
|
|
166
209
|
```ruby
|
167
210
|
class Transaction < ActiveRecord::Base
|
@@ -177,17 +220,12 @@ class Transaction < ActiveRecord::Base
|
|
177
220
|
|
178
221
|
end
|
179
222
|
|
180
|
-
# Now instantiating with a specific currency overrides
|
223
|
+
# Now instantiating with a specific currency overrides
|
181
224
|
# the model and global currencies
|
182
225
|
t = Transaction.new(:amount_cents => 2500, :currency => "CAD")
|
183
226
|
t.amount == Money.new(2500, "CAD") # true
|
184
227
|
```
|
185
228
|
|
186
|
-
WARNING: In this case :with_currency is not permitted and the usage
|
187
|
-
of this parameter will cause an ArgumentError exception.
|
188
|
-
|
189
|
-
In general, the use of this strategy is discouraged unless there is a reason.
|
190
|
-
|
191
229
|
### Configuration parameters
|
192
230
|
|
193
231
|
You can handle a bunch of configuration params through ```money.rb``` initializer:
|
data/Rakefile
CHANGED
@@ -16,9 +16,34 @@ require 'rspec/core/rake_task'
|
|
16
16
|
|
17
17
|
RSpec::Core::RakeTask.new
|
18
18
|
|
19
|
-
task :default => :
|
19
|
+
task :default => "spec:all"
|
20
20
|
task :test => :spec
|
21
21
|
|
22
|
+
namespace :spec do
|
23
|
+
desc "Run Tests against mongoid"
|
24
|
+
task :mongoid2 do
|
25
|
+
sh "BUNDLE_GEMFILE='gemfiles/mongoid2.gemfile' bundle --quiet"
|
26
|
+
sh "BUNDLE_GEMFILE='gemfiles/mongoid2.gemfile' bundle exec rake -t spec"
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "Run Tests against activerecord"
|
30
|
+
task :activerecord do
|
31
|
+
sh "bundle --quiet"
|
32
|
+
sh "bundle exec rake -t spec"
|
33
|
+
end
|
34
|
+
|
35
|
+
desc "Run Tests against all ORMs"
|
36
|
+
task :all do
|
37
|
+
# Mongoid 2
|
38
|
+
sh "BUNDLE_GEMFILE='gemfiles/mongoid2.gemfile' bundle --quiet"
|
39
|
+
sh "BUNDLE_GEMFILE='gemfiles/mongoid2.gemfile' bundle exec rake -t spec"
|
40
|
+
|
41
|
+
# ActiveRecord
|
42
|
+
sh "bundle --quiet"
|
43
|
+
sh "bundle exec rake -t spec"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
22
47
|
desc "Update AUTHORS file"
|
23
48
|
task :authors do
|
24
49
|
sh "git shortlog -s | awk '{ print $2 \" \" $3 }' > AUTHORS"
|
@@ -21,11 +21,10 @@ module MoneyRails
|
|
21
21
|
":with_currency or :with_model_currency")
|
22
22
|
end
|
23
23
|
|
24
|
-
# Optional
|
25
|
-
|
26
|
-
# Overrides default currency
|
27
|
-
model_currency_name = options[:with_model_currency] ||
|
24
|
+
# Optional accessor to be run on an instance to detect currency
|
25
|
+
instance_currency_name = options[:with_model_currency] ||
|
28
26
|
options[:model_currency] || "currency"
|
27
|
+
instance_currency_name = instance_currency_name.to_s
|
29
28
|
|
30
29
|
# This attribute allows per column currency values
|
31
30
|
# Overrides row and default currency
|
@@ -47,63 +46,45 @@ module MoneyRails
|
|
47
46
|
name = subunit_name << "_money"
|
48
47
|
end
|
49
48
|
|
50
|
-
|
49
|
+
# Include numericality validation if needed
|
50
|
+
validates_numericality_of subunit_name, :allow_nil => options[:allow_nil] if MoneyRails.include_validations
|
51
51
|
|
52
|
-
|
53
|
-
|
54
|
-
|
52
|
+
define_method name do
|
53
|
+
amount = send(subunit_name)
|
54
|
+
attr_currency = send("currency_for_#{name}")
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
}
|
61
|
-
converter = Proc.new { |value|
|
62
|
-
raise(ArgumentError, "Only Money objects are allowed for assignment")
|
63
|
-
}
|
64
|
-
else
|
65
|
-
mappings = [[subunit_name, "cents"]]
|
66
|
-
constructor = Proc.new { |cents|
|
67
|
-
Money.new(cents, field_currency_name || self.respond_to?(:currency) &&
|
68
|
-
self.currency || Money.default_currency)
|
69
|
-
}
|
70
|
-
converter = Proc.new { |value|
|
71
|
-
if options[:allow_nil] && value.blank?
|
72
|
-
nil
|
73
|
-
elsif value.respond_to?(:to_money)
|
74
|
-
value.to_money(field_currency_name || self.respond_to?(:currency) &&
|
75
|
-
self.currency || Money.default_currency)
|
76
|
-
else
|
77
|
-
raise(ArgumentError, "Can't convert #{value.class} to Money")
|
78
|
-
end
|
79
|
-
}
|
80
|
-
end
|
56
|
+
# Dont create a new Money instance if the values haven't changed
|
57
|
+
memoized = instance_variable_get("@#{name}")
|
58
|
+
return memoized if memoized && memoized.cents == amount &&
|
59
|
+
memoized.currency == attr_currency
|
81
60
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
:mapping => mappings,
|
86
|
-
:constructor => constructor,
|
87
|
-
:converter => converter,
|
88
|
-
:allow_nil => options[:allow_nil]
|
61
|
+
amount = Money.new(amount, attr_currency) unless amount.blank?
|
62
|
+
|
63
|
+
instance_variable_set "@#{name}", amount
|
89
64
|
end
|
90
65
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
value = nil if value.blank?
|
97
|
-
send "#{name}_without_blank_support=", value
|
98
|
-
end
|
99
|
-
alias_method_chain "#{name}=", :blank_support
|
66
|
+
define_method "#{name}=" do |value|
|
67
|
+
if options[:allow_nil] && value.blank?
|
68
|
+
money = nil
|
69
|
+
else
|
70
|
+
money = value.is_a?(Money) ? value : value.to_money(send("currency_for_#{name}"))
|
100
71
|
end
|
72
|
+
|
73
|
+
send("#{subunit_name}=", money.try(:cents))
|
74
|
+
send("#{instance_currency_name}=", money.try(:currency).try(:iso_code)) if self.respond_to?("#{instance_currency_name}=")
|
75
|
+
|
76
|
+
instance_variable_set "@#{name}", money
|
101
77
|
end
|
102
78
|
|
103
|
-
#
|
104
|
-
|
105
|
-
|
106
|
-
|
79
|
+
define_method "currency_for_#{name}" do
|
80
|
+
if self.respond_to?(instance_currency_name) && send(instance_currency_name).present?
|
81
|
+
Money::Currency.find(send(instance_currency_name))
|
82
|
+
elsif field_currency_name
|
83
|
+
Money::Currency.find(field_currency_name)
|
84
|
+
elsif self.class.respond_to?(:currency)
|
85
|
+
self.class.currency
|
86
|
+
else
|
87
|
+
Money.default_currency
|
107
88
|
end
|
108
89
|
end
|
109
90
|
end
|
data/lib/money-rails/hooks.rb
CHANGED
@@ -11,7 +11,7 @@ module MoneyRails
|
|
11
11
|
begin; require 'mongoid'; rescue LoadError; end
|
12
12
|
if defined? ::Mongoid
|
13
13
|
if ::Mongoid::VERSION =~ /^2(.*)/
|
14
|
-
#
|
14
|
+
require 'money-rails/mongoid/two' # Loading the file is enough
|
15
15
|
end
|
16
16
|
|
17
17
|
if ::Mongoid::VERSION =~ /^3(.*)/
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Class name does not really matches the folder hierarchy, because
|
2
|
+
# in order for (de)serialization to work, the class must be re-opened.
|
3
|
+
# But this file brings mongoid 2.X compat., so...
|
4
|
+
|
5
|
+
class Money
|
6
|
+
include ::Mongoid::Fields::Serializable
|
7
|
+
|
8
|
+
# Mongo friendly -> Money
|
9
|
+
def deserialize(object)
|
10
|
+
return nil if object.nil?
|
11
|
+
|
12
|
+
object = object.with_indifferent_access
|
13
|
+
::Money.new object[:cents], object[:currency_iso]
|
14
|
+
end
|
15
|
+
|
16
|
+
# Money -> Mongo friendly
|
17
|
+
def serialize(object)
|
18
|
+
return nil unless object.is_a? Money
|
19
|
+
|
20
|
+
{
|
21
|
+
:cents => object.cents,
|
22
|
+
:currency_iso => object.currency.iso_code
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
data/lib/money-rails/version.rb
CHANGED
data/money-rails.gemspec
CHANGED
@@ -1,217 +1,247 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
|
3
|
+
if defined? ActiveRecord
|
4
|
+
describe MoneyRails::ActiveRecord::Monetizable do
|
4
5
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
it "attaches a Money object to model field" do
|
13
|
-
@product.price.should be_an_instance_of(Money)
|
14
|
-
@product.discount_value.should be_an_instance_of(Money)
|
15
|
-
@product.bonus.should be_an_instance_of(Money)
|
16
|
-
end
|
17
|
-
|
18
|
-
it "returns the expected money amount as a Money object" do
|
19
|
-
@product.price.should == Money.new(3000, "USD")
|
20
|
-
end
|
21
|
-
|
22
|
-
it "assigns the correct value from a Money object" do
|
23
|
-
@product.price = Money.new(3210, "USD")
|
24
|
-
@product.save.should be_true
|
25
|
-
@product.price_cents.should == 3210
|
26
|
-
end
|
27
|
-
|
28
|
-
it "respects :as argument" do
|
29
|
-
@product.discount_value.should == Money.new(150, "USD")
|
30
|
-
end
|
31
|
-
|
32
|
-
it "uses numericality validation" do
|
33
|
-
@product.price_cents = "foo"
|
34
|
-
@product.save.should be_false
|
6
|
+
describe "monetize" do
|
7
|
+
before :each do
|
8
|
+
@product = Product.create(:price_cents => 3000, :discount => 150,
|
9
|
+
:bonus_cents => 200, :optional_price => 100)
|
10
|
+
@service = Service.create(:charge_cents => 2000, :discount_cents => 120)
|
11
|
+
end
|
35
12
|
|
36
|
-
|
37
|
-
|
38
|
-
|
13
|
+
it "attaches a Money object to model field" do
|
14
|
+
@product.price.should be_an_instance_of(Money)
|
15
|
+
@product.discount_value.should be_an_instance_of(Money)
|
16
|
+
@product.bonus.should be_an_instance_of(Money)
|
17
|
+
end
|
39
18
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
19
|
+
it "returns the expected money amount as a Money object" do
|
20
|
+
@product.price.should == Money.new(3000, "USD")
|
21
|
+
end
|
44
22
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
23
|
+
it "assigns the correct value from a Money object" do
|
24
|
+
@product.price = Money.new(3210, "USD")
|
25
|
+
@product.save.should be_true
|
26
|
+
@product.price_cents.should == 3210
|
27
|
+
end
|
50
28
|
|
51
|
-
|
52
|
-
|
53
|
-
|
29
|
+
it "respects :as argument" do
|
30
|
+
@product.discount_value.should == Money.new(150, "USD")
|
31
|
+
end
|
54
32
|
|
55
|
-
|
56
|
-
|
57
|
-
|
33
|
+
it "uses numericality validation" do
|
34
|
+
@product.price_cents = "foo"
|
35
|
+
@product.save.should be_false
|
58
36
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
end
|
37
|
+
@product.price_cents = 2000
|
38
|
+
@product.save.should be_true
|
39
|
+
end
|
63
40
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
@product.price.currency_as_string.should == "USD"
|
69
|
-
end
|
41
|
+
it "doesn't allow nil by default" do
|
42
|
+
@product.price_cents = nil
|
43
|
+
@product.save.should be_false
|
44
|
+
end
|
70
45
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
46
|
+
it "allows nil if optioned" do
|
47
|
+
@product.optional_price = nil
|
48
|
+
@product.save.should be_true
|
49
|
+
@product.optional_price.should be_nil
|
50
|
+
end
|
76
51
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
@service.discount.currency_as_string.should == "EUR"
|
81
|
-
end
|
52
|
+
it "uses Money default currency if :with_currency has not been used" do
|
53
|
+
@service.discount.currency.should == Money::Currency.find(:eur)
|
54
|
+
end
|
82
55
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
@product.price.cents.should == 2500
|
87
|
-
@product.price.currency_as_string.should == "USD"
|
56
|
+
it "overrides default currency with the currency registered for the model" do
|
57
|
+
@product.price.currency.should == Money::Currency.find(:usd)
|
58
|
+
end
|
88
59
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
end
|
60
|
+
it "overrides default currency with the value of :with_currency argument" do
|
61
|
+
@service.charge.currency.should == Money::Currency.find(:usd)
|
62
|
+
@product.bonus.currency.should == Money::Currency.find(:gbp)
|
63
|
+
end
|
94
64
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
65
|
+
it "assigns correctly Money objects to the attribute" do
|
66
|
+
@product.price = Money.new(2500, :USD)
|
67
|
+
@product.save.should be_true
|
68
|
+
@product.price.cents.should == 2500
|
69
|
+
@product.price.currency_as_string.should == "USD"
|
70
|
+
end
|
100
71
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
72
|
+
it "assigns correctly Fixnum objects to the attribute" do
|
73
|
+
@product.price = 25
|
74
|
+
@product.save.should be_true
|
75
|
+
@product.price.cents.should == 2500
|
76
|
+
@product.price.currency_as_string.should == "USD"
|
106
77
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
78
|
+
@service.discount = 2
|
79
|
+
@service.save.should be_true
|
80
|
+
@service.discount.cents.should == 200
|
81
|
+
@service.discount.currency_as_string.should == "EUR"
|
82
|
+
end
|
112
83
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
84
|
+
it "assigns correctly String objects to the attribute" do
|
85
|
+
@product.price = "25"
|
86
|
+
@product.save.should be_true
|
87
|
+
@product.price.cents.should == 2500
|
88
|
+
@product.price.currency_as_string.should == "USD"
|
118
89
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
end
|
90
|
+
@service.discount = "2"
|
91
|
+
@service.save.should be_true
|
92
|
+
@service.discount.cents.should == 200
|
93
|
+
@service.discount.currency_as_string.should == "EUR"
|
94
|
+
end
|
125
95
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
end
|
96
|
+
it "overrides default, model currency with the value of :with_currency in fixnum assignments" do
|
97
|
+
@product.bonus = 25
|
98
|
+
@product.save.should be_true
|
99
|
+
@product.bonus.cents.should == 2500
|
100
|
+
@product.bonus.currency_as_string.should == "GBP"
|
132
101
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
end
|
102
|
+
@service.charge = 2
|
103
|
+
@service.save.should be_true
|
104
|
+
@service.charge.cents.should == 200
|
105
|
+
@service.charge.currency_as_string.should == "USD"
|
106
|
+
end
|
139
107
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
end
|
108
|
+
it "overrides default, model currency with the value of :with_currency in string assignments" do
|
109
|
+
@product.bonus = "25"
|
110
|
+
@product.save.should be_true
|
111
|
+
@product.bonus.cents.should == 2500
|
112
|
+
@product.bonus.currency_as_string.should == "GBP"
|
146
113
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
114
|
+
@service.charge = "2"
|
115
|
+
@service.save.should be_true
|
116
|
+
@service.charge.cents.should == 200
|
117
|
+
@service.charge.currency_as_string.should == "USD"
|
118
|
+
end
|
152
119
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
end
|
120
|
+
it "overrides default currency with model currency, in fixnum assignments" do
|
121
|
+
@product.discount_value = 5
|
122
|
+
@product.save.should be_true
|
123
|
+
@product.discount_value.cents.should == 500
|
124
|
+
@product.discount_value.currency_as_string.should == "USD"
|
125
|
+
end
|
160
126
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
context "for model with currency column:" do
|
168
|
-
before :each do
|
169
|
-
@transaction = Transaction.create(:amount_cents => 2400, :tax_cents => 600,
|
170
|
-
:currency => :usd)
|
171
|
-
@dummy_product1 = DummyProduct.create(:price_cents => 2400, :currency => :usd)
|
172
|
-
@dummy_product2 = DummyProduct.create(:price_cents => 2600) # nil currency
|
127
|
+
it "overrides default currency with model currency, in string assignments" do
|
128
|
+
@product.discount_value = "5"
|
129
|
+
@product.save.should be_true
|
130
|
+
@product.discount_value.cents.should == 500
|
131
|
+
@product.discount_value.currency_as_string.should == "USD"
|
173
132
|
end
|
174
133
|
|
175
|
-
it "
|
176
|
-
@
|
134
|
+
it "falls back to default currency, in fixnum assignments" do
|
135
|
+
@service.discount = 5
|
136
|
+
@service.save.should be_true
|
137
|
+
@service.discount.cents.should == 500
|
138
|
+
@service.discount.currency_as_string.should == "EUR"
|
177
139
|
end
|
178
140
|
|
179
|
-
it "
|
180
|
-
@
|
141
|
+
it "falls back to default currency, in string assignments" do
|
142
|
+
@service.discount = "5"
|
143
|
+
@service.save.should be_true
|
144
|
+
@service.discount.cents.should == 500
|
145
|
+
@service.discount.currency_as_string.should == "EUR"
|
181
146
|
end
|
182
147
|
|
183
|
-
it "
|
184
|
-
@
|
148
|
+
it "sets field to nil, in nil assignments if allow_nil is set" do
|
149
|
+
@product.optional_price = nil
|
150
|
+
@product.save.should be_true
|
151
|
+
@product.optional_price.should be_nil
|
185
152
|
end
|
186
153
|
|
187
|
-
it "
|
188
|
-
|
154
|
+
it "sets field to nil, in instantiation if allow_nil is set" do
|
155
|
+
pr = Product.new(:optional_price => nil, :price_cents => 5320,
|
156
|
+
:discount => 350, :bonus_cents => 320)
|
157
|
+
pr.optional_price.should be_nil
|
158
|
+
pr.save.should be_true
|
159
|
+
pr.optional_price.should be_nil
|
189
160
|
end
|
190
161
|
|
191
|
-
it "
|
192
|
-
|
193
|
-
|
162
|
+
it "sets field to nil, in blank assignments if allow_nil is set" do
|
163
|
+
@product.optional_price = ""
|
164
|
+
@product.save.should be_true
|
165
|
+
@product.optional_price.should be_nil
|
194
166
|
end
|
195
167
|
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
168
|
+
context "for column with currency:" do
|
169
|
+
it "is overridden by instance currency" do
|
170
|
+
product = Product.create(:price_cents => 5320, :discount => 350, :bonus_cents => 320)
|
171
|
+
product.stub(:currency) { "EUR" }
|
172
|
+
product.bonus.currency_as_string.should == "EUR"
|
173
|
+
end
|
201
174
|
end
|
202
175
|
|
203
|
-
|
204
|
-
|
205
|
-
|
176
|
+
context "for column with model currency:" do
|
177
|
+
it "has default currency if not specified" do
|
178
|
+
product = Product.create(:sale_price_amount => 1234)
|
179
|
+
product.sale_price.currency_as_string == 'USD'
|
180
|
+
end
|
181
|
+
it "is overridden by instance currency column" do
|
182
|
+
product = Product.create(:sale_price_amount => 1234,
|
183
|
+
:sale_price_currency_code => 'CAD')
|
184
|
+
product.sale_price.currency_as_string.should == 'CAD'
|
185
|
+
end
|
206
186
|
end
|
207
187
|
|
188
|
+
context "for model with currency column:" do
|
189
|
+
before :each do
|
190
|
+
@transaction = Transaction.create(:amount_cents => 2400, :tax_cents => 600,
|
191
|
+
:currency => :usd)
|
192
|
+
@dummy_product1 = DummyProduct.create(:price_cents => 2400, :currency => :usd)
|
193
|
+
@dummy_product2 = DummyProduct.create(:price_cents => 2600) # nil currency
|
194
|
+
end
|
195
|
+
|
196
|
+
it "serializes correctly the currency to a new instance of model" do
|
197
|
+
d = DummyProduct.new
|
198
|
+
d.price = Money.new(10, "EUR")
|
199
|
+
d.save!
|
200
|
+
d.reload
|
201
|
+
d.currency.should == "EUR"
|
202
|
+
end
|
203
|
+
|
204
|
+
it "overrides default currency with the value of row currency" do
|
205
|
+
@transaction.amount.currency.should == Money::Currency.find(:usd)
|
206
|
+
end
|
207
|
+
|
208
|
+
it "overrides default currency with the currency registered for the model" do
|
209
|
+
@dummy_product2.price.currency.should == Money::Currency.find(:gbp)
|
210
|
+
end
|
211
|
+
|
212
|
+
it "overrides default and model currency with the row currency" do
|
213
|
+
@dummy_product1.price.currency.should == Money::Currency.find(:usd)
|
214
|
+
end
|
215
|
+
|
216
|
+
it "constructs the money attribute from the stored mapped attribute values" do
|
217
|
+
@transaction.amount.should == Money.new(2400, :usd)
|
218
|
+
end
|
219
|
+
|
220
|
+
it "instantiates correctly Money objects from the mapped attributes" do
|
221
|
+
t = Transaction.new(:amount_cents => 2500, :currency => "CAD")
|
222
|
+
t.amount.should == Money.new(2500, "CAD")
|
223
|
+
end
|
224
|
+
|
225
|
+
it "assigns correctly Money objects to the attribute" do
|
226
|
+
@transaction.amount = Money.new(2500, :eur)
|
227
|
+
@transaction.save.should be_true
|
228
|
+
@transaction.amount.cents.should == Money.new(2500, :eur).cents
|
229
|
+
@transaction.amount.currency_as_string.should == "EUR"
|
230
|
+
end
|
231
|
+
|
232
|
+
it "uses default currency if a non Money object is assigned to the attribute" do
|
233
|
+
@transaction.amount = 234
|
234
|
+
@transaction.amount.currency_as_string.should == "USD"
|
235
|
+
end
|
236
|
+
|
237
|
+
end
|
208
238
|
end
|
209
|
-
end
|
210
239
|
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
240
|
+
describe "register_currency" do
|
241
|
+
it "attaches currency at model level" do
|
242
|
+
Product.currency.should == Money::Currency.find(:usd)
|
243
|
+
DummyProduct.currency.should == Money::Currency.find(:gbp)
|
244
|
+
end
|
215
245
|
end
|
216
246
|
end
|
217
247
|
end
|