coinage 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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,6 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'coinage/core_ext'
5
+ require 'coinage/exchange'
6
+ require 'coinage/money'
@@ -0,0 +1,4 @@
1
+ require 'coinage/core_ext/array'
2
+ require 'coinage/core_ext/class'
3
+ require 'coinage/core_ext/numeric'
4
+ require 'coinage/core_ext/string'
@@ -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,16 @@
1
+ class Numeric
2
+
3
+ def to_money
4
+ Money.new(self)
5
+ end
6
+
7
+ alias :cents :to_money
8
+ alias :cent :to_money
9
+
10
+ def dollars
11
+ Money.new(self*100)
12
+ end
13
+
14
+ alias :dollar :dollars
15
+
16
+ end
@@ -0,0 +1,7 @@
1
+ class String
2
+
3
+ def to_money
4
+ Money.new(self)
5
+ end
6
+
7
+ end
@@ -0,0 +1,5 @@
1
+ require 'coinage/exchange/base'
2
+ require 'coinage/exchange/none'
3
+ require 'coinage/exchange/variable'
4
+ require 'coinage/exchange/yahoo'
5
+ require 'coinage/exchange/xurrency'
@@ -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,14 @@
1
+ module Coinage
2
+ module Exchange
3
+ class None
4
+
5
+ include Base
6
+
7
+ def rate(current, target)
8
+ super(current, target)
9
+ 1
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ module Coinage
2
+ module Exchange
3
+ class Variable
4
+
5
+ include Base
6
+
7
+ def rate(current, target)
8
+ validate_currencies(current, target)
9
+ 2*rand
10
+ end
11
+
12
+ end
13
+ end
14
+ 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,9 @@
1
+ module Coinage
2
+ module Exchange
3
+ class Yahoo
4
+
5
+ include Base
6
+
7
+ end
8
+ end
9
+ 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
+