fixer 0.1.1 → 0.2.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.
- data/.gitignore +4 -0
- data/.rspec +1 -0
- data/.rvmrc +2 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +31 -0
- data/README.markdown +18 -8
- data/Rakefile +12 -0
- data/fixer.gemspec +26 -0
- data/lib/fixer/cache.rb +52 -0
- data/lib/fixer/version.rb +2 -1
- data/lib/fixer.rb +7 -8
- data/spec/fixer/cache_spec.rb +80 -0
- data/spec/fixer_spec.rb +22 -23
- data/spec/spec_helper.rb +1 -0
- metadata +24 -17
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
-cfd
|
data/.rvmrc
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
fixer (0.2.0)
|
5
|
+
nokogiri (~> 1.4.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: http://rubygems.org/
|
9
|
+
specs:
|
10
|
+
diff-lcs (1.1.2)
|
11
|
+
nokogiri (1.4.3.1)
|
12
|
+
rake (0.8.7)
|
13
|
+
rspec (2.0.0.rc)
|
14
|
+
rspec-core (= 2.0.0.rc)
|
15
|
+
rspec-expectations (= 2.0.0.rc)
|
16
|
+
rspec-mocks (= 2.0.0.rc)
|
17
|
+
rspec-core (2.0.0.rc)
|
18
|
+
rspec-expectations (2.0.0.rc)
|
19
|
+
diff-lcs (>= 1.1.2)
|
20
|
+
rspec-mocks (2.0.0.rc)
|
21
|
+
rspec-core (= 2.0.0.rc)
|
22
|
+
rspec-expectations (= 2.0.0.rc)
|
23
|
+
|
24
|
+
PLATFORMS
|
25
|
+
ruby
|
26
|
+
|
27
|
+
DEPENDENCIES
|
28
|
+
fixer!
|
29
|
+
nokogiri (~> 1.4.0)
|
30
|
+
rake
|
31
|
+
rspec (= 2.0.0.rc)
|
data/README.markdown
CHANGED
@@ -3,18 +3,28 @@ Fixer
|
|
3
3
|
|
4
4
|
Fixer is a Ruby wrapper to the [current and historical foreign exchange rate feeds provided by the European Central Bank](http://www.ecb.europa.eu/stats/exchange/eurofxref/html/index.en.html).
|
5
5
|
|
6
|
-
|
6
|
+
I am also throwing in a *minimal* cache. Use if it suits you or implement your own.
|
7
|
+
|
8
|
+
Bonus: Use the Money gem.
|
7
9
|
|
8
10
|
Usage
|
9
11
|
-----
|
10
12
|
|
11
|
-
#
|
12
|
-
Fixer.
|
13
|
+
# Download the daily rates.
|
14
|
+
Fixer.daily
|
15
|
+
|
16
|
+
# Download the 90-day historical rates.
|
17
|
+
Fixer.historical_90
|
13
18
|
|
14
|
-
#
|
15
|
-
Fixer.
|
19
|
+
# Download all historical rates.
|
20
|
+
Fixer.historical
|
16
21
|
|
17
|
-
#
|
18
|
-
Fixer
|
22
|
+
# Use the built-in cache.
|
23
|
+
cache = Fixer::Cache
|
24
|
+
cache.to_usd
|
25
|
+
=> 1.397
|
26
|
+
cache.base = "EUR"
|
27
|
+
cache.to_eur
|
28
|
+
=> 0.7158
|
19
29
|
|
20
|
-
# It doesn't get simpler than this.
|
30
|
+
# It doesn't get any simpler than this.
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "bundler"
|
3
|
+
require "rspec/core/rake_task"
|
4
|
+
|
5
|
+
Bundler::GemHelper.install_tasks
|
6
|
+
|
7
|
+
desc "Run all specs in spec directory"
|
8
|
+
RSpec::Core::RakeTask.new(:spec) do |spec|
|
9
|
+
spec.pattern = "spec/**/*_spec.rb"
|
10
|
+
end
|
11
|
+
|
12
|
+
task :default => :spec
|
data/fixer.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "fixer/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "fixer"
|
7
|
+
s.version = Fixer::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Hakan Ensari"]
|
10
|
+
s.email = ["code@papercavalier.com"]
|
11
|
+
s.homepage = "http://fixer.heroku.com"
|
12
|
+
s.summary = "A Ruby wrapper to the European Central Bank exchange rate feeds"
|
13
|
+
s.description = "Fixer is a Ruby wrapper to the current and historical foreign exchange or FX rate feeds provided by the European Central Bank."
|
14
|
+
|
15
|
+
s.rubyforge_project = "fixer"
|
16
|
+
|
17
|
+
s.add_dependency("nokogiri", ["~> 1.4.0"])
|
18
|
+
|
19
|
+
s.add_development_dependency("rake")
|
20
|
+
s.add_development_dependency("rspec", ["= 2.0.0.rc"])
|
21
|
+
|
22
|
+
s.files = `git ls-files`.split("\n")
|
23
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
24
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
25
|
+
s.require_paths = ["lib"]
|
26
|
+
end
|
data/lib/fixer/cache.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Fixer
|
3
|
+
class Cache
|
4
|
+
class << self
|
5
|
+
def base
|
6
|
+
@base || "EUR"
|
7
|
+
end
|
8
|
+
|
9
|
+
def base=(name)
|
10
|
+
@base = name
|
11
|
+
end
|
12
|
+
|
13
|
+
def expire
|
14
|
+
@rates = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(sym, *args, &block)
|
18
|
+
if sym.to_s =~ /^to_(.*)$/
|
19
|
+
counter = $1.upcase
|
20
|
+
var_name = "@#{base}_#{counter}".to_sym
|
21
|
+
rate = instance_variable_get(var_name)
|
22
|
+
return rate unless rate.nil?
|
23
|
+
|
24
|
+
rate = quote(counter) / quote(base)
|
25
|
+
rate = (rate * 10000).round.to_f / 10000
|
26
|
+
instance_variable_set(var_name, rate)
|
27
|
+
rate
|
28
|
+
else
|
29
|
+
super
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def rates
|
36
|
+
begin
|
37
|
+
@rates ||= Fixer.daily.first[:rates].push({ :currency => "EUR", :rate => "1.0" })
|
38
|
+
rescue
|
39
|
+
raise Error, "Cache not valid"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def quote(counter)
|
44
|
+
rate = rates.detect { |rate| rate[:currency] == counter }
|
45
|
+
raise(Error, "Currency not valid") if rate.nil?
|
46
|
+
rate[:rate].to_f
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Error < StandardError; end
|
52
|
+
end
|
data/lib/fixer/version.rb
CHANGED
data/lib/fixer.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'open-uri'
|
3
3
|
require 'nokogiri'
|
4
|
+
require File.dirname(__FILE__) + '/fixer/cache'
|
4
5
|
|
5
6
|
module Fixer
|
6
|
-
BASE_URL = 'http://www.ecb.europa.eu/stats/eurofxref/'
|
7
|
-
|
8
7
|
class << self
|
9
8
|
def daily
|
10
9
|
get('daily')
|
@@ -17,20 +16,20 @@ module Fixer
|
|
17
16
|
def historical_90
|
18
17
|
get('hist-90d')
|
19
18
|
end
|
20
|
-
|
19
|
+
|
21
20
|
private
|
22
21
|
|
23
22
|
def get(type)
|
24
|
-
|
25
|
-
feed
|
26
|
-
doc
|
23
|
+
path = "http://www.ecb.europa.eu/stats/eurofxref/eurofxref-#{type}.xml"
|
24
|
+
feed = open(path).read
|
25
|
+
doc = Nokogiri::XML(feed)
|
27
26
|
doc.xpath('/gesmes:Envelope/xmlns:Cube/xmlns:Cube', doc.root.namespaces).map do |snapshot|
|
28
27
|
{
|
29
28
|
:date => snapshot['time'],
|
30
29
|
:rates => snapshot.xpath('./xmlns:Cube').map do |fx|
|
31
30
|
{
|
32
|
-
:currency
|
33
|
-
:rate
|
31
|
+
:currency => fx['currency'],
|
32
|
+
:rate => fx['rate']
|
34
33
|
}
|
35
34
|
end
|
36
35
|
}
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
module Fixer
|
5
|
+
describe Cache do
|
6
|
+
let(:mock_rates) do
|
7
|
+
[
|
8
|
+
{ :currency=>"USD", :rate=>"1.3856" },
|
9
|
+
{ :currency=>"JPY", :rate=>"114.98" },
|
10
|
+
{ :currency=>"GBP", :rate=>"0.87260" },
|
11
|
+
{ :currency=>"CAD", :rate=>"1.4018" },
|
12
|
+
{ :currency=>"EUR", :rate=>"1.0" }
|
13
|
+
]
|
14
|
+
end
|
15
|
+
|
16
|
+
let(:cache) do
|
17
|
+
cache = Fixer::Cache
|
18
|
+
cache.base = "EUR"
|
19
|
+
cache.expire
|
20
|
+
cache
|
21
|
+
end
|
22
|
+
|
23
|
+
it "sets base currency" do
|
24
|
+
cache.base = "USD"
|
25
|
+
cache.base.should eql "USD"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "quotes" do
|
29
|
+
cache.instance_variable_set(:@rates, mock_rates)
|
30
|
+
cache.send(:quote, "USD").should eql 1.3856
|
31
|
+
end
|
32
|
+
|
33
|
+
it "caches" do
|
34
|
+
cache.instance_variable_set(:@rates, "foo")
|
35
|
+
cache.send(:rates).should eql "foo"
|
36
|
+
end
|
37
|
+
|
38
|
+
it "seeds cache" do
|
39
|
+
cache.send(:rates).detect { |rate| rate[:currency] == "USD" }[:rate].should_not be_nil
|
40
|
+
end
|
41
|
+
|
42
|
+
it "expires cache" do
|
43
|
+
cache.instance_variable_set(:@rates, "foo")
|
44
|
+
cache.expire
|
45
|
+
cache.instance_variable_get(:@rates).should be_nil
|
46
|
+
end
|
47
|
+
|
48
|
+
it "raises an error if cache not valid" do
|
49
|
+
Fixer.stub!(:daily).and_return("foo")
|
50
|
+
lambda { cache.send(:rates) }.should raise_error Fixer::Error, "Cache not valid"
|
51
|
+
end
|
52
|
+
|
53
|
+
it "returns an exchange rate" do
|
54
|
+
cache.instance_variable_set(:@rates, mock_rates)
|
55
|
+
cache.to_eur.should eql 1.0
|
56
|
+
cache.to_usd.should eql 1.3856
|
57
|
+
|
58
|
+
cache.base = "GBP"
|
59
|
+
cache.to_gbp.should eql 1.0
|
60
|
+
cache.to_usd.should eql 1.5879
|
61
|
+
end
|
62
|
+
|
63
|
+
it "caches requested exchange rates" do
|
64
|
+
cache.instance_variable_set(:@rates, mock_rates)
|
65
|
+
cache.to_usd
|
66
|
+
cache.instance_variable_get(:@EUR_USD).should eql 1.3856
|
67
|
+
end
|
68
|
+
|
69
|
+
it "raises an error if counter currency not valid" do
|
70
|
+
cache.instance_variable_set(:@rates, mock_rates)
|
71
|
+
lambda { cache.to_foo }.should raise_error Fixer::Error, "Currency not valid"
|
72
|
+
end
|
73
|
+
|
74
|
+
it "raises an error if base currency not valid" do
|
75
|
+
cache.instance_variable_set(:@rates, mock_rates)
|
76
|
+
cache.base = "FOO"
|
77
|
+
lambda { cache.to_usd }.should raise_error Fixer::Error, "Currency not valid"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/spec/fixer_spec.rb
CHANGED
@@ -1,32 +1,31 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'spec_helper'
|
2
3
|
|
3
4
|
describe Fixer do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
end
|
5
|
+
it "downloads daily rates" do
|
6
|
+
rates = Fixer.daily
|
7
|
+
rates.size.should eql 1
|
8
|
+
rates.first.should have_key :date
|
9
|
+
rates.first.should have_key :rates
|
10
|
+
end
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
end
|
12
|
+
it "downloads 90-day historical" do
|
13
|
+
rates = Fixer.historical_90
|
14
|
+
# We should have more than 50 working days in a three-month period
|
15
|
+
rates.size.should > 50
|
16
|
+
rates.each do |rate|
|
17
|
+
rate.should have_key :date
|
18
|
+
rate.should have_key :rates
|
20
19
|
end
|
20
|
+
end
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
end
|
22
|
+
it "downloads historical" do
|
23
|
+
rates = Fixer.historical
|
24
|
+
# We should have a lot of working days in history
|
25
|
+
rates.size.should > 1000
|
26
|
+
rates.each do |rate|
|
27
|
+
rate.should have_key :date
|
28
|
+
rate.should have_key :rates
|
30
29
|
end
|
31
30
|
end
|
32
31
|
end
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 0.
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Hakan Ensari
|
@@ -14,11 +14,12 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-10-
|
17
|
+
date: 2010-10-07 00:00:00 +01:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: nokogiri
|
22
|
+
prerelease: false
|
22
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
23
24
|
none: false
|
24
25
|
requirements:
|
@@ -30,10 +31,10 @@ dependencies:
|
|
30
31
|
- 0
|
31
32
|
version: 1.4.0
|
32
33
|
type: :runtime
|
33
|
-
prerelease: false
|
34
34
|
version_requirements: *id001
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
36
|
name: rake
|
37
|
+
prerelease: false
|
37
38
|
requirement: &id002 !ruby/object:Gem::Requirement
|
38
39
|
none: false
|
39
40
|
requirements:
|
@@ -43,10 +44,10 @@ dependencies:
|
|
43
44
|
- 0
|
44
45
|
version: "0"
|
45
46
|
type: :development
|
46
|
-
prerelease: false
|
47
47
|
version_requirements: *id002
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: rspec
|
50
|
+
prerelease: false
|
50
51
|
requirement: &id003 !ruby/object:Gem::Requirement
|
51
52
|
none: false
|
52
53
|
requirements:
|
@@ -59,7 +60,6 @@ dependencies:
|
|
59
60
|
- rc
|
60
61
|
version: 2.0.0.rc
|
61
62
|
type: :development
|
62
|
-
prerelease: false
|
63
63
|
version_requirements: *id003
|
64
64
|
description: Fixer is a Ruby wrapper to the current and historical foreign exchange or FX rate feeds provided by the European Central Bank.
|
65
65
|
email:
|
@@ -71,10 +71,19 @@ extensions: []
|
|
71
71
|
extra_rdoc_files: []
|
72
72
|
|
73
73
|
files:
|
74
|
-
-
|
75
|
-
-
|
74
|
+
- .gitignore
|
75
|
+
- .rspec
|
76
|
+
- .rvmrc
|
77
|
+
- Gemfile
|
78
|
+
- Gemfile.lock
|
76
79
|
- LICENSE
|
77
80
|
- README.markdown
|
81
|
+
- Rakefile
|
82
|
+
- fixer.gemspec
|
83
|
+
- lib/fixer.rb
|
84
|
+
- lib/fixer/cache.rb
|
85
|
+
- lib/fixer/version.rb
|
86
|
+
- spec/fixer/cache_spec.rb
|
78
87
|
- spec/fixer_spec.rb
|
79
88
|
- spec/spec_helper.rb
|
80
89
|
has_rdoc: true
|
@@ -82,8 +91,8 @@ homepage: http://fixer.heroku.com
|
|
82
91
|
licenses: []
|
83
92
|
|
84
93
|
post_install_message:
|
85
|
-
rdoc_options:
|
86
|
-
|
94
|
+
rdoc_options: []
|
95
|
+
|
87
96
|
require_paths:
|
88
97
|
- lib
|
89
98
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -91,7 +100,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
91
100
|
requirements:
|
92
101
|
- - ">="
|
93
102
|
- !ruby/object:Gem::Version
|
94
|
-
hash: -4559416944163579922
|
95
103
|
segments:
|
96
104
|
- 0
|
97
105
|
version: "0"
|
@@ -101,17 +109,16 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
101
109
|
- - ">="
|
102
110
|
- !ruby/object:Gem::Version
|
103
111
|
segments:
|
104
|
-
-
|
105
|
-
|
106
|
-
- 7
|
107
|
-
version: 1.3.7
|
112
|
+
- 0
|
113
|
+
version: "0"
|
108
114
|
requirements: []
|
109
115
|
|
110
|
-
rubyforge_project:
|
116
|
+
rubyforge_project: fixer
|
111
117
|
rubygems_version: 1.3.7
|
112
118
|
signing_key:
|
113
119
|
specification_version: 3
|
114
120
|
summary: A Ruby wrapper to the European Central Bank exchange rate feeds
|
115
121
|
test_files:
|
122
|
+
- spec/fixer/cache_spec.rb
|
116
123
|
- spec/fixer_spec.rb
|
117
124
|
- spec/spec_helper.rb
|