exchange 1.0.4 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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