they-yahoo_stock 1.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/History.txt +98 -0
  2. data/Manifest.txt +37 -0
  3. data/README.rdoc +151 -0
  4. data/Rakefile +12 -0
  5. data/features/history.feature +23 -0
  6. data/features/quotes.feature +23 -0
  7. data/features/script_symbol.feature +23 -0
  8. data/features/step_definitions/history_steps.rb +44 -0
  9. data/features/step_definitions/quotes_steps.rb +45 -0
  10. data/features/step_definitions/script_symbol_steps.rb +40 -0
  11. data/lib/yahoo_stock.rb +21 -0
  12. data/lib/yahoo_stock/base.rb +37 -0
  13. data/lib/yahoo_stock/history.rb +32 -0
  14. data/lib/yahoo_stock/interface.rb +71 -0
  15. data/lib/yahoo_stock/interface/history.rb +187 -0
  16. data/lib/yahoo_stock/interface/quote.rb +287 -0
  17. data/lib/yahoo_stock/interface/scrip_symbol.rb +74 -0
  18. data/lib/yahoo_stock/quote.rb +158 -0
  19. data/lib/yahoo_stock/result.rb +21 -0
  20. data/lib/yahoo_stock/result/array_format.rb +23 -0
  21. data/lib/yahoo_stock/result/hash_format.rb +37 -0
  22. data/lib/yahoo_stock/result/xml_format.rb +37 -0
  23. data/lib/yahoo_stock/scrip_symbol.rb +61 -0
  24. data/spec/spec_helper.rb +4 -0
  25. data/spec/yahoo_stock/base_spec.rb +48 -0
  26. data/spec/yahoo_stock/history_spec.rb +75 -0
  27. data/spec/yahoo_stock/interface/history_spec.rb +317 -0
  28. data/spec/yahoo_stock/interface/quote_spec.rb +414 -0
  29. data/spec/yahoo_stock/interface/scrip_symbol_spec.rb +120 -0
  30. data/spec/yahoo_stock/interface_spec.rb +112 -0
  31. data/spec/yahoo_stock/quote_spec.rb +258 -0
  32. data/spec/yahoo_stock/result/array_format_spec.rb +14 -0
  33. data/spec/yahoo_stock/result/hash_format_spec.rb +65 -0
  34. data/spec/yahoo_stock/result/xml_format_spec.rb +54 -0
  35. data/spec/yahoo_stock/result_spec.rb +33 -0
  36. data/spec/yahoo_stock/scrip_symbol_spec.rb +46 -0
  37. metadata +114 -0
@@ -0,0 +1,40 @@
1
+ Given /^I know the name of the company "([^\"]*)"$/ do |company_name|
2
+ @company_name = company_name
3
+ end
4
+
5
+ When /^I try to find the scrip symbols for "([^\"]*)"$/ do |company_name|
6
+ @symbol = YahooStock::ScripSymbol.new(company_name)
7
+ end
8
+
9
+ Then /^I should be able to get scrip symbol results as a comma separated values in a string$/ do
10
+ @symbol.results.output.should =~ /.*,.*/
11
+ end
12
+
13
+ Given /^I have the scrip symbol results for the symbol "([^\"]*)"$/ do |company|
14
+ @symbol_format = YahooStock::ScripSymbol.new(company)
15
+ @results = @symbol_format.results
16
+ end
17
+
18
+ When /^I want to get the scrip symbol results in array format$/ do
19
+ @array_output = @symbol_format.results(:to_array).output
20
+ end
21
+
22
+ Then /^I should get an array of values for scrip symbol$/ do
23
+ @array_output.should_not be_empty
24
+ end
25
+
26
+ When /^I want to get the scrip symbol results in hash format$/ do
27
+ @hash_output = @symbol_format.results(:to_hash).output
28
+ end
29
+
30
+ Then /^I should get an array of hash key values for scrip symbol$/ do
31
+ @hash_output.first.keys.should_not be_nil
32
+ end
33
+
34
+ When /^I want to get the scrip symbol results in xml format$/ do
35
+ @xml_output = @symbol_format.results(:to_xml).output
36
+ end
37
+
38
+ Then /^I should be able to get scrip symbol result as xml$/ do
39
+ @xml_output.should =~ /^\<xml*/
40
+ end
@@ -0,0 +1,21 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'date'
5
+ require 'csv'
6
+ require 'yahoo_stock/base.rb'
7
+ require 'yahoo_stock/interface.rb'
8
+ require 'yahoo_stock/history.rb'
9
+ require 'yahoo_stock/scrip_symbol.rb'
10
+ require 'yahoo_stock/quote.rb'
11
+ require 'yahoo_stock/interface/quote.rb'
12
+ require 'yahoo_stock/interface/history.rb'
13
+ require 'yahoo_stock/interface/scrip_symbol.rb'
14
+ require 'yahoo_stock/result.rb'
15
+ require 'yahoo_stock/result/array_format.rb'
16
+ require 'yahoo_stock/result/hash_format.rb'
17
+ require 'yahoo_stock/result/xml_format.rb'
18
+
19
+ module YahooStock
20
+ VERSION = '1.0.3'
21
+ end
@@ -0,0 +1,37 @@
1
+ # YahooStock::Base class to find and output results.
2
+ module YahooStock
3
+
4
+ class Base
5
+
6
+ def initialize(interface)
7
+ @interface = interface
8
+ end
9
+
10
+ # Uses the values method of the interface used to show results .
11
+ def find
12
+ @interface.values
13
+ end
14
+
15
+ # Method takes one parameter type or a block
16
+ # type uses the existing format classes namespaced in Result::ClassName.output
17
+ # e.g. * results :to_array
18
+ # * results {YourSpecialFormat.method_name}
19
+ # to store
20
+ def results(type=nil, &block)
21
+ if block_given?
22
+ yield
23
+ else
24
+ return YahooStock::Result.new(find) if !type || type.to_s.empty?
25
+ format_type = type.to_s.sub(/^to_/,'').strip
26
+ return YahooStock::Result.const_get("#{format_type.capitalize}Format").new(find){data_attributes}
27
+ end
28
+ end
29
+
30
+ # Abstract method
31
+ def data_attributes
32
+ raise 'Abstract method called. Use the subclass data_attributes method'
33
+ end
34
+
35
+ end
36
+
37
+ end
@@ -0,0 +1,32 @@
1
+ module YahooStock
2
+ class History < Base
3
+
4
+ class HistoryError < RuntimeError; end
5
+
6
+ def initialize(options)
7
+ unless options.is_a?(Hash)
8
+ raise HistoryError, 'A hash of start_date, end_date and stock_symbol is expected as parameters'
9
+ end
10
+ @interface = YahooStock::Interface::History.new(options)
11
+ end
12
+
13
+ def find
14
+ @find_values = super()
15
+ @find_values.sub(/Date.*\s/,'') if @find_values
16
+ end
17
+
18
+ def values_with_header
19
+ @interface.values
20
+ end
21
+
22
+ def data_attributes
23
+ return unless values_with_header
24
+ data_attributes = /Date.*\s/.match(values_with_header)
25
+ unless data_attributes.nil?
26
+ @data_attributes = data_attributes[0].sub(/\s*$/,'').split(',')
27
+ @data_attributes.map &:strip
28
+ end
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,71 @@
1
+ =begin
2
+ Main class to interface with Yahoo
3
+ =end
4
+
5
+ require 'net/http'
6
+ require 'observer'
7
+ module YahooStock
8
+ # ==DESCRIPTION:
9
+ #
10
+ # Class to generate the right url and interface with yahoo
11
+ #
12
+ class Interface
13
+ include Observable
14
+ class InterfaceError < RuntimeError ; end
15
+
16
+ BASE_URLS = {
17
+ :quote => "http://download.finance.yahoo.com/d/quotes.csv",
18
+ :history => "http://ichart.finance.yahoo.com/table.csv",
19
+ :scrip_symbol => "http://finance.yahoo.com/lookup/all"
20
+ } unless defined?(BASE_URLS)
21
+
22
+ attr_accessor :base_url, :uri_parameters
23
+
24
+ # Send request to the uri and get results
25
+ def get
26
+ begin
27
+ response = Net::HTTP.get_response(URI.parse(uri))
28
+ rescue => e
29
+ raise InterfaceError, "#{e.message}\n\n#{e.backtrace}"
30
+ end
31
+ response.code == '200' ? response.body : response_error(response)
32
+ end
33
+
34
+ # Generates full url to connect to yahoo
35
+ def uri
36
+ raise InterfaceError, 'Base url is require to generate full uri.' unless @base_url
37
+ return @base_url if @uri_parameters.nil? || @uri_parameters.empty?
38
+ params_with_values = []
39
+ @uri_parameters.each {|k,v| params_with_values << "#{k}=#{v}"}
40
+ URI.encode(@base_url+'?'+params_with_values.join('&'))
41
+ end
42
+
43
+ # Get result string
44
+ def values
45
+ @values ||= get
46
+ end
47
+
48
+ def update
49
+ @values = nil
50
+ end
51
+
52
+ private
53
+
54
+ # Generate error for debugging when something goes wrong while sending
55
+ # request to yahoo
56
+ def response_error(response)
57
+ trace = "Response \n\nbody : #{response.body}\n\ncode: #{response.code}\n\nmessage: #{response.message}"
58
+ case @base_url
59
+ when BASE_URLS[:quote]
60
+ raise InterfaceError, "Error :: Could not get stock data \n\n#{trace}"
61
+ when BASE_URLS[:history]
62
+ raise InterfaceError, "Error :: Could not get historical data \n\n#{trace}"
63
+ when BASE_URLS[:scrip_symbol]
64
+ raise InterfaceError, "Error :: Could not get stock symbol \n\n#{trace}"
65
+ else
66
+ raise InterfaceError, "Error connecting to #{@base_url} \n\n#{trace}"
67
+ end
68
+ end
69
+
70
+ end
71
+ end
@@ -0,0 +1,187 @@
1
+ =begin
2
+ params:
3
+ s=stock symbol
4
+ a=month-1
5
+ b=date
6
+ c=year
7
+ d=month-1
8
+ e=date
9
+ f=year
10
+ g=d or w or m
11
+ ignore=.csv
12
+
13
+ =end
14
+
15
+ module YahooStock
16
+ # ==DESCRIPTION:
17
+ #
18
+ # Class to generate the right url and interface with yahoo to get history of a stock
19
+ #
20
+ class Interface::History < Interface
21
+
22
+ class HistoryError < RuntimeError ; end
23
+
24
+ attr_reader :stock_symbol, :start_date, :end_date, :type
25
+
26
+ # The options parameter expects a hash with 4 key value pairs
27
+ #
28
+ # :stock_symbol => 'goog', :start_date => Date.today-30,
29
+ # :end_date => Date.today-10, :type => :weekly
30
+ #
31
+ # The type option accepts :daily, :monthly, :weekly and :dividend values
32
+ #
33
+ # The type key is optional, if not used then by default uses :daily
34
+ #
35
+ # and provides daily history for the specified date range
36
+ #
37
+ def initialize(options)
38
+ @base_url = BASE_URLS[:history]
39
+ validate_keys(options)
40
+
41
+ @stock_symbol = options[:stock_symbol]
42
+ @start_date = options[:start_date]
43
+ @end_date = options[:end_date]
44
+ @type = options[:type].nil? ? :daily : options[:type].to_sym
45
+
46
+ validate_type_values(options[:type]) if options[:type]
47
+ validate_stock_symbol(options)
48
+ validate_start_date(options)
49
+ validate_end_date(options)
50
+ validate_history_range
51
+ end
52
+
53
+ # Make sure that stock symbol is not nil or an empty string before setting it
54
+ def stock_symbol=(stock_symbol)
55
+ validate_stock_symbol_attribute(stock_symbol)
56
+ @stock_symbol = stock_symbol
57
+ end
58
+
59
+ # Before setting start date
60
+ # - Make sure that it is of type date
61
+ # - Make sure that it is before the end date
62
+ def start_date=(start_date)
63
+ validate_start_date_type(start_date)
64
+ validate_history_range(start_date)
65
+ @start_date = start_date
66
+ end
67
+
68
+ # Before setting end date
69
+ # - Make sure that it is of type date
70
+ # - Make sure that it is after the start date
71
+ def end_date=(end_date)
72
+ validate_end_date_type(end_date)
73
+ validate_history_range(start_date, end_date)
74
+ @end_date = end_date
75
+ end
76
+
77
+ # Set type to specify whether daily, weekly, monthly or dividend history required
78
+ def type=(type)
79
+ validate_type_values(type)
80
+ @type = type
81
+ end
82
+
83
+ # Generate full uri with the help of uri method of the superclass
84
+ def uri
85
+ history_type = case type
86
+ when :daily then 'd'
87
+ when :weekly then 'w'
88
+ when :monthly then 'm'
89
+ when :dividend then 'v'
90
+ end
91
+ @uri_parameters = {:a => sprintf("%02d", start_date.month-1), :b => start_date.day,
92
+ :c => start_date.year, :d => sprintf("%02d", end_date.month-1),
93
+ :e => end_date.day, :f => end_date.year, :s => stock_symbol,
94
+ :g => history_type, :ignore => '.csv'}
95
+ super()
96
+ end
97
+
98
+ # Get uri content with the help of get method of the super class
99
+ def get
100
+ if stock_symbol.nil? || start_date.nil? || end_date.nil?
101
+ raise HistoryError, 'Cannot send request unless all parameters, ie stock_symbol, start and end date are present'
102
+ end
103
+ if !(start_date.is_a?(Date) || end_date.is_a?(Date))
104
+ raise HistoryError, 'Start and end date should be of type Date'
105
+ end
106
+ uri
107
+ super()
108
+ end
109
+
110
+ private
111
+
112
+ def validate_type_values(type_value=type)
113
+ valid_values = [:daily, :weekly, :monthly, :dividend]
114
+ unless valid_values.include?(type_value)
115
+ raise HistoryError, "Allowed values for type are #{valid_values.join(', ')}"
116
+ end
117
+ end
118
+
119
+ def validate_stock_symbol(options)
120
+ unless options.keys.include?(:stock_symbol)
121
+ raise HistoryError, ':stock_symbol key is not present in the parameter hash'
122
+ end
123
+ validate_stock_symbol_attribute
124
+ end
125
+
126
+ def validate_stock_symbol_attribute(symbol=stock_symbol)
127
+ if symbol.nil? || symbol.empty?
128
+ raise HistoryError, ':stock_symbol value cannot be nil or blank'
129
+ end
130
+ end
131
+
132
+ def validate_start_date(options)
133
+ validate_date_options(:start_date, options)
134
+ validate_start_date_type
135
+ end
136
+
137
+ def validate_end_date(options)
138
+ validate_date_options(:end_date, options)
139
+ validate_end_date_type
140
+ end
141
+
142
+ def validate_history_range(s_date = start_date, e_date = end_date)
143
+ if s_date >= Date.today
144
+ raise HistoryError, "Start date must be in the past"
145
+ end
146
+ if e_date >= Date.today
147
+ raise HistoryError, "End date must be in the past"
148
+ end
149
+ if e_date < s_date
150
+ raise HistoryError, "End date must be greater than the start date"
151
+ end
152
+ end
153
+
154
+ def validate_end_date_type(e_date=end_date)
155
+ unless e_date.is_a?(Date)
156
+ raise HistoryError, "End date must be of type Date"
157
+ end
158
+ end
159
+
160
+ def validate_start_date_type(s_date=start_date)
161
+ unless s_date.is_a?(Date)
162
+ raise HistoryError, "Start date must be of type Date"
163
+ end
164
+ end
165
+
166
+ def validate_date_options(date_type, options)
167
+ unless options.keys.include?(date_type)
168
+ raise HistoryError, ":#{date_type} key is not present in the parameter hash"
169
+ end
170
+
171
+ if options[date_type].nil? || options[date_type].to_s.empty?
172
+ raise HistoryError, ":#{date_type} value cannot be blank"
173
+ end
174
+ end
175
+
176
+ def validate_keys(options)
177
+ valid_keys = [:stock_symbol, :start_date, :end_date, :type]
178
+ invalid_keys = []
179
+ options.keys.each{|key| invalid_keys << key unless valid_keys.include?(key) }
180
+ unless invalid_keys.length.zero?
181
+ raise HistoryError, "An invalid key '#{invalid_keys.join(',')}' is passed in the parameters. Allowed keys are #{valid_keys.join(', ')}"
182
+ end
183
+ end
184
+
185
+ end
186
+
187
+ end
@@ -0,0 +1,287 @@
1
+ =begin
2
+
3
+ Parameter source:
4
+ http://www.gummy-stuff.org/Yahoo-data.htm
5
+ http://finance.yahoo.com/d/quotes.csv?s=XOM+BBDb.TO+JNJ+MSFT&f=snd1l1yr
6
+
7
+ =end
8
+
9
+ module YahooStock
10
+ # ==DESCRIPTION:
11
+ #
12
+ # Class to generate the right url and interface with yahoo to get daily stock data
13
+ #
14
+ class Interface::Quote < Interface
15
+
16
+ class QuoteError < RuntimeError ; end
17
+
18
+ STD_PARAMETERS = {
19
+ :symbol => 's',
20
+ :name => 'n',
21
+ :last_trade_price_only => 'l1',
22
+ :last_trade_date => 'd1',
23
+ :last_trade_time => 't1',
24
+ :change_with_percent_change => 'c',
25
+ :change => 'c1',
26
+ :previous_close => 'p',
27
+ :change_in_percent => 'p2',
28
+ :open => 'o',
29
+ :day_low => 'g',
30
+ :day_high => 'h',
31
+ :volume => 'v',
32
+ :last_trade_with_time => 'l',
33
+ :day_range => 'm',
34
+ :ticker_trend => 't7',
35
+ :ask => 'a',
36
+ :average_daily_volume => 'a2',
37
+ :bid => 'b',
38
+ :bid_size => 'b4'
39
+ } unless defined?(STD_PARAMETERS)
40
+
41
+ EXTENDED_PARAMETERS = {
42
+ :symbol => 's',
43
+ :name => 'n',
44
+ :fifty_two_week_range => 'w',
45
+ :change_from_52_week_low => 'j5',
46
+ :percent_change_from_52_week_low => 'j6',
47
+ :change_from_52_week_high => 'k4',
48
+ :percent_change_from_52_week_high => 'k5',
49
+ :earnings_per_share => 'e',
50
+ :short_ratio => 's7',
51
+ :p_e_ratio => 'r',
52
+ :dividend_pay_date => 'r1',
53
+ :ex_dividend_date => 'q',
54
+ :dividend_per_share => 'd',
55
+ :dividend_yield => 'y',
56
+ :one_yr_target_price => 't8',
57
+ :market_capitalization => 'j1',
58
+ :eps_estimate_current_year => 'e7',
59
+ :eps_estimate_next_year => 'e8',
60
+ :eps_estimate_next_quarter => 'e9',
61
+ :peg_ratio => 'r5',
62
+ :price_eps_estimate_current_year => 'r6',
63
+ :price_eps_estimate_next_year => 'r7',
64
+ :book_value => 'b4',
65
+ :ebitda => 'j4',
66
+ :fifty_day_moving_average => 'm3',
67
+ :two_hundred_day_moving_average => 'm4',
68
+ :change_from_200_day_moving_average => 'm5',
69
+ :percent_change_from_200_day_moving_average => 'm6',
70
+ :change_from_50_day_moving_average => 'm7',
71
+ :percent_change_from_50_day_moving_average => 'm8',
72
+ :shares_owned => 's1',
73
+ :price_paid => 'p1',
74
+ :commission => 'c3',
75
+ :holdings_value => 'v1',
76
+ :day_value_change => 'w1',
77
+ :trade_date => 'd2',
78
+ :holdings_gain_percent => 'g1',
79
+ :annualized_gain => 'g3',
80
+ :holdings_gain => 'g4',
81
+ :stock_exchange => 'x',
82
+ :high_limit => 'l2',
83
+ :low_limit => 'l3',
84
+ :notes => 'n4',
85
+ :fifty_two_week_low => 'j',
86
+ :fifty_two_week_high => 'k',
87
+ :more_info => 'i',
88
+ } unless defined?(EXTENDED_PARAMETERS)
89
+
90
+ REALTIME_PARAMETERS = {
91
+ :symbol => 's',
92
+ :name => 'n',
93
+ :ask_real_time => 'b2',
94
+ :bid_real_time => 'b3',
95
+ :change => 'c1',
96
+ :change_real_time => 'c6',
97
+ :after_hours_change_real_time => 'c8',
98
+ :holdings_gain_percent_real_time => 'g5',
99
+ :holdings_gain_real_time => 'g6',
100
+ :order_book_real_time => 'i5',
101
+ :market_cap_real_time => 'j3',
102
+ :last_trade_real_time_with_time => 'k1',
103
+ :change_percent_real_time => 'k2',
104
+ :day_range_real_time => 'm2',
105
+ :p_e_ratio_real_time => 'r2',
106
+ :holdings_value_real_time => 'v7',
107
+ :day_value_change_real_time => 'w4',
108
+ } unless defined?(REALTIME_PARAMETERS)
109
+
110
+ attr_reader :stock_symbols
111
+
112
+ # The stock_params_hash parameter expects a hash with two key value pairs
113
+ #
114
+ # :stock_symbols => 'Array of stock symbols'
115
+ #
116
+ # e.g. :stock_symbols => ['YHOO']
117
+ #
118
+ # another hash :read_parameters => ['array of values']
119
+ #
120
+ # e.g. :read_parameters => [:last_trade_price_only, :last_trade_date]
121
+ def initialize(stock_params_hash)
122
+ unless stock_params_hash
123
+ raise QuoteError, 'You must pass a hash of stock symbols and the data you would like to see'
124
+ end
125
+ if !stock_params_hash[:stock_symbols] || stock_params_hash[:stock_symbols].length.zero?
126
+ raise(QuoteError, 'No stocks passed')
127
+ end
128
+ if !stock_params_hash[:read_parameters] || stock_params_hash[:read_parameters].length.zero?
129
+ raise QuoteError, 'Read parameters are not provided'
130
+ end
131
+ @stock_symbols = stock_params_hash[:stock_symbols]
132
+ @yahoo_url_parameters = stock_params_hash[:read_parameters]
133
+ @base_url = BASE_URLS[:quote]
134
+ add_observer(self)
135
+ end
136
+
137
+ def stock_symbols=(stock_symbol)
138
+ symbols_on_read = @stock_symbols.dup
139
+ @stock_symbols ||= []
140
+ @stock_symbols << stock_symbol unless @stock_symbols.include?(stock_symbol)
141
+ run_observers if symbols_on_read != @stock_symbols
142
+ end
143
+
144
+ def yahoo_url_parameters
145
+ return [] if !@yahoo_url_parameters || @yahoo_url_parameters.empty?
146
+ @yahoo_url_parameters.map(&:id2name).sort.map(&:to_sym)
147
+ end
148
+
149
+ def yahoo_url_parameters=(yahoo_url_parameter)
150
+ params_on_read = @yahoo_url_parameters.dup
151
+ unless allowed_parameters.include?(yahoo_url_parameter)
152
+ raise QuoteError, "Interface parameter #{yahoo_url_parameter} is not a valid parameter."
153
+ end
154
+ @yahoo_url_parameters ||= []
155
+ @yahoo_url_parameters << yahoo_url_parameter unless @yahoo_url_parameters.include?(yahoo_url_parameter)
156
+ run_observers if params_on_read != @yahoo_url_parameters
157
+ end
158
+
159
+ # Generate full url to be sent to yahoo
160
+ def uri
161
+ @all_stock_symbols = stock_symbols.join('+')
162
+ invalid_params = yahoo_url_parameters-allowed_parameters
163
+ unless invalid_params.length.zero?
164
+ raise QuoteError, "The parameters '#{invalid_params.join(', ')}' are not valid. Please check using YahooStock::Interface::Quote#allowed_parameters or YahooStock::Quote#valid_parameters"
165
+ end
166
+ @parameter_values = yahoo_url_parameters.collect {|v| parameters[v]}.join('')
167
+ if @all_stock_symbols.empty?
168
+ raise QuoteError, "You must add atleast one stock symbol to get stock data"
169
+ end
170
+ if @parameter_values.empty?
171
+ raise QuoteError, "You must add atleast one parameter to get stock data"
172
+ end
173
+ @uri_parameters = {:s => @all_stock_symbols, :f => @parameter_values}
174
+ super()
175
+ end
176
+
177
+ # Read the result using get method in super class
178
+ def get
179
+ uri
180
+ super()
181
+ end
182
+
183
+ # TODO MOVE TO THE HASH CLASS
184
+ # Returns results for the stock symbols as a hash.
185
+ # The hash keys are the stock symbols and the values are a hash of the keys and
186
+ # values asked for that stock.
187
+ def results
188
+ stock = {}
189
+ values.each_with_index do |values, index|
190
+ parsed_values = values.split(',')
191
+ stock[stock_symbols[index]] ||= {}
192
+ parsed_values.each_with_index do |value, i|
193
+ stock[stock_symbols[index]][yahoo_url_parameters[i]] = value
194
+ end
195
+ end
196
+ stock
197
+ end
198
+
199
+ # Add stock symbols to the url.
200
+ def add_symbols(*symbols)
201
+ symbols.each {|symbol| self.stock_symbols = symbol }
202
+ end
203
+
204
+ # Remove stock symbols from the url.
205
+ def remove_symbols(*symbols)
206
+ symbols.each do |symbol|
207
+ unless stock_symbols.include?(symbol)
208
+ raise QuoteError, "Cannot remove stock symbol #{symbol} as it is currently not present."
209
+ end
210
+ if stock_symbols.length == 1
211
+ raise QuoteError, "Symbol #{symbol} is the last symbol. Please add another symbol before removing this."
212
+ end
213
+ stock_symbols.reject!{|stock_sym| stock_sym == symbol}
214
+ run_observers
215
+ end
216
+ end
217
+
218
+ # Add parameters to the url.
219
+ def add_parameters(*parameters)
220
+ parameters.each {|parameter| self.yahoo_url_parameters = parameter }
221
+ end
222
+
223
+ # Remove parameters from the url.
224
+ def remove_parameters(*parameters)
225
+ parameters.each do |parameter|
226
+ unless yahoo_url_parameters.include?(parameter)
227
+ raise QuoteError, "Parameter #{parameter} is not present in current list"
228
+ end
229
+ if yahoo_url_parameters.length == 1
230
+ raise QuoteError, "Parameter #{parameter} is the last parameter. Please add another parameter before removing this."
231
+ end
232
+ @yahoo_url_parameters.reject!{|parameter_key| parameter_key == parameter}
233
+ run_observers
234
+ end
235
+ end
236
+
237
+ # Returns an array of parameters that can be passed to yahoo.
238
+ def allowed_parameters
239
+ parameters.keys
240
+ end
241
+
242
+ # Add standard parameters
243
+ def add_standard_params
244
+ add_grouped_parameters(STD_PARAMETERS.keys)
245
+ end
246
+
247
+ # Add extended parameters
248
+ def add_extended_params
249
+ add_grouped_parameters(EXTENDED_PARAMETERS.keys)
250
+ end
251
+
252
+ # Add realtime parameters
253
+ def add_realtime_params
254
+ add_grouped_parameters(REALTIME_PARAMETERS.keys)
255
+ end
256
+
257
+ # Clear all existing parameters from the current instance.
258
+ def clear_parameters
259
+ @yahoo_url_parameters.clear
260
+ run_observers
261
+ end
262
+
263
+ # Clear all existing symbols from the current instance.
264
+ def clear_symbols
265
+ stock_symbols.clear
266
+ run_observers
267
+ end
268
+
269
+ private
270
+
271
+ def add_grouped_parameters(parameter_group)
272
+ return if (parameter_group - yahoo_url_parameters).empty?
273
+ clear_parameters
274
+ parameter_group.each { |parameter| add_parameters(parameter) }
275
+ end
276
+
277
+ def run_observers
278
+ changed
279
+ notify_observers
280
+ end
281
+
282
+ def parameters
283
+ STD_PARAMETERS.merge(EXTENDED_PARAMETERS).merge(REALTIME_PARAMETERS)
284
+ end
285
+
286
+ end
287
+ end