exchange 1.0.4 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0a30e01a5f55d57a9201b8da420a8c3849ca73c6
4
+ data.tar.gz: 3fde15afd9be50c593edab5bf94f517cc55e24b2
5
+ SHA512:
6
+ metadata.gz: ef1eafc5d5d39a7e68508285e6c2b1dfd194034fd11792106180d63265b1ad500d0c362f2c77cb35aaa510f47412704559b09c47fbcd210afee3f398d319f2f6
7
+ data.tar.gz: 27dce37969266711bbba84ddb2777e78b25fc3bca17092364ca0baadb787fa92843904b2020224c338d2d47c2d67907a917caf3b7bbaa0fd0cdae670c4bb60ea
@@ -0,0 +1 @@
1
+ exchange
@@ -0,0 +1 @@
1
+ 2.0.0
@@ -1,13 +1,10 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.8.7
4
3
  - 1.9.2
5
4
  - 1.9.3
6
5
  - 2.0.0
7
- - ree
8
- - rbx-18mode
9
6
  - rbx-19mode
10
- - jruby-18mode
11
7
  - jruby-19mode
8
+ - jruby-20mode
12
9
 
13
10
  script: bundle exec rspec
@@ -1,4 +1,4 @@
1
- = exchange {<img src="https://secure.travis-ci.org/beatrichartz/exchange.png?branch=master" />}[http://travis-ci.org/beatrichartz/exchange] {<img src="https://gemnasium.com/beatrichartz/exchange.png" alt="Dependency Status" />}[https://gemnasium.com/beatrichartz/exchange] {<img src="https://codeclimate.com/badge.png" />}[https://codeclimate.com/github/beatrichartz/exchange]
1
+ = exchange {<img src="https://secure.travis-ci.org/beatrichartz/exchange.png?branch=master" />}[http://travis-ci.org/beatrichartz/exchange] {<img src="https://gemnasium.com/beatrichartz/exchange.png" alt="Dependency Status" />}[https://gemnasium.com/beatrichartz/exchange] {<img src="https://codeclimate.com/github/beatrichartz/exchange.png" />}[https://codeclimate.com/github/beatrichartz/exchange]
2
2
 
3
3
  The Exchange Gem gives you easy access to currency functions directly on your Numbers. {It is tested against}[http://travis-ci.org/beatrichartz/exchange]: ruby 2.0, 1.9, ruby 1.8, ree and rubinius (1.8 and 1.9 mode). Exchange will in future versions take advantage of 2.0 features, be sure to check this page for updates!
4
4
 
@@ -3,7 +3,7 @@ module Exchange
3
3
 
4
4
  # The current version of the exchange gem
5
5
  #
6
- VERSION = '1.0.4'
6
+ VERSION = '1.1.0'
7
7
 
8
8
  # The root installation path of the gem
9
9
  # @version 0.5
@@ -21,6 +21,6 @@ module Exchange
21
21
  # @version 0.10
22
22
  # @since 0.10
23
23
  #
24
- NoCurrencyError = Class.new(ArgumentError)
24
+ NoCurrencyError = Class.new ArgumentError
25
25
 
26
26
  end
@@ -48,7 +48,7 @@ module Exchange
48
48
  # @raise [CachingWithoutBlockError] an Argument Error when no mandatory block has been given
49
49
  #
50
50
  def cached api, opts={}, &block
51
- raise CachingWithoutBlockError.new('Caching needs a block') unless block_given?
51
+ raise_caching_needs_block! unless block_given?
52
52
 
53
53
  block.call
54
54
  end
@@ -63,6 +63,8 @@ module Exchange
63
63
  # Generates a key which can handle expiration by itself
64
64
  # @param [Exchange::ExternalAPI::Subclass] api The API to store the data for
65
65
  # @param [Hash] opts The options for caching
66
+ # @option opts [Array] :key_for An array of additional key elements
67
+ # @option opts [Time] :at The timestamp for the key
66
68
  # @return [String] A string that can be used as cache key
67
69
  # @example
68
70
  # Exchange::Cache::Base.key(Exchange::ExternalAPI::OpenExchangeRates, Time.gm(2012,1,1)) #=> "Exchange_ExternalAPI_CurrencyBot_2012_1"
@@ -91,6 +93,12 @@ module Exchange
91
93
  def helper
92
94
  @helper ||= Exchange::Helper
93
95
  end
96
+
97
+ # Raise a caching needs a block error
98
+ #
99
+ def raise_caching_needs_block!
100
+ raise CachingWithoutBlockError.new('Caching needs a block')
101
+ end
94
102
 
95
103
  end
96
104
 
@@ -98,6 +106,6 @@ module Exchange
98
106
  # @since 0.1
99
107
  # @version 0.1
100
108
  #
101
- CachingWithoutBlockError = Class.new(ArgumentError)
109
+ CachingWithoutBlockError = Class.new ArgumentError
102
110
  end
103
111
  end
@@ -8,10 +8,16 @@ module Exchange
8
8
  # @since 0.9
9
9
  #
10
10
  class Configuration < Exchange::Configurable
11
+
12
+ # Additional properties which are proprietary to the cache configuration
13
+ #
11
14
  attr_accessor :expire, :host, :port, :path
12
15
 
13
16
  class << self
14
17
 
18
+ # Alias method chain to set the client to nil before an attribute of the configuration is set.
19
+ # @param setters The attribute names for which the chain has to be installed
20
+ #
15
21
  def wipe_client_before_setting *setters
16
22
 
17
23
  setters.each do |setter|
@@ -27,26 +33,39 @@ module Exchange
27
33
 
28
34
  end
29
35
 
36
+ # delegate all necessary methods to the instance
37
+ #
30
38
  def_delegators :instance, :expire, :expire=, :host, :host=, :port, :port=, :path, :path=
39
+
40
+ # set the client to nil before setting the host or the port
41
+ #
31
42
  wipe_client_before_setting :host, :port
32
43
 
33
- # Overrides the parent class method to wipe the client before setting
44
+ # Overrides the parent class method to set the client to nil before setting the configuration via a hash
45
+ # @param [Hash] hash The hash with the configuration options to set the configuration to
34
46
  #
35
47
  def set hash
36
48
  wipe_subclass_client!
37
49
  super
38
50
  end
39
51
 
52
+ # The parent module to get the constants from
53
+ # @returns [Class] the Cache class, always
54
+ #
40
55
  def parent_module
41
56
  Cache
42
57
  end
43
58
 
59
+ # The key of the configuration
60
+ #
44
61
  def key
45
62
  :cache
46
63
  end
47
64
 
48
65
  private
49
66
 
67
+ # set the client instance variable to nil if possible
68
+ #
50
69
  def wipe_subclass_client!
51
70
  subclass.wipe_client! if subclass && subclass.respond_to?(:wipe_client!)
52
71
  end
@@ -3,9 +3,10 @@ module Exchange
3
3
  module Cache
4
4
 
5
5
  # @author Beat Richartz
6
- # A class that allows to store api call results in files. THIS NOT A RECOMMENDED CACHING OPTION!
6
+ # A class that allows to store api call results in files.
7
7
  # It just may be necessary to cache large files somewhere, this class allows you to do that
8
- #
8
+ # @note This is not a recommended caching option
9
+ #
9
10
  # @version 0.6
10
11
  # @since 0.3
11
12
 
@@ -21,6 +22,8 @@ module Exchange
21
22
  # @raise [CachingWithoutBlockError] an Argument Error when no mandatory block has been given
22
23
  #
23
24
  def cached api, opts={}, &block
25
+ raise_caching_needs_block! unless block_given?
26
+
24
27
  today = Time.now
25
28
  dir = config.path
26
29
  path = ::File.join(dir, key(api, opts[:cache_period]))
@@ -50,7 +53,7 @@ module Exchange
50
53
  # @example
51
54
  # Exchange::Cache::Base.key(Exchange::ExternalAPI::OpenExchangeRates, :monthly) #=> "Exchange_ExternalAPI_CurrencyBot_monthly_2012_1"
52
55
  #
53
- def key(api_class, cache_period=:daily)
56
+ def key api_class, cache_period=:daily
54
57
  time = Time.now
55
58
  [api_class.to_s.gsub(/::/, '_'), cache_period, time.year, time.send(cache_period == :monthly ? :month : :yday)].join('_')
56
59
  end
@@ -14,7 +14,9 @@ module Exchange
14
14
  # end
15
15
  #
16
16
  class Memcached < Base
17
-
17
+
18
+ # delegate client and wiping the client to the instance
19
+ #
18
20
  def_delegators :instance, :client, :wipe_client!
19
21
 
20
22
  # instantiates a memcached client and memoizes it in a class variable.
@@ -46,7 +46,7 @@ module Exchange
46
46
  # @param [Hash] opts The options for caching
47
47
  # @return [String] A string that can be used as instance variable name
48
48
  #
49
- def instance_variable_name(api, opts)
49
+ def instance_variable_name api, opts
50
50
  conversion_time = helper.assure_time(opts[:at], :default => :now)
51
51
  time = Time.now
52
52
  expire_hourly = config.expire == :hourly || nil
@@ -33,7 +33,7 @@ module Exchange
33
33
  # @raise [CachingWithoutBlockError] an Argument Error when no mandatory block has been given
34
34
  #
35
35
  def cached api, opts={}, &block
36
- raise CachingWithoutBlockError.new('Caching needs a block') unless block_given?
36
+ raise_caching_needs_block! unless block_given?
37
37
 
38
38
  result = client.fetch key(api, opts), :expires_in => config.expire == :daily ? 86400 : 3600, &block
39
39
  client.delete(key(api, opts)) unless result && !result.to_s.empty?
@@ -14,6 +14,8 @@ module Exchange
14
14
  # end
15
15
  class Redis < Base
16
16
 
17
+ # delegate the client and wiping the client to the instance
18
+ #
17
19
  def_delegators :instance, :client, :wipe_client!
18
20
 
19
21
  # instantiates a redis client and memoizes it in a class variable.
@@ -41,7 +43,7 @@ module Exchange
41
43
  # @yield [] This method takes a mandatory block with an arity of 0 and calls it if no cached result is available
42
44
  # @raise [CachingWithoutBlockError] an Argument Error when no mandatory block has been given
43
45
  #
44
- def cached api, opts={}, &block
46
+ def cached api, opts={}, &block
45
47
  if result = client.get(key(api, opts))
46
48
  result = opts[:plain] ? result : result.decachify
47
49
  else
@@ -12,6 +12,9 @@ module Exchange
12
12
 
13
13
  def_delegators :instance, :subclass, :subclass=, :set
14
14
 
15
+ # Alias method chain to instantiate the subclass from a symbol should it not be a class
16
+ # @return [NilClass, Class] The subclass or nil
17
+ #
15
18
  def subclass_with_constantize
16
19
  self.subclass = parent_module.const_get camelize(self.subclass_without_constantize) unless !self.subclass_without_constantize || self.subclass_without_constantize.is_a?(Class)
17
20
  subclass_without_constantize
@@ -2,6 +2,8 @@
2
2
  module Exchange
3
3
  module Cachify
4
4
 
5
+ # Use cachify as an alias for Marshal dumping
6
+ #
5
7
  def cachify
6
8
  Marshal.dump self
7
9
  end
@@ -10,6 +12,8 @@ module Exchange
10
12
 
11
13
  module Decachify
12
14
 
15
+ # Use cachify as an alias for Marshal loading
16
+ #
13
17
  def decachify
14
18
  Marshal.load self
15
19
  end
@@ -124,7 +124,7 @@ module Exchange
124
124
  # Exchange::ExternalAPI::Base.new.rate(:usd, :eur, :at => Time.gm(3,23,2009))
125
125
  # #=> 1.232231231
126
126
  #
127
- def rate(from, to, opts={})
127
+ def rate from, to, opts={}
128
128
  rate = cache.cached(self.class, opts.merge(:key_for => [from, to])) do
129
129
  update(opts)
130
130
 
@@ -150,7 +150,7 @@ module Exchange
150
150
  # Exchange::ExternalAPI::Base.new.convert(23, :eur, :chf, :at => Time.gm(12,1,2011))
151
151
  # #=> 30.12
152
152
  #
153
- def convert(amount, from, to, opts={})
153
+ def convert amount, from, to, opts={}
154
154
  amount * rate(from, to, opts)
155
155
  end
156
156
 
@@ -52,7 +52,7 @@ module Exchange
52
52
  # @param [Array] retry_with An array of urls to retry the API call with if the call to the original URL should fail. These values will be shifted until a call succeeds or the number of maximum retries is reached
53
53
  # @todo install a timeout for slow requests, but respect when loading large files
54
54
  #
55
- def load_url(url, retries, retry_with)
55
+ def load_url url, retries, retry_with
56
56
  begin
57
57
  result = URI.parse(url).open.read
58
58
  rescue SocketError
@@ -13,6 +13,8 @@ module Exchange
13
13
 
14
14
  def_delegators :instance, :retries, :retries=, :app_id, :app_id=, :protocol, :protocol=, :fallback, :fallback=
15
15
 
16
+ # Constantize fallback apis on the fly
17
+ #
16
18
  def fallback_with_constantize
17
19
  self.fallback = Array(fallback_without_constantize).map do |fb|
18
20
  unless !fb || fb.is_a?(Class)
@@ -27,10 +29,16 @@ module Exchange
27
29
  alias_method :fallback_without_constantize, :fallback
28
30
  alias_method :fallback, :fallback_with_constantize
29
31
 
32
+ # The configuration parent module
33
+ # @return [Class] ExternalAPI, always
34
+ #
30
35
  def parent_module
31
36
  ExternalAPI
32
37
  end
33
38
 
39
+ # The configuration key
40
+ # @return [Symbol] :api, always
41
+ #
34
42
  def key
35
43
  :api
36
44
  end
@@ -32,7 +32,7 @@ module Exchange
32
32
  # @since 0.1
33
33
  # @version 0.7
34
34
  #
35
- def update(opts={})
35
+ def update opts={}
36
36
  time = helper.assure_time(opts[:at], :default => :now)
37
37
  times = map_retry_times time
38
38
 
@@ -57,7 +57,7 @@ module Exchange
57
57
  # @param [Time] time The exchange rate date for which the URL should be built
58
58
  # @return [String] An ECB API URL to get the xml from
59
59
  #
60
- def api_url(time)
60
+ def api_url time
61
61
  border = Time.now - 90 * 86400
62
62
  [ "#{config.protocol}:/",
63
63
  API_URL,
@@ -24,7 +24,7 @@ module Exchange
24
24
  # @example Update the Open Exchange Rates API to use the file of March 2, 2010
25
25
  # Exchange::ExternalAPI::OpenExchangeRates.new.update(:at => Time.gm(3,2,2010))
26
26
  #
27
- def update(opts={})
27
+ def update opts={}
28
28
  time = helper.assure_time(opts[:at])
29
29
 
30
30
  Call.new(api_url(time), :at => time, :api => self.class) do |result|
@@ -52,7 +52,7 @@ module Exchange
52
52
  # @since 0.1
53
53
  # @version 0.2.6
54
54
  #
55
- def api_url(time=nil)
55
+ def api_url time=nil
56
56
  today = Time.now
57
57
  [
58
58
  "#{config.protocol}:/",
@@ -10,7 +10,7 @@ module Exchange
10
10
  class Random < Base
11
11
 
12
12
  CURRENCIES = Exchange::ISO.currencies
13
- RANDOM_RATES = lambda { Hash[*CURRENCIES.zip(CURRENCIES.size.times.map{|i| rand}).flatten] }
13
+ RANDOM_RATES = lambda { Hash[*CURRENCIES.map{|c| [c, rand] }.flatten] }
14
14
 
15
15
  # Updates the rates with new random ones
16
16
  # The call gets cached for a maximum of 24 hours.
@@ -20,7 +20,7 @@ module Exchange
20
20
  # @example Update the currency bot API to use the file of March 2, 2010
21
21
  # Exchange::ExternalAPI::XavierMedia.new.update(:at => Time.gm(3,2,2010))
22
22
  #
23
- def update(opts={})
23
+ def update opts={}
24
24
  @base = :usd
25
25
  @rates = RANDOM_RATES.call
26
26
  @timestamp = Time.now.to_i
@@ -23,7 +23,7 @@ module Exchange
23
23
  # @example Update the currency bot API to use the file of March 2, 2010
24
24
  # Exchange::ExternalAPI::XavierMedia.new.update(:at => Time.gm(3,2,2010))
25
25
  #
26
- def update(opts={})
26
+ def update opts={}
27
27
  time = helper.assure_time(opts[:at], :default => :now)
28
28
  api_url = api_url(time)
29
29
 
@@ -40,7 +40,7 @@ module Exchange
40
40
  # @param [Time] time The exchange rate date for which the URL should be built
41
41
  # @return [String] An Xaviermedia API URL for the specified time
42
42
  #
43
- def api_url(time)
43
+ def api_url time
44
44
  ["#{config.protocol}:/", API_URL, "#{time.strftime("%Y/%m/%d")}.xml"].join('/')
45
45
  end
46
46
 
@@ -51,7 +51,7 @@ module Exchange
51
51
  # @since 0.6
52
52
  # @version 0.6
53
53
  #
54
- def api_opts(opts={})
54
+ def api_opts opts={}
55
55
  retry_urls = config.retries.times.map { |i| api_url(opts[:at] - 86400 * (i+1)) }
56
56
 
57
57
  { :format => :xml, :at => opts[:at], :retry_with => retry_urls }
@@ -63,7 +63,7 @@ module Exchange
63
63
  # @since 0.7
64
64
  # @version 0.7
65
65
  #
66
- def extract_timestamp(result)
66
+ def extract_timestamp result
67
67
  Time.gm(*result.css('fx_date').children[0].to_s.split('-')).to_i
68
68
  end
69
69
 
@@ -73,7 +73,7 @@ module Exchange
73
73
  # @since 0.7
74
74
  # @version 0.7
75
75
  #
76
- def extract_rates(result)
76
+ def extract_rates result
77
77
  rates_array = result.css('fx currency_code').children.map{|c| c.to_s.downcase.to_sym }.zip(result.css('fx rate').children.map{|c| BigDecimal.new(c.to_s) }).flatten
78
78
  to_hash!(rates_array)
79
79
  end
@@ -84,7 +84,7 @@ module Exchange
84
84
  # @since 0.7
85
85
  # @version 0.7
86
86
  #
87
- def extract_base_currency(result)
87
+ def extract_base_currency result
88
88
  result.css('basecurrency').children[0].to_s.downcase.to_sym
89
89
  end
90
90
 
@@ -18,7 +18,7 @@ module Exchange
18
18
  # @param [Hash] opts Options for assertion
19
19
  # @option opts [Symbol] :default a method that can be sent to Time if the argument is nil (:now for example)
20
20
  #
21
- def assure_time(arg=nil, opts={})
21
+ def assure_time arg=nil, opts={}
22
22
  if arg
23
23
  arg.kind_of?(Time) ? arg : Time.gm(*arg.split('-'))
24
24
  elsif opts[:default]
@@ -23,7 +23,7 @@ module Exchange
23
23
 
24
24
  def install_operation op
25
25
  self.class_eval <<-EOV
26
- def #{op}(amount, currency, precision=nil, opts={})
26
+ def #{op} amount, currency, precision=nil, opts={}
27
27
  minor = definitions[currency][:minor_unit]
28
28
  money = amount.is_a?(BigDecimal) ? amount : BigDecimal.new(amount.to_s, precision_for(amount, currency))
29
29
  if opts[:psych] && minor > 0
@@ -87,7 +87,7 @@ module Exchange
87
87
  # Exchange::ISO.instantiate("4523", "usd") #=> #<Bigdecimal 4523.00>
88
88
  # @note Reinstantiation is not needed in case the amount is already a big decimal. In this case, the maximum precision is already given.
89
89
  #
90
- def instantiate(amount, currency)
90
+ def instantiate amount, currency
91
91
  if amount.is_a?(BigDecimal)
92
92
  amount
93
93
  else
@@ -111,18 +111,14 @@ module Exchange
111
111
  # @example Convert a currency to a string without the currency
112
112
  # Exchange::ISO.stringif(34.34, :omr, :amount_only => true) #=> "34.340"
113
113
  #
114
- def stringify(amount, currency, opts={})
114
+ def stringify amount, currency, opts={}
115
115
  definition = definitions[currency]
116
116
  separators = definition[:separators] || {}
117
117
  format = "%.#{definition[:minor_unit]}f"
118
118
  string = format % amount
119
119
  major, minor = string.split('.')
120
-
121
- if separators[:major] && opts[:format] != :plain
122
- major.reverse!
123
- major.gsub!(/(\d{3})(?=.)/) { $1 + separators[:major] }
124
- major.reverse!
125
- end
120
+
121
+ major.gsub!(/(\d)(?=(\d\d\d)+(?!\d))/) { $1 + separators[:major] } if separators[:major] && opts[:format] != :plain
126
122
 
127
123
  string = minor ? major + (opts[:format] == :plain || !separators[:minor] ? '.' : separators[:minor]) + minor : major
128
124
  pre = [[:amount, :plain].include?(opts[:format]) && '', opts[:format] == :symbol && definition[:symbol], currency.to_s.upcase + ' '].detect{|a| a.is_a?(String)}
@@ -130,6 +126,14 @@ module Exchange
130
126
  "#{pre}#{string}"
131
127
  end
132
128
 
129
+ # Returns the symbol for a given currency. Returns nil if no symbol is present
130
+ # @param currency The currency to return the symbol for
131
+ # @return [String, NilClass] The symbol or nil
132
+ #
133
+ def symbol currency
134
+ definitions[currency][:symbol]
135
+ end
136
+
133
137
  # Use this to round a currency amount. This allows us to round exactly to the number of minors the currency has in the
134
138
  # iso definition
135
139
  # @param [BigDecimal, Fixed, Float, String] amount The amount of money you want to round
@@ -159,7 +163,7 @@ module Exchange
159
163
 
160
164
  # Forwards the assure_time method to the instance using singleforwardable
161
165
  #
162
- def_delegators :instance, :definitions, :instantiate, :stringify, :round, :ceil, :floor, :currencies, :country_map, :defines?, :assert_currency!
166
+ def_delegators :instance, :definitions, :instantiate, :stringify, :symbol, :round, :ceil, :floor, :currencies, :country_map, :defines?, :assert_currency!
163
167
 
164
168
  private
165
169
 
@@ -176,6 +180,9 @@ module Exchange
176
180
  new_hsh
177
181
  end
178
182
 
183
+ # Interpolates a string with separators every 3 characters
184
+ #
185
+
179
186
  # get a precision for a specified amount and a specified currency
180
187
  # @params [Float, Integer] amount The amount to get the precision for
181
188
  # @params [Symbol] currency the currency to get the precision for
@@ -85,8 +85,8 @@ module Exchange
85
85
  #
86
86
  def to other, options={}
87
87
  other = ISO.assert_currency!(other)
88
-
89
- if api_supports_currency?(other)
88
+
89
+ if api_supports_currency?(other) && api_supports_currency?(currency)
90
90
  opts = { :at => time, :from => self }.merge(options)
91
91
  Money.new(api.new.convert(value, currency, other, opts), other, opts)
92
92
  elsif fallback!
@@ -111,9 +111,12 @@ module Exchange
111
111
  # @!macro [attach] install_operation
112
112
  #
113
113
  def install_operation op
114
- define_method op do |*precision|
115
- psych = precision.first == :psych
116
- Exchange::Money.new(ISO.send(op, self.value, self.currency, psych ? nil : precision.first, {:psych => psych}), currency, :at => time, :from => self)
114
+ define_method op do |*arguments|
115
+ psych = arguments.first == :psych
116
+ precision = psych ? nil : arguments.first
117
+ val = ISO.send(op, self.value, self.currency, precision, {:psych => psych})
118
+
119
+ Exchange::Money.new(val, currency, :at => time, :from => self)
117
120
  end
118
121
  end
119
122
 
@@ -309,12 +312,18 @@ module Exchange
309
312
  # @example Convert a currency with a three decimal minor to a string with a currency symbol
310
313
  # Exchange::Money.new(34.34, :usd).to_s(:symbol) #=> "$34.34"
311
314
  # @example Convert a currency with a three decimal minor to a string with just the amount
312
- # Exchange::Money.new(34.34, :omr).to_s(:amount) #=> "34.340"
315
+ # Exchange::Money.new(3423.34, :usd).to_s(:amount) #=> "3,423.34"
316
+ # @example Convert a currency with just the plain amount using decimal notation
317
+ # Exchange::Money.new(3423.34, :omr).to_s(:plain) #=> "3423.340"
313
318
  #
314
319
  def to_s format=:currency
315
320
  ISO.stringify(value, currency, :format => format)
316
321
  end
317
322
 
323
+ # Returns the symbol for the given currency
324
+ # @since 1.0
325
+ # @version 0.1
326
+
318
327
  private
319
328
 
320
329
  # Fallback to the next api defined in the api fallbacks. Changes the api for the given instance
@@ -340,7 +349,7 @@ module Exchange
340
349
  # @since 0.6
341
350
  # @version 0.6
342
351
  #
343
- def is_currency? other
352
+ def is_money? other
344
353
  other.is_a?(Exchange::Money)
345
354
  end
346
355
 
@@ -351,7 +360,7 @@ module Exchange
351
360
  # @version 0.6
352
361
  #
353
362
  def is_same_currency? other
354
- is_currency?(other) && other.currency == currency
363
+ is_money?(other) && other.currency == currency
355
364
  end
356
365
 
357
366
  # determine if another given object is an instance of Exchange::Money and has another currency
@@ -361,7 +370,7 @@ module Exchange
361
370
  # @version 0.6
362
371
  #
363
372
  def is_other_currency? other
364
- is_currency?(other) && other.currency != currency
373
+ is_money?(other) && other.currency != currency
365
374
  end
366
375
 
367
376
  # determine wether the chosen api supports converting the given currency
@@ -379,7 +388,7 @@ module Exchange
379
388
  # @version 0.6
380
389
  #
381
390
  def test_for_currency_mix_error other
382
- raise ImplicitConversionError.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.implicit_conversions && other.is_a?(Money) && other.currency != currency
391
+ raise ImplicitConversionError.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.implicit_conversions && is_other_currency?(other)
383
392
  end
384
393
 
385
394
  # Helper method to raise a no rate error for a given currency if no rate is given
@@ -389,13 +398,13 @@ module Exchange
389
398
  # @version 0.7.2
390
399
  #
391
400
  def raise_no_rate_error other
392
- raise NoRateError.new("Cannot convert to #{other} because the defined api nor the fallbacks provide a rate")
401
+ raise NoRateError.new("Cannot convert #{currency} to #{other} because the defined api nor the fallbacks provide a rate")
393
402
  end
394
403
 
395
404
  end
396
405
 
397
406
  # The error that will get thrown when implicit conversions take place and are not allowed
398
407
  #
399
- ImplicitConversionError = Class.new(StandardError)
408
+ ImplicitConversionError = Class.new StandardError
400
409
 
401
410
  end
@@ -781,6 +781,22 @@ describe "Exchange::ISO" do
781
781
  subject.floor(BigDecimal.new("23.232524"), :clp, nil, {:psych => true}).should == BigDecimal.new("19")
782
782
  end
783
783
  end
784
+ describe "self.symbol" do
785
+ context "with a symbol present" do
786
+ it "should return the symbol" do
787
+ subject.symbol(:usd).should == '$'
788
+ subject.symbol(:gbp).should == '£'
789
+ subject.symbol(:eur).should == '€'
790
+ end
791
+ end
792
+ context "with no symbol present" do
793
+ it "should return nil" do
794
+ subject.symbol(:chf).should be_nil
795
+ subject.symbol(:etb).should be_nil
796
+ subject.symbol(:tnd).should be_nil
797
+ end
798
+ end
799
+ end
784
800
  describe "self.stringify" do
785
801
  it "should stringify a currency according to ISO 4217 Definitions" do
786
802
  subject.stringify(BigDecimal.new("23234234.232524"), :tnd).should == "TND 23234234.233"
@@ -75,14 +75,28 @@ describe "Exchange::Money" do
75
75
  end
76
76
  end
77
77
  end
78
- context "with a currency not provided by the given api" do
78
+ context "with a 'from' currency not provided by the given api" do
79
+ subject { Exchange::Money.new(36.36, :chf) }
80
+ context "but provided by a fallback api" do
81
+ it "should use the fallback" do
82
+ subject.api::CURRENCIES.should_receive(:include?).with(:usd).and_return true
83
+ subject.api::CURRENCIES.should_receive(:include?).with(:chf).and_return true
84
+ URI.should_receive(:parse).with("http://openexchangerates.org/api/latest.json?app_id=").once.and_raise Exchange::ExternalAPI::APIError
85
+ mock_api("http://api.finance.xaviermedia.com/api/#{Time.now.strftime("%Y/%m/%d")}.xml", fixture('api_responses/example_xml_api.xml'), 3)
86
+ subject.to(:usd).value.round(2).should == 40.00
87
+ subject.to(:usd).currency.should == :usd
88
+ subject.to(:usd).should be_kind_of Exchange::Money
89
+ end
90
+ end
91
+ end
92
+ context "with a 'to' currency not provided by the given api" do
79
93
  context "but provided by a fallback api" do
80
94
  it "should use the fallback" do
81
95
  subject.api::CURRENCIES.stub! :include? => false
82
96
  mock_api("http://api.finance.xaviermedia.com/api/#{Time.now.strftime("%Y/%m/%d")}.xml", fixture('api_responses/example_xml_api.xml'), 3)
83
- subject.to(:ch).value.round(2).should == 36.36
84
- subject.to(:ch).currency.should == :chf
85
- subject.to(:ch).should be_kind_of Exchange::Money
97
+ subject.to(:chf).value.round(2).should == 36.36
98
+ subject.to(:chf).currency.should == :chf
99
+ subject.to(:chf).should be_kind_of Exchange::Money
86
100
  end
87
101
  end
88
102
  context "but not provided by any fallback api" do
metadata CHANGED
@@ -1,81 +1,72 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: exchange
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
5
- prerelease:
4
+ version: 1.1.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Beat Richartz
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-04-08 00:00:00.000000000 Z
11
+ date: 2013-07-29 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: nokogiri
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - '>='
20
18
  - !ruby/object:Gem::Version
21
19
  version: 1.0.0
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - '>='
28
25
  - !ruby/object:Gem::Version
29
26
  version: 1.0.0
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: json
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - '>='
36
32
  - !ruby/object:Gem::Version
37
33
  version: 1.0.0
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - '>='
44
39
  - !ruby/object:Gem::Version
45
40
  version: 1.0.0
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: yard
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
- - - ! '>='
45
+ - - '>='
52
46
  - !ruby/object:Gem::Version
53
47
  version: 0.7.4
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
- - - ! '>='
52
+ - - '>='
60
53
  - !ruby/object:Gem::Version
61
54
  version: 0.7.4
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: bundler
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
- - - ! '>='
59
+ - - '>='
68
60
  - !ruby/object:Gem::Version
69
61
  version: 1.0.0
70
62
  type: :development
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
- - - ! '>='
66
+ - - '>='
76
67
  - !ruby/object:Gem::Version
77
68
  version: 1.0.0
78
- description: ! 'Simple Money handling for your ruby app – ISO4217-compatible Currency
69
+ description: 'Simple Money handling for your ruby app – ISO4217-compatible Currency
79
70
  instantiation, conversion, string formatting and more via an intuitive DSL: 1.in(:usd).to(:eur)'
80
71
  email: attr_accessor@gmail.com
81
72
  executables: []
@@ -85,6 +76,8 @@ files:
85
76
  - .document
86
77
  - .gitignore
87
78
  - .rspec
79
+ - .ruby-gemset
80
+ - .ruby-version
88
81
  - .travis.yml
89
82
  - Gemfile
90
83
  - LICENSE.txt
@@ -165,27 +158,26 @@ files:
165
158
  homepage: http://beatrichartz.github.com/exchange
166
159
  licenses:
167
160
  - MIT
161
+ metadata: {}
168
162
  post_install_message:
169
163
  rdoc_options: []
170
164
  require_paths:
171
165
  - lib
172
166
  required_ruby_version: !ruby/object:Gem::Requirement
173
- none: false
174
167
  requirements:
175
- - - ! '>='
168
+ - - '>='
176
169
  - !ruby/object:Gem::Version
177
170
  version: '0'
178
171
  required_rubygems_version: !ruby/object:Gem::Requirement
179
- none: false
180
172
  requirements:
181
- - - ! '>='
173
+ - - '>='
182
174
  - !ruby/object:Gem::Version
183
175
  version: '0'
184
176
  requirements: []
185
177
  rubyforge_project:
186
- rubygems_version: 1.8.25
178
+ rubygems_version: 2.0.3
187
179
  signing_key:
188
- specification_version: 3
180
+ specification_version: 4
189
181
  summary: Simple Money handling for your ruby app
190
182
  test_files:
191
183
  - spec/exchange/cache/base_spec.rb