coinage 0.2.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/LICENSE +20 -0
- data/README.textile +140 -0
- data/Rakefile +64 -0
- data/lib/coinage.rb +6 -0
- data/lib/coinage/core_ext.rb +4 -0
- data/lib/coinage/core_ext/array.rb +16 -0
- data/lib/coinage/core_ext/class.rb +54 -0
- data/lib/coinage/core_ext/numeric.rb +16 -0
- data/lib/coinage/core_ext/string.rb +7 -0
- data/lib/coinage/exchange.rb +5 -0
- data/lib/coinage/exchange/base.rb +23 -0
- data/lib/coinage/exchange/none.rb +14 -0
- data/lib/coinage/exchange/variable.rb +14 -0
- data/lib/coinage/exchange/xurrency.rb +30 -0
- data/lib/coinage/exchange/yahoo.rb +9 -0
- data/lib/coinage/money.rb +168 -0
- metadata +81 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008 Chris Lloyd
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,140 @@
|
|
1
|
+
Handle and convert your Money!
|
2
|
+
|
3
|
+
Think of Coinage as a weedy, overworked guy. Amongst other things, this man enjoys exercising Biro's so that his collection's ink levels, when ordered, form a sine wave. So yeah, he also likes the finer points of money. So does Coinage.
|
4
|
+
|
5
|
+
Here's what you want to know. Coinage allows for: simple conversion between currencies; reliable monetary arithmetic; multi-national input parsing; easy Rails & Merb money handling.
|
6
|
+
|
7
|
+
This is _not_ meant as a replacement for "ActiveMerchant":http://www.activemerchant.org/, it _is_, however, meant as a replacement for "Money":http://dist.leetsoft.com/api/money/.
|
8
|
+
|
9
|
+
*WARNING:* To quote 1998, this software is still very much "under construction". I really wouldn't recommend using it in production until it reaches 1.0.
|
10
|
+
|
11
|
+
|
12
|
+
h2. Install
|
13
|
+
|
14
|
+
sudo gem install coinage
|
15
|
+
|
16
|
+
|
17
|
+
h2. Usage
|
18
|
+
|
19
|
+
require 'coinage'
|
20
|
+
|
21
|
+
a = 5.dollars
|
22
|
+
|
23
|
+
b = Money.new(5, :aud)
|
24
|
+
|
25
|
+
a + b
|
26
|
+
|
27
|
+
Ok, lets work through this example. First off, you have to require Coinage. @a@ is then created as a new @Money@ instance with the value of $5. Whenever new @Money@ instances are created without specifying a currency (like in the second line) the currency defaults to United States Dollars. This can be changed as follows:
|
28
|
+
|
29
|
+
Money.default_currency = :aud
|
30
|
+
|
31
|
+
The third line creates a new @Money@ instance but this time in Australian Dollars. Note that @a@ does not necessarily equal @b@ as it is unlikely that the two currencies will ever reach parity.
|
32
|
+
|
33
|
+
Finally, @a + b@ adds the two variables together. Out of the box this should return a new @Money@ object with value USD$10. It is in US Dollars as any arithmetic operator sets the result's currency to the first operand's currency. The result has a value of 10 dollars because by default, Coinage uses the @Coinage::Exchange::None@ exchange, which always provides an exchange rate of 1. This default will change as the other exchanges become more stable.
|
34
|
+
|
35
|
+
The default exchange can be changed:
|
36
|
+
|
37
|
+
Money.default_exchange = Coinage::Exchange::Xurrency.new
|
38
|
+
|
39
|
+
h3. ActiveRecord
|
40
|
+
|
41
|
+
For the moment, its not nat pretty integrating Coinage with money, but its quite easy:
|
42
|
+
|
43
|
+
composed_of :price, :class_name => 'Money', :mapping => %w(price_in_cents currency) do |price|
|
44
|
+
price.to_money
|
45
|
+
end
|
46
|
+
|
47
|
+
That creates a virtual property, @value@, which maps to the database columns @price_in_cents@ and @currency@. Coinage stores all values as cents. It also creates a setter method, @value=@ which can accept a string so in your view you can have something like:
|
48
|
+
|
49
|
+
<%= f.text_field :value %>
|
50
|
+
|
51
|
+
That will work with mass assignment (read: out of the box) and deal with storing all monetary values. Automagic!
|
52
|
+
|
53
|
+
h3. Defining Custom Exchanges
|
54
|
+
|
55
|
+
Say you have MaiSpecialAPI giving you currency rates and you want to use that instead of Xurrency or Yahoo. Just create a new exchange like the one below and put all your code in the @rate@ method.
|
56
|
+
|
57
|
+
class MaiSpecialExchange
|
58
|
+
|
59
|
+
include Coinage::Exchange::Base
|
60
|
+
|
61
|
+
def rate(current, target)
|
62
|
+
super(current, target) # Verifies that both currencies are supported by your exchange
|
63
|
+
|
64
|
+
MaiSpecial.get(current, target)
|
65
|
+
|
66
|
+
end
|
67
|
+
|
68
|
+
def supported_currencies
|
69
|
+
[ :lolz, :catz ]
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
Then just set
|
75
|
+
|
76
|
+
Money.default_exchange = MaiSpecialExchange.new
|
77
|
+
|
78
|
+
and Wallah! Your all set.
|
79
|
+
|
80
|
+
|
81
|
+
h2. Notes
|
82
|
+
|
83
|
+
* _Namespaces_: While all of Coinage's internal classes are defined in the @Coinage@ namespace, the @Money@ class is defined in the global namespace.
|
84
|
+
* _Class Extension_: @Numeric@ and @String@ are given methods such as @:to_money@ and @:cents@.
|
85
|
+
* _Commutativity_: With two Money instances, @a + b != b + a@ if @a@ and @b@ have different currencies. This is because currencies are not quantitative and favors the instance acted upon (@a@) rather than the passed instance (@b@).
|
86
|
+
* _Supported Currencies_: Are currently hard coded. Not the friendliest interface to work with, but it should work for the most part.
|
87
|
+
|
88
|
+
|
89
|
+
h2. Plans
|
90
|
+
|
91
|
+
* _Cache exchange calls_: This will provide a major speed-up. Cache to a few different back-ends (flat file, memcached, db) so that calls only have to be made at a defined frequency, rather than every time a conversion is needed. Rake tasks will also be provided if you want to update this cache externally.
|
92
|
+
* _More exchange APIs_: At the moment it only supports the Xurrency API. I plan on adding support for Yahoo and XE Premium (when I get a sample response). It is very easy to add your own API back-ends.
|
93
|
+
* _Use libxml-ruby_: By far the fastest XML parser out there, it currently is fairly undocumented. Definitely switch to it for parsing exchange data.
|
94
|
+
* _Rails 2.2 Integration_: When 2.2 comes out Coinage will be there supporting the new i18n & L10n features in Rails core.
|
95
|
+
* _ActiveRecord & DataMapper support_: Provide a class method which will make it easier to transform ordinary fields into Money objects.
|
96
|
+
* _String Parsing_: Be able to parse a wide variety of user input such as: 'AUD$50', '$50 AUD', '€50', '€50,99', '50,000.00' and '50.000,00'.
|
97
|
+
|
98
|
+
|
99
|
+
h2. Authors
|
100
|
+
|
101
|
+
* "Chris Lloyd":http://chrislloyd.com.au
|
102
|
+
* You
|
103
|
+
|
104
|
+
|
105
|
+
h2. Contribute
|
106
|
+
|
107
|
+
You can checkout the source or fork it yourself from Github.
|
108
|
+
|
109
|
+
git clone git://github.com/chrislloyd/coinage.git
|
110
|
+
|
111
|
+
If you submit a successful patch then you'll be given full commit rights to the project.
|
112
|
+
|
113
|
+
|
114
|
+
h2. Bugs
|
115
|
+
|
116
|
+
Bug tracking is handled by "Ditz":http://ditz.rubyforge.org.
|
117
|
+
|
118
|
+
|
119
|
+
h2. License
|
120
|
+
|
121
|
+
Copyright (c) 2008 Chris Lloyd.
|
122
|
+
|
123
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
124
|
+
a copy of this software and associated documentation files (the
|
125
|
+
'Software'), to deal in the Software without restriction, including
|
126
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
127
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
128
|
+
permit persons to whom the Software is furnished to do so, subject to
|
129
|
+
the following conditions:
|
130
|
+
|
131
|
+
The above copyright notice and this permission notice shall be
|
132
|
+
included in all copies or substantial portions of the Software.
|
133
|
+
|
134
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
135
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
136
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
137
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
138
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
139
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
140
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake/gempackagetask'
|
3
|
+
require 'rubygems/specification'
|
4
|
+
require 'spec/rake/spectask'
|
5
|
+
require 'date'
|
6
|
+
|
7
|
+
GEM = "coinage"
|
8
|
+
GEM_VERSION = "0.2.0"
|
9
|
+
AUTHOR = "Chris Lloyd"
|
10
|
+
EMAIL = "christopher.lloyd@gmail.com"
|
11
|
+
HOMEPAGE = "http://github.com/chrislloyd/coinage"
|
12
|
+
SUMMARY = "Handle and convert your Money!"
|
13
|
+
|
14
|
+
spec = Gem::Specification.new do |s|
|
15
|
+
s.name = GEM
|
16
|
+
s.version = GEM_VERSION
|
17
|
+
s.platform = Gem::Platform::RUBY
|
18
|
+
s.rubyforge_project = GEM
|
19
|
+
s.has_rdoc = true
|
20
|
+
s.extra_rdoc_files = ['README.textile', 'LICENSE']
|
21
|
+
s.summary = SUMMARY
|
22
|
+
s.description = s.summary
|
23
|
+
s.author = AUTHOR
|
24
|
+
s.email = EMAIL
|
25
|
+
s.homepage = HOMEPAGE
|
26
|
+
|
27
|
+
# Uncomment this to add a dependency
|
28
|
+
# s.add_dependency "foo"
|
29
|
+
s.add_dependency 'hpricot'
|
30
|
+
|
31
|
+
s.require_path = 'lib'
|
32
|
+
s.files = %w(LICENSE README.textile Rakefile) + Dir.glob("{lib,specs}/**/*")
|
33
|
+
end
|
34
|
+
|
35
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
36
|
+
pkg.gem_spec = spec
|
37
|
+
end
|
38
|
+
|
39
|
+
task :default => :spec
|
40
|
+
task :specs => :spec
|
41
|
+
|
42
|
+
desc "Run all examples"
|
43
|
+
Spec::Rake::SpecTask.new('spec') do |t|
|
44
|
+
t.spec_opts = ['--options', 'spec/spec.opts']
|
45
|
+
t.spec_files = FileList['spec/**/**/*.rb']
|
46
|
+
t.rcov = true
|
47
|
+
t.rcov_opts = ['--exclude',"#{GEM}\\.rb,spec/,rspec-*,rcov-*"]
|
48
|
+
end
|
49
|
+
|
50
|
+
desc "install the gem locally"
|
51
|
+
task :install => [:package] do
|
52
|
+
sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
|
53
|
+
end
|
54
|
+
|
55
|
+
desc "create a gemspec file"
|
56
|
+
task :make_spec do
|
57
|
+
File.open("#{GEM}.gemspec", "w") do |file|
|
58
|
+
file.puts spec.to_ruby
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
task :bugs do
|
63
|
+
sh %{ditz html ; open html/index.html} if PLATFORM == 'universal-darwin9.0'
|
64
|
+
end
|
data/lib/coinage.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
class Array
|
2
|
+
|
3
|
+
# Extracts options from a set of arguments. Removes and returns the last
|
4
|
+
# element in the array if it's a hash, otherwise returns a blank hash.
|
5
|
+
#
|
6
|
+
# def options(*args)
|
7
|
+
# args.extract_options!
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# options(1, 2) # => {}
|
11
|
+
# options(1, 2, :a => :b) # => {:a=>:b}
|
12
|
+
def extract_options!
|
13
|
+
last.is_a?(::Hash) ? pop : {}
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# Extends the class object with class and instance accessors for class attributes,
|
2
|
+
# just like the native attr* accessors for instance attributes.
|
3
|
+
#
|
4
|
+
# class Person
|
5
|
+
# cattr_accessor :hair_colors
|
6
|
+
# end
|
7
|
+
#
|
8
|
+
# Person.hair_colors = [:brown, :black, :blonde, :red]
|
9
|
+
class Class
|
10
|
+
def cattr_reader(*syms)
|
11
|
+
syms.flatten.each do |sym|
|
12
|
+
next if sym.is_a?(Hash)
|
13
|
+
class_eval(<<-EOS, __FILE__, __LINE__)
|
14
|
+
unless defined? @@#{sym}
|
15
|
+
@@#{sym} = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.#{sym}
|
19
|
+
@@#{sym}
|
20
|
+
end
|
21
|
+
|
22
|
+
def #{sym}
|
23
|
+
@@#{sym}
|
24
|
+
end
|
25
|
+
EOS
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def cattr_writer(*syms)
|
30
|
+
options = syms.extract_options!
|
31
|
+
syms.flatten.each do |sym|
|
32
|
+
class_eval(<<-EOS, __FILE__, __LINE__)
|
33
|
+
unless defined? @@#{sym}
|
34
|
+
@@#{sym} = nil
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.#{sym}=(obj)
|
38
|
+
@@#{sym} = obj
|
39
|
+
end
|
40
|
+
|
41
|
+
#{"
|
42
|
+
def #{sym}=(obj)
|
43
|
+
@@#{sym} = obj
|
44
|
+
end
|
45
|
+
" unless options[:instance_writer] == false }
|
46
|
+
EOS
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def cattr_accessor(*syms)
|
51
|
+
cattr_reader(*syms)
|
52
|
+
cattr_writer(*syms)
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Coinage
|
2
|
+
module Exchange
|
3
|
+
module Base
|
4
|
+
|
5
|
+
def rate(current, target)
|
6
|
+
validate_currencies(current, target)
|
7
|
+
end
|
8
|
+
|
9
|
+
def supported_currencies
|
10
|
+
[:usd, :aud, :sgd]
|
11
|
+
end
|
12
|
+
|
13
|
+
def validate_currency(*currencies)
|
14
|
+
currencies.each do |currency|
|
15
|
+
raise ArgumentError unless supported_currencies.include?(currency)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
alias :validate_currencies :validate_currency
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
# Would use this, but it only reads in from a file!
|
3
|
+
# require 'libxml'
|
4
|
+
require 'open-uri'
|
5
|
+
require 'hpricot'
|
6
|
+
|
7
|
+
module Coinage
|
8
|
+
module Exchange
|
9
|
+
class Xurrency
|
10
|
+
|
11
|
+
include Base
|
12
|
+
|
13
|
+
def rate(current, target)
|
14
|
+
super(current, target)
|
15
|
+
uri = "http://xurrency.com/1/#{current}/#{target}/feed"
|
16
|
+
|
17
|
+
(Hpricot(open(uri).read)/'dc:value').inner_html
|
18
|
+
end
|
19
|
+
|
20
|
+
# From:
|
21
|
+
# http://xurrency.com/currencies
|
22
|
+
def supported_currencies
|
23
|
+
[ :ars, :aud, :brl, :bgn, :cad, :cny, :cop, :hrk, :czk, :dkk, :eek, :eur, :hkd, :huf, :isk, :inr,
|
24
|
+
:jpy, :krw, :lvl, :ltl, :myr, :mxn, :ron, :try, :nzd, :nok, :php, :pln, :gbp, :rub, :sgd, :skk,
|
25
|
+
:zar, :lkr, :sek, :chf, :twd, :thb, :usd, :vef ]
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
class Money
|
2
|
+
|
3
|
+
@@default_currency = :usd
|
4
|
+
cattr_accessor :default_currency
|
5
|
+
|
6
|
+
@@default_exchange = Coinage::Exchange::None.new
|
7
|
+
cattr_accessor :default_exchange
|
8
|
+
|
9
|
+
attr_accessor :cents, :currency
|
10
|
+
attr_writer :exchange
|
11
|
+
|
12
|
+
def initialize(cents=0, currency=self.class.default_currency)
|
13
|
+
@currency = currency
|
14
|
+
|
15
|
+
case cents
|
16
|
+
when Float
|
17
|
+
@cents = cents_from_float(cents)
|
18
|
+
when String
|
19
|
+
@cents = cents_from_string(cents)
|
20
|
+
else
|
21
|
+
@cents = cents.to_i
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def zero?
|
27
|
+
cents.zero?
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_money
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_i
|
35
|
+
cents
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_f
|
39
|
+
cents.to_f/100
|
40
|
+
end
|
41
|
+
|
42
|
+
def dollars
|
43
|
+
(cents/100).round
|
44
|
+
end
|
45
|
+
|
46
|
+
def round
|
47
|
+
(cents.to_f/100).round
|
48
|
+
end
|
49
|
+
|
50
|
+
def exchange
|
51
|
+
@exchange || self.class.default_exchange
|
52
|
+
end
|
53
|
+
|
54
|
+
def exchange_to!(target)
|
55
|
+
raise ArgumentError, "#{exchange.class} does not support conversions from #{currency.to_s.upcase}" unless exchange.supported_currencies.include?(currency)
|
56
|
+
raise ArgumentError, "#{exchange.class} does not support conversions to #{target.to_s.upcase}" unless exchange.supported_currencies.include?(target)
|
57
|
+
unless currency == target
|
58
|
+
self.cents, self.currency = cents * exchange.rate(currency, target), target
|
59
|
+
end
|
60
|
+
self
|
61
|
+
end
|
62
|
+
|
63
|
+
def exchange_to(target)
|
64
|
+
dup.exchange_to!(target)
|
65
|
+
end
|
66
|
+
|
67
|
+
def *(target)
|
68
|
+
case target
|
69
|
+
when Numeric
|
70
|
+
self.class.new( cents * target, currency )
|
71
|
+
when Money
|
72
|
+
self.class.new( cents * target.exchange_to(currency).cents, currency )
|
73
|
+
else
|
74
|
+
raise ArgumentError
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def /(target)
|
79
|
+
case target
|
80
|
+
when Numeric
|
81
|
+
self.class.new( cents / target, currency )
|
82
|
+
when Money
|
83
|
+
self.class.new( cents / target.exchange_to(currency).cents, currency )
|
84
|
+
else
|
85
|
+
raise ArgumentError
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def +(target)
|
90
|
+
case target
|
91
|
+
when Numeric
|
92
|
+
self.class.new( cents + target, currency )
|
93
|
+
when Money
|
94
|
+
self.class.new( cents + target.exchange_to(currency).cents, currency )
|
95
|
+
else
|
96
|
+
raise ArgumentError
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def -(target)
|
101
|
+
case target
|
102
|
+
when Numeric
|
103
|
+
self.class.new( cents - target, currency )
|
104
|
+
when Money
|
105
|
+
self.class.new( cents - target.exchange_to(currency).cents, currency )
|
106
|
+
else
|
107
|
+
raise ArgumentError
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Uses == instead of .eql? to perform a type conversion between Float and Fixnum
|
112
|
+
def eql?(target)
|
113
|
+
case target
|
114
|
+
when Numeric
|
115
|
+
cents == target
|
116
|
+
when Money
|
117
|
+
if currency == target.currency
|
118
|
+
cents == target.cents
|
119
|
+
else
|
120
|
+
cents == target.exchange_to(currency).cents
|
121
|
+
end
|
122
|
+
else
|
123
|
+
false
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
alias :== :eql?
|
128
|
+
alias :equal? :eql?
|
129
|
+
|
130
|
+
def method_missing(method, *arguments)
|
131
|
+
if new_currency = extract_currency_from_method(method)
|
132
|
+
return exchange_to(new_currency)
|
133
|
+
else
|
134
|
+
super
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def coerce(other)
|
139
|
+
return self, other
|
140
|
+
end
|
141
|
+
|
142
|
+
def to_s
|
143
|
+
to_f.to_s
|
144
|
+
end
|
145
|
+
|
146
|
+
# def <=>
|
147
|
+
|
148
|
+
private
|
149
|
+
|
150
|
+
def cents_from_float(cents)
|
151
|
+
cents.round.to_i
|
152
|
+
end
|
153
|
+
|
154
|
+
def cents_from_string(cents, seperator='.')
|
155
|
+
if cents.include?(seperator)
|
156
|
+
cents.delete(seperator).to_i
|
157
|
+
else
|
158
|
+
cents.to_i*100
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def extract_currency_from_method(method)
|
163
|
+
return nil unless method = method.to_s.match(/^to_[[:lower:]]+$/)
|
164
|
+
expected_currency = method[0].delete('to_').to_sym
|
165
|
+
exchange.supported_currencies.include?(expected_currency) ? expected_currency : nil
|
166
|
+
end
|
167
|
+
|
168
|
+
end
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: coinage
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chris Lloyd
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-09-02 00:00:00 +10:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hpricot
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
description: Handle and convert your Money!
|
26
|
+
email: christopher.lloyd@gmail.com
|
27
|
+
executables: []
|
28
|
+
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README.textile
|
33
|
+
- LICENSE
|
34
|
+
files:
|
35
|
+
- LICENSE
|
36
|
+
- README.textile
|
37
|
+
- Rakefile
|
38
|
+
- lib/coinage
|
39
|
+
- lib/coinage/core_ext
|
40
|
+
- lib/coinage/core_ext/array.rb
|
41
|
+
- lib/coinage/core_ext/class.rb
|
42
|
+
- lib/coinage/core_ext/numeric.rb
|
43
|
+
- lib/coinage/core_ext/string.rb
|
44
|
+
- lib/coinage/core_ext.rb
|
45
|
+
- lib/coinage/exchange
|
46
|
+
- lib/coinage/exchange/base.rb
|
47
|
+
- lib/coinage/exchange/none.rb
|
48
|
+
- lib/coinage/exchange/variable.rb
|
49
|
+
- lib/coinage/exchange/xurrency.rb
|
50
|
+
- lib/coinage/exchange/yahoo.rb
|
51
|
+
- lib/coinage/exchange.rb
|
52
|
+
- lib/coinage/money.rb
|
53
|
+
- lib/coinage.rb
|
54
|
+
has_rdoc: true
|
55
|
+
homepage: http://github.com/chrislloyd/coinage
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options: []
|
58
|
+
|
59
|
+
require_paths:
|
60
|
+
- lib
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: "0"
|
66
|
+
version:
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: "0"
|
72
|
+
version:
|
73
|
+
requirements: []
|
74
|
+
|
75
|
+
rubyforge_project: coinage
|
76
|
+
rubygems_version: 1.2.0
|
77
|
+
signing_key:
|
78
|
+
specification_version: 2
|
79
|
+
summary: Handle and convert your Money!
|
80
|
+
test_files: []
|
81
|
+
|