exchange 0.9.0 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- exchange (0.9.0)
4
+ exchange (0.10.0)
5
5
  json (>= 1.0.0)
6
6
 
7
7
  GEM
data/README.rdoc CHANGED
@@ -7,21 +7,24 @@ You can use it with just plain ruby projects, in Rails 2 and 3, Sinatra or whate
7
7
  == Installation
8
8
  === Bundler / Rails
9
9
  Add it to your Gemfile
10
- gem "exchange", ">=0.8.0"
10
+ gem "exchange", ">=0.10.0"
11
11
  === Manually
12
12
  Just install it as a gem
13
13
  gem install exchange
14
14
  Then require it
15
15
  require 'exchange'
16
+
17
+ === Deprecations in 0.10.0
18
+ The old gem API (3.eur, 5.usd.to_chf) has been abandoned in favor of the new (1.in(:eur), 5.in(:usd).to(:chf)). This resolves a conflict of the earlier versions with the active support "try" method, which also stands for turkish lira. It also makes dynamic instantiation of currencies easier (in(currency) is better than send(currency), to(currency) much better than send(:"to_#{currency}")) Version 0.9.0 still supports the old API, so if you don't have the time to change now, just continue to use the old version and change later. There are no other changes than the api and no bugfixes from 0.9.0 to 0.10.0.
16
19
 
17
20
  == Features
18
21
 
19
22
  === Easy Conversion
20
23
 
21
24
  Conversion of currencies does not get any easier
22
- 1.eur.to_usd
25
+ 1.in(:eur).to(:usd)
23
26
  or better for historic dates
24
- 1.eur.to_usd(:at => Time.now - 84600)
27
+ 1.in(:eur).to(:usd, :at => Time.now - 84600)
25
28
 
26
29
 
27
30
  === Precise Calculation
@@ -36,8 +39,8 @@ Whereas
36
39
 
37
40
  Exchange uses BigDecimal in all its currency and conversion operations, so you will not be a likely victim for floating point inaccuracies. It even does implicitly convert counterparts of basic operations like (* / - +) to calculate your values safely:
38
41
 
39
- (50.usd * 0.29).round 0 #=> "USD 15.00"
40
- (0.29 * 50.usd).round 0 #=> 15
42
+ (50.in(:usd) * 0.29).round 0 #=> "USD 15.00"
43
+ (0.29 * 50.in(:usd)).round 0 #=> 15
41
44
 
42
45
  === BigDecimal? Sounds slow to me
43
46
 
@@ -76,7 +79,7 @@ Now you're able to do this:
76
79
 
77
80
  article = Article.new
78
81
  article.price #=> will give you money in the currency defined on the manager
79
- article.price = 3.45.usd #Will implicitly convert the price if manager.currency is not :usd
82
+ article.price = 3.45.in(:usd) #Will implicitly convert the price if manager.currency is not :usd
80
83
 
81
84
  You can feed the currency option with a proc or a symbol representing the method. For in-depth information about typecasting {visit the documentation here}[http://rubydoc.info/github/beatrichartz/exchange/Exchange/Typecasting]
82
85
 
@@ -85,12 +88,12 @@ You can feed the currency option with a proc or a symbol representing the method
85
88
  You're hitting the internet only daily to get new rates (hourly updates are available if you're eager to have the absolutely newest ones)
86
89
 
87
90
  === ISO 4217 Currency formatting
88
- On of the issues with currencies is: You never know the format they should be in. With Exchange, you can just use the currencies
91
+ One of the issues with currencies is: You never know the format they should be in. With Exchange, you can just use the currencies
89
92
  to_s method, which takes care of the right format for you. You can either have a string with the currency code in front, or just the amount in the right format
90
93
 
91
- 49.567.usd.to_s #=> "USD 49.57"
92
- 45.jpy.to_s #=> "JPY 45"
93
- 34.34.omr.to_s #=> "OMR 34.340"
94
+ 49.567.in(:usd).to_s #=> "USD 49.57"
95
+ 45.in(:jpy).to_s #=> "JPY 45"
96
+ 34.34.in(:omr).to_s #=> "OMR 34.340"
94
97
 
95
98
  === Use three great APIs or your own
96
99
 
@@ -120,88 +123,88 @@ But, same here, if you don't like any of these or want to use your own caching s
120
123
 
121
124
  Converting one currency to another is as easy as 1,2,3. Don't be afraid, even if it returns a currency object, all Fixed and Float operations can be applied as method missing routes to the value
122
125
 
123
- 1.usd.to_eur #=> #<Exchange::Money @value=0.93 @currency=:eur>
124
- 2.3.dkk.to_sek #=> #<Exchange::Money @value=3.33 @currency=:sek>
125
- 45.54.nok.to_sek #=> #<Exchange::Money @value=3.33 @currency=:sek>
126
+ 1.in(:usd).to(:eur) #=> #<Exchange::Money @value=0.93 @currency=:eur>
127
+ 2.3.in(:dkk).to(:sek) #=> #<Exchange::Money @value=3.33 @currency=:sek>
128
+ 45.54.in(:nok).to(:sek) #=> #<Exchange::Money @value=3.33 @currency=:sek>
126
129
 
127
130
  Easily convert one currency to another at a historical rate
128
131
 
129
- 1.52.usd.to_eur :at => '2011-01-01' #=> #<Exchange::Money @value=1.23 @currency=:eur>
130
- 3.45.eur.to_sek :at => Time.gm(2011,3,3) #=> #<Exchange::Money @value=19.23 @currency=:sek>
131
- 345.sek.to_nok :at => Time.gm(2011,3,3) #=> #<Exchange::Money @value=348 @currency=:nok>
132
+ 1.52.in(:usd).to :eur, :at => '2011-01-01' #=> #<Exchange::Money @value=1.23 @currency=:eur>
133
+ 3.45.in(:eur).to :sek, :at => Time.gm(2011,3,3) #=> #<Exchange::Money @value=19.23 @currency=:sek>
134
+ 345.in(:sek).to :nok, :at => Time.gm(2011,3,3) #=> #<Exchange::Money @value=348 @currency=:nok>
132
135
 
133
136
  Or even define an instance of currency as historic by adding a time.
134
137
 
135
- 1.52.usd(:at => '2011-01-01').to_eur #=> #<Exchange::Money @value=1.23 @currency=:eur>
136
- 3.45.eur(:at => Time.gm(2011,3,3)).to_sek #=> #<Exchange::Money @value=19.23 @currency=:sek>
137
- 345.sek(:at => Time.gm(2011,3,3)).to_nok #=> #<Exchange::Money @value=348 @currency=:nok>
138
+ 1.52.in(:usd, :at => '2011-01-01').to(:eur) #=> #<Exchange::Money @value=1.23 @currency=:eur>
139
+ 3.45.in(:usd, :at => Time.gm(2011,3,3)).to(:sek) #=> #<Exchange::Money @value=19.23 @currency=:sek>
140
+ 345.in(:usd, :at => Time.gm(2011,3,3)).to(:nok) #=> #<Exchange::Money @value=348 @currency=:nok>
138
141
 
139
142
  Do multiple conversion steps at once (if in any way useful)
140
143
 
141
- 3.chf.to_eur(:at => '2011-02-04').to_usd #=> #<Exchange::Money @value=5.3 @currency=:eur>
144
+ 3.in(:chf).to(:eur, :at => '2011-02-04').to(:usd) #=> #<Exchange::Money @value=5.3 @currency=:eur>
142
145
 
143
146
 
144
147
  === Compare
145
148
 
146
149
  Compare Currencies, they will convert implicitly
147
150
 
148
- 2.eur > 2.usd #=> true (2.usd get converted to eur and compared)
149
- 2.nok < 2.sek #=> false (2.sek get converted to nok and compared)
150
- 5.eur == 4.34.chf #=> true
151
- 50.eur == 4.34.chf #=> false
152
- 50.eur.to_sek == 50.eur #=> true
153
- 50.eur(:at => '2011-1-1') == 50.sek #=> false
151
+ 2.in(:eur) > 2.in(:usd) #=> true (2.in(:usd) get converted to eur and compared)
152
+ 2.in(:nok) < 2.in(:sek) #=> false (2.in(:sek) get converted to nok and compared)
153
+ 5.in(:eur) == 4.34.in(:chf) #=> true
154
+ 50.in(:eur) == 4.34.in(:chf) #=> false
155
+ 50.in(:eur).to(:sek) == 50.in(:eur) #=> true
156
+ 50.in(:eur, :at => '2011-1-1') == 50.in(:sek) #=> false
154
157
 
155
158
  Sort multiple currencies at once
156
159
 
157
- [5.eur, 4.usd, 4.chf(:at => '2010-01-01')].sort #=> [#<Exchange::Money @value=4 @currency=:usd>, #<Exchange::Money @value=4 @currency=:chf>, #<Exchange::Money @value=5 @currency=:eur>]
160
+ [5.in(:eur), 4.in(:usd), 4.in(:chf, :at => '2010-01-01')].sort #=> [#<Exchange::Money @value=4 @currency=:usd>, #<Exchange::Money @value=4 @currency=:chf>, #<Exchange::Money @value=5 @currency=:eur>]
158
161
 
159
162
  This is true, because it uses the same historic conversion rate
160
163
 
161
- 3.eur(:at => '201-01-01').to_usd == 3.eur.to_usd(:at => '201-01-01')
164
+ 3.in(:eur, :at => '201-01-01').to(:usd) == 3.in(:eur).to(:usd, :at => '201-01-01')
162
165
 
163
166
  But this is false, obviously, because the second instance uses the present exchange rate which differs from the historic one (if the two rates match, this will be true again)
164
167
 
165
- 3.eur(:at => '2001-01-01').to_usd == 3.eur.to_usd
168
+ 3.in(:eur, :at => '2001-01-01').to(:usd) == 3.in(:eur).to(:usd)
166
169
 
167
170
  === Operate
168
171
 
169
172
  Add, Subtract, Multiply, Divide Currencies and don't lose a dime. The result will get returned in the currency of the first argument
170
173
 
171
- 1.usd + 1.32.eur #=> #<Exchange::Money @value=2.54 @currency=:usd>
172
- 1.usd - 1.32.eur #=> #<Exchange::Money @value=-0.2 @currency=:usd>
173
- 1.usd * 1.32.eur #=> #<Exchange::Money @value=3.44 @currency=:usd>
174
- 1.usd / 1.32.eur #=> #<Exchange::Money @value=0.89 @currency=:usd>
174
+ 1.in(:usd) + 1.32.in(:eur) #=> #<Exchange::Money @value=2.54 @currency=:usd>
175
+ 1.in(:usd) - 1.32.in(:eur) #=> #<Exchange::Money @value=-0.2 @currency=:usd>
176
+ 1.in(:usd) * 1.32.in(:eur) #=> #<Exchange::Money @value=3.44 @currency=:usd>
177
+ 1.in(:usd) / 1.32.in(:eur) #=> #<Exchange::Money @value=0.89 @currency=:usd>
175
178
 
176
179
 
177
180
  If you define a currency object as historic. It will use historic conversion if it gets converted (in this example, the 1.32 eur will get converted to usd at the rate of January 1 2008)
178
181
 
179
- 1.usd - 1.32.eur(:at => '2008-1-1') #=> #<Exchange::Money @value=2.54 @currency=:usd>
182
+ 1.in(:usd) - 1.32.in(:eur, :at => '2008-1-1') #=> #<Exchange::Money @value=2.54 @currency=:usd>
180
183
 
181
184
  You can just instantiate currencies and apply operations. Rounding will by default round the currency to its ISO4217 decimal precision:
182
185
 
183
- 3.123.eur.round #=> #<Exchange::Money @value=3.12 @currency=:eur>
186
+ 3.123.in(:eur).round #=> #<Exchange::Money @value=3.12 @currency=:eur>
184
187
 
185
188
  You can also pass the precision you wish for as an argument, round, ceil, floor act like normal:
186
189
 
187
- 3.1234.eur.round(0) #=> #<Exchange::Money @value=3 @currency=:eur>
190
+ 3.1234.in(:eur).round(0) #=> #<Exchange::Money @value=3 @currency=:eur>
188
191
 
189
192
  Convert one currency to another and round, ceil or floor it, it still retains currency information of the actual and previous currency
190
193
 
191
- 1.34.usd.to_eur.round(0) #=> #<Exchange::Money @value=1 @currency=:eur>
192
- 10.34.usd.to_nok.ceil(0) #=> #<Exchange::Money @value=45 @currency=:nok>
193
- 5.34.usd.to_eur.floor(0) #=> #<Exchange::Money @value=4 @currency=:eur>
194
- 5.34.usd.to_eur.floor.from #=> #<Exchange::Money @value=5.34 @currency=:usd>
194
+ 1.34.in(:usd).to(:eur).round(0) #=> #<Exchange::Money @value=1 @currency=:eur>
195
+ 10.34.in(:usd).to(:nok).ceil(0) #=> #<Exchange::Money @value=45 @currency=:nok>
196
+ 5.34.in(:usd).to(:eur).floor(0) #=> #<Exchange::Money @value=4 @currency=:eur>
197
+ 5.34.in(:usd).to(:eur).floor.from #=> #<Exchange::Money @value=5.34 @currency=:usd>
195
198
 
196
199
 
197
200
  === Retain Information
198
201
 
199
202
  Access the original currency and its value after conversion, even over multiple steps
200
203
 
201
- converted = 2.eur.to_usd #=> #<Exchange::Money @value=2.12 @currency=:usd>
202
- converted.from #=> #<Exchange::Money @value=2 @currency=:eur>
203
- converted2 = converted.to_nok #=> #<Exchange::Money @value=22.12 @currency=:nok>
204
- converted2.from #=> #<Exchange::Money @value=2.12 @currency=:usd>
204
+ converted = 2.in(:eur).to(:usd) #=> #<Exchange::Money @value=2.12 @currency=:usd>
205
+ converted.from #=> #<Exchange::Money @value=2 @currency=:eur>
206
+ converted2 = converted.to(:nok) #=> #<Exchange::Money @value=22.12 @currency=:nok>
207
+ converted2.from #=> #<Exchange::Money @value=2.12 @currency=:usd>
205
208
 
206
209
  == Configuration
207
210
 
@@ -232,11 +235,20 @@ The options available are
232
235
 
233
236
  If you're afraid of mixed currency operations, just don't allow them
234
237
  Exchange.configuration.allow_mixed_operations = false
235
- 1.usd + 1.eur #=> MixedCurrencyError
238
+ 1.in(:usd) + 1.in(:eur) #=> MixedCurrencyError
236
239
 
237
240
  === Caching Options
238
241
 
239
- Use Memcached to cache the result (default). Exchange will cache the API files with a key starting with 'exchange_'
242
+ In Key/Value stores, exchange will cache the API files with a key starting with 'exchange_'
243
+
244
+ Use Memory to cache the result (default).
245
+ Exchange.configuration = Exchange::Configuration.new do |c|
246
+ c.cache = {
247
+ :subclass => :memory
248
+ }
249
+ end
250
+
251
+ Use Memcached to cache the result.
240
252
  Exchange.configuration = Exchange::Configuration.new do |c|
241
253
  c.cache = {
242
254
  :subclass => :memcached,
@@ -245,7 +257,7 @@ Use Memcached to cache the result (default). Exchange will cache the API files w
245
257
  }
246
258
  end
247
259
 
248
- Use Redis to cache the result. Exchange will cache the API files with a key starting with 'exchange_'
260
+ Use Redis to cache the result.
249
261
  Exchange.configuration = Exchange::Configuration.new do |c|
250
262
  c.cache = {
251
263
  :subclass => :redis,
@@ -254,7 +266,7 @@ Use Redis to cache the result. Exchange will cache the API files with a key star
254
266
  }
255
267
  end
256
268
 
257
- Use Rails to cache the result. Exchange will cache the API files with a key starting with 'exchange_'
269
+ Use Rails to cache the result.
258
270
  Exchange.configuration = Exchange::Configuration.new do |c|
259
271
  c.cache = {
260
272
  :subclass => :rails
@@ -263,17 +275,18 @@ Use Rails to cache the result. Exchange will cache the API files with a key star
263
275
 
264
276
  === API Options
265
277
 
266
- Use the Xaviermedia API as the source of your conversion rates
278
+ Use the Xaviermedia API
267
279
  Exchange.configuration = Exchange::Configuration.new do |c|
268
280
  c.api = {
269
281
  :subclass => :xavier_media
270
282
  }
271
283
  end
272
284
 
273
- Use the open exchange rates Open Source API as a source of your conversion rates
285
+ Use the open exchange rates Open Source API
274
286
  Exchange.configuration = Exchange::Configuration.new do |c|
275
287
  c.api = {
276
- :subclass => :open_exchange_rates
288
+ :subclass => :open_exchange_rates,
289
+ :app_id => "Your open exchange rates app id"
277
290
  }
278
291
  end
279
292
 
data/Rakefile CHANGED
@@ -19,9 +19,9 @@ Jeweler::Tasks.new do |gem|
19
19
  gem.license = "MIT"
20
20
  gem.summary = %Q{Simple Exchange Rate operations for your ruby app}
21
21
  gem.description = %Q{The Exchange Gem gives you easy access to currency functions directly on your Numbers. Imagine a conversion as easy as
22
- 1.eur.to_usd
22
+ 1.in(:eur).to(:usd)
23
23
  or even better
24
- 1.eur.to_usd(:at => Time.now - 84600)
24
+ 1.in(:eur).to(:usd, :at => Time.now - 84600)
25
25
  which gets you an exchange at the rates of yesterday.}
26
26
  gem.email = "exchange_gem@gmail.com"
27
27
  gem.authors = ["Beat Richartz"]
@@ -1,3 +1,4 @@
1
+ require 'rubygems'
1
2
  require 'benchmark'
2
3
  require 'bigdecimal'
3
4
 
@@ -6,7 +7,7 @@ class Helper
6
7
  begin
7
8
  require a_gem.to_s
8
9
  return true
9
- rescue LoadError => e
10
+ rescue LoadError
10
11
  puts "You do not have #{a_gem} installed. gem install #{a_gem} to benchmark it\n\n"
11
12
  return false
12
13
  end
@@ -31,15 +32,15 @@ two = BigDecimal.new("4.234")
31
32
  3.times { results[:big_decimal] << Benchmark.realtime { operations.times { one * two } } }
32
33
 
33
34
  if helper.load_or_omit(:money)
34
- Money.add_rate("USD", "CAD", 1.24515)
35
+ m = Money.us_dollar(50)
35
36
  results[:money] = []
36
- 3.times { results[:money] << Benchmark.realtime { operations.times { Money.us_dollar(50) * 0.29 } } }
37
+ 3.times { results[:money] << Benchmark.realtime { operations.times { m * 0.29 } } }
37
38
  end
38
39
 
39
40
  if helper.load_or_omit(:exchange)
40
- 1.usd.to_cad #make the rate available in memory
41
+ m = 50.in(:usd)
41
42
  results[:exchange] = []
42
- 3.times { results[:exchange] << Benchmark.realtime { operations.times { 50.usd * 0.29 } } }
43
+ 3.times { results[:exchange] << Benchmark.realtime { operations.times { m * 0.29 } } }
43
44
  end
44
45
 
45
46
  puts "#{operations} operations\n\n"
data/changelog.rdoc CHANGED
@@ -1,5 +1,8 @@
1
1
  = Changes to Exchange
2
2
 
3
+ == 0.10.0
4
+ - Changed the gem API to be less invasive in the numeric classes. The deprecated API had issues with three letter methods defined in active support, mainly the try method. The new API has just two methods, in(currency) and to(currency). There are no known conflicts at this time.
5
+
3
6
  == 0.9.0
4
7
  - added memory cache support as the new default. This allows you to cache without any external requirement, directly in ruby.
5
8
  - Added a typecasting helper for money to exchange. This allows you to typecast any given attribute into a instance of Exchange::Money
Binary file
data/exchange.gemspec CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
7
7
  s.version = Exchange::VERSION
8
8
  s.authors = ["Beat Richartz"]
9
9
  s.date = "2012-10-09"
10
- s.description = "The Exchange Gem gives you easy access to currency functions directly on your Numbers. Imagine a conversion as easy as \n 1.eur.to_usd\n or even better \n 1.eur.to_usd(:at => Time.now - 84600)\n which gets you an exchange at the rates of yesterday."
10
+ s.description = "The Exchange Gem gives you easy access to currency functions directly on your Numbers. Imagine a conversion as easy as \n 1.in(:eur).to(:usd) or even better \n 1.in(:eur).to(:usd, :at => Time.now - 84600)\n which gets you an exchange at the rates of yesterday."
11
11
  s.email = "exchange_gem@gmail.com"
12
12
  s.homepage = "http://github.com/beatrichartz/exchange"
13
13
  s.licenses = ["MIT"]
data/lib/exchange/base.rb CHANGED
@@ -2,7 +2,7 @@ module Exchange
2
2
 
3
3
  # The current version of the exchange gem
4
4
  #
5
- VERSION = '0.9.0'
5
+ VERSION = '0.10.0'
6
6
 
7
7
  # The root installation path of the gem
8
8
  # @version 0.5
@@ -16,4 +16,10 @@ module Exchange
16
16
  #
17
17
  NoRateError = Class.new StandardError
18
18
 
19
+ # The error that gets thrown if the given currency is not a currency
20
+ # @version 0.10
21
+ # @since 0.10
22
+ #
23
+ NoCurrencyError = Class.new(ArgumentError)
24
+
19
25
  end
@@ -4,17 +4,36 @@ module Exchange
4
4
  #
5
5
  module ErrorSafe
6
6
 
7
+ # Installs a method chain that overwrites the old error prone meth with the new one
8
+ #
9
+ def self.money_error_preventing_method_chain base, meth
10
+ base.send :alias_method, :"#{meth}with_errors", meth
11
+ base.send :alias_method, meth, :"#{meth}without_errors"
12
+ end
13
+
14
+ # @!macro prevent_errors_with_exchange_for
15
+ # Prevents float errors when dealing with instances of Exchange::Money
16
+ # By Typecasting the float into a Big Decimal
17
+ # @method $1(other)
18
+ #
19
+ def self.prevent_errors_with_exchange_for base, meth
20
+ base.send(:define_method, :"#{meth}without_errors", lambda { |other|
21
+ if other.is_a?(Exchange::Money)
22
+ BigDecimal.new(self.to_s).send(meth, other).to_f
23
+ else
24
+ send(:"#{meth}with_errors", other)
25
+ end
26
+ })
27
+ money_error_preventing_method_chain base, meth
28
+ end
29
+
7
30
  def self.included base
8
31
  %W(* / + -).each do |meth|
9
- base.send(:define_method, :"#{meth}without_errors", lambda { |other|
10
- if other.is_a?(Exchange::Money)
11
- (BigDecimal.new(self.to_s).send(meth, other)).to_f
12
- else
13
- send(:"#{meth}with_errors", other)
14
- end
15
- })
16
- base.send :alias_method, :"#{meth}with_errors", meth.to_sym
17
- base.send :alias_method, meth.to_sym, :"#{meth}without_errors"
32
+
33
+ # @macro prevent_errors_with_exchange_for
34
+ #
35
+ prevent_errors_with_exchange_for base, meth.to_sym
36
+
18
37
  end
19
38
  end
20
39
 
@@ -1,38 +1,40 @@
1
1
  module Exchange
2
2
 
3
- # The conversability module which will get included in Fixnum and Float, giving them the currency exchange methods
3
+ # The conversability module which will get included in Fixnum and Float, giving them the in currency instantiate methods
4
4
  # @author Beat Richartz
5
- # @version 0.2
5
+ # @version 0.10
6
6
  # @since 0.1
7
7
  #
8
8
  module Conversability
9
9
 
10
- # Dynamic method generation is used here to allow instantiation and immediate conversion of Currency objects from
11
- # a common Fixnum or Float or BigDecimal. Since some builds of ruby 1.9 handle certain type conversion of Fixnum, Float
12
- # and others via method missing, this is not handled via method missing because it would seriously break down performance.
10
+ # The in method instantiates a money object from a numeric type.
11
+ # @param [Symbol] currency the currency to instantiate the money with
12
+ # @param [Hash] options The options to instantiate the currency with
13
+ # @option [Time] :at The time for which the currency should be instantiated
13
14
  #
14
15
  # @example Instantiate from any type of number
15
- # 40.usd => #<Exchange::Money @value=40 @currency=:usd>
16
- # -33.nok => #<Exchange::Money @value=-33 @currency=:nok>
17
- # 33.333.sek => #<Exchange::Money @value=33.333 @currency=:sek>
16
+ # 40.in(:usd) => #<Exchange::Money @value=40 @currency=:usd>
17
+ # -33.in(:nok) => #<Exchange::Money @value=-33 @currency=:nok>
18
+ # 33.333.in(:sek) => #<Exchange::Money @value=33.333 @currency=:sek>
18
19
  # @example Instantiate and immediatly convert
19
- # 1.usd.to_eur => #<Exchange::Money @value=0.79 @currency=:eur>
20
- # 1.nok.to_chf => #<Exchange::Money @value=6.55 @currency=:chf>
21
- # -3.5.dkk.to_huf => #<Exchange::Money @value=-346.55 @currency=:huf>
20
+ # 1.in(:usd).to(:eur) => #<Exchange::Money @value=0.79 @currency=:eur>
21
+ # 1.in(:nok).to(:chf) => #<Exchange::Money @value=6.55 @currency=:chf>
22
+ # -3.5.in(:chf).to(:dkk) => #<Exchange::Money @value=-346.55 @currency=:huf>
22
23
  # @example Instantiate and immediatly convert at a specific time in the past
23
- # 1.usd.to_eur(:at => Time.now - 86400) => #<Exchange::Money @value=0.80 @currency=:eur>
24
- # 1.nok.to_chf(:at => Time.now - 3600) => #<Exchange::Money @value=6.57 @currency=:chf>
25
- # -3.5.dkk.to_huf(:at => Time.now - 172800) => #<Exchange::Money @value=-337.40 @currency=:huf>
24
+ # 1.in(:usd).to(:eur, :at => Time.now - 86400) => #<Exchange::Money @value=0.80 @currency=:eur>
25
+ # 1.in(:nok).to(:chf, :at => Time.now - 3600) => #<Exchange::Money @value=6.57 @currency=:chf>
26
+ # -3.5.in(:dkk).to(:huf, :at => Time.now - 172800) => #<Exchange::Money @value=-337.40 @currency=:huf>
26
27
  #
27
28
  # @since 0.1
28
- # @version 0.2
29
+ # @version 0.10
29
30
  #
30
- ISO4217.currencies.each do |c|
31
- define_method c do |*args|
32
- Money.new(self, c, *args)
31
+ def in currency, options={}
32
+ if ISO4217.currencies.include? currency
33
+ Money.new(self, currency, options)
34
+ else
35
+ raise Exchange::NoCurrencyError.new("#{currency} is not a currency")
33
36
  end
34
- end
35
-
37
+ end
36
38
  end
37
39
  end
38
40
 
@@ -39,13 +39,13 @@ module Exchange
39
39
  # @param [Hash] opts Optional Parameters for instantiation
40
40
  # @option opts [Time] :at The time at which conversion took place
41
41
  # @option opts [String,Symbol] :from The money object this money object was converted from
42
- # @version 0.2
42
+ # @version 0.9
43
43
  #
44
44
  # @example Instantiate a money object of 40 US Dollars
45
45
  # Exchange::Money.new(40, :usd)
46
46
  # #=> #<Exchange::Money @number=40.0 @currency=:usd @time=#<Time>>
47
47
  # @example Instantiate a money object of 40 US Dollars and convert it to Euro. It shows the conversion date and the original currency
48
- # Exchange::Money.new(40, :usd).to_eur(:at => Time.gm(2012,9,1))
48
+ # Exchange::Money.new(40, :usd).to(:eur, :at => Time.gm(2012,9,1))
49
49
  # #=> #<Exchange::Money @number=37.0 @currency=:usd @time=#<Time> @from=#<Exchange::Money @number=40.0 @currency=:usd>>
50
50
  #
51
51
  def initialize value, currency_arg=nil, opts={}, &block
@@ -61,38 +61,32 @@ module Exchange
61
61
 
62
62
  # Method missing is used to handle conversions from one money object to another. It only handles currencies which are available in
63
63
  # the API class set in the configuration.
64
- # @example Calls convert_to with 'chf'
65
- # Exchange::Money.new(40,:usd).to_chf
66
- # @example Calls convert_to with 'sek' and :at => Time.gm(2012,2,2)
67
- # Exchange::Money.new(40,:nok).to_sek(:at => Time.gm(2012,2,2))
64
+ # @example convert to chf
65
+ # Exchange::Money.new(40,:usd).to(:chf)
66
+ # @example convert to sek at a given time
67
+ # Exchange::Money.new(40,:nok).to(:sek, :at => Time.gm(2012,2,2))
68
68
  #
69
69
  def method_missing method, *args, &block
70
70
  value.send method, *args, &block
71
71
  end
72
72
 
73
- ISO4217.currencies.each do |c|
74
- define_method :"to_#{c}" do |*args|
75
- if api_supports_currency?(c)
76
- convert_to c, { :at => time }.merge(args.first || {})
77
- else
78
- raise_no_rate_error(c)
79
- end
80
- end
81
- end
82
-
83
73
  # Converts this instance of currency into another currency
84
74
  # @return [Exchange::Money] An instance of Exchange::Money with the converted number and the converted currency
85
75
  # @param [Symbol, String] other The currency to convert the number to
86
- # @param [Hash] opts An options hash
76
+ # @param [Hash] options An options hash
87
77
  # @option [Time] :at The timestamp of the rate the conversion took place in
88
78
  # @example convert to 'chf'
89
- # Exchange::Money.new(40,:usd).convert_to('chf')
79
+ # Exchange::Money.new(40,:usd).to(:chf)
90
80
  # @example convert to 'sek' at a specific rate
91
- # Exchange::Money.new(40,:nok).convert_to('sek', :at => Time.gm(2012,2,2))
81
+ # Exchange::Money.new(40,:nok).to(:sek, :at => Time.gm(2012,2,2))
92
82
  #
93
- def convert_to other, opts={}
94
- opts[:from] = self
95
- Money.new(api.new.convert(value, currency, other, opts), other, opts)
83
+ def to other, options={}
84
+ if api_supports_currency?(other)
85
+ opts = { :at => time, :from => self }.merge(options)
86
+ Money.new(api.new.convert(value, currency, other, opts), other, opts)
87
+ else
88
+ raise_no_rate_error(other)
89
+ end
96
90
  end
97
91
 
98
92
  class << self
@@ -100,7 +94,7 @@ module Exchange
100
94
  private
101
95
 
102
96
  # @private
103
- # @macro [attach] install_operations
97
+ # @!macro [attach] install_operation
104
98
  #
105
99
  def install_operation op
106
100
  define_method op do |*precision|
@@ -109,14 +103,14 @@ module Exchange
109
103
  end
110
104
 
111
105
  # @private
112
- # @macro [attach] base_operations
106
+ # @!macro [attach] base_operation
113
107
  # @method $1(other)
114
108
  #
115
109
  def base_operation op
116
110
  self.class_eval <<-EOV
117
111
  def #{op}(other)
118
112
  test_for_currency_mix_error(other)
119
- new_value = value #{op} (other.kind_of?(Money) ? other.convert_to(self.currency, :at => other.time) : BigDecimal.new(other.to_s))
113
+ new_value = value #{op} (other.kind_of?(Money) ? other.to(self.currency, :at => other.time) : BigDecimal.new(other.to_s))
120
114
  Exchange::Money.new(new_value, currency, :at => time, :from => self)
121
115
  end
122
116
  EOV
@@ -253,7 +247,7 @@ module Exchange
253
247
  if is_same_currency?(other)
254
248
  other.round.value == self.round.value
255
249
  elsif is_currency?(other)
256
- other.convert_to(currency, :at => other.time).round.value == self.round.value
250
+ other.to(currency, :at => other.time).round.value == self.round.value
257
251
  else
258
252
  value == other
259
253
  end
@@ -277,7 +271,7 @@ module Exchange
277
271
  if is_same_currency?(other)
278
272
  value <=> other.value
279
273
  elsif is_other_currency?(other)
280
- value <=> other.convert_to(currency, :at => other.time).value
274
+ value <=> other.to(currency, :at => other.time).value
281
275
  else
282
276
  value <=> other
283
277
  end
@@ -352,7 +346,7 @@ module Exchange
352
346
  # @version 0.6
353
347
  #
354
348
  def test_for_currency_mix_error other
355
- raise CurrencyMixError.new("You\'re trying to mix up #{currency} with #{other.currency}. You denied mixing currencies in the configuration, allow it or convert the currencies before mixing") if !Exchange.configuration.allow_mixed_operations && other.kind_of?(Money) && other.currency != currency
349
+ raise CurrencyMixError.new("You\'re trying to mix up #{currency} with #{other.currency}. You denied mixing currencies in the configuration, allow it or convert the currencies before mixing") if !Exchange.configuration.allow_mixed_operations && other.is_a?(Money) && other.currency != currency
356
350
  end
357
351
 
358
352
  # Helper method to raise a no rate error for a given currency if no rate is given
@@ -16,7 +16,7 @@ module Exchange
16
16
  #
17
17
  # end
18
18
  #
19
- # MyClass.find(1).update_attributes :price => 1.usd
19
+ # MyClass.find(1).update_attributes :price => 1.in(:usd)
20
20
  # MyClass.find(1).price #=> 0.77 EUR
21
21
  #
22
22
  # @example The getter sets the currency automatically to the currency set in the definition (example in Ohm)
@@ -40,54 +40,122 @@ module Exchange
40
40
  # managermanager.update :currency => :usd
41
41
  # my_instance.price #=> instance of exchange currency in usd
42
42
  #
43
+ # @author Beat Richartz
44
+ # @since 0.9.0
45
+ # @version 0.9.0
46
+ #
43
47
  module Typecasting
44
-
45
- # installs a setter and a getter for the attribute you want to typecast as exchange money
46
48
 
47
- def money *attributes
48
-
49
- options = attributes.last.is_a?(Hash) ? attributes.pop : {}
49
+ # Installs a money getter
50
+ # @!macro [attach] install_money_getter
51
+ # @method $1
52
+ #
53
+ def install_money_getter attribute, options={}
50
54
 
51
- attributes.each do |attribute|
55
+ define_method :"#{attribute}_with_exchange_typecasting" do
56
+ currency = evaluate_money_option(options[:currency]) if options[:currency]
57
+
58
+ test_for_currency_error(currency)
52
59
 
53
- define_method :"#{attribute}_with_exchange_typecasting" do
54
- currency = evaluate_money_option(options[:currency]) if options[:currency]
55
- raise NoCurrencyError.new("No currency is given for typecasting #{attributes.join(', ')}. Make sure a currency is present") unless currency
56
-
57
- time = evaluate_money_option(options[:at]) if options[:at]
58
-
59
- Exchange::Money.new(send(:"#{attribute}_without_exchange_typecasting")) do |c|
60
- c.currency = currency
61
- c.time = time if time
62
- end
60
+ time = evaluate_money_option(options[:at]) if options[:at]
61
+
62
+ Exchange::Money.new(send(:"#{attribute}_without_exchange_typecasting")) do |c|
63
+ c.currency = currency
64
+ c.time = time if time
63
65
  end
64
- alias_method :"#{attribute}_without_exchange_typecasting", attribute.to_sym
65
- alias_method attribute.to_sym, :"#{attribute}_with_exchange_typecasting"
66
+ end
67
+ exchange_typecasting_alias_method_chain attribute
66
68
 
67
- define_method :"#{attribute}_with_exchange_typecasting=" do |data|
68
- att = send(attribute)
69
-
70
- if !data.respond_to?(:currency)
71
- send(:"#{attribute}_without_exchange_typecasting=", data)
72
- elsif att.currency == data.currency
73
- send(:"#{attribute}_without_exchange_typecasting=", data.value)
74
- elsif att.currency != data.currency
75
- send(:"#{attribute}_without_exchange_typecasting=", data.send(:"to_#{att.currency}").value)
76
- end
77
- end
78
- alias_method :"#{attribute}_without_exchange_typecasting=", :"#{attribute}="
79
- alias_method :"#{attribute}=", :"#{attribute}_with_exchange_typecasting="
69
+ end
70
+
71
+ # Installs a money setter
72
+ # @!macro [attach] install_money_setter
73
+ # @method $1(data)
74
+ #
75
+ def install_money_setter attribute, options={}
76
+ define_method :"#{attribute}_with_exchange_typecasting=" do |data|
77
+ att = send(attribute)
78
+ attribute_setter = :"#{attribute}_without_exchange_typecasting="
80
79
 
80
+ if !data.respond_to?(:currency)
81
+ send(attribute_setter, data)
82
+ elsif att.currency == data.currency
83
+ send(attribute_setter, data.value)
84
+ elsif att.currency != data.currency
85
+ send(attribute_setter, data.to(att.currency).value)
86
+ end
81
87
  end
82
-
88
+ exchange_typecasting_alias_method_chain attribute, '='
89
+ end
90
+
91
+ # Install an alias method chain for an attribute
92
+ # @param [String, Symbol] attribute The attribute to install the alias method chain for
93
+ # @param [String] setter The setter sign ('=') if this is a setter
94
+ #
95
+ def exchange_typecasting_alias_method_chain attribute, setter=nil
96
+ alias_method :"#{attribute}_without_exchange_typecasting#{setter}", :"#{attribute}#{setter}"
97
+ alias_method :"#{attribute}#{setter}", :"#{attribute}_with_exchange_typecasting#{setter}"
98
+ end
99
+
100
+ # @!macro [attach] install_money_option_eval
101
+ # @method evaluate_money_option
102
+ #
103
+ def install_money_option_eval
83
104
  define_method :evaluate_money_option do |option|
84
105
  option.is_a?(Proc) ? instance_eval(&option) : send(option)
85
106
  end
86
107
  end
87
-
88
- # Is raised when no currency is given for typecasting
108
+
109
+ # @!macro [attach] install_currency_error_tester
110
+ # @method test_for_currency_error
89
111
  #
90
- NoCurrencyError = Class.new(ArgumentError)
112
+ def install_currency_error_tester
113
+ define_method :test_for_currency_error do |currency|
114
+ raise NoCurrencyError.new("No currency is given for typecasting #{attribute}. Make sure a currency is present") unless currency
115
+ end
116
+ end
117
+
118
+ # installs a setter and a getter for the attribute you want to typecast as exchange money
119
+ # @overload def money(*attributes, options={})
120
+ # @param [Symbol] attributes The attributes you want to typecast as money.
121
+ # @param [Hash] options Pass a hash as last argument as options
122
+ # @option options [Symbol, Proc] :currency The currency to evaluate the money with. Can be a symbol or a proc
123
+ # @option options [Symbol, Proc] :at The time at which the currency should be casted. All conversions of this currency will take place at this time
124
+ # @raise [NoCurrencyError] if no currency option is given or the currency evals to nil
125
+ # @example configure money with symbols, the currency option here will call the method currency in the object context
126
+ # money :price, :currency => :currency, :time => :created_at
127
+ # @example configure money with a proc, the proc will be called with the object as an argument. This is equivalent to the example above
128
+ # money :price, :currency => lambda {|o| o.currency}, :time => lambda{|o| o.created_at}
129
+ #
130
+ def money *attributes
131
+
132
+ options = attributes.last.is_a?(Hash) ? attributes.pop : {}
133
+
134
+ attributes.each do |attribute|
135
+
136
+ # Get the attribute typecasted into money
137
+ # @return [Exchange::Money] an instance of money
138
+ #
139
+ install_money_getter attribute, options
140
+
141
+ # Set the attribute either with money or just any data
142
+ # Implicitly converts values given that are not in the same currency as the currency option evaluates to
143
+ # @param [Exchange::Money, String, Numberic] data The data to set the attribute to
144
+ #
145
+ install_money_setter attribute, options
146
+
147
+ end
148
+
149
+ # Evaluates options given either as symbols or as procs
150
+ # @param [Symbol, Proc] option The option to evaluate
151
+ #
152
+ install_money_option_eval
153
+
154
+ # Evaluates whether an error should be raised because there is no currency present
155
+ # @param [Symbol] currency The currency, if given
156
+ #
157
+ install_currency_error_tester
158
+ end
91
159
 
92
160
  end
93
161
 
@@ -15,7 +15,7 @@ describe "Exchange::ErrorSafe" do
15
15
  describe "money safe calculation" do
16
16
  describe "*" do
17
17
  it "should calculate correctly with exchange money" do
18
- (0.29 * 50.usd).round.should == 15
18
+ (0.29 * 50.in(:usd)).round.should == 15
19
19
  end
20
20
  it "should not touch other operations" do
21
21
  (0.29 * 50).round.should == 14
@@ -23,7 +23,7 @@ describe "Exchange::ErrorSafe" do
23
23
  end
24
24
  describe "/" do
25
25
  it "should calculate correctly with exchange money" do
26
- (((1829.82 / 12.usd) * 100).round.to_f / 100).to_f.should == 152.49
26
+ (((1829.82 / 12.in(:usd)) * 100).round.to_f / 100).to_f.should == 152.49
27
27
  end
28
28
  it "should not touch other operations" do
29
29
  (((1829.82 / 12) * 100).round.to_f / 100).should == 152.48
@@ -31,7 +31,7 @@ describe "Exchange::ErrorSafe" do
31
31
  end
32
32
  describe "+" do
33
33
  it "should calculate correctly with exchange money" do
34
- (1.0e+25 + BigDecimal.new("9999999999999999900000000").usd).round.to_f.should == 2.0e+25
34
+ (1.0e+25 + BigDecimal.new("9999999999999999900000000").in(:usd)).round.to_f.should == 2.0e+25
35
35
  end
36
36
  it "should not touch other operations" do
37
37
  (1.0e+25 + BigDecimal.new("9999999999999999900000000")).round.should == 20000000000000001811939328
@@ -39,7 +39,7 @@ describe "Exchange::ErrorSafe" do
39
39
  end
40
40
  describe "-" do
41
41
  it "should calculate correctly with exchange money" do
42
- (1.0e+25 - BigDecimal.new("9999999999999999900000000").usd).round.should == 100000000
42
+ (1.0e+25 - BigDecimal.new("9999999999999999900000000").in(:usd)).round.should == 100000000
43
43
  end
44
44
  it "should not touch other operations" do
45
45
  (1.0e+25 - BigDecimal.new("9999999999999999900000000")).should == 0
@@ -11,92 +11,94 @@ describe "Exchange::Conversability" do
11
11
  after(:all) do
12
12
  Exchange.configuration.reset
13
13
  end
14
- it "should define all currencies on Fixnum, Float and BigDecimal" do
15
- Exchange::ISO4217.definitions.keys.each do |c|
16
- 1.should be_respond_to(c.to_s.downcase.to_sym)
17
- 1.1.should be_respond_to(c.to_s.downcase.to_sym)
18
- BigDecimal.new("1").should be_respond_to(c.to_s.downcase.to_sym)
19
- end
20
- end
21
14
  context "with a fixnum" do
22
15
  it "should allow to convert to a currency" do
23
- 3.eur.should be_kind_of Exchange::Money
24
- 3.eur.value.should == 3
16
+ 3.in(:eur).should be_kind_of Exchange::Money
17
+ 3.in(:eur).value.should == 3
25
18
  end
26
19
  it "should allow to convert to a curreny with a negative number" do
27
- -3.eur.should be_kind_of Exchange::Money
28
- -3.eur.value.should == -3
20
+ -3.in(:eur).should be_kind_of Exchange::Money
21
+ -3.in(:eur).value.should == -3
29
22
  end
30
23
  it "should allow to do full conversions" do
31
24
  mock_api("http://api.finance.xaviermedia.com/api/2012/08/27.xml", fixture('api_responses/example_xml_api.xml'), 3)
32
- 3.eur.to_chf.should be_kind_of Exchange::Money
33
- 3.eur.to_chf.value.round(2).should == 3.68
34
- 3.eur.to_chf.currency.should == :chf
25
+ 3.in(:eur).to(:chf).should be_kind_of Exchange::Money
26
+ 3.in(:eur).to(:chf).value.round(2).should == 3.68
27
+ 3.in(:eur).to(:chf).currency.should == :chf
35
28
  end
36
29
  it "should allow to do full conversions with negative numbers" do
37
30
  mock_api("http://api.finance.xaviermedia.com/api/2012/08/27.xml", fixture('api_responses/example_xml_api.xml'), 3)
38
- -3.eur.to_chf.should be_kind_of Exchange::Money
39
- -3.eur.to_chf.value.round(2).should == -3.68
40
- -3.eur.to_chf.currency.should == :chf
31
+ -3.in(:eur).to(:chf).should be_kind_of Exchange::Money
32
+ -3.in(:eur).to(:chf).value.round(2).should == -3.68
33
+ -3.in(:eur).to(:chf).currency.should == :chf
41
34
  end
42
35
  it "should allow to define a historic time in which the currency should be interpreted" do
43
- 3.chf(:at => Time.gm(2010,1,1)).time.yday.should == 1
44
- 3.chf(:at => Time.gm(2010,1,1)).time.year.should == 2010
45
- 3.chf(:at => '2010-01-01').time.year.should == 2010
36
+ 3.in(:chf, :at => Time.gm(2010,1,1)).time.yday.should == 1
37
+ 3.in(:chf, :at => Time.gm(2010,1,1)).time.year.should == 2010
38
+ 3.in(:chf, :at => '2010-01-01').time.year.should == 2010
39
+ end
40
+ it "should raise a no currency error if the currency does not exist" do
41
+ lambda { 35.in(:zzz) }.should raise_error(Exchange::NoCurrencyError, "zzz is not a currency")
46
42
  end
47
43
  end
48
44
  context "with a float" do
49
45
  it "should allow to convert to a currency" do
50
- 3.25.eur.should be_kind_of Exchange::Money
51
- 3.25.eur.value.round(2).should == 3.25
46
+ 3.25.in(:eur).should be_kind_of Exchange::Money
47
+ 3.25.in(:eur).value.round(2).should == 3.25
52
48
  end
53
49
  it "should allow to convert to a curreny with a negative number" do
54
- -3.25.eur.should be_kind_of Exchange::Money
55
- -3.25.eur.value.round(2).should == -3.25
50
+ -3.25.in(:eur).should be_kind_of Exchange::Money
51
+ -3.25.in(:eur).value.round(2).should == -3.25
56
52
  end
57
53
  it "should allow to do full conversions" do
58
54
  mock_api("http://api.finance.xaviermedia.com/api/2012/08/27.xml", fixture('api_responses/example_xml_api.xml'), 3)
59
- 3.25.eur.to_chf.should be_kind_of Exchange::Money
60
- 3.25.eur.to_chf.value.round(2).should == 3.99
61
- 3.25.eur.to_chf.currency.should == :chf
55
+ 3.25.in(:eur).to(:chf).should be_kind_of Exchange::Money
56
+ 3.25.in(:eur).to(:chf).value.round(2).should == 3.99
57
+ 3.25.in(:eur).to(:chf).currency.should == :chf
62
58
  end
63
59
  it "should allow to do full conversions with negative numbers" do
64
60
  mock_api("http://api.finance.xaviermedia.com/api/2012/08/27.xml", fixture('api_responses/example_xml_api.xml'), 3)
65
- -3.25.eur.to_chf.should be_kind_of Exchange::Money
66
- -3.25.eur.to_chf.value.round(2).should == -3.99
67
- -3.25.eur.to_chf.currency.should == :chf
61
+ -3.25.in(:eur).to(:chf).should be_kind_of Exchange::Money
62
+ -3.25.in(:eur).to(:chf).value.round(2).should == -3.99
63
+ -3.25.in(:eur).to(:chf).currency.should == :chf
68
64
  end
69
65
  it "should allow to define a historic time in which the currency should be interpreted" do
70
- 3.25.chf(:at => Time.gm(2010,1,1)).time.yday.should == 1
71
- 3.25.chf(:at => Time.gm(2010,1,1)).time.year.should == 2010
72
- 3.25.chf(:at => '2010-01-01').time.year.should == 2010
66
+ 3.25.in(:chf, :at => Time.gm(2010,1,1)).time.yday.should == 1
67
+ 3.25.in(:chf, :at => Time.gm(2010,1,1)).time.year.should == 2010
68
+ 3.25.in(:chf, :at => '2010-01-01').time.year.should == 2010
69
+ end
70
+ it "should raise a no currency error if the currency does not exist" do
71
+ lambda { 35.23.in(:zzz) }.should raise_error(Exchange::NoCurrencyError, "zzz is not a currency")
73
72
  end
74
73
  end
75
74
  context "with a big decimal" do
76
75
  it "should allow to convert to a currency" do
77
- BigDecimal.new("3.25").eur.should be_kind_of Exchange::Money
78
- BigDecimal.new("3.25").eur.value.round(2).should == 3.25
76
+ BigDecimal.new("3.25").in(:eur).should be_kind_of Exchange::Money
77
+ BigDecimal.new("3.25").in(:eur).value.round(2).should == 3.25
79
78
  end
80
79
  it "should allow to convert to a curreny with a negative number" do
81
- BigDecimal.new("-3.25").eur.should be_kind_of Exchange::Money
82
- BigDecimal.new("-3.25").eur.value.round(2).should == -3.25
80
+ BigDecimal.new("-3.25").in(:eur).should be_kind_of Exchange::Money
81
+ BigDecimal.new("-3.25").in(:eur).value.round(2).should == -3.25
83
82
  end
84
83
  it "should allow to do full conversions" do
85
84
  mock_api("http://api.finance.xaviermedia.com/api/2012/08/27.xml", fixture('api_responses/example_xml_api.xml'), 3)
86
- BigDecimal.new("3.25").eur.to_chf.should be_kind_of Exchange::Money
87
- BigDecimal.new("3.25").eur.to_chf.value.round(2).should == 3.99
88
- BigDecimal.new("3.25").eur.to_chf.currency.should == :chf
85
+ BigDecimal.new("3.25").in(:eur).to(:chf).should be_kind_of Exchange::Money
86
+ BigDecimal.new("3.25").in(:eur).to(:chf).value.round(2).should == 3.99
87
+ BigDecimal.new("3.25").in(:eur).to(:chf).currency.should == :chf
89
88
  end
90
89
  it "should allow to do full conversions with negative numbers" do
91
90
  mock_api("http://api.finance.xaviermedia.com/api/2012/08/27.xml", fixture('api_responses/example_xml_api.xml'), 3)
92
- BigDecimal.new("-3.25").eur.to_chf.should be_kind_of Exchange::Money
93
- BigDecimal.new("-3.25").eur.to_chf.value.round(2).should == -3.99
94
- BigDecimal.new("-3.25").eur.to_chf.currency.should == :chf
91
+ BigDecimal.new("-3.25").in(:eur).to(:chf).should be_kind_of Exchange::Money
92
+ BigDecimal.new("-3.25").in(:eur).to(:chf).value.round(2).should == -3.99
93
+ BigDecimal.new("-3.25").in(:eur).to(:chf).currency.should == :chf
95
94
  end
96
95
  it "should allow to define a historic time in which the currency should be interpreted" do
97
- BigDecimal.new("3.25").chf(:at => Time.gm(2010,1,1)).time.yday.should == 1
98
- BigDecimal.new("3.25").chf(:at => Time.gm(2010,1,1)).time.year.should == 2010
99
- BigDecimal.new("3.25").chf(:at => '2010-01-01').time.year.should == 2010
96
+ BigDecimal.new("3.25").in(:chf, :at => Time.gm(2010,1,1)).time.yday.should == 1
97
+ BigDecimal.new("3.25").in(:chf, :at => Time.gm(2010,1,1)).time.year.should == 2010
98
+ BigDecimal.new("3.25").in(:chf, :at => '2010-01-01').time.year.should == 2010
99
+ end
100
+ it "should raise a no currency error if the currency does not exist" do
101
+ lambda { BigDecimal.new("3.25").in(:zzz) }.should raise_error(Exchange::NoCurrencyError, "zzz is not a currency")
100
102
  end
101
103
  end
102
104
  end
@@ -59,7 +59,7 @@ describe "Exchange::ExternalAPI::OpenExchangeRates" do
59
59
  subject.convert(70, :sek, :usd, :at => Time.gm(2011,9,9)).round(2).should == 10.38
60
60
  end
61
61
  it "should convert right when the year is the same, but the yearday is not" do
62
- mock_api("https://openexchangerates.org/api/historical/#{Time.now.year}-0#{Time.now.month > 9 ? Time.now.month - 1 : Time.now.month + 1}-01.json?app_id=", fixture('api_responses/example_json_api.json'))
62
+ mock_api("https://openexchangerates.org/api/historical/#{Time.now.year}-#{'0' if Time.now.month < 11}#{Time.now.month > 9 ? Time.now.month - 1 : Time.now.month + 1}-01.json?app_id=", fixture('api_responses/example_json_api.json'))
63
63
  subject.convert(70, :sek, :usd, :at => Time.gm(Time.now.year,Time.now.month > 9 ? Time.now.month - 1 : Time.now.month + 1,1)).round(2).should == 10.38
64
64
  end
65
65
  it "should convert right when the yearday is the same, but the year is not" do
@@ -55,7 +55,7 @@ describe "Exchange::ExternalAPI::XavierMedia" do
55
55
  subject.convert(70, :sek, :usd, :at => Time.gm(2011,9,9)).round(2).should == 10.35
56
56
  end
57
57
  it "should convert right when the year is the same, but the yearday is not" do
58
- mock_api("http://api.finance.xaviermedia.com/api/#{Time.now.year}/0#{Time.now.month > 9 ? Time.now.month - 1 : Time.now.month + 1}/01.xml", fixture('api_responses/example_xml_api.xml'))
58
+ mock_api("http://api.finance.xaviermedia.com/api/#{Time.now.year}/#{'0' if Time.now.month < 11}#{Time.now.month > 9 ? Time.now.month - 1 : Time.now.month + 1}/01.xml", fixture('api_responses/example_xml_api.xml'))
59
59
  subject.convert(70, :sek, :usd, :at => Time.gm(Time.now.year,Time.now.month > 9 ? Time.now.month - 1 : Time.now.month + 1,1)).round(2).should == 10.35
60
60
  end
61
61
  it "should convert right when the yearday is the same, but the year is not" do
@@ -32,12 +32,12 @@ describe "Exchange::Money" do
32
32
  currency.time.should == Time.gm(2012,9,9)
33
33
  end
34
34
  end
35
- describe "convert_to" do
35
+ describe "to" do
36
36
  it "should be able to convert itself to other currencies" do
37
37
  mock_api("http://openexchangerates.org/api/latest.json?app_id=", fixture('api_responses/example_json_api.json'), 3)
38
- subject.convert_to(:chf).value.round(2).should == 36.5
39
- subject.convert_to(:chf).currency.should == :chf
40
- subject.convert_to(:chf).should be_kind_of Exchange::Money
38
+ subject.to(:chf).value.round(2).should == 36.5
39
+ subject.to(:chf).currency.should == :chf
40
+ subject.to(:chf).should be_kind_of Exchange::Money
41
41
  end
42
42
  end
43
43
  describe "operations" do
@@ -324,7 +324,7 @@ describe "Exchange::Money" do
324
324
  let(:comp2) { Exchange::Money.new(40, :usd) }
325
325
  let(:comp3) { Exchange::Money.new(50, :eur) }
326
326
  let(:comp4) { Exchange::Money.new(45, :eur) }
327
- let(:comp5) { Exchange::Money.new(50, :eur).to_usd }
327
+ let(:comp5) { Exchange::Money.new(50, :eur).to(:usd) }
328
328
  let(:comp6) { Exchange::Money.new(66.1, :usd, :at => Time.gm(2011,1,1)) }
329
329
  before(:each) do
330
330
  mock_api("http://openexchangerates.org/api/latest.json?app_id=", fixture('api_responses/example_json_api.json'), 2)
@@ -464,7 +464,7 @@ describe "Exchange::Money" do
464
464
  it "should be able to convert via to_currency to other currencies" do
465
465
  mock_api("http://openexchangerates.org/api/latest.json?app_id=", fixture('api_responses/example_json_api.json'), 6)
466
466
  {:chf => 36.5, :usd => 40.0, :dkk => 225.12, :sek => 269.85, :nok => 232.06, :rub => 1205.24}.each do |currency, value|
467
- c = subject.send(:"to_#{currency}")
467
+ c = subject.to(currency)
468
468
  c.value.round(2).should == value
469
469
  c.currency.should == currency
470
470
  end
@@ -472,17 +472,17 @@ describe "Exchange::Money" do
472
472
  it "should be able to convert via to_currency to other currencies and use historic data" do
473
473
  mock_api("http://openexchangerates.org/api/historical/2011-10-09.json?app_id=", fixture('api_responses/example_json_api.json'), 6)
474
474
  {:chf => 36.5, :usd => 40.0, :dkk => 225.12, :sek => 269.85, :nok => 232.06, :rub => 1205.24}.each do |currency, value|
475
- c = subject.send(:"to_#{currency}", :at => Time.gm(2011,10,9))
475
+ c = subject.to(currency, :at => Time.gm(2011,10,9))
476
476
  c.value.round(2).should == value
477
477
  c.currency.should == currency
478
478
  end
479
479
  end
480
480
  it "should use the own time if defined as historic to convert" do
481
481
  mock_api("http://openexchangerates.org/api/historical/2011-01-01.json?app_id=", fixture('api_responses/example_json_api.json'), 2)
482
- 5.eur(:at => Time.gm(2011,1,1)).to_usd.value.should == 5.eur.to_usd(:at => Time.gm(2011,1,1)).value
482
+ 5.in(:eur, :at => Time.gm(2011,1,1)).to(:usd).value.should == 5.in(:eur).to(:usd, :at => Time.gm(2011,1,1)).value
483
483
  end
484
484
  it "should raise errors for currency conversions it does not have rates for" do
485
- lambda { subject.to_ssp }.should raise_error(Exchange::NoRateError)
485
+ lambda { subject.to(:ssp) }.should raise_error(Exchange::NoRateError)
486
486
  end
487
487
  it "should pass on methods it does not understand to its number" do
488
488
  subject.to_f.should == 40
@@ -70,16 +70,16 @@ describe "Exchange::Typecasting" do
70
70
  end
71
71
  it "should set the value if the given value is numeric" do
72
72
  subject.price = 0.83
73
- subject.price.should == 0.83.eur
73
+ subject.price.should == 0.83.in(:eur)
74
74
  end
75
75
  it "should not convert the value if the given value is in the same currency" do
76
- subject.price = 0.83.eur
77
- subject.price.should == 0.83.eur
76
+ subject.price = 0.83.in(:eur)
77
+ subject.price.should == 0.83.in(:eur)
78
78
  end
79
79
  it "should convert the value if the given value is in another currency" do
80
80
  mock_api("http://api.finance.xaviermedia.com/api/#{Time.now.strftime("%Y/%m/%d")}.xml", fixture('api_responses/example_xml_api.xml'))
81
- subject.price = 0.83.usd
82
- subject.price.should == 0.62.eur
81
+ subject.price = 0.83.in(:usd)
82
+ subject.price.should == 0.62.in(:eur)
83
83
  end
84
84
  end
85
85
 
metadata CHANGED
@@ -1,84 +1,73 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: exchange
3
- version: !ruby/object:Gem::Version
4
- hash: 59
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.10.0
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 9
9
- - 0
10
- version: 0.9.0
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Beat Richartz
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2012-10-09 00:00:00 Z
19
- dependencies:
20
- - !ruby/object:Gem::Dependency
12
+ date: 2012-10-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
21
15
  name: json
22
- prerelease: false
23
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
24
17
  none: false
25
- requirements:
26
- - - ">="
27
- - !ruby/object:Gem::Version
28
- hash: 23
29
- segments:
30
- - 1
31
- - 0
32
- - 0
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
33
21
  version: 1.0.0
34
22
  type: :runtime
35
- version_requirements: *id001
36
- - !ruby/object:Gem::Dependency
37
- name: yard
38
23
  prerelease: false
39
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.0.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: yard
32
+ requirement: !ruby/object:Gem::Requirement
40
33
  none: false
41
- requirements:
42
- - - ">="
43
- - !ruby/object:Gem::Version
44
- hash: 11
45
- segments:
46
- - 0
47
- - 7
48
- - 4
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
49
37
  version: 0.7.4
50
38
  type: :development
51
- version_requirements: *id002
52
- - !ruby/object:Gem::Dependency
53
- name: bundler
54
39
  prerelease: false
55
- requirement: &id003 !ruby/object:Gem::Requirement
40
+ version_requirements: !ruby/object:Gem::Requirement
56
41
  none: false
57
- requirements:
58
- - - ">="
59
- - !ruby/object:Gem::Version
60
- hash: 23
61
- segments:
62
- - 1
63
- - 0
64
- - 0
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 0.7.4
46
+ - !ruby/object:Gem::Dependency
47
+ name: bundler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
65
53
  version: 1.0.0
66
54
  type: :development
67
- version_requirements: *id003
68
- description: |-
69
- The Exchange Gem gives you easy access to currency functions directly on your Numbers. Imagine a conversion as easy as
70
- 1.eur.to_usd
71
- or even better
72
- 1.eur.to_usd(:at => Time.now - 84600)
73
- which gets you an exchange at the rates of yesterday.
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.0
62
+ description: ! "The Exchange Gem gives you easy access to currency functions directly
63
+ on your Numbers. Imagine a conversion as easy as \n 1.in(:eur).to(:usd) or even
64
+ better \n 1.in(:eur).to(:usd, :at => Time.now - 84600)\n which gets you an exchange
65
+ at the rates of yesterday."
74
66
  email: exchange_gem@gmail.com
75
67
  executables: []
76
-
77
68
  extensions: []
78
-
79
69
  extra_rdoc_files: []
80
-
81
- files:
70
+ files:
82
71
  - .document
83
72
  - .gitignore
84
73
  - .rspec
@@ -90,6 +79,7 @@ files:
90
79
  - Rakefile
91
80
  - benchmark/benchmark.rb
92
81
  - changelog.rdoc
82
+ - exchange-0.9.0.gem
93
83
  - exchange.gemspec
94
84
  - iso4217.yml
95
85
  - lib/exchange.rb
@@ -158,39 +148,31 @@ files:
158
148
  - spec/support/api_responses/example_json_api.json
159
149
  - spec/support/api_responses/example_xml_api.xml
160
150
  homepage: http://github.com/beatrichartz/exchange
161
- licenses:
151
+ licenses:
162
152
  - MIT
163
153
  post_install_message:
164
154
  rdoc_options: []
165
-
166
- require_paths:
155
+ require_paths:
167
156
  - lib
168
- required_ruby_version: !ruby/object:Gem::Requirement
157
+ required_ruby_version: !ruby/object:Gem::Requirement
169
158
  none: false
170
- requirements:
171
- - - ">="
172
- - !ruby/object:Gem::Version
173
- hash: 3
174
- segments:
175
- - 0
176
- version: "0"
177
- required_rubygems_version: !ruby/object:Gem::Requirement
159
+ requirements:
160
+ - - ! '>='
161
+ - !ruby/object:Gem::Version
162
+ version: '0'
163
+ required_rubygems_version: !ruby/object:Gem::Requirement
178
164
  none: false
179
- requirements:
180
- - - ">="
181
- - !ruby/object:Gem::Version
182
- hash: 3
183
- segments:
184
- - 0
185
- version: "0"
165
+ requirements:
166
+ - - ! '>='
167
+ - !ruby/object:Gem::Version
168
+ version: '0'
186
169
  requirements: []
187
-
188
170
  rubyforge_project:
189
171
  rubygems_version: 1.8.24
190
172
  signing_key:
191
173
  specification_version: 3
192
174
  summary: Simple Exchange Rate operations for your ruby app
193
- test_files:
175
+ test_files:
194
176
  - spec/exchange/cache/base_spec.rb
195
177
  - spec/exchange/cache/configuration_spec.rb
196
178
  - spec/exchange/cache/file_spec.rb