samlown-money 2.3.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +0 -0
- data/MIT-LICENSE +21 -0
- data/Manifest.txt +25 -0
- data/README.rdoc +137 -0
- data/Rakefile +27 -0
- data/lib/money.rb +29 -0
- data/lib/money/acts_as_money.rb +41 -0
- data/lib/money/core_extensions.rb +139 -0
- data/lib/money/errors.rb +4 -0
- data/lib/money/money.rb +319 -0
- data/lib/money/variable_exchange_bank.rb +101 -0
- data/money.gemspec +37 -0
- data/rails/init.rb +3 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/db/database.yml +4 -0
- data/spec/db/schema.rb +14 -0
- data/spec/money/acts_as_money_spec.rb +95 -0
- data/spec/money/core_extensions_spec.rb +44 -0
- data/spec/money/exchange_bank_spec.rb +46 -0
- data/spec/money/money_spec.rb +266 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +19 -0
- metadata +99 -0
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'money/errors'
|
2
|
+
require 'net/http'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hpricot'
|
5
|
+
|
6
|
+
# Class for aiding in exchanging money between different currencies.
|
7
|
+
# By default, the Money class uses an object of this class (accessible through
|
8
|
+
# Money#bank) for performing currency exchanges.
|
9
|
+
#
|
10
|
+
# By default, VariableExchangeBank has no knowledge about conversion rates.
|
11
|
+
# One must manually specify them with +add_rate+, after which one can perform
|
12
|
+
# exchanges with +exchange+. For example:
|
13
|
+
#
|
14
|
+
# bank = Money::VariableExchangeBank.new
|
15
|
+
# bank.add_rate("CAD", 0.803115)
|
16
|
+
# bank.add_rate("USD", 1.24515)
|
17
|
+
#
|
18
|
+
# # Exchange 100 CAD to USD:
|
19
|
+
# bank.exchange(100_00, "CAD", "USD") # => 15504
|
20
|
+
# # Exchange 100 USD to CAD:
|
21
|
+
# bank.exchange(100_00, "USD", "CAD") # => 6450
|
22
|
+
class Money
|
23
|
+
class VariableExchangeBank
|
24
|
+
# Returns the singleton instance of VariableExchangeBank.
|
25
|
+
#
|
26
|
+
# By default, <tt>Money.default_bank</tt> returns the same object.
|
27
|
+
def self.instance
|
28
|
+
@@singleton
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize
|
32
|
+
@rates = {}
|
33
|
+
@rates["USD"] = 1.0
|
34
|
+
end
|
35
|
+
|
36
|
+
def add_rate(currency, rate)
|
37
|
+
@rates[currency.upcase] = (currency.upcase != Money.default_currency) ? (rate * @rates[Money.default_currency]) : rate
|
38
|
+
end
|
39
|
+
|
40
|
+
def get_rate(currency = nil)
|
41
|
+
return nil unless @rates[currency]
|
42
|
+
(currency != Money.default_currency) ? @rates[currency.upcase] / @rates[Money.default_currency] : @rates[currency.upcase]
|
43
|
+
end
|
44
|
+
|
45
|
+
# Given two currency names, checks whether they're both the same currency.
|
46
|
+
#
|
47
|
+
# bank = VariableExchangeBank.new
|
48
|
+
# bank.same_currency?("usd", "USD") # => true
|
49
|
+
# bank.same_currency?("usd", "EUR") # => false
|
50
|
+
def same_currency?(currency1, currency2)
|
51
|
+
currency1.upcase == currency2.upcase
|
52
|
+
end
|
53
|
+
|
54
|
+
# Exchange the given amount of cents in +from_currency+ to +to_currency+.
|
55
|
+
# Returns the amount of cents in +to_currency+ as an integer, rounded down.
|
56
|
+
#
|
57
|
+
# If the conversion rate is unknown, then Money::UnknownRate will be raised.
|
58
|
+
def exchange(cents, from_currency, to_currency)
|
59
|
+
from_currency.upcase!
|
60
|
+
to_currency.upcase!
|
61
|
+
if !@rates[from_currency] or !@rates[to_currency]
|
62
|
+
raise Money::UnknownRate, "No conversion rate known for '#{from_currency}' -> '#{to_currency}'"
|
63
|
+
end
|
64
|
+
((cents / @rates[from_currency]) * @rates[to_currency]).round
|
65
|
+
end
|
66
|
+
|
67
|
+
# Fetch rates
|
68
|
+
def fetch_rates
|
69
|
+
xml = Hpricot.XML(
|
70
|
+
Net::HTTP.get(
|
71
|
+
URI.parse('http://www.ecb.int/stats/eurofxref/eurofxref-daily.xml')
|
72
|
+
)
|
73
|
+
)
|
74
|
+
|
75
|
+
@rates["EUR"] = 1.0
|
76
|
+
(xml/:Cube).each do |ele|
|
77
|
+
@rates[ele['currency'].upcase] = ele['rate'].to_f if ele['currency']
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
# Auto fetch the currencies every X seconds
|
83
|
+
# if no time is give, will fetch every hour
|
84
|
+
def auto_fetch(time = 60*60)
|
85
|
+
@auto_fetch.kill if (@auto_fetch && @auto_fetch.alive?)
|
86
|
+
@auto_fetch = Thread.new {
|
87
|
+
loop do
|
88
|
+
self.fetch_rates
|
89
|
+
sleep time
|
90
|
+
end
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
# stop auto fetch
|
95
|
+
def stop_fetch
|
96
|
+
@auto_fetch.kill if (@auto_fetch && @auto_fetch.alive?)
|
97
|
+
end
|
98
|
+
|
99
|
+
@@singleton = VariableExchangeBank.new
|
100
|
+
end
|
101
|
+
end
|
data/money.gemspec
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{money}
|
5
|
+
s.version = "2.3.3.2"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Money Team"]
|
9
|
+
s.date = %q{2009-05-14}
|
10
|
+
s.description = %q{This library aids one in handling money and different currencies.}
|
11
|
+
s.email = ["see@readme"]
|
12
|
+
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "README.rdoc"]
|
13
|
+
s.files = ["History.txt", "MIT-LICENSE", "Manifest.txt", "README.rdoc", "Rakefile", "lib/money.rb", "lib/money/acts_as_money.rb", "lib/money/core_extensions.rb", "lib/money/errors.rb", "lib/money/money.rb", "lib/money/variable_exchange_bank.rb", "money.gemspec", "rails/init.rb", "script/console", "script/destroy", "script/generate", "spec/db/database.yml", "spec/db/schema.rb", "spec/money/acts_as_money_spec.rb", "spec/money/core_extensions_spec.rb", "spec/money/exchange_bank_spec.rb", "spec/money/money_spec.rb", "spec/spec.opts", "spec/spec_helper.rb" ]
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.homepage = %q{http://github.com/samlown/money}
|
16
|
+
s.rdoc_options = ["--main", "README.rdoc"]
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
s.rubyforge_project = %q{money}
|
19
|
+
s.rubygems_version = %q{1.3.1}
|
20
|
+
s.summary = %q{This library aids one in handling money and different currencies.}
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
+
s.specification_version = 2
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
27
|
+
s.add_development_dependency(%q<newgem>, [">= 1.3.0"])
|
28
|
+
s.add_development_dependency(%q<hoe>, [">= 1.8.0"])
|
29
|
+
else
|
30
|
+
s.add_dependency(%q<newgem>, [">= 1.3.0"])
|
31
|
+
s.add_dependency(%q<hoe>, [">= 1.8.0"])
|
32
|
+
end
|
33
|
+
else
|
34
|
+
s.add_dependency(%q<newgem>, [">= 1.3.0"])
|
35
|
+
s.add_dependency(%q<hoe>, [">= 1.8.0"])
|
36
|
+
end
|
37
|
+
end
|
data/rails/init.rb
ADDED
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/postgis_adapter.rb'}"
|
9
|
+
puts "Loading postgis_adapter gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
data/spec/db/schema.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
ActiveRecord::Schema.define() do
|
2
|
+
|
3
|
+
create_table :accounts, :force => true do |t|
|
4
|
+
t.integer :value_cents, :total_cents
|
5
|
+
t.string :value_currency, :total_currency
|
6
|
+
end
|
7
|
+
|
8
|
+
create_table :products, :force => true do |t|
|
9
|
+
t.integer :value_cents, :tax_pennys
|
10
|
+
t.string :value_currency
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
require File.dirname(__FILE__) + '/../../rails/init.rb'
|
3
|
+
|
4
|
+
class Account < ActiveRecord::Base
|
5
|
+
has_money :value, :total, :allow_nil => true
|
6
|
+
end
|
7
|
+
|
8
|
+
class Product < ActiveRecord::Base
|
9
|
+
has_money :value, :allow_nil => false
|
10
|
+
has_money :tax, :cents => "pennys", :with_currency => false
|
11
|
+
|
12
|
+
validates_numericality_of :value_cents, :greater_than => 0
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "Acts as Money" do
|
16
|
+
|
17
|
+
|
18
|
+
it "should accept nil" do
|
19
|
+
@account = Account.create(:value => nil)
|
20
|
+
@account.should be_valid
|
21
|
+
@account.value.should be_nil
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should require money" do
|
25
|
+
@product = Product.create(:value => nil)
|
26
|
+
@product.should have(1).errors
|
27
|
+
@product.value.should == Money.new(0)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should require money" do
|
31
|
+
@product_fake = Product.create(:value => nil)
|
32
|
+
@product_fake.value_cents.should eql(0)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should create" do
|
36
|
+
@account = Account.create!(:value => 10, :total => "20 BRL")
|
37
|
+
@account.should be_valid
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should write to the db" do
|
41
|
+
lambda do
|
42
|
+
@account = Account.create!(:value => 10, :total => "20 BRL")
|
43
|
+
end.should change(Account, :count).by(1)
|
44
|
+
Account.last.total.format.should eql("R$20,00")
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
describe "Account" do
|
50
|
+
|
51
|
+
before(:each) do
|
52
|
+
@account = Account.create!(:value => 10, :total => "20 BRL")
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should return an instance of Money" do
|
56
|
+
@account.value.should be_instance_of(Money)
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should format out nicely" do
|
60
|
+
@account.value.format.should eql("$10.00")
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should include nicely" do
|
64
|
+
@account.value.to_s.should eql("10.00")
|
65
|
+
@account.total.to_s.should eql("20.00")
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should map cents" do
|
69
|
+
@account.value_cents.to_s.should eql("1000")
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should map currency" do
|
73
|
+
@account.value_currency.should eql("USD")
|
74
|
+
@account.total_currency.should eql("BRL")
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
describe "Product" do
|
80
|
+
|
81
|
+
before(:each) do
|
82
|
+
@product = Product.create(:value => 10, :tax => 2)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should map attributes" do
|
86
|
+
@product.pennys.should eql(200)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should map currency on tax" do
|
90
|
+
@product.should_not respond_to(:tax_currency)
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
|
3
|
+
describe "Money core extensions" do
|
4
|
+
describe "Numberic#to_money works" do
|
5
|
+
it "should convert integer to money" do
|
6
|
+
money = 1234.to_money
|
7
|
+
money.cents.should == 1234_00
|
8
|
+
money.currency.should == Money.default_currency
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should convert float to money" do
|
12
|
+
money = 100.37.to_money
|
13
|
+
money.cents.should == 100_37
|
14
|
+
money.currency.should == Money.default_currency
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "String#to_money works" do
|
19
|
+
it { "100".to_money.should == Money.new(100_00) }
|
20
|
+
it { "100.37".to_money.should == Money.new(100_37) }
|
21
|
+
it { "100,37".to_money.should == Money.new(100_37) }
|
22
|
+
it { "100 000".to_money.should == Money.new(100_000_00) }
|
23
|
+
it { "100.000,45".to_money.should == Money.new(100_000_45) }
|
24
|
+
it { "-100.100,45".to_money.should == Money.new(-100_100_45) }
|
25
|
+
|
26
|
+
it { "100 USD".to_money.should == Money.new(100_00, "USD") }
|
27
|
+
it { "-100 USD".to_money.should == Money.new(-100_00, "USD") }
|
28
|
+
it { "100 EUR".to_money.should == Money.new(100_00, "EUR") }
|
29
|
+
it { "100.37 EUR".to_money.should == Money.new(100_37, "EUR") }
|
30
|
+
it { "100,37 EUR".to_money.should == Money.new(100_37, "EUR") }
|
31
|
+
|
32
|
+
it { "USD 100".to_money.should == Money.new(100_00, "USD") }
|
33
|
+
it { "EUR 100".to_money.should == Money.new(100_00, "EUR") }
|
34
|
+
it { "EUR 100.37".to_money.should == Money.new(100_37, "EUR") }
|
35
|
+
it { "CAD -100.37".to_money.should == Money.new(-100_37, "CAD") }
|
36
|
+
it { "EUR 100,37".to_money.should == Money.new(100_37, "EUR") }
|
37
|
+
it { "EUR -100,37".to_money.should == Money.new(-100_37, "EUR") }
|
38
|
+
|
39
|
+
it { "BRL 100,37".to_money.should == Money.new(100_37, "BRL") }
|
40
|
+
it { "BRL -100,37".to_money.should == Money.new(-100_37, "BRL") }
|
41
|
+
|
42
|
+
it {"$100 USD".to_money.should == Money.new(100_00, "USD") }
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
|
3
|
+
describe Money::VariableExchangeBank do
|
4
|
+
before :each do
|
5
|
+
@bank = Money::VariableExchangeBank.new
|
6
|
+
end
|
7
|
+
|
8
|
+
it "returns the previously specified conversion rate" do
|
9
|
+
@bank.add_rate("USD", 0.788332676)
|
10
|
+
@bank.add_rate("EUR", 122.631477)
|
11
|
+
@bank.get_rate("USD").should == 0.788332676
|
12
|
+
@bank.get_rate("EUR").should == 122.631477
|
13
|
+
end
|
14
|
+
|
15
|
+
it "treats currency names case-insensitively" do
|
16
|
+
@bank.add_rate("usd", 1)
|
17
|
+
@bank.get_rate("USD").should == 1
|
18
|
+
@bank.same_currency?("USD", "usd").should be_true
|
19
|
+
@bank.same_currency?("EUR", "usd").should be_false
|
20
|
+
end
|
21
|
+
|
22
|
+
it "returns nil if the conversion rate is unknown" do
|
23
|
+
@bank.get_rate("American Pesos").should be_nil
|
24
|
+
end
|
25
|
+
|
26
|
+
it "exchanges money from one currency to another according to the specified conversion rates" do
|
27
|
+
@bank.add_rate("USD", 1.0)
|
28
|
+
@bank.add_rate("EUR", 0.5)
|
29
|
+
@bank.add_rate("YEN", 5)
|
30
|
+
@bank.exchange(10_00, "USD", "EUR").should == 5_00
|
31
|
+
@bank.exchange(500_00, "EUR", "YEN").should == 5000_00
|
32
|
+
end
|
33
|
+
|
34
|
+
it "rounds the exchanged result down" do
|
35
|
+
@bank.add_rate("USD", 1.0)
|
36
|
+
@bank.add_rate("EUR", 0.788332676)
|
37
|
+
@bank.add_rate("YEN", 122.631477)
|
38
|
+
@bank.exchange(10_00, "USD", "EUR").should == 788
|
39
|
+
@bank.exchange(500_00, "EUR", "YEN").should == 7777901
|
40
|
+
end
|
41
|
+
|
42
|
+
it "raises Money::UnknownRate upon conversion if the conversion rate is unknown" do
|
43
|
+
block = lambda { @bank.exchange(10, "USD", "ABC") }
|
44
|
+
block.should raise_error(Money::UnknownRate)
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,266 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
3
|
+
|
4
|
+
describe Money do
|
5
|
+
|
6
|
+
it { Money.new(10_00).to_f.should eql(10.0) }
|
7
|
+
it { Money.new(10_00).to_s.should eql("10.00") }
|
8
|
+
|
9
|
+
it "is associated to the singleton instance of VariableExchangeBank by default" do
|
10
|
+
Money.new(0).bank.object_id.should == Money::VariableExchangeBank.instance.object_id
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should return the amount of cents passed to the constructor" do
|
14
|
+
Money.new(200_00, "USD").cents.should == 200_00
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should rounds the given cents to an integer" do
|
18
|
+
Money.new(1.0, "USD").cents.should == 1
|
19
|
+
Money.new(1.01, "USD").cents.should == 1
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should use the floor method" do
|
23
|
+
Money.new(1.50, "USD").cents.should == 1
|
24
|
+
Money.new(1.99, "USD").cents.should == 1
|
25
|
+
end
|
26
|
+
|
27
|
+
it "#currency returns the currency passed to the constructor" do
|
28
|
+
Money.new(200_00, "USD").currency.should == "USD"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "#zero? returns whether the amount is 0" do
|
32
|
+
Money.new(0, "USD").should be_zero
|
33
|
+
Money.new(0, "EUR").should be_zero
|
34
|
+
Money.new(1, "USD").should_not be_zero
|
35
|
+
Money.new(10, "YEN").should_not be_zero
|
36
|
+
Money.new(-1, "EUR").should_not be_zero
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should exchange_to exchanges the amount via its exchange bank" do
|
40
|
+
money = Money.new(100_00, "USD")
|
41
|
+
money.bank.should_receive(:exchange).with(100_00, "USD", "EUR").and_return(200_00)
|
42
|
+
money.exchange_to("EUR")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should exchange_to exchanges the amount properly" do
|
46
|
+
money = Money.new(100_00, "USD")
|
47
|
+
money.bank.should_receive(:exchange).with(100_00, "USD", "EUR").and_return(200_00)
|
48
|
+
money.exchange_to("EUR").should == Money.new(200_00, "EUR")
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should returns true if and only if their amount and currency are equal" do
|
52
|
+
Money.new(1_00, "USD").should == Money.new(1_00, "USD")
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should add retaining the currency" do
|
56
|
+
(Money.new(1_00, "USD") + 10).should == Money.new(1_10, "USD")
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should substract retaining the currency" do
|
60
|
+
(Money.new(1_00, "USD") - 10).should == Money.new(90, "USD")
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should multiply while retaining the currency" do
|
64
|
+
(Money.new(1_00, "USD") * 10).should == Money.new(10_00, "USD")
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should divides while retaining the currency" do
|
68
|
+
(Money.new(10_00, "USD") / 10).should == Money.new(1_00, "USD")
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should create a new Money object of 0 cents if empty" do
|
72
|
+
Money.empty.should == Money.new(0)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "Money.ca_dollar creates a new Money object of the given value in CAD" do
|
76
|
+
Money.ca_dollar(50).should == Money.new(50, "CAD")
|
77
|
+
end
|
78
|
+
|
79
|
+
it "Money.us_dollar creates a new Money object of the given value in USD" do
|
80
|
+
Money.us_dollar(50).should == Money.new(50, "USD")
|
81
|
+
end
|
82
|
+
|
83
|
+
it "Money.euro creates a new Money object of the given value in EUR" do
|
84
|
+
Money.euro(50).should == Money.new(50, "EUR")
|
85
|
+
end
|
86
|
+
|
87
|
+
it "Money.real creates a new Money object of the given value in BRL" do
|
88
|
+
Money.real(50).should == Money.new(50, "BRL")
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "Installments" do
|
92
|
+
|
93
|
+
it "# divides the money ammout in installments add first" do
|
94
|
+
@money = Money.new(10_00).split_in_installments(3)
|
95
|
+
@money[0].cents.should eql(334)
|
96
|
+
@money[1].cents.should eql(333)
|
97
|
+
@money[2].cents.should eql(333)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "# divides the money ammout in installments add last" do
|
101
|
+
@money = Money.new(10_00).split_in_installments(3,true)
|
102
|
+
@money.to_s.should eql(["3.33", "3.33", "3.34"])
|
103
|
+
end
|
104
|
+
|
105
|
+
it "# divides the money ammout in installments base on payment" do
|
106
|
+
money = Money.new(3_00)
|
107
|
+
Money.new(10_00).in_installments_of(money)[0].cents.should eql(334)
|
108
|
+
Money.new(10_00).in_installments_of(money)[1].cents.should eql(333)
|
109
|
+
Money.new(10_00).in_installments_of(money)[2].cents.should eql(333)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "shuld sum array" do
|
113
|
+
Money.new(10_00).split_in_installments(3).sum.cents.should eql(1000)
|
114
|
+
end
|
115
|
+
|
116
|
+
it "should calculate tax" do
|
117
|
+
Money.new(100).add_tax(20).cents.should eql(120)
|
118
|
+
Money.new(100).add_tax(-20).cents.should eql(80)
|
119
|
+
end
|
120
|
+
|
121
|
+
it "shuld to_s wallet" do
|
122
|
+
@money = Money.new(10_00)
|
123
|
+
@money.split_in_installments(3).to_s.should eql(["3.34", "3.33", "3.33"])
|
124
|
+
end
|
125
|
+
|
126
|
+
it "shuld sum array" do
|
127
|
+
@money = Money.new(10_00).add_tax(10)
|
128
|
+
@money.split_in_installments(3).sum.cents.should eql(1100)
|
129
|
+
end
|
130
|
+
|
131
|
+
end
|
132
|
+
|
133
|
+
describe "Taxes and Interest" do
|
134
|
+
|
135
|
+
it "Money.add_rate works" do
|
136
|
+
Money.add_rate("USD", 1.0)
|
137
|
+
Money.add_rate("EUR", 10)
|
138
|
+
Money.new(10_00, "EUR").exchange_to("USD").should == Money.new(1_00, "USD")
|
139
|
+
end
|
140
|
+
|
141
|
+
it "Money method missing exchange" do
|
142
|
+
Money.add_rate("EUR", 1.0)
|
143
|
+
Money.add_rate("BRL", 10)
|
144
|
+
Money.new(10_00, "EUR").as_brl.should == Money.new(100_00, "BRL")
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should calculate compound tax" do
|
148
|
+
m = Money.new(1000_00)
|
149
|
+
m.compound_interest(12.99,12).to_s.should eql("137.91")
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should simple interest" do
|
153
|
+
m = Money.new(1000_00)
|
154
|
+
m.simple_interest(12.99,12).to_s.should eql("129.90")
|
155
|
+
end
|
156
|
+
|
157
|
+
it "should calculate compound interest" do
|
158
|
+
m = Money.new(2500_00)
|
159
|
+
m.compound_interest(12.99,3).to_s.should eql("82.06")
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should calculate compound interest" do
|
163
|
+
m = Money.new(2500_00)
|
164
|
+
m.compound_interest(12.99,3,6).to_s.should eql("165.91")
|
165
|
+
end
|
166
|
+
|
167
|
+
end
|
168
|
+
|
169
|
+
describe "Format out " do
|
170
|
+
|
171
|
+
describe "Options" do
|
172
|
+
before(:each) do
|
173
|
+
@cash = Money.new(2_00, "JPY")
|
174
|
+
end
|
175
|
+
|
176
|
+
it { @cash.format.should eql("¥2.00") }
|
177
|
+
it { @cash.format(:symbol => "R$ ").should eql("R$ 2.00") }
|
178
|
+
it { @cash.format(:no_cents => true).should eql("¥2") }
|
179
|
+
it { @cash.format(:no_cents => true, :symbol => "R$ ").should eql("R$ 2") }
|
180
|
+
it { @cash.format(:html => true).should eql("¥2.00") }
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
it { Money.new(0).format.should eql("$0.00") }
|
185
|
+
it { Money.new(0).format(:display_free => true).should eql("free") }
|
186
|
+
it { Money.new(0).format(:display_free => "GRATIS").should eql("GRATIS") }
|
187
|
+
|
188
|
+
it { Money.new(9).format.should eql("$0.09") }
|
189
|
+
it { Money.new(99).format.should eql("$0.99") }
|
190
|
+
it { Money.new(800).format.should eql("$8.00") }
|
191
|
+
it { Money.new(-8000).format(:no_cents => true).should eql("$-80") }
|
192
|
+
it { Money.new(80000).format.should eql("$800.00") }
|
193
|
+
it { Money.new(800000).format.should eql("$8,000.00") }
|
194
|
+
it { Money.new(-8000000, "JPY").format(:no_cents => true).should eql("¥-80.000") }
|
195
|
+
it { Money.new(87654321, "BRL").format.should eql("R$876.543,21") }
|
196
|
+
it { Money.new(800000000, "BRL").format.should eql("R$8.000.000,00") }
|
197
|
+
it { Money.new(8000000000, "BRL").format.should eql("R$80.000.000,00") }
|
198
|
+
it { Money.new(80000000000, "CAD").format.should eql("$800,000,000.00") }
|
199
|
+
it { Money.new(880000000000, "GBP").format(:no_cents => true).should eql("£8,800,000,000") }
|
200
|
+
it { Money.new(8800000000088, "EUR").format.should eql("€88,000,000,000.88") }
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
describe "Actions involving two Money objects" do
|
206
|
+
describe "if the other Money object has the same currency" do
|
207
|
+
it "#<=> compares the two objects' amounts" do
|
208
|
+
(Money.new(1_00, "USD") <=> Money.new(1_00, "USD")).should == 0
|
209
|
+
(Money.new(1_00, "USD") <=> Money.new(99, "USD")).should > 0
|
210
|
+
(Money.new(1_00, "USD") <=> Money.new(2_00, "USD")).should < 0
|
211
|
+
end
|
212
|
+
|
213
|
+
it "#+ adds the other object's amount to the current object's amount while retaining the currency" do
|
214
|
+
(Money.new(10_00, "USD") + Money.new(90, "USD")).should == Money.new(10_90, "USD")
|
215
|
+
end
|
216
|
+
|
217
|
+
it "#- substracts the other object's amount from the current object's amount while retaining the currency" do
|
218
|
+
(Money.new(10_00, "USD") - Money.new(90, "USD")).should == Money.new(9_10, "USD")
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
describe "if the other Money object has a different currency" do
|
223
|
+
it "#<=> compares the two objects' amount after converting the other object's amount to its own currency" do
|
224
|
+
target = Money.new(200_00, "EUR")
|
225
|
+
target.should_receive(:exchange_to).with("USD").and_return(Money.new(300_00, "USD"))
|
226
|
+
(Money.new(100_00, "USD") <=> target).should < 0
|
227
|
+
|
228
|
+
target = Money.new(200_00, "EUR")
|
229
|
+
target.should_receive(:exchange_to).with("USD").and_return(Money.new(100_00, "USD"))
|
230
|
+
(Money.new(100_00, "USD") <=> target).should == 0
|
231
|
+
|
232
|
+
target = Money.new(200_00, "EUR")
|
233
|
+
target.should_receive(:exchange_to).with("USD").and_return(Money.new(99_00, "USD"))
|
234
|
+
(Money.new(100_00, "USD") <=> target).should > 0
|
235
|
+
end
|
236
|
+
|
237
|
+
it "#+ adds the other object's amount, converted to this object's currency, to this object's amount while retaining its currency" do
|
238
|
+
other = Money.new(90, "EUR")
|
239
|
+
other.should_receive(:exchange_to).with("USD").and_return(Money.new(9_00, "USD"))
|
240
|
+
(Money.new(10_00, "USD") + other).should == Money.new(19_00, "USD")
|
241
|
+
end
|
242
|
+
|
243
|
+
it "#- substracts the other object's amount, converted to this object's currency, from this object's amount while retaining its currency" do
|
244
|
+
other = Money.new(90, "EUR")
|
245
|
+
other.should_receive(:exchange_to).with("USD").and_return(Money.new(9_00, "USD"))
|
246
|
+
(Money.new(10_00, "USD") - other).should == Money.new(1_00, "USD")
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
describe "Comparisons involving Numeric objects" do
|
252
|
+
it "#<=> compares integer amounts" do
|
253
|
+
(Money.new(1_00) <=> 1).should == 0
|
254
|
+
(Money.new(1_00) <=> 2).should == -1
|
255
|
+
(Money.new(1_00) <=> -1).should == 1
|
256
|
+
(Money.new(10_01) <=> 10).should == 1
|
257
|
+
end
|
258
|
+
|
259
|
+
it "#<=> compares float amounts" do
|
260
|
+
(Money.new(1_00) <=> 1.0).should == 0
|
261
|
+
(Money.new(1_00) <=> 2.0).should == -1
|
262
|
+
(Money.new(1_00) <=> 0.5).should == 1
|
263
|
+
(Money.new(10_01) <=> 10.0).should == 1
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|