eod_data 0.2.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 +7 -0
- data/.gitignore +14 -0
- data/.rspec +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +5 -0
- data/eod_data.gemspec +27 -0
- data/lib/eod_data.rb +5 -0
- data/lib/eod_data/version.rb +3 -0
- data/lib/eod_data/web_error.rb +3 -0
- data/lib/eod_data/web_response.rb +53 -0
- data/lib/eod_data/web_service.rb +220 -0
- data/spec/spec_helper.rb +74 -0
- data/spec/web_service_spec.rb +62 -0
- metadata +144 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3ef51eae22119172e8e9e2b4559601bff4e3d2be
|
4
|
+
data.tar.gz: 20607e6d872aee0e4405c2977700a65c75ef57dc
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 34f9c4660ef83922bd85dfe0492985bdcfd0c50f39b4b9695da323c900d0a026e0251886ee0a6624ecb08195288cc483ba355e8307bd7df55e984393d4cce35b
|
7
|
+
data.tar.gz: f321b0f6b461c368128418108f7ecd73ae296b5dace6e8dd8898160a376d9be32402cd297afea2a5eb592a02f254309906fb0e1de10d1f2c58dec013faeecb06
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Roy Ratcliffe
|
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
|
+
NON-INFRINGEMENT. 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.
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# EodData
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'eod_data'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install eod_data
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write usage instructions here
|
24
|
+
|
25
|
+
## Contributing
|
26
|
+
|
27
|
+
1. Fork it ( https://github.com/[my-github-username]/eod_data/fork )
|
28
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
30
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/eod_data.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'eod_data/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'eod_data'
|
8
|
+
spec.version = EodData::VERSION
|
9
|
+
spec.authors = ['Roy Ratcliffe']
|
10
|
+
spec.email = ['roy@pioneeringsoftware.co.uk']
|
11
|
+
spec.summary = %q{}
|
12
|
+
spec.description = %q{}
|
13
|
+
spec.homepage = ''
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
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_runtime_dependency 'nokogiri'
|
22
|
+
spec.add_runtime_dependency 'activesupport'
|
23
|
+
spec.add_development_dependency 'bundler'
|
24
|
+
spec.add_development_dependency 'rake'
|
25
|
+
spec.add_development_dependency 'rspec'
|
26
|
+
spec.add_development_dependency 'teamcity-ruby-client'
|
27
|
+
end
|
data/lib/eod_data.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'active_support/core_ext/string'
|
2
|
+
|
3
|
+
module EodData
|
4
|
+
# Represents an XML response from EODData, encapsulates the XML root element.
|
5
|
+
class WebResponse
|
6
|
+
attr_writer :root
|
7
|
+
|
8
|
+
# The XML root element should have the name RESPONSE or LOGINRESPONSE, or
|
9
|
+
# for that matter anything ending with RESPONSE. LOGINRESPONSE applies to
|
10
|
+
# token-access responses.
|
11
|
+
def initialize(root)
|
12
|
+
raise WebError, "Invalid response name #{root.name}" unless root.name.end_with?('RESPONSE')
|
13
|
+
@root = root
|
14
|
+
end
|
15
|
+
|
16
|
+
def name
|
17
|
+
@root.name
|
18
|
+
end
|
19
|
+
|
20
|
+
# Collects elements at +path+ whose element name matches +name+; where the
|
21
|
+
# name defaults to the path in singular, e.g. path at ITEMS, collecting
|
22
|
+
# elements by name ITEM. The answer is an array of hashes.
|
23
|
+
def elements_at(path, name=path.singularize)
|
24
|
+
@root.at(path).elements.select do |element|
|
25
|
+
element.name == name
|
26
|
+
end.map do |element|
|
27
|
+
hash = {}
|
28
|
+
element.attribute_nodes.each do |attr|
|
29
|
+
hash[attr.name.underscore.to_sym] = attr.content
|
30
|
+
end
|
31
|
+
hash
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def respond_to?(symbol)
|
36
|
+
return true if symbol.to_s.end_with?('_string')
|
37
|
+
return true if symbol.to_s.end_with?('_text')
|
38
|
+
return true if symbol.to_s.end_with?('s')
|
39
|
+
super
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
# Sending method +something_string+ looks for attribute +something+ and
|
45
|
+
# answers its value.
|
46
|
+
def method_missing(symbol, *args)
|
47
|
+
return @root.attribute(symbol.to_s.sub(/_string$/, '').capitalize).to_s if symbol.to_s.end_with?('_string')
|
48
|
+
return @root.at(symbol.to_s.sub(/_text$/, '').upcase).text if symbol.to_s.end_with?('_text')
|
49
|
+
return elements_at(symbol.to_s.sub(/_/, '').upcase) if symbol.to_s.end_with?('s')
|
50
|
+
super
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,220 @@
|
|
1
|
+
require 'net/https'
|
2
|
+
require 'uri'
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
require 'eod_data/web_response'
|
6
|
+
require 'eod_data/web_error'
|
7
|
+
|
8
|
+
class Hash
|
9
|
+
def convert_string_values!(type_and_keys = {})
|
10
|
+
type_and_keys.each do |type, keys|
|
11
|
+
keys.each do |key|
|
12
|
+
value = fetch(key)
|
13
|
+
case value
|
14
|
+
when String
|
15
|
+
store(key, case type
|
16
|
+
when :date_time
|
17
|
+
DateTime.parse(value)
|
18
|
+
when :integer
|
19
|
+
value.to_i
|
20
|
+
when :boolean
|
21
|
+
value.casecmp('true') == 0 || value.casecmp('yes') == 0
|
22
|
+
else
|
23
|
+
value
|
24
|
+
end)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
self
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module EodData
|
33
|
+
class WebService
|
34
|
+
URI_BASE = 'http://ws.eoddata.com/data.asmx/'
|
35
|
+
|
36
|
+
def membership
|
37
|
+
post_with_token('Membership', &raise_block_unless_success).membership_text
|
38
|
+
end
|
39
|
+
|
40
|
+
# Answers the number of months of history that EODData authorises for the
|
41
|
+
# user to download. Use the following block of Ruby code to collect the
|
42
|
+
# number of authorised months for all the available exchanges, where
|
43
|
+
# +service+ is an EodData::WebService instance.
|
44
|
+
#
|
45
|
+
# service.exchange_list.collect do |exchange|
|
46
|
+
# exchange[:code]
|
47
|
+
# end.collect do |code|
|
48
|
+
# service.exchange_months(code)
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
def exchange_months(exchange)
|
52
|
+
post_with_token('ExchangeMonths',
|
53
|
+
{
|
54
|
+
'Exchange' => exchange
|
55
|
+
},
|
56
|
+
&raise_block_unless_success).months_text.to_i
|
57
|
+
end
|
58
|
+
|
59
|
+
# Answers an array of exchanges represented as hashes. Each hash has the
|
60
|
+
# following symbol keys, with their values automatically converted to
|
61
|
+
# appropriate types: date-times, integers and booleans.
|
62
|
+
#
|
63
|
+
# :code
|
64
|
+
# :name
|
65
|
+
# :last_trade_date_time
|
66
|
+
# :country
|
67
|
+
# :currency
|
68
|
+
# :advances
|
69
|
+
# :declines
|
70
|
+
# :suffix
|
71
|
+
# :time_zone
|
72
|
+
# :is_intraday
|
73
|
+
# :intraday_start_date
|
74
|
+
# :has_intraday_product
|
75
|
+
#
|
76
|
+
def exchange_list
|
77
|
+
post_with_token('ExchangeList', &raise_block_unless_success).exchanges.map do |exchange|
|
78
|
+
exchange.convert_string_values!(
|
79
|
+
date_time: %w(last_trade_date_time intraday_start_date).map(&:to_sym),
|
80
|
+
integer: %w(advances declines).map(&:to_sym),
|
81
|
+
boolean: %w(is_intraday has_intraday_product).map(&:to_sym),
|
82
|
+
)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def symbol_list(exchange)
|
87
|
+
post_with_token('SymbolList',
|
88
|
+
{
|
89
|
+
'Exchange' => exchange
|
90
|
+
},
|
91
|
+
&raise_block_unless_success).symbols.map do |symbol|
|
92
|
+
symbol.convert_string_values!(
|
93
|
+
date_time: %w(date_time).map(&:to_sym)
|
94
|
+
)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Symbol changes have keys:
|
99
|
+
#
|
100
|
+
# :date_time
|
101
|
+
# :old_symbol
|
102
|
+
# :new_symbol
|
103
|
+
# :exchange_code
|
104
|
+
# :new_exchange_code
|
105
|
+
#
|
106
|
+
def symbol_changes_by_exchange(exchange)
|
107
|
+
post_with_token('SymbolChangesByExchange',
|
108
|
+
{
|
109
|
+
'Exchange' => exchange
|
110
|
+
},
|
111
|
+
&raise_block_unless_success).symbol_changes
|
112
|
+
end
|
113
|
+
|
114
|
+
# Answers end-of-day quotes either for an entire exchange or for a given
|
115
|
+
# list of symbols at a given exchange. Answers an array of hashes, each
|
116
|
+
# representing an individual quote with keys as follows.
|
117
|
+
#
|
118
|
+
# :symbol
|
119
|
+
# :description
|
120
|
+
# :name
|
121
|
+
# :date_time
|
122
|
+
# :open
|
123
|
+
# :high
|
124
|
+
# :low
|
125
|
+
# :close
|
126
|
+
# :volume
|
127
|
+
# :open_interest
|
128
|
+
# :previous
|
129
|
+
# :change
|
130
|
+
# :bid
|
131
|
+
# :ask
|
132
|
+
# :previous_close
|
133
|
+
# :next_open
|
134
|
+
# :modified
|
135
|
+
#
|
136
|
+
def quote_list(exchange, symbols=nil)
|
137
|
+
uri_path = 'QuoteList'
|
138
|
+
params = {'Exchange' => exchange}
|
139
|
+
symbols = [] if symbols.nil?
|
140
|
+
unless symbols.empty?
|
141
|
+
uri_path << '2'
|
142
|
+
params['Symbols'] = symbols.join(',')
|
143
|
+
end
|
144
|
+
post_with_token(uri_path, params, &raise_block_unless_success).quotes
|
145
|
+
end
|
146
|
+
|
147
|
+
def quote_list_by_date(exchange, date)
|
148
|
+
post_with_token('QuoteListByDate',
|
149
|
+
{
|
150
|
+
'Exchange' => exchange,
|
151
|
+
'QuoteDate' => date.strftime('%Y%m%d')
|
152
|
+
},
|
153
|
+
&raise_block_unless_success).quotes
|
154
|
+
end
|
155
|
+
|
156
|
+
# Expects the environment to define a user name and password for use with
|
157
|
+
# the EODData API. However, when running within a Rails application, login
|
158
|
+
# first checks for an eod_data.yml configuration file in your Rails +config+
|
159
|
+
# folder; loading the user name and password from it if the file exists.
|
160
|
+
def login
|
161
|
+
if defined?(Rails)
|
162
|
+
path = File.join(Rails.configuration.paths['config'].expanded[0], 'eod_data.yml')
|
163
|
+
if File.exist?(path)
|
164
|
+
hash = YAML.load(File.open(path))
|
165
|
+
username = hash['username']
|
166
|
+
password = hash['password']
|
167
|
+
end
|
168
|
+
end
|
169
|
+
username ||= ENV['EODDATA_USERNAME']
|
170
|
+
password ||= ENV['EODDATA_PASSWORD']
|
171
|
+
post('Login',
|
172
|
+
'Username' => username,
|
173
|
+
'Password' => password) do |response|
|
174
|
+
raise WebError, "Invalid login response #{response.name}" unless response.name == 'LOGINRESPONSE'
|
175
|
+
end.token_string
|
176
|
+
end
|
177
|
+
|
178
|
+
# Answers the current EODData authentication token or obtains a new token.
|
179
|
+
def token
|
180
|
+
@token ||= login
|
181
|
+
end
|
182
|
+
|
183
|
+
# Sends an authenticated POST request to EODData web services. Answers an
|
184
|
+
# XML root element encapsulated by a WebResponse object if successful.
|
185
|
+
def post_with_token(uri_path, params={})
|
186
|
+
post(uri_path, params.merge({'Token' => token}))
|
187
|
+
end
|
188
|
+
|
189
|
+
# Sends a POST request to EODData. Answers a WebResponse object
|
190
|
+
# encapsulating the XML root element taken from the response body. This
|
191
|
+
# assumes that the body contains XML. Raises an exception if the HTML
|
192
|
+
# response code is not 200. After parsing the XML, sends the result to a
|
193
|
+
# block if given. Use this to validate the response and raise exceptions
|
194
|
+
# when invalid.
|
195
|
+
def post(uri_path, params={})
|
196
|
+
response = Net::HTTP.post_form(URI.join(URI_BASE, uri_path), params)
|
197
|
+
raise WebError, "Invalid response code #{response.code}" unless response.code.to_i == 200
|
198
|
+
response = WebResponse.new(Nokogiri::XML(response.body) do |config|
|
199
|
+
config.strict.nonet
|
200
|
+
end.root)
|
201
|
+
yield response if block_given?
|
202
|
+
response
|
203
|
+
end
|
204
|
+
|
205
|
+
private
|
206
|
+
|
207
|
+
# Answers a block which when it yields raises an exception if the block's
|
208
|
+
# WebResponse argument does not have a message attribute matching the given
|
209
|
+
# string.
|
210
|
+
def raise_block_unless_message(string)
|
211
|
+
Proc.new do |response|
|
212
|
+
raise WebError, "Invalid response message #{response.message_string}" unless response.message_string == string
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def raise_block_unless_success
|
217
|
+
raise_block_unless_message 'Success'
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,74 @@
|
|
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
|
+
# The generated `.rspec` file contains `--require spec_helper` which will cause this
|
4
|
+
# file to always be loaded, without a need to explicitly require it in any files.
|
5
|
+
#
|
6
|
+
# Given that it is always loaded, you are encouraged to keep this file as
|
7
|
+
# light-weight as possible. Requiring heavyweight dependencies from this file
|
8
|
+
# will add to the boot time of your test suite on EVERY test run, even for an
|
9
|
+
# individual file that may not need all of that loaded. Instead, make a
|
10
|
+
# separate helper file that requires this one and then use it only in the specs
|
11
|
+
# that actually need it.
|
12
|
+
#
|
13
|
+
# The `.rspec` file also contains a few flags that are not defaults but that
|
14
|
+
# users commonly want.
|
15
|
+
#
|
16
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
17
|
+
RSpec.configure do |config|
|
18
|
+
# These two settings work together to allow you to limit a spec run
|
19
|
+
# to individual examples or groups you care about by tagging them with
|
20
|
+
# `:focus` metadata. When nothing is tagged with `:focus`, all examples
|
21
|
+
# get run.
|
22
|
+
config.filter_run :focus
|
23
|
+
config.run_all_when_everything_filtered = true
|
24
|
+
|
25
|
+
# Many RSpec users commonly either run the entire suite or an individual
|
26
|
+
# file, and it's useful to allow more verbose output when running an
|
27
|
+
# individual spec file.
|
28
|
+
if config.files_to_run.one?
|
29
|
+
# Use the documentation formatter for detailed output,
|
30
|
+
# unless a formatter has already been configured
|
31
|
+
# (e.g. via a command-line flag).
|
32
|
+
config.default_formatter = 'doc'
|
33
|
+
end
|
34
|
+
|
35
|
+
# Print the 10 slowest examples and example groups at the
|
36
|
+
# end of the spec run, to help surface which specs are running
|
37
|
+
# particularly slow.
|
38
|
+
config.profile_examples = 10
|
39
|
+
|
40
|
+
# Run specs in random order to surface order dependencies. If you find an
|
41
|
+
# order dependency and want to debug it, you can fix the order by providing
|
42
|
+
# the seed, which is printed after each run.
|
43
|
+
# --seed 1234
|
44
|
+
config.order = :random
|
45
|
+
|
46
|
+
# Seed global randomization in this process using the `--seed` CLI option.
|
47
|
+
# Setting this allows you to use `--seed` to deterministically reproduce
|
48
|
+
# test failures related to randomization by passing the same `--seed` value
|
49
|
+
# as the one that triggered the failure.
|
50
|
+
Kernel.srand config.seed
|
51
|
+
|
52
|
+
# rspec-expectations config goes here. You can use an alternate
|
53
|
+
# assertion/expectation library such as wrong or the stdlib/minitest
|
54
|
+
# assertions if you prefer.
|
55
|
+
config.expect_with :rspec do |expectations|
|
56
|
+
# Enable only the newer, non-monkey-patching expect syntax.
|
57
|
+
# For more details, see:
|
58
|
+
# - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax
|
59
|
+
expectations.syntax = :expect
|
60
|
+
end
|
61
|
+
|
62
|
+
# rspec-mocks config goes here. You can use an alternate test double
|
63
|
+
# library (such as bogus or mocha) by changing the `mock_with` option here.
|
64
|
+
config.mock_with :rspec do |mocks|
|
65
|
+
# Enable only the newer, non-monkey-patching expect syntax.
|
66
|
+
# For more details, see:
|
67
|
+
# - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
|
68
|
+
mocks.syntax = :expect
|
69
|
+
|
70
|
+
# Prevents you from mocking or stubbing a method that does not exist on
|
71
|
+
# a real object. This is generally recommended.
|
72
|
+
mocks.verify_partial_doubles = true
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'eod_data'
|
3
|
+
|
4
|
+
describe EodData::WebService do
|
5
|
+
let(:web_service) { EodData::WebService.new }
|
6
|
+
|
7
|
+
it 'exists' do
|
8
|
+
expect(web_service).not_to be_nil
|
9
|
+
end
|
10
|
+
|
11
|
+
context 'without username or password' do
|
12
|
+
# Save and restore the environment's user name and password. Store as
|
13
|
+
# instance variables; save before, restore after.
|
14
|
+
before do
|
15
|
+
@username = ENV.delete('EODDATA_USERNAME')
|
16
|
+
@password = ENV.delete('EODDATA_PASSWORD')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'throws a web error, invalid response code 500' do
|
20
|
+
expect { web_service.exchange_list }.to raise_error(EodData::WebError)
|
21
|
+
end
|
22
|
+
|
23
|
+
after do
|
24
|
+
ENV['EODDATA_USERNAME'] = @username
|
25
|
+
ENV['EODDATA_PASSWORD'] = @password
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'exchange list' do
|
30
|
+
let(:exchanges) { web_service.exchange_list }
|
31
|
+
|
32
|
+
it 'answers an array of exchange hashes' do
|
33
|
+
expect(exchanges).to be_an_instance_of(Array)
|
34
|
+
expect(exchanges.collect(&:class).uniq).to eq([Hash])
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'values have appropriate types' do
|
38
|
+
exchanges.each do |exchange|
|
39
|
+
%w(last_trade_date_time intraday_start_date).map(&:to_sym).each do |key|
|
40
|
+
expect(exchange[key].class).to eq(DateTime) if exchange[key]
|
41
|
+
end
|
42
|
+
%w(advances declines).map(&:to_sym).each do |key|
|
43
|
+
expect(exchange[key].class).to eq(Fixnum) if exchange[key]
|
44
|
+
end
|
45
|
+
%w(is_intraday has_intraday_product).map(&:to_sym).each do |key|
|
46
|
+
expect([TrueClass, FalseClass]).to include(exchange[key].class) if exchange[key]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'answers an array of symbols for NYSE' do
|
53
|
+
symbols = web_service.symbol_list('NYSE')
|
54
|
+
expect(symbols).to be_an_instance_of(Array)
|
55
|
+
expect(symbols.collect(&:class).uniq).to eq([Hash])
|
56
|
+
symbols.each do |symbol|
|
57
|
+
%w(date_time).map(&:to_sym).each do |key|
|
58
|
+
expect(symbol[key].class).to eq(DateTime) if symbol[key]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
metadata
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: eod_data
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Roy Ratcliffe
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-09-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nokogiri
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
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: bundler
|
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: rake
|
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: 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: teamcity-ruby-client
|
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
|
+
description: ''
|
98
|
+
email:
|
99
|
+
- roy@pioneeringsoftware.co.uk
|
100
|
+
executables: []
|
101
|
+
extensions: []
|
102
|
+
extra_rdoc_files: []
|
103
|
+
files:
|
104
|
+
- ".gitignore"
|
105
|
+
- ".rspec"
|
106
|
+
- Gemfile
|
107
|
+
- LICENSE.txt
|
108
|
+
- README.md
|
109
|
+
- Rakefile
|
110
|
+
- eod_data.gemspec
|
111
|
+
- lib/eod_data.rb
|
112
|
+
- lib/eod_data/version.rb
|
113
|
+
- lib/eod_data/web_error.rb
|
114
|
+
- lib/eod_data/web_response.rb
|
115
|
+
- lib/eod_data/web_service.rb
|
116
|
+
- spec/spec_helper.rb
|
117
|
+
- spec/web_service_spec.rb
|
118
|
+
homepage: ''
|
119
|
+
licenses:
|
120
|
+
- MIT
|
121
|
+
metadata: {}
|
122
|
+
post_install_message:
|
123
|
+
rdoc_options: []
|
124
|
+
require_paths:
|
125
|
+
- lib
|
126
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
requirements: []
|
137
|
+
rubyforge_project:
|
138
|
+
rubygems_version: 2.4.6
|
139
|
+
signing_key:
|
140
|
+
specification_version: 4
|
141
|
+
summary: ''
|
142
|
+
test_files:
|
143
|
+
- spec/spec_helper.rb
|
144
|
+
- spec/web_service_spec.rb
|