eod_data 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --warnings
3
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in eod_data.gemspec
4
+ gemspec
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
@@ -0,0 +1,5 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new('spec')
5
+ task default: :spec
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,5 @@
1
+ require 'eod_data/version'
2
+ require 'eod_data/web_service'
3
+
4
+ module EodData
5
+ end
@@ -0,0 +1,3 @@
1
+ module EodData
2
+ VERSION = '0.2.0'
3
+ end
@@ -0,0 +1,3 @@
1
+ module EodData
2
+ class WebError < StandardError; end
3
+ end
@@ -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
@@ -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