cambio 0.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,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in cambio.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Phil Nash
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,57 @@
1
+ # Cambio
2
+
3
+ A gem to wrap the Open Exchange Rates API from http://openexchangerates.org
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'cambio'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install cambio
18
+
19
+ ## Usage
20
+
21
+ First you will need an App ID, you can sign up for one at https://openexchangerates.org/signup.
22
+
23
+ Cambio.configure do |config|
24
+ config.app_id
25
+ end
26
+
27
+ Get the latest exchange rates
28
+
29
+ Cambio.latest
30
+
31
+ You can also get the raw JSON response
32
+
33
+ Cambio.latest :raw => true
34
+
35
+ Get the currencies available through the API
36
+
37
+ Cambio.currencies
38
+ # or
39
+ Cambio.currencies :raw => true
40
+
41
+ Get the historical exchange rates
42
+
43
+ Cambio.historical('2012-08-17')
44
+ # or with a date object
45
+ Cambio.historical(Date.parse('17/08/2012'))
46
+
47
+
48
+
49
+
50
+
51
+ ## Contributing
52
+
53
+ 1. Fork it
54
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
55
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
56
+ 4. Push to the branch (`git push origin my-new-feature`)
57
+ 5. Create new Pull Request
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.test_files = FileList['spec/*_spec.rb','spec/cambio/*_spec.rb']
7
+ t.verbose = true
8
+ end
9
+ task :spec => :test
10
+ task :default => :test
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/cambio/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Phil Nash"]
6
+ gem.email = ["philnash@gmail.com"]
7
+ gem.description = %q{A simple wrapper for the Open Exchange Rates API}
8
+ gem.summary = %q{A simple wrapper for the Open Exchange Rates API}
9
+ gem.homepage = "http://github.com/philnash/cambio"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "cambio"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Cambio::VERSION
17
+
18
+ gem.add_development_dependency 'vcr', '~> 2.2.4'
19
+ gem.add_development_dependency 'webmock', '~> 1.8.8'
20
+
21
+ gem.add_runtime_dependency 'faraday', '~> 0.8.1'
22
+ gem.add_runtime_dependency 'faraday_middleware', '~> 0.8.8'
23
+ gem.add_runtime_dependency 'hashie', '~> 1.2.0'
24
+ end
@@ -0,0 +1,102 @@
1
+ require "faraday"
2
+ require "faraday_middleware"
3
+ require "hashie"
4
+ require 'faraday/raise_on_error'
5
+ require "cambio/version"
6
+ require "cambio/configuration"
7
+ require "cambio/error"
8
+
9
+ # Wrapper for the Open Exchange Rates API
10
+ module Cambio
11
+ class << self
12
+ # Public: Configure the API wrapper. A convenience method that yields a
13
+ # configuration object on which you can set the app_id and endpoint.
14
+ #
15
+ # Examples
16
+ #
17
+ # Cambio.configure do |config|
18
+ # config.app_id = 'YOUR_APP_ID'
19
+ # end
20
+ def configure
21
+ yield(configuration)
22
+ end
23
+
24
+ # Public: Access to the underlying configuration object
25
+ def configuration
26
+ @configuration ||= Configuration.new
27
+ end
28
+
29
+ # Public: Retrieve the latest rates from the API
30
+ #
31
+ # opts - A hash of options to affect the returned result (default: {}):
32
+ # :raw - A Boolean to describe whether you want the raw JSON response
33
+ #
34
+ # Examples
35
+ #
36
+ # Cambio.latest
37
+ # # => #<Hashie::Mash base="USD" ... >
38
+ #
39
+ # Cambio.latest :raw => true
40
+ # # => "{\n\t\"disclaimer\": \"This data..." ... }"
41
+ #
42
+ # # Returns a Hashie::Mash of the rates (or raw JSON, if raw is passed)
43
+ def latest(opts={})
44
+ response = connection(opts).get 'latest.json'
45
+ response.body
46
+ end
47
+
48
+ # Public: Retrieve the currencies supplied by the API
49
+ #
50
+ # opts - A hash of options to affect the returned result (default: {}):
51
+ # :raw - A Boolean to describe whether you want the raw JSON response
52
+ #
53
+ # Examples
54
+ #
55
+ # Cambio.currencies
56
+ # # => #<Hashie::Mash AED="United Arab Emirates Dirham" ... >
57
+ #
58
+ # Cambio.currencies
59
+ # # => "{\n\t\"AED\": \"United Arab Emirates Dirham\", ... }"
60
+ #
61
+ # Returns a Hashie::Mash of the currencies (or raw JSON, if raw is passed)
62
+ def currencies(opts={})
63
+ response = connection(opts).get 'currencies.json'
64
+ response.body
65
+ end
66
+
67
+ # Public: Retrieve the historical rates supplied by the API
68
+ #
69
+ # date - A string in the format YYYY-MM-DD or a date object referring to the
70
+ # day on which you want to get the rates from
71
+ # opts - A hash of options to affect the returned result (default: {}):
72
+ # :raw - A Boolean to describe whether you want the raw JSON response
73
+ #
74
+ # Examples
75
+ #
76
+ # Cambio.historical('2012-08-17')
77
+ #
78
+ # Cambio.historical(Date.today)
79
+ #
80
+ # Returns a Hashie::Mash of the rates from the date specified (or raw JSON,
81
+ # if raw is passsed)
82
+ def historical(date, opts={})
83
+ date = date.strftime("%Y-%m-%d") if date.respond_to?(:strftime)
84
+ response = connection(opts).get "historical/#{date}.json"
85
+ response.body
86
+ end
87
+
88
+ private
89
+
90
+ # Internal: access to a faraday connection
91
+ def connection(opts={})
92
+ Faraday.new(:url => configuration.endpoint, :params => { :app_id => configuration.app_id }) do |conn|
93
+ unless opts[:raw]
94
+ conn.response :mashify
95
+ conn.response :json
96
+ end
97
+ conn.response :raise_on_error
98
+ conn.adapter Faraday.default_adapter # make requests with Net::HTTP
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,31 @@
1
+ module Cambio
2
+ # Internal: An object to hold the internal configuration of the Cambio module
3
+ class Configuration
4
+ # Public: Gets/Sets the String app_id
5
+ attr_accessor :app_id
6
+ # Public: Gets/Sets the String endpoint
7
+ attr_accessor :endpoint
8
+
9
+ # Internal: By default, don't set an app ID
10
+ DEFAULT_APP_ID = nil
11
+
12
+ # Internal: The default API endpoint, it can also be set to use https
13
+ DEFAULT_ENDPOINT = 'http://openexchangerates.org/api/'
14
+
15
+ # Internal: The configuration object should be initialized by the Cambio
16
+ # module
17
+ def initialize
18
+ @endpoint = DEFAULT_ENDPOINT
19
+ end
20
+
21
+ # Public: resets the internal configuration to the defaults
22
+ #
23
+ # Examples
24
+ #
25
+ # configuration.reset
26
+ def reset
27
+ @endpoint = DEFAULT_ENDPOINT
28
+ @app_id = DEFAULT_APP_ID
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,13 @@
1
+ module Cambio
2
+ # Internal: Custom error class for all other Cambio errors
3
+ class Error < StandardError; end
4
+
5
+ # Internal: Raised when trying to use the API with the wrong or no credentials
6
+ class UnauthorisedError < Error; end
7
+
8
+ # Internal: Raised when 404 error is returned from the API
9
+ class NotFoundError < Error; end
10
+
11
+ # Internal: Raised when a 500 error is returned from the API
12
+ class ServerError < Error; end
13
+ end
@@ -0,0 +1,3 @@
1
+ module Cambio
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,32 @@
1
+ require 'json' unless defined?(::JSON)
2
+ module FaradayMiddleware
3
+ class RaiseOnError < Faraday::Response::Middleware
4
+ def on_complete(env)
5
+ case env[:status]
6
+ when 404
7
+ raise Cambio::NotFoundError, build_message(env)
8
+ when 401
9
+ raise Cambio::UnauthorisedError, build_message(env)
10
+ when 500
11
+ raise Cambio::ServerError, build_message(env)
12
+ when 400...600
13
+ raise Cambio::Error, build_message(env)
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def build_message(env)
20
+ body = get_body(env[:body])
21
+ "#{env[:status]}: #{body['message']}. #{body['description']}"
22
+ end
23
+
24
+ def get_body(body)
25
+ if !body.nil? && !body.empty? && body.kind_of?(String)
26
+ parsed_body = ::JSON.parse(body)
27
+ end
28
+ parsed_body.nil? || parsed_body.empty? ? {} : parsed_body
29
+ end
30
+ end
31
+ end
32
+ Faraday.register_middleware :response, :raise_on_error => FaradayMiddleware::RaiseOnError
@@ -0,0 +1,51 @@
1
+ require File.expand_path('../../spec_helper', __FILE__)
2
+
3
+ describe Cambio::Configuration do
4
+ before do
5
+ @configuration = Cambio::Configuration.new
6
+ end
7
+
8
+ it 'should default to the http url' do
9
+ @configuration.endpoint.must_equal 'http://openexchangerates.org/api/'
10
+ end
11
+
12
+ it 'should default to no app id' do
13
+ @configuration.app_id.must_be_nil
14
+ end
15
+
16
+ describe 'setting the endpoint' do
17
+ before do
18
+ @configuration.endpoint = 'https://openexchangerates.org/api/'
19
+ end
20
+
21
+ it 'should update the endpoint' do
22
+ @configuration.endpoint.must_equal 'https://openexchangerates.org/api/'
23
+ end
24
+ end
25
+
26
+ describe 'setting the app_id' do
27
+ before do
28
+ @configuration.app_id = 'abc123'
29
+ end
30
+
31
+ it 'should set the app_id' do
32
+ @configuration.app_id.must_equal 'abc123'
33
+ end
34
+ end
35
+
36
+ describe 'resetting the configuration' do
37
+ before do
38
+ @configuration.app_id = 'abc123'
39
+ @configuration.endpoint = 'https://openexchangerates.org/api/'
40
+ @configuration.reset
41
+ end
42
+
43
+ it 'should reset the app_id to default' do
44
+ @configuration.app_id.must_be_nil
45
+ end
46
+
47
+ it 'should reset the endpoint to default' do
48
+ @configuration.endpoint.must_equal 'http://openexchangerates.org/api/'
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,195 @@
1
+ require File.expand_path('../spec_helper', __FILE__)
2
+
3
+ describe Cambio do
4
+ describe 'when configuring with an api key' do
5
+ before do
6
+ Cambio.configure do |config|
7
+ config.app_id = 'bcc1d1ad9f4d4dd69b12a64c913857fe'
8
+ end
9
+ end
10
+
11
+ after do
12
+ Cambio.configuration.reset
13
+ end
14
+
15
+ it 'should be configured with an api key' do
16
+ Cambio.configuration.app_id.must_equal 'bcc1d1ad9f4d4dd69b12a64c913857fe'
17
+ end
18
+
19
+ it 'should be configured with an endpoint' do
20
+ Cambio.configuration.endpoint.must_equal 'http://openexchangerates.org/api/'
21
+ end
22
+
23
+ it 'should have a configuration object' do
24
+ Cambio.configuration.must_be_instance_of Cambio::Configuration
25
+ end
26
+
27
+ describe 'accessing the api' do
28
+ describe 'when getting the latest rates' do
29
+ before do
30
+ VCR.use_cassette 'latest' do
31
+ @latest = Cambio.latest
32
+ end
33
+ end
34
+
35
+ it 'should return a hashie' do
36
+ @latest.must_be_instance_of Hashie::Mash
37
+ end
38
+
39
+ [:disclaimer, :license, :timestamp, :base, :rates].each do |field|
40
+ it "should have the #{field}" do
41
+ @latest.must_respond_to(field)
42
+ end
43
+ end
44
+ end
45
+
46
+ describe 'when getting historical rates' do
47
+ describe 'with an object that responds to strftime' do
48
+ before do
49
+ @date = Time.at(1344896438)
50
+ VCR.use_cassette 'historical_date' do
51
+ @historical = Cambio.historical(@date)
52
+ end
53
+ end
54
+
55
+ it 'should return a hashie' do
56
+ @historical.must_be_instance_of Hashie::Mash
57
+ end
58
+
59
+ [:disclaimer, :license, :timestamp, :base, :rates].each do |field|
60
+ it "should have the #{field}" do
61
+ @historical.must_respond_to(field)
62
+ end
63
+ end
64
+
65
+ it 'must rause a not found error when the year doesn\'t exist' do
66
+ lambda {
67
+ VCR.use_cassette 'historical_missing' do
68
+ Cambio.historical('00000')
69
+ end
70
+ }.must_raise Cambio::NotFoundError
71
+ end
72
+ end
73
+
74
+ describe 'with a date string' do
75
+ before do
76
+ VCR.use_cassette 'historical_string' do
77
+ @historical = Cambio.historical('2011-02-01')
78
+ end
79
+ end
80
+
81
+ it 'should return a hashie' do
82
+ @historical.must_be_instance_of Hashie::Mash
83
+ end
84
+
85
+ [:disclaimer, :license, :timestamp, :base, :rates].each do |field|
86
+ it "should have the #{field}" do
87
+ @historical.must_respond_to(field)
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ describe 'when getting the currencies' do
94
+ before do
95
+ VCR.use_cassette 'currencies', do
96
+ @currencies = Cambio.currencies
97
+ end
98
+ end
99
+
100
+ it 'should return a hashie' do
101
+ @currencies.must_be_instance_of Hashie::Mash
102
+ end
103
+
104
+ it 'should return a USD' do
105
+ @currencies['USD'].must_equal "United States Dollar"
106
+ end
107
+ end
108
+
109
+ describe 'when getting raw results' do
110
+ describe 'for the latest rates' do
111
+ before do
112
+ VCR.use_cassette 'latest' do
113
+ @latest = Cambio.latest(:raw => true)
114
+ end
115
+ end
116
+
117
+ it 'should return a JSON string' do
118
+ @latest.must_be_instance_of String
119
+ end
120
+ end
121
+
122
+ describe 'for historical rates' do
123
+ describe 'with a date object' do
124
+ before do
125
+ @date = Time.at(1344896438)
126
+ VCR.use_cassette 'historical_date' do
127
+ @historical = Cambio.historical(@date, :raw => true)
128
+ end
129
+ end
130
+
131
+ it 'should return a string' do
132
+ @historical.must_be_instance_of String
133
+ end
134
+ end
135
+
136
+ describe 'with a date string' do
137
+ before do
138
+ VCR.use_cassette 'historical_string' do
139
+ @historical = Cambio.historical('2011-02-01', :raw => true)
140
+ end
141
+ end
142
+
143
+ it 'should return a string' do
144
+ @historical.must_be_instance_of String
145
+ end
146
+ end
147
+ end
148
+
149
+ describe 'for the currencies' do
150
+ before do
151
+ VCR.use_cassette 'currencies' do
152
+ @currencies = Cambio.currencies(:raw => true)
153
+ end
154
+ end
155
+
156
+ it 'should return a JSON string' do
157
+ @currencies.must_be_instance_of String
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+
164
+ describe 'when using with an incorrect api key' do
165
+ before do
166
+ Cambio.configure { |c| c.app_id = 'wrong' }
167
+ end
168
+
169
+ it 'must raise an error for latest' do
170
+ lambda {
171
+ VCR.use_cassette 'latest_unauth' do
172
+ Cambio.latest
173
+ end
174
+ }.must_raise Cambio::UnauthorisedError
175
+ end
176
+ it 'must raise an error for currencies' do
177
+ lambda {
178
+ VCR.use_cassette 'currencies_unauth' do
179
+ Cambio.currencies
180
+ end
181
+ }.must_raise Cambio::UnauthorisedError
182
+ end
183
+ it 'must raise an error for currencies' do
184
+ lambda {
185
+ VCR.use_cassette 'historical_unauth' do
186
+ Cambio.historical('2011-02-01')
187
+ end
188
+ }.must_raise Cambio::UnauthorisedError
189
+ end
190
+
191
+ after do
192
+ Cambio.configuration.reset
193
+ end
194
+ end
195
+ end