exchange 1.1.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/README.rdoc +4 -4
- data/exchange.gemspec +1 -1
- data/lib/exchange/base.rb +1 -1
- data/lib/exchange/core_extensions/float/error_safe.rb +4 -4
- data/lib/exchange/iso.rb +20 -4
- data/lib/exchange/money.rb +1 -1
- data/spec/exchange/cache/base_spec.rb +1 -1
- data/spec/exchange/cache/memcached_spec.rb +4 -4
- data/spec/exchange/cache/memory_spec.rb +2 -2
- data/spec/exchange/cache/rails_spec.rb +3 -3
- data/spec/exchange/cache/redis_spec.rb +2 -2
- data/spec/exchange/core_extensions/float/error_safe_spec.rb +1 -1
- data/spec/exchange/core_extensions/numeric/conversability_spec.rb +1 -1
- data/spec/exchange/external_api/call_spec.rb +2 -2
- data/spec/exchange/external_api/ecb_spec.rb +1 -1
- data/spec/exchange/external_api/random_spec.rb +1 -1
- data/spec/exchange/helper_spec.rb +1 -1
- data/spec/exchange/iso_spec.rb +26 -0
- data/spec/exchange/money_spec.rb +4 -4
- data/spec/spec_helper.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91f5a8992aa043b158277a444d70b03dcc83dba6
|
4
|
+
data.tar.gz: 1666ddfa3deca602192b2ac49a9171f329b25d9c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d301bbf1ef608479b9b51e5ca4eab45f1a35c62265f445c37f1c82e628aa8a884f4063fc3f6865c2afdd593c429f30fe32f2b26d629856ef36f59478d4fcba90
|
7
|
+
data.tar.gz: 6c51362dfbdb84d89feb15d8c4b9a34f01bf39cded31ae93ea50dfbc6930ea461d2c1f11e0b13da61a83ced54581f01ca6ef0e2e01ec02e54ccb369a1e250db6
|
data/.travis.yml
CHANGED
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
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
|
-
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,
|
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, ree and rubinius (1.9 and 2.0 mode). Exchange will in future versions take advantage of 2.1 features like refinements, be sure to check this page for updates!
|
4
4
|
|
5
5
|
You can use it with just plain ruby projects, in Rails 2 and 3, Sinatra or whatever Framework you like.
|
6
6
|
|
@@ -9,7 +9,7 @@ You can use it with just plain ruby projects, in Rails 2 and 3, Sinatra or whate
|
|
9
9
|
== Installation
|
10
10
|
=== Bundler / Rails
|
11
11
|
Add it to your Gemfile
|
12
|
-
gem "exchange", ">=
|
12
|
+
gem "exchange", ">= 1.1.0"
|
13
13
|
=== Manually
|
14
14
|
Just install it as a gem
|
15
15
|
gem install exchange
|
@@ -52,7 +52,7 @@ Exchange uses BigDecimal in all its currency and conversion operations, so you w
|
|
52
52
|
|
53
53
|
=== BigDecimal? Sounds slow to me
|
54
54
|
|
55
|
-
If performance combined with conversion is your concern,
|
55
|
+
If performance combined with conversion is your concern, don't worry. With ruby 1.9.3, you'll get about the following results for money instantiation (e.g. 1.in(:eur))
|
56
56
|
|
57
57
|
1000 operations
|
58
58
|
|
@@ -426,6 +426,6 @@ Please note that only open source APIs can be accepted as contributions to this
|
|
426
426
|
|
427
427
|
== Copyright
|
428
428
|
|
429
|
-
Copyright (c)
|
429
|
+
Copyright (c) 2013 Beat Richartz. See LICENSE.txt for
|
430
430
|
further details.
|
431
431
|
|
data/exchange.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
|
|
8
8
|
s.authors = ["Beat Richartz"]
|
9
9
|
s.description = "Simple Money handling for your ruby app – ISO4217-compatible Currency instantiation, conversion, string formatting and more via an intuitive DSL: 1.in(:usd).to(:eur)"
|
10
10
|
s.email = "attr_accessor@gmail.com"
|
11
|
-
s.homepage = "http://beatrichartz.github.
|
11
|
+
s.homepage = "http://beatrichartz.github.io/exchange"
|
12
12
|
s.licenses = ["MIT"]
|
13
13
|
s.require_paths = ["lib"]
|
14
14
|
s.summary = "Simple Money handling for your ruby app"
|
data/lib/exchange/base.rb
CHANGED
@@ -8,8 +8,8 @@ module Exchange
|
|
8
8
|
# Installs a method chain that overwrites the old error prone meth with the new one
|
9
9
|
#
|
10
10
|
def self.money_error_preventing_method_chain base, meth
|
11
|
-
base.send :alias_method, :"#{meth}
|
12
|
-
base.send :alias_method, meth, :"#{meth}
|
11
|
+
base.send :alias_method, :"#{meth}_with_errors", meth
|
12
|
+
base.send :alias_method, meth, :"#{meth}_without_errors"
|
13
13
|
end
|
14
14
|
|
15
15
|
# @!macro prevent_errors_with_exchange_for
|
@@ -18,11 +18,11 @@ module Exchange
|
|
18
18
|
# @method $1(other)
|
19
19
|
#
|
20
20
|
def self.prevent_errors_with_exchange_for base, meth
|
21
|
-
base.send(:define_method, :"#{meth}
|
21
|
+
base.send(:define_method, :"#{meth}_without_errors", lambda { |other|
|
22
22
|
if other.is_a?(Exchange::Money)
|
23
23
|
BigDecimal.new(self.to_s).send(meth, other.value).to_f
|
24
24
|
else
|
25
|
-
send(:"#{meth}
|
25
|
+
send(:"#{meth}_with_errors", other)
|
26
26
|
end
|
27
27
|
})
|
28
28
|
money_error_preventing_method_chain base, meth
|
data/lib/exchange/iso.rb
CHANGED
@@ -180,19 +180,35 @@ module Exchange
|
|
180
180
|
new_hsh
|
181
181
|
end
|
182
182
|
|
183
|
-
# Interpolates a string with separators every 3 characters
|
184
|
-
#
|
185
|
-
|
186
183
|
# get a precision for a specified amount and a specified currency
|
184
|
+
#
|
187
185
|
# @params [Float, Integer] amount The amount to get the precision for
|
188
186
|
# @params [Symbol] currency the currency to get the precision for
|
189
187
|
#
|
190
188
|
def precision_for amount, currency
|
191
189
|
defined_minor_precision = definitions[currency][:minor_unit]
|
192
|
-
|
190
|
+
match = amount.to_s.match(/^-?(\d*)\.?(\d*)e?(-?\d+)?$/).to_a[1..3]
|
191
|
+
given_major_precision, given_minor_precision = precision_from_match *match
|
193
192
|
|
194
193
|
given_major_precision + [defined_minor_precision, given_minor_precision].max
|
195
194
|
end
|
196
195
|
|
196
|
+
# Get the precision from a match with /^-?(\d*)\.?(\d*)e?(-?\d+)?$/
|
197
|
+
#
|
198
|
+
# @params [String] major The major amount of the match as a string
|
199
|
+
# @params [String] minor The minor amount of the match as a string
|
200
|
+
# @params [String] rational The rational of the match as a string
|
201
|
+
# @return [Array] An array containing the major and the minor precision in this order
|
202
|
+
#
|
203
|
+
def precision_from_match major, minor, rational=nil
|
204
|
+
if rational
|
205
|
+
leftover_minor_precision = minor.eql?('0') ? 0 : minor.size
|
206
|
+
rational_precision = rational.delete('-').to_i
|
207
|
+
[major.size, leftover_minor_precision.send(rational.start_with?('-') ? :+ : :-, rational_precision)]
|
208
|
+
else
|
209
|
+
[major, minor].map(&:size)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
197
213
|
end
|
198
214
|
end
|
data/lib/exchange/money.rb
CHANGED
@@ -86,7 +86,7 @@ module Exchange
|
|
86
86
|
def to other, options={}
|
87
87
|
other = ISO.assert_currency!(other)
|
88
88
|
|
89
|
-
if api_supports_currency?(
|
89
|
+
if api_supports_currency?(currency) && api_supports_currency?(other)
|
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!
|
@@ -6,7 +6,7 @@ describe "Exchange::Cache::Base" do
|
|
6
6
|
describe "key generation" do
|
7
7
|
before(:each) do
|
8
8
|
time = Time.gm 2012, 03, 01, 23, 23, 23
|
9
|
-
Time.stub
|
9
|
+
Time.stub :now => time
|
10
10
|
end
|
11
11
|
context "with a daily cache" do
|
12
12
|
it "should build a timestamped key with the class given, the yearday and the year" do
|
@@ -15,7 +15,7 @@ describe "Exchange::CacheDalli::Client" do
|
|
15
15
|
Exchange.configuration.reset
|
16
16
|
end
|
17
17
|
describe "client" do
|
18
|
-
let(:client) {
|
18
|
+
let(:client) { double('memcached') }
|
19
19
|
before(:each) do
|
20
20
|
Exchange::GemLoader.new('dalli').try_load
|
21
21
|
end
|
@@ -28,7 +28,7 @@ describe "Exchange::CacheDalli::Client" do
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
describe "wipe_client!" do
|
31
|
-
let(:client) {
|
31
|
+
let(:client) { double('memcached') }
|
32
32
|
|
33
33
|
it "should set the client to nil" do
|
34
34
|
Dalli::Client.should_receive(:new).with("HOST:PORT").and_return(client)
|
@@ -38,7 +38,7 @@ describe "Exchange::CacheDalli::Client" do
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
describe "cached" do
|
41
|
-
let(:client) {
|
41
|
+
let(:client) { double('memcached', :get => '') }
|
42
42
|
before(:each) do
|
43
43
|
Dalli::Client.should_receive(:new).with("HOST:PORT").and_return(client)
|
44
44
|
end
|
@@ -69,7 +69,7 @@ describe "Exchange::CacheDalli::Client" do
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
context "when no cached result exists" do
|
72
|
-
let(:client) {
|
72
|
+
let(:client) { double('memcached') }
|
73
73
|
context "when returning nil" do
|
74
74
|
before(:each) do
|
75
75
|
subject.should_receive(:key).with('API_CLASS', {}).twice.and_return('KEY')
|
@@ -15,7 +15,7 @@ describe "Exchange::Cache::Memory" do
|
|
15
15
|
context "with a class" do
|
16
16
|
before(:each) do
|
17
17
|
@time = Time.gm(2012,10,10,12)
|
18
|
-
Time.stub
|
18
|
+
Time.stub :now => @time
|
19
19
|
end
|
20
20
|
it "should yield a valid instance variable name" do
|
21
21
|
subject.send(:instance_variable_name, Exchange::ExternalAPI::Ecb, {}).should == '@exchange_externalapi_ecb_2012_284_2012_284'
|
@@ -25,7 +25,7 @@ describe "Exchange::Cache::Memory" do
|
|
25
25
|
describe "clean_out_cache" do
|
26
26
|
before(:each) do
|
27
27
|
@time = Time.gm(2012,10,10,12)
|
28
|
-
Time.stub
|
28
|
+
Time.stub :now => @time
|
29
29
|
subject.instance_variable_set '@exchange_externalapi_someapi_2011_284_2011_284_chfeur', 'Expired'
|
30
30
|
subject.instance_variable_set '@exchange_externalapi_someapi_2010_284_2012_284_chfeur', 'Valid1'
|
31
31
|
subject.instance_variable_set '@exchange_externalapi_ecb_2012_283_2011_283', 'Expired'
|
@@ -18,7 +18,7 @@ describe "Exchange::Cache::Rails" do
|
|
18
18
|
Exchange.configuration.reset
|
19
19
|
end
|
20
20
|
describe "client" do
|
21
|
-
let(:client) {
|
21
|
+
let(:client) { double('rails_cache') }
|
22
22
|
it "should set up a client on the specified host and port for the cache" do
|
23
23
|
::Rails.should_receive(:cache).and_return(client)
|
24
24
|
subject.client.should == client
|
@@ -29,7 +29,7 @@ describe "Exchange::Cache::Rails" do
|
|
29
29
|
lambda { subject.cached('API_CLASS') }.should raise_error(Exchange::Cache::CachingWithoutBlockError)
|
30
30
|
end
|
31
31
|
context "when a result is returned" do
|
32
|
-
let(:client) {
|
32
|
+
let(:client) { double('rails_cache') }
|
33
33
|
context "with a daily cache" do
|
34
34
|
before(:each) do
|
35
35
|
subject.should_receive(:key).with('API_CLASS', {}).and_return('KEY')
|
@@ -56,7 +56,7 @@ describe "Exchange::Cache::Rails" do
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
context "when no result is returned" do
|
59
|
-
let(:client) {
|
59
|
+
let(:client) { double('rails_cache') }
|
60
60
|
before(:each) do
|
61
61
|
subject.should_receive(:key).with('API_CLASS', {}).at_most(3).times.and_return('KEY')
|
62
62
|
::Rails.should_receive(:cache).twice.and_return(client)
|
@@ -17,7 +17,7 @@ describe "Exchange::Cache::Redis" do
|
|
17
17
|
Exchange.configuration.reset
|
18
18
|
end
|
19
19
|
describe "client" do
|
20
|
-
let(:client) {
|
20
|
+
let(:client) { double('redis') }
|
21
21
|
after(:each) do
|
22
22
|
subject.instance_variable_set "@client", nil
|
23
23
|
end
|
@@ -27,7 +27,7 @@ describe "Exchange::Cache::Redis" do
|
|
27
27
|
end
|
28
28
|
end
|
29
29
|
describe "cached" do
|
30
|
-
let(:client) {
|
30
|
+
let(:client) { double('redis', :get => nil) }
|
31
31
|
before(:each) do
|
32
32
|
::Redis.should_receive(:new).with(:host => 'HOST', :port => 'PORT').and_return(client)
|
33
33
|
end
|
@@ -27,7 +27,7 @@ describe "Exchange::ExternalAPI::Call" do
|
|
27
27
|
@count = 0
|
28
28
|
@uri_mock.should_receive(:open).at_most(3).times.and_return do
|
29
29
|
@count += 1
|
30
|
-
@count == 3 ?
|
30
|
+
@count == 3 ? double('opened', :read => fixture('api_responses/example_json_api.json')) : raise(OpenURI::HTTPError.new('404', 'URI'))
|
31
31
|
end
|
32
32
|
Exchange::ExternalAPI::Call.new('JSON_API') do |result|
|
33
33
|
result.should == JSON.load(fixture('api_responses/example_json_api.json'))
|
@@ -62,7 +62,7 @@ describe "Exchange::ExternalAPI::Call" do
|
|
62
62
|
@count = 0
|
63
63
|
@uri_mock.should_receive(:open).at_most(3).times.and_return do
|
64
64
|
@count += 1
|
65
|
-
@count == 3 ?
|
65
|
+
@count == 3 ? double('opened', :read => fixture('api_responses/example_xml_api.xml')) : raise(OpenURI::HTTPError.new('404', 'URI'))
|
66
66
|
end
|
67
67
|
Exchange::ExternalAPI::Call.new('XML_API', :format => :xml) do |result|
|
68
68
|
result.to_s.should == Nokogiri::XML.parse(fixture('api_responses/example_xml_api.xml').sub("\n", '')).to_s
|
data/spec/exchange/iso_spec.rb
CHANGED
@@ -737,6 +737,32 @@ describe "Exchange::ISO" do
|
|
737
737
|
end
|
738
738
|
end
|
739
739
|
end
|
740
|
+
context "given a float with scientific notation" do
|
741
|
+
context "with bigger precision than the definition" do
|
742
|
+
it "should instantiate a big decimal with the given precision" do
|
743
|
+
BigDecimal.should_receive(:new).with("6.0e-05",6).and_return('INSTANCE')
|
744
|
+
subject.instantiate(6.0e-05, :tnd).should == 'INSTANCE'
|
745
|
+
BigDecimal.should_receive(:new).with("600000.0",8).and_return('INSTANCE')
|
746
|
+
subject.instantiate(6.0e05, :sar).should == 'INSTANCE'
|
747
|
+
BigDecimal.should_receive(:new).with("1.456e-08",12).and_return('INSTANCE')
|
748
|
+
subject.instantiate(1.456e-08, :omr).should == 'INSTANCE'
|
749
|
+
BigDecimal.should_receive(:new).with("145600000.0",12).and_return('INSTANCE')
|
750
|
+
subject.instantiate(1.456e08, :omr).should == 'INSTANCE'
|
751
|
+
end
|
752
|
+
end
|
753
|
+
context "with smaller precision than the definition" do
|
754
|
+
it "should instantiate a big decimal with the defined precision" do
|
755
|
+
BigDecimal.should_receive(:new).with("0.6",4).and_return('INSTANCE')
|
756
|
+
subject.instantiate(6.0e-01, :tnd).should == 'INSTANCE'
|
757
|
+
BigDecimal.should_receive(:new).with("60.0",4).and_return('INSTANCE')
|
758
|
+
subject.instantiate(6.0e01, :sar).should == 'INSTANCE'
|
759
|
+
BigDecimal.should_receive(:new).with("0.14",4).and_return('INSTANCE')
|
760
|
+
subject.instantiate(1.4e-01, :omr).should == 'INSTANCE'
|
761
|
+
BigDecimal.should_receive(:new).with("14.56",5).and_return('INSTANCE')
|
762
|
+
subject.instantiate(1.456e01, :omr).should == 'INSTANCE'
|
763
|
+
end
|
764
|
+
end
|
765
|
+
end
|
740
766
|
context "given a big decimal" do
|
741
767
|
let!(:bigdecimal) { BigDecimal.new("23.23") }
|
742
768
|
it "should instantiate a big decimal according to the iso standards" do
|
data/spec/exchange/money_spec.rb
CHANGED
@@ -92,7 +92,7 @@ describe "Exchange::Money" do
|
|
92
92
|
context "with a 'to' currency not provided by the given api" do
|
93
93
|
context "but provided by a fallback api" do
|
94
94
|
it "should use the fallback" do
|
95
|
-
subject.api::CURRENCIES.stub
|
95
|
+
subject.api::CURRENCIES.stub :include? => false
|
96
96
|
mock_api("http://api.finance.xaviermedia.com/api/#{Time.now.strftime("%Y/%m/%d")}.xml", fixture('api_responses/example_xml_api.xml'), 3)
|
97
97
|
subject.to(:chf).value.round(2).should == 36.36
|
98
98
|
subject.to(:chf).currency.should == :chf
|
@@ -101,9 +101,9 @@ describe "Exchange::Money" do
|
|
101
101
|
end
|
102
102
|
context "but not provided by any fallback api" do
|
103
103
|
it "should raise the no rate error" do
|
104
|
-
Exchange::ExternalAPI::OpenExchangeRates::CURRENCIES.stub
|
105
|
-
Exchange::ExternalAPI::XavierMedia::CURRENCIES.stub
|
106
|
-
Exchange::ExternalAPI::Ecb::CURRENCIES.stub
|
104
|
+
Exchange::ExternalAPI::OpenExchangeRates::CURRENCIES.stub :include? => false
|
105
|
+
Exchange::ExternalAPI::XavierMedia::CURRENCIES.stub :include? => false
|
106
|
+
Exchange::ExternalAPI::Ecb::CURRENCIES.stub :include? => false
|
107
107
|
lambda { subject.to(:xaf) }.should raise_error Exchange::NoRateError
|
108
108
|
end
|
109
109
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -21,7 +21,7 @@ module HelperMethods
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def mock_api(adress, response, count=1)
|
24
|
-
@uri_mock =
|
24
|
+
@uri_mock = double('uri', :open => double('opened_uri', :read => response))
|
25
25
|
URI.should_receive(:parse).with(adress).at_most(count).times.and_return(@uri_mock)
|
26
26
|
end
|
27
27
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: exchange
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Beat Richartz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-11-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -155,7 +155,7 @@ files:
|
|
155
155
|
- spec/support/api_responses/example_historic_json.json
|
156
156
|
- spec/support/api_responses/example_json_api.json
|
157
157
|
- spec/support/api_responses/example_xml_api.xml
|
158
|
-
homepage: http://beatrichartz.github.
|
158
|
+
homepage: http://beatrichartz.github.io/exchange
|
159
159
|
licenses:
|
160
160
|
- MIT
|
161
161
|
metadata: {}
|
@@ -175,7 +175,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
175
175
|
version: '0'
|
176
176
|
requirements: []
|
177
177
|
rubyforge_project:
|
178
|
-
rubygems_version: 2.0.
|
178
|
+
rubygems_version: 2.0.7
|
179
179
|
signing_key:
|
180
180
|
specification_version: 4
|
181
181
|
summary: Simple Money handling for your ruby app
|