Skip to content

Commit 8765a87

Browse files
committed
Added base functionality
1 parent 96be7a6 commit 8765a87

File tree

5 files changed

+139
-29
lines changed

5 files changed

+139
-29
lines changed

lib/iex/api/config/client.rb

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module Client
66
ca_file
77
ca_path
88
endpoint
9+
sse_endpoint
910
open_timeout
1011
proxy
1112
publishable_token
@@ -24,6 +25,7 @@ def reset!
2425
self.ca_file = defined?(OpenSSL) ? OpenSSL::X509::DEFAULT_CERT_FILE : nil
2526
self.ca_path = defined?(OpenSSL) ? OpenSSL::X509::DEFAULT_CERT_DIR : nil
2627
self.endpoint = 'https://cloud.iexapis.com/v1'
28+
self.sse_endpoint = 'https://cloud-sse.iexapis.com/v1'
2729
self.publishable_token = ENV['IEX_API_PUBLISHABLE_TOKEN']
2830
self.secret_token = ENV['IEX_API_SECRET_TOKEN']
2931
self.user_agent = "IEX Ruby Client/#{IEX::VERSION}"

lib/iex/cloud/request.rb

+17-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,25 @@
11
module IEX
22
module Cloud
33
module Request
4+
STREAM_EVENT_DELIMITER = "\r\n\r\n".freeze
5+
46
def get(path, options = {})
57
request(:get, path, options)
68
end
79

10+
def get_stream(path, options = {})
11+
buffer = ""
12+
event_parser = Proc.new do |chunk|
13+
events = (buffer + chunk).lines(STREAM_EVENT_DELIMITER)
14+
buffer = events.last.end_with?(STREAM_EVENT_DELIMITER) ? '' : events.delete_at(-1)
15+
events.each do |event|
16+
yield JSON.parse(event.gsub(/\Adata: /, ''))
17+
end
18+
end
19+
20+
request(:get, path, { endpoint: sse_endpoint, request: { on_data: event_parser } }.merge(options))
21+
end
22+
823
def post(path, options = {})
924
request(:post, path, options)
1025
end
@@ -20,16 +35,16 @@ def delete(path, options = {})
2035
private
2136

2237
def request(method, path, options)
23-
path = [endpoint, path].join('/')
38+
path = [options.delete(:endpoint) || endpoint, path].join('/')
2439
response = connection.send(method) do |request|
40+
request.options.merge!(options.delete(:request)) if options.key?(:request)
2541
case method
2642
when :get, :delete
2743
request.url(path, options)
2844
when :post, :put
2945
request.path = path
3046
request.body = options.to_json unless options.empty?
3147
end
32-
request.options.merge!(options.delete(:request)) if options.key?(:request)
3348
end
3449
response.body
3550
end

lib/iex/endpoints/quote.rb

+13
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,19 @@ def quote(symbol, options = {})
66
rescue Faraday::ResourceNotFound => e
77
raise IEX::Errors::SymbolNotFoundError.new(symbol, e.response[:body])
88
end
9+
10+
# @param symbols - a list of symbols
11+
# @param options[:interval] sets intervals such as 1Second, 5Second, or 1Minute
12+
def stream_quote(symbols, options = {})
13+
options[:symbols] = Array(symbols).join(',')
14+
interval = options.delete(:interval)
15+
16+
get_stream("stocksUS#{interval}", { token: secret_token }.merge(options)) do |payload|
17+
payload.each do |quote|
18+
yield IEX::Resources::Quote.new(quote)
19+
end
20+
end
21+
end
922
end
1023
end
1124
end

spec/fixtures/iex/stream_quote/spy.yml

+61
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

spec/iex/endpoints/quote_spec.rb

+46-27
Original file line numberDiff line numberDiff line change
@@ -2,39 +2,58 @@
22

33
describe IEX::Resources::Quote do
44
include_context 'client'
5-
context 'known symbol', vcr: { cassette_name: 'quote/msft' } do
6-
subject do
7-
client.quote('MSFT')
8-
end
9-
it 'retrieves a quote' do
10-
expect(subject.symbol).to eq 'MSFT'
11-
expect(subject.company_name).to eq 'Microsoft Corp.'
12-
expect(subject.market_cap).to eq 915_754_985_600
13-
end
14-
it 'coerces numbers' do
15-
expect(subject.latest_price).to eq 119.36
16-
expect(subject.change).to eq(-0.61)
17-
expect(subject.week_52_high).to eq 120.82
18-
expect(subject.week_52_low).to eq 87.73
19-
expect(subject.change_percent).to eq(-0.00508)
20-
expect(subject.change_percent_s).to eq '-0.51%'
21-
expect(subject.extended_change_percent).to eq(-0.00008)
22-
expect(subject.extended_change_percent_s).to eq '-0.01%'
5+
6+
describe '#quote' do
7+
context 'known symbol', vcr: { cassette_name: 'quote/msft' } do
8+
subject do
9+
client.quote('MSFT')
10+
end
11+
it 'retrieves a quote' do
12+
expect(subject.symbol).to eq 'MSFT'
13+
expect(subject.company_name).to eq 'Microsoft Corp.'
14+
expect(subject.market_cap).to eq 915_754_985_600
15+
end
16+
it 'coerces numbers' do
17+
expect(subject.latest_price).to eq 119.36
18+
expect(subject.change).to eq(-0.61)
19+
expect(subject.week_52_high).to eq 120.82
20+
expect(subject.week_52_low).to eq 87.73
21+
expect(subject.change_percent).to eq(-0.00508)
22+
expect(subject.change_percent_s).to eq '-0.51%'
23+
expect(subject.extended_change_percent).to eq(-0.00008)
24+
expect(subject.extended_change_percent_s).to eq '-0.01%'
25+
end
26+
it 'coerces times' do
27+
expect(subject.latest_update).to eq 1_554_408_000_193
28+
expect(subject.latest_update_t).to eq Time.at(1_554_408_000)
29+
expect(subject.iex_last_updated).to eq 1_554_407_999_529
30+
expect(subject.iex_last_updated_t).to eq Time.at(1_554_407_999)
31+
end
2332
end
24-
it 'coerces times' do
25-
expect(subject.latest_update).to eq 1_554_408_000_193
26-
expect(subject.latest_update_t).to eq Time.at(1_554_408_000)
27-
expect(subject.iex_last_updated).to eq 1_554_407_999_529
28-
expect(subject.iex_last_updated_t).to eq Time.at(1_554_407_999)
33+
34+
context 'invalid symbol', vcr: { cassette_name: 'quote/invalid' } do
35+
subject do
36+
client.quote('INVALID')
37+
end
38+
it 'fails with SymbolNotFoundError' do
39+
expect { subject }.to raise_error IEX::Errors::SymbolNotFoundError, 'Symbol INVALID Not Found'
40+
end
2941
end
3042
end
3143

32-
context 'invalid symbol', vcr: { cassette_name: 'quote/invalid' } do
44+
describe '#stream_quote' do
3345
subject do
34-
client.quote('INVALID')
46+
quotes = []
47+
client.stream_quote('SPY', interval: '5Second') do |quote|
48+
quotes << quote
49+
end
50+
51+
quotes.first
3552
end
36-
it 'fails with SymbolNotFoundError' do
37-
expect { subject }.to raise_error IEX::Errors::SymbolNotFoundError, 'Symbol INVALID Not Found'
53+
54+
it 'retrieves a quote', vcr: { cassette_name: 'stream_quote/spy' } do
55+
expect(subject.symbol).to eq('SPY')
56+
expect(subject.close).to eq(433.63)
3857
end
3958
end
4059
end

0 commit comments

Comments
 (0)