money-rails 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Build Status](https://secure.travis-ci.org/RubyMoney/money-rails.png?branch=master)](http://travis-ci.org/RubyMoney/money-rails)
|
4
|
+
[![Dependency Status](https://gemnasium.com/RubyMoney/money-rails.png)](https://gemnasium.com/RubyMoney/money-rails)
|
5
|
+
[![Code Climate](https://codeclimate.com/badge.png)](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
|