oanda_api_v20 0.0.6 → 1.0.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.
- checksums.yaml +4 -4
- data/.rspec +2 -0
- data/CHANGELOG.md +9 -0
- data/lib/oanda_api_v20/client.rb +11 -6
- data/lib/oanda_api_v20/version.rb +1 -1
- data/oanda_api_v20.gemspec +5 -0
- data/spec/oanda_api_v20/client_spec.rb +143 -0
- data/spec/oanda_api_v20/oanda_api_v20_spec.rb +10 -0
- data/spec/spec_helper.rb +34 -0
- metadata +52 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8de51715b0686fbc75824fe6da40c38d82aa83dd
|
4
|
+
data.tar.gz: 35a0b883d5abf389da6745d33080762011270891
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a783da10af447a552a7e182a34a38e29a6b492c7fc58ec204967f46d54393465b3f12d4fb0c7ad50cf4da5c7c4ea57685cd12416f5704f59f413c02c6bda0241
|
7
|
+
data.tar.gz: 3b52f9e02531e69aedff3e5adb9333cd6d07f37f12fdc6db8dc86cd78bba0d7844ce3caaa0b56013a2d1aaaabb638360202e151a4383005c1bdd91cff525163d
|
data/.rspec
ADDED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 1.0.0
|
4
|
+
#### 2016-08-23
|
5
|
+
* Added the RSpec Gem for writing unit tests.
|
6
|
+
* Added the WebMock Gem to stub HTTP requests to Oanda API when writing unit tests.
|
7
|
+
* Raises a NoMethodError exception when the method does not exist. Previously nil was returned.
|
8
|
+
* Raises an OandaApiV20::RequestError exception when the status code is not 2xx.
|
9
|
+
* Fixed an issue where the requests to Oanda API was limited to 30 requests per minute instead of 30 requests per second. Whoops!
|
10
|
+
* Fixed a bug where the response body would sometimes be nil and cause the client to crash.
|
11
|
+
|
3
12
|
## 0.0.6
|
4
13
|
#### 2016-08-18
|
5
14
|
* HTTP exception handling added.
|
data/lib/oanda_api_v20/client.rb
CHANGED
@@ -44,19 +44,22 @@ module OandaApiV20
|
|
44
44
|
api = Api.new(api_attributes)
|
45
45
|
|
46
46
|
if api.respond_to?(last_action)
|
47
|
+
api_result = {}
|
47
48
|
set_last_api_request_at
|
48
49
|
govern_api_request_rate
|
49
50
|
|
50
51
|
begin
|
51
|
-
response = Http::Exceptions.
|
52
|
+
response = Http::Exceptions.wrap_and_check do
|
52
53
|
last_arguments.nil? || last_arguments.empty? ? api.send(last_action, &block) : api.send(last_action, *last_arguments, &block)
|
53
54
|
end
|
54
55
|
rescue Http::Exceptions::HttpException => e
|
55
56
|
raise OandaApiV20::RequestError, e.message
|
56
57
|
end
|
57
58
|
|
58
|
-
|
59
|
-
|
59
|
+
if response.body && !response.body.empty?
|
60
|
+
api_result.merge!(JSON.parse(response.body))
|
61
|
+
set_last_transaction_id(api_result['lastTransactionID']) if api_result['lastTransactionID']
|
62
|
+
end
|
60
63
|
end
|
61
64
|
|
62
65
|
api_result
|
@@ -64,6 +67,8 @@ module OandaApiV20
|
|
64
67
|
set_last_action_and_arguments(name, args)
|
65
68
|
set_account_id(args.first) if name == :account
|
66
69
|
self
|
70
|
+
else
|
71
|
+
super
|
67
72
|
end
|
68
73
|
end
|
69
74
|
|
@@ -77,7 +82,7 @@ module OandaApiV20
|
|
77
82
|
|
78
83
|
def govern_api_request_rate
|
79
84
|
return unless last_api_request_at[0]
|
80
|
-
halt =
|
85
|
+
halt = 1 - (last_api_request_at[MAX_REQUESTS_PER_SECOND_ALLOWED - 1] - last_api_request_at[0])
|
81
86
|
sleep halt if halt > 0
|
82
87
|
end
|
83
88
|
|
@@ -102,8 +107,8 @@ module OandaApiV20
|
|
102
107
|
self.account_id = id
|
103
108
|
end
|
104
109
|
|
105
|
-
def set_last_transaction_id(
|
106
|
-
self.last_transaction_id =
|
110
|
+
def set_last_transaction_id(id)
|
111
|
+
self.last_transaction_id = id
|
107
112
|
end
|
108
113
|
|
109
114
|
def set_http_verb(action, last_action)
|
data/oanda_api_v20.gemspec
CHANGED
@@ -20,6 +20,11 @@ Gem::Specification.new do |s|
|
|
20
20
|
s.add_dependency 'persistent_httparty', '~> 0.1'
|
21
21
|
s.add_dependency 'http-exceptions', '~> 0.1'
|
22
22
|
|
23
|
+
s.add_development_dependency 'rspec', '~> 3.4'
|
24
|
+
s.add_development_dependency 'webmock', '~> 2.1'
|
25
|
+
s.add_development_dependency 'timecop', '~> 0.8'
|
26
|
+
|
23
27
|
s.files = `git ls-files`.split("\n")
|
28
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
24
29
|
s.require_paths = ['lib']
|
25
30
|
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe OandaApiV20::Client do
|
4
|
+
describe '#initialize' do
|
5
|
+
it 'sets the access_token attribute when supplied' do
|
6
|
+
c = OandaApiV20::Client.new(access_token: 'my_access_token')
|
7
|
+
expect(c.access_token).to eq('my_access_token')
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'sets the base URI to practice when the practice option was supplied and set to true' do
|
11
|
+
c = OandaApiV20::Client.new(practice: true)
|
12
|
+
expect(c.base_uri).to eq('https://api-fxpractice.oanda.com/v3')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'sets the base URI to live when the practice option was supplied and set to false' do
|
16
|
+
c = OandaApiV20::Client.new(practice: false)
|
17
|
+
expect(c.base_uri).to eq('https://api-fxtrade.oanda.com/v3')
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'sets the base URI to live when the practice option was not supplied' do
|
21
|
+
c = OandaApiV20::Client.new
|
22
|
+
expect(c.base_uri).to eq('https://api-fxtrade.oanda.com/v3')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#method_missing' do
|
27
|
+
let(:c) { OandaApiV20::Client.new(access_token: 'my_access_token') }
|
28
|
+
|
29
|
+
context 'when an OandaApiV20::Api method has been called' do
|
30
|
+
it 'saves the method called' do
|
31
|
+
c.accounts
|
32
|
+
expect(c.send(:last_action)).to eq(:accounts)
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'saves the attributes supplied' do
|
36
|
+
c.account('100-100-100')
|
37
|
+
expect(c.send(:last_action)).to eq(:account)
|
38
|
+
expect(c.send(:last_arguments)).to eq(['100-100-100'])
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'saves the account ID when calling the account method' do
|
42
|
+
c.account('100-100-100')
|
43
|
+
expect(c.send(:account_id)).to eq('100-100-100')
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'returns a OandaApiV20::Client instance' do
|
47
|
+
expect(c.account('100-100-100')).to be_an_instance_of(OandaApiV20::Client)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when an action method has been called' do
|
52
|
+
let(:response_account) { '{"account":{"id":"100-100-100","NAV":"100000.0000","balance":"100000.0000","lastTransactionID":"99","orders":[],"positions":[],"trades":[],"pendingOrderCount":0},"lastTransactionID":"99"}' }
|
53
|
+
let!(:request_account) { stub_request(:get, 'https://api-fxtrade.oanda.com/v3/accounts/100-100-100').to_return(status: 200, body: response_account, headers: {}) }
|
54
|
+
let(:response_accounts) { '{"accounts":[{"id":"100-100-100","tags":[]}]}' }
|
55
|
+
let!(:request_accounts) { stub_request(:get, 'https://api-fxtrade.oanda.com/v3/accounts').to_return(status: 200, body: response_accounts, headers: {}) }
|
56
|
+
|
57
|
+
before(:each) do
|
58
|
+
allow(c).to receive(:sleep)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'sets the equivalent HTTP verb' do
|
62
|
+
c.accounts.show
|
63
|
+
expect(c.send(:http_verb)).to eq(:get)
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'makes a request to Oanda API' do
|
67
|
+
c.accounts.show
|
68
|
+
expect(request_accounts).to have_been_requested
|
69
|
+
expect(request_accounts).to have_been_requested.at_most_once
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'returns the response from Oanda API' do
|
73
|
+
expect(c.accounts.show).to eq(JSON.parse(response_accounts))
|
74
|
+
expect(c.account('100-100-100').show).to eq(JSON.parse(response_account))
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'sets the last transaction ID when returned' do
|
78
|
+
c.account('100-100-100').show
|
79
|
+
expect(c.send(:last_transaction_id)).to eq('99')
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'sets the last request made at time' do
|
83
|
+
expect(c.send(:last_api_request_at).last).to be_nil
|
84
|
+
c.accounts.show
|
85
|
+
expect(c.send(:last_api_request_at).last).to_not be_nil
|
86
|
+
expect(Time.parse(c.send(:last_api_request_at).last.to_s)).to be_an_instance_of(Time)
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'raises an OandaApiV20::RequestError exception when receiving anything other than a 2xx response from Oanda API' do
|
90
|
+
stub_request(:get, 'https://api-fxtrade.oanda.com/v3/accounts/100-100-109').to_return(status: 401, body: '', headers: {})
|
91
|
+
expect{ c.account('100-100-109').show }.to raise_error(OandaApiV20::RequestError)
|
92
|
+
end
|
93
|
+
|
94
|
+
describe 'governing request rate' do
|
95
|
+
before(:each) do
|
96
|
+
Timecop.freeze(Time.local('2016-08-01 06:00:00'))
|
97
|
+
end
|
98
|
+
|
99
|
+
after(:each) do
|
100
|
+
Timecop.return
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'is not allowed to make 30 requests or more per second' do
|
104
|
+
expect(c).to receive(:sleep).at_least(:twice)
|
105
|
+
30.times { c.accounts.show }
|
106
|
+
Timecop.freeze('2016-08-01 06:00:01')
|
107
|
+
30.times { c.accounts.show }
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'is allowed to make less than 30 requests per second' do
|
111
|
+
expect(c).to_not receive(:sleep)
|
112
|
+
29.times { c.accounts.show }
|
113
|
+
Timecop.freeze('2016-08-01 06:00:01')
|
114
|
+
29.times { c.accounts.show }
|
115
|
+
Timecop.freeze('2016-08-01 06:00:02')
|
116
|
+
29.times { c.accounts.show }
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'halts all API requests for the remainder of the second when 30 requests have been made in one second' do
|
120
|
+
expect(c).to receive(:sleep).with(0.7)
|
121
|
+
Timecop.freeze('2016-08-01 06:00:00.0')
|
122
|
+
10.times { c.accounts.show }
|
123
|
+
Timecop.freeze('2016-08-01 06:00:00.1')
|
124
|
+
10.times { c.accounts.show }
|
125
|
+
Timecop.freeze('2016-08-01 06:00:00.3')
|
126
|
+
10.times { c.accounts.show }
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'when a method other than an OandaApiV20::Api method has been called' do
|
132
|
+
it 'throws a NoMethodError exception' do
|
133
|
+
expect{ c.accounts.show_me_the_money }.to raise_error(NoMethodError)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context 'when a method other than an action method has been called' do
|
138
|
+
it 'throws a NoMethodError exception' do
|
139
|
+
expect{ c.this_method_definitely_does_not_exist }.to raise_error(NoMethodError)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
Bundler.setup
|
3
|
+
|
4
|
+
require 'webmock/rspec'
|
5
|
+
require 'timecop'
|
6
|
+
require 'oanda_api_v20'
|
7
|
+
|
8
|
+
WebMock.disable_net_connect!(allow_localhost: true)
|
9
|
+
|
10
|
+
RSpec.configure do |config|
|
11
|
+
config.expect_with :rspec do |expectations|
|
12
|
+
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
13
|
+
end
|
14
|
+
|
15
|
+
config.mock_with :rspec do |mocks|
|
16
|
+
mocks.verify_partial_doubles = true
|
17
|
+
end
|
18
|
+
|
19
|
+
# These two settings work together to allow you to limit a spec run
|
20
|
+
# to individual examples or groups you care about by tagging them with
|
21
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
22
|
+
# get run.
|
23
|
+
config.filter_run :focus
|
24
|
+
config.run_all_when_everything_filtered = true
|
25
|
+
|
26
|
+
# This setting enables warnings. It's recommended, but in some cases may
|
27
|
+
# be too noisy due to issues in dependencies.
|
28
|
+
config.warnings = true
|
29
|
+
|
30
|
+
# Print the 10 slowest examples and example groups at the
|
31
|
+
# end of the spec run, to help surface which specs are running
|
32
|
+
# particularly slow.
|
33
|
+
config.profile_examples = 10
|
34
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oanda_api_v20
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kobus Joubert
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-08-
|
11
|
+
date: 2016-08-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: httparty
|
@@ -52,6 +52,48 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0.1'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.4'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.4'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: webmock
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.1'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.1'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: timecop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.8'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.8'
|
55
97
|
description: Ruby client that supports the Oanda REST API V20 methods.
|
56
98
|
email: kobus@translate3d.com
|
57
99
|
executables: []
|
@@ -59,6 +101,7 @@ extensions: []
|
|
59
101
|
extra_rdoc_files: []
|
60
102
|
files:
|
61
103
|
- ".gitignore"
|
104
|
+
- ".rspec"
|
62
105
|
- CHANGELOG.md
|
63
106
|
- Gemfile
|
64
107
|
- LICENSE
|
@@ -76,6 +119,9 @@ files:
|
|
76
119
|
- lib/oanda_api_v20/transactions.rb
|
77
120
|
- lib/oanda_api_v20/version.rb
|
78
121
|
- oanda_api_v20.gemspec
|
122
|
+
- spec/oanda_api_v20/client_spec.rb
|
123
|
+
- spec/oanda_api_v20/oanda_api_v20_spec.rb
|
124
|
+
- spec/spec_helper.rb
|
79
125
|
homepage: http://rubygems.org/gems/oanda_api_v20
|
80
126
|
licenses:
|
81
127
|
- MIT
|
@@ -100,4 +146,7 @@ rubygems_version: 2.5.1
|
|
100
146
|
signing_key:
|
101
147
|
specification_version: 4
|
102
148
|
summary: Ruby Oanda REST API V20
|
103
|
-
test_files:
|
149
|
+
test_files:
|
150
|
+
- spec/oanda_api_v20/client_spec.rb
|
151
|
+
- spec/oanda_api_v20/oanda_api_v20_spec.rb
|
152
|
+
- spec/spec_helper.rb
|