fixer 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|