rk_fx_rates 0.0.1

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,26 @@
1
+ module RkFxRates
2
+ module Provider
3
+ class Ecb
4
+
5
+ def self.data_url
6
+ return 'http://www.ecb.europa.eu/stats/eurofxref/eurofxref-hist-90d.xml'
7
+ end
8
+
9
+ # see rk_fx_rates.rb for data structure description
10
+ def self.generate_fx_hash(data)
11
+ fx_hash = Hash.new
12
+ fx_hash["downloaded_at"] = Date.today
13
+
14
+ data["Envelope"]["Cube"]["Cube"].each do |daily|
15
+ date = Chronic.parse( daily["time"] ).to_date
16
+ fx_hash[date] = Hash.new
17
+ daily["Cube"].each do |currency|
18
+ fx_hash[date] [currency["currency"]] = currency["rate"].to_f
19
+ end
20
+ end
21
+ return fx_hash
22
+ end
23
+
24
+ end # Ecb
25
+ end # Provider
26
+ end # RkFxRates
@@ -0,0 +1,3 @@
1
+ module RkFxRates
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,73 @@
1
+ require "rk_fx_rates/version"
2
+ require 'rk_fx_rates/provider/ecb'
3
+
4
+ require "httparty"
5
+ require 'chronic'
6
+ require 'yaml/store'
7
+ require 'pry-remote'
8
+
9
+ # To make sure the data from all source will be compatible, they are converted to a standardized data structure
10
+ # {
11
+ # downloaded_at: "2013-11-27",
12
+ # data: {
13
+ # "2013-11-27" => {
14
+ # 'USD' => 1.3547,
15
+ # 'GBP' => 0.83830,
16
+ # ...
17
+ # },
18
+ # "2013-11-26" => ...
19
+ # }
20
+ # }
21
+
22
+ module RkFxRates
23
+ class ExchangeRate
24
+ include HTTParty
25
+
26
+ def initialize(source = RkFxRates::Provider::Ecb)
27
+ @source = source
28
+ end
29
+
30
+ def retrieve
31
+ retrieve_latest unless File.exists?('exchange_rates.store')
32
+ fx_hash = YAML::load_file('exchange_rates.store')['rates']
33
+
34
+ fx_hash = retrieve_latest if fx_hash['downloaded_at'] < Chronic.parse('last weekday').to_date
35
+ return fx_hash
36
+ end
37
+
38
+ def retrieve_latest
39
+ response = HTTParty.get(@source.data_url)
40
+ fx_hash = @source.generate_fx_hash(response.parsed_response)
41
+
42
+ # persist latest rates
43
+ store(fx_hash)
44
+
45
+ return fx_hash
46
+ end
47
+
48
+ def at(date, from_currency, to_currency)
49
+ return to_euro(date, from_currency) * from_euro(date, to_currency)
50
+ end
51
+
52
+ private
53
+
54
+ def from_euro(date, to_currency)
55
+ return 1.0 if to_currency == 'EUR'
56
+ data = retrieve
57
+ rate = data[date][to_currency]
58
+ end
59
+
60
+ def to_euro(date, from_currency)
61
+ return 1.0 if from_currency == 'EUR'
62
+ data = retrieve
63
+ rate = 1.0 / data[date][from_currency]
64
+ end
65
+
66
+ def store(fx_hash)
67
+ store = YAML::Store.new("exchange_rates.store")
68
+ store.transaction do
69
+ store['rates'] = fx_hash
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rk_fx_rates/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "rk_fx_rates"
8
+ spec.version = RkFxRates::VERSION
9
+ spec.authors = ["Rainer Kuhn"]
10
+ spec.email = ["ticktricktrack@gmail.com"]
11
+ spec.description = %q{Obtains foreign exchange rates}
12
+ spec.summary = %q{}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "pry-remote"
25
+ spec.add_development_dependency "guard-rspec"
26
+ spec.add_development_dependency "webmock"
27
+
28
+ spec.add_runtime_dependency 'httparty'
29
+ spec.add_runtime_dependency 'chronic'
30
+ spec.add_runtime_dependency 'pry-remote'
31
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe RkFxRates do
4
+
5
+ before(:each) do
6
+ @fx_api = RkFxRates::ExchangeRate.new
7
+ end
8
+
9
+ it "should download the current exchange rates" do
10
+ @fx_api.retrieve_latest.should_not be nil
11
+ end
12
+
13
+ it "should display the download date" do
14
+ @fx_api.retrieve_latest["downloaded_at"].should == Date.today
15
+ end
16
+
17
+ # using the last monday to make sure the test runs with a weekday date
18
+ # simplified case with Euro as the start currency
19
+ it "should calcute the Exchange rate from EUR to USD" do
20
+ date = Chronic.parse('last monday').to_date
21
+ @fx_api.at( date, 'EUR', 'USD' ).should be > 1.2
22
+ end
23
+
24
+ it "should calculate the exchange rate from GBP to USD" do
25
+ date = Chronic.parse('last monday').to_date
26
+ @fx_api.at(date, 'GBP', 'USD').should be > 1.5
27
+ end
28
+
29
+ it "should calculate the exchange rate from EUR to EUR" do
30
+ date = Chronic.parse('last monday').to_date
31
+ @fx_api.at(date, 'EUR', 'EUR').should be == 1.0
32
+ end
33
+
34
+ context "with existing and current data" do
35
+ before(:each) do
36
+ @fx_api.retrieve_latest
37
+ WebMock.disable_net_connect!
38
+ end
39
+
40
+ it "should not download the data again" do
41
+ date = Chronic.parse('last monday').to_date
42
+ @fx_api.at(date, 'GBP', 'USD').should be > 1.5
43
+ end
44
+
45
+ after(:each) do
46
+ WebMock.allow_net_connect!
47
+ end
48
+ end
49
+
50
+ end
@@ -0,0 +1,23 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+
8
+ require 'rk_fx_rates.rb'
9
+ require 'webmock/rspec'
10
+
11
+ RSpec.configure do |config|
12
+ config.treat_symbols_as_metadata_keys_with_true_values = true
13
+ config.run_all_when_everything_filtered = true
14
+ config.filter_run :focus
15
+
16
+ # Run specs in random order to surface order dependencies. If you find an
17
+ # order dependency and want to debug it, you can fix the order by providing
18
+ # the seed, which is printed after each run.
19
+ # --seed 1234
20
+ config.order = 'random'
21
+
22
+ WebMock.allow_net_connect!
23
+ end
metadata ADDED
@@ -0,0 +1,188 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rk_fx_rates
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Rainer Kuhn
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-11-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '1.3'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '1.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry-remote
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard-rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: webmock
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: httparty
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: chronic
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: pry-remote
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description: Obtains foreign exchange rates
140
+ email:
141
+ - ticktricktrack@gmail.com
142
+ executables: []
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - .gitignore
147
+ - .rspec
148
+ - .ruby-gemset
149
+ - .rvmrc
150
+ - Gemfile
151
+ - Guardfile
152
+ - LICENSE.txt
153
+ - README.md
154
+ - Rakefile
155
+ - exchange_rates.store
156
+ - lib/rk_fx_rates.rb
157
+ - lib/rk_fx_rates/provider/ecb.rb
158
+ - lib/rk_fx_rates/version.rb
159
+ - rk_fx_rates.gemspec
160
+ - spec/lib/rk_fx_rates_spec.rb
161
+ - spec/spec_helper.rb
162
+ homepage: ''
163
+ licenses:
164
+ - MIT
165
+ metadata: {}
166
+ post_install_message:
167
+ rdoc_options: []
168
+ require_paths:
169
+ - lib
170
+ required_ruby_version: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - '>='
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
175
+ required_rubygems_version: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - '>='
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ requirements: []
181
+ rubyforge_project:
182
+ rubygems_version: 2.1.11
183
+ signing_key:
184
+ specification_version: 4
185
+ summary: ''
186
+ test_files:
187
+ - spec/lib/rk_fx_rates_spec.rb
188
+ - spec/spec_helper.rb