datasift 2.1.1 → 3.0.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +100 -0
  4. data/Gemfile.lock +32 -0
  5. data/README.md +38 -79
  6. data/VERSION +1 -1
  7. data/datasift.gemspec +21 -24
  8. data/examples/auth.rb +44 -0
  9. data/examples/core_api_eg.rb +46 -0
  10. data/examples/historics_eg.rb +50 -0
  11. data/examples/historics_preview_eg.rb +30 -0
  12. data/examples/live_stream_eg.rb +89 -0
  13. data/examples/managed_source_eg.rb +56 -0
  14. data/examples/pull.rb +44 -0
  15. data/examples/push_eg.rb +56 -0
  16. data/lib/api/api_resource.rb +23 -0
  17. data/lib/datasift.rb +287 -14
  18. data/lib/errors.rb +59 -0
  19. data/lib/historics.rb +76 -0
  20. data/lib/historics_preview.rb +20 -0
  21. data/lib/live_stream.rb +53 -0
  22. data/lib/managed_source.rb +57 -0
  23. data/lib/push.rb +156 -0
  24. data/tests/core_api_test.rb +42 -0
  25. metadata +51 -73
  26. data/Rakefile +0 -34
  27. data/config.yml +0 -2
  28. data/examples/consume-stream.rb +0 -63
  29. data/examples/deletes.rb +0 -52
  30. data/examples/dpu.rb +0 -115
  31. data/examples/football-buffered.rb +0 -51
  32. data/examples/football.rb +0 -53
  33. data/examples/historics.sh +0 -2
  34. data/examples/historics/create-from-csdl.rb +0 -71
  35. data/examples/historics/create-from-hash.rb +0 -65
  36. data/examples/historics/delete.rb +0 -30
  37. data/examples/historics/env.rb +0 -37
  38. data/examples/historics/list.rb +0 -30
  39. data/examples/historics/start.rb +0 -30
  40. data/examples/historics/stop.rb +0 -30
  41. data/examples/historics/view.rb +0 -28
  42. data/examples/push.sh +0 -2
  43. data/examples/push/delete.rb +0 -33
  44. data/examples/push/env.rb +0 -53
  45. data/examples/push/list.rb +0 -30
  46. data/examples/push/pause.rb +0 -33
  47. data/examples/push/push-from-hash.rb +0 -72
  48. data/examples/push/push-historic-from-csdl.rb +0 -98
  49. data/examples/push/push-stream-from-csdl.rb +0 -70
  50. data/examples/push/resume.rb +0 -33
  51. data/examples/push/stop.rb +0 -33
  52. data/examples/push/view-log.rb +0 -45
  53. data/examples/push/view.rb +0 -31
  54. data/examples/twitter-track.rb +0 -61
  55. data/lib/DataSift/apiclient.rb +0 -73
  56. data/lib/DataSift/definition.rb +0 -202
  57. data/lib/DataSift/exceptions.rb +0 -33
  58. data/lib/DataSift/historic.rb +0 -316
  59. data/lib/DataSift/managed_source.rb +0 -263
  60. data/lib/DataSift/mockapiclient.rb +0 -44
  61. data/lib/DataSift/push_definition.rb +0 -115
  62. data/lib/DataSift/push_subscription.rb +0 -330
  63. data/lib/DataSift/stream_consumer.rb +0 -166
  64. data/lib/DataSift/stream_consumer_http.rb +0 -188
  65. data/lib/DataSift/user.rb +0 -311
  66. data/test/helper.rb +0 -95
  67. data/test/test_definition.rb +0 -273
  68. data/test/test_historics.rb +0 -233
  69. data/test/test_pushdefinition.rb +0 -92
  70. data/test/test_pushsubscription.rb +0 -17
  71. data/test/test_user.rb +0 -130
  72. data/test/testdata.yml +0 -30
@@ -0,0 +1,30 @@
1
+ require './auth'
2
+ class HistoricsPreviewApi < DataSiftExample
3
+ def initialize
4
+ super
5
+ run
6
+ end
7
+
8
+ def run
9
+ begin
10
+ puts 'Creating hash'
11
+ stream = @datasift.compile 'interaction.content contains "datasift"'
12
+ hash = stream[:data][:hash]
13
+
14
+ puts 'Creating a preview'
15
+ # see http://dev.datasift.com/docs/rest-api/previewcreate for docs
16
+ parameters = 'interaction.author.link,targetVol,hour;interaction.type,freqDist,10'
17
+ start = Time.now.to_i - (3600 * 48) # 48hrs ago
18
+ source = @datasift.historics_preview.create(hash, parameters, start)
19
+ puts source
20
+
21
+ puts 'Getting preview data'
22
+ puts @datasift.historics_preview.get source[:data][:id]
23
+
24
+ rescue DataSiftError => dse
25
+ puts dse.message
26
+ end
27
+ end
28
+ end
29
+
30
+ HistoricsPreviewApi.new
@@ -0,0 +1,89 @@
1
+ require './auth'
2
+ class StreamingApi < DataSiftExample
3
+
4
+ def initialize
5
+ super
6
+ @datasift = DataSift::Client.new(@config)
7
+ run
8
+ end
9
+
10
+ def run
11
+ begin
12
+ rubyReceived = 0
13
+ pythonReceived = 0
14
+ ruby = 'interaction.content contains "ruby"'
15
+ rubyStream = @datasift.compile ruby
16
+
17
+ python = 'interaction.content contains "python"'
18
+ pythonStream = @datasift.compile python
19
+
20
+ on_delete = lambda { |stream, m| puts 'We must delete this to be compliant ==> ' + m }
21
+
22
+ on_error = lambda do |stream, e|
23
+ puts 'A serious error has occurred'
24
+ puts e.message
25
+ end
26
+
27
+ on_message_ruby = lambda do |message, stream, hash|
28
+ rubyReceived += 1
29
+ puts "Ruby #{rubyReceived}, #{message}"
30
+
31
+ if rubyReceived >= 10
32
+ puts 'un-subscribing from ruby stream '+ hash
33
+ stream.unsubscribe hash
34
+ end
35
+ end
36
+
37
+ on_message_python = lambda do |message, stream, hash|
38
+ pythonReceived += 1
39
+ puts "python #{pythonReceived}, #{message}"
40
+
41
+ if pythonReceived >= 10
42
+ puts 'un-subscribing from python stream '+ hash
43
+ stream.unsubscribe hash
44
+ end
45
+ end
46
+
47
+ on_connect = lambda do |stream|
48
+ #
49
+ puts 'subscribing to python stream '+ pythonStream[:data][:hash]
50
+ stream.subscribe(pythonStream[:data][:hash], on_message_python)
51
+ puts 'Subscribed to '+ pythonStream[:data][:hash]
52
+ sleep 1
53
+ #
54
+ puts 'subscribing to ruby stream '+ rubyStream[:data][:hash]
55
+ stream.subscribe(rubyStream[:data][:hash], on_message_ruby)
56
+ puts 'Subscribed to '+ rubyStream[:data][:hash]
57
+ end
58
+
59
+ on_close = lambda do |stream|
60
+ puts 'closed'
61
+ end
62
+
63
+ on_datasift_message = lambda do |stream, message, hash|
64
+ #not all messages have a hash
65
+ puts "is_success = #{message[:is_success]}, is_failure = #{message[:is_failure]}, is_warning = #{message[:is_warning]}, is_tick = #{message[:is_tick]}"
66
+ puts "DataSift Message #{hash} ==> #{message}"
67
+ end
68
+
69
+ conn = DataSift::new_stream(@config, on_delete, on_error, on_connect, on_close)
70
+ conn.on_datasift_message = on_datasift_message
71
+ #can do something else here now...
72
+ puts 'Do some other business stuff...'
73
+ conn.stream.read_thread.join
74
+ #rescue DataSiftError
75
+ rescue DataSiftError => dse
76
+ puts "Error #{dse.message}"
77
+ # Then match specific one to take action - All errors thrown by the client extend DataSiftError
78
+ case dse
79
+ when ConnectionError
80
+ # some connection error
81
+ when AuthError
82
+ when BadRequestError
83
+ else
84
+ # do something else...
85
+ end
86
+ end
87
+ end
88
+ end
89
+ StreamingApi.new
@@ -0,0 +1,56 @@
1
+ require './auth'
2
+ class ManagedSourceApi < DataSiftExample
3
+ def initialize
4
+ super
5
+ run
6
+ end
7
+
8
+ def run
9
+ begin
10
+ puts 'Creating a managed source'
11
+ parameters = {:likes => true,
12
+ :posts_by_others => true,
13
+ :comments => true
14
+ }
15
+ resources = [{
16
+ :parameters => {
17
+ :url => 'http://www.facebook.com/thegaurdian',
18
+ :title => 'Some news page',
19
+ :id => :thegaurdian
20
+ }
21
+ }]
22
+ auth = [{
23
+ :parameters => {
24
+ :value => 'CAAIUKbXn8xsBAN5MnINICUT9gEBsZBh3hKoSEeIMP0ZA4zadMr64X6ljvZC4VBZCyYr9tyhih5nO0R39A1FQ848v0mZA6d3ehIHuSbKb7avtfLOtL5XKDYRIXHmRWreyxxVc3jk7CIa4ZCI5AAKeUUO3GUS8EaPdYVh9rO5FvvNmIatzz6k8el'
25
+ }
26
+ }]
27
+
28
+ source = @datasift.managed_source.create('facebook_page', 'My managed source', parameters, resources, auth)
29
+ puts source
30
+
31
+ id = source[:data][:id]
32
+
33
+ puts 'Starting delivery for my private source'
34
+ puts @datasift.managed_source.start id
35
+
36
+ puts 'Updating'
37
+ puts @datasift.managed_source.update(id, 'facebook_page', 'Updated source', parameters, resources, auth)
38
+
39
+ puts 'Getting info from DataSift about my page'
40
+ puts @datasift.managed_source.get id
41
+
42
+ puts 'Fetching logs'
43
+ puts @datasift.managed_source.log id
44
+
45
+ puts 'Stopping'
46
+ puts @datasift.managed_source.stop id
47
+
48
+ puts 'Deleting'
49
+ puts @datasift.managed_source.delete id
50
+ rescue DataSiftError => dse
51
+ puts dse.message
52
+ end
53
+ end
54
+ end
55
+
56
+ ManagedSourceApi.new
@@ -0,0 +1,44 @@
1
+ require './auth'
2
+ class PushApi < DataSiftExample
3
+ def initialize
4
+ super
5
+ end
6
+
7
+ def run
8
+ begin
9
+ @params = {:output_type => 'pull'}
10
+ puts 'Validating'
11
+ if @datasift.push.valid? @params
12
+ stream = @datasift.compile 'interaction.content contains "music"'
13
+ subscription = create_push(stream[:data][:hash])
14
+
15
+ subscription_id = subscription[:data][:id]
16
+ # Pull interactions from the push queue - this will only work if we have set
17
+ # the Push Subscription output_type above to 'pull'
18
+
19
+ puts 'Waiting...'
20
+ sleep 10
21
+
22
+ # Passing a lambda is more efficient because it is executed once for each interaction received
23
+ # this saves having to iterate over the array returned so the same iteration isn't done twice
24
+ puts 'Pulling'
25
+ @datasift.push.pull(subscription_id, 20971520, '', lambda{ |e| puts "on_message => #{e}" })
26
+
27
+ puts 'Waiting...'
28
+ sleep 10
29
+ puts 'Pulling'
30
+ @datasift.push.pull(subscription_id, 20971520, '', lambda{ |e| puts "on_message => #{e}" })
31
+
32
+ puts 'Stop Subscription'
33
+ @datasift.push.stop subscription_id
34
+
35
+ puts 'Delete Subscription'
36
+ @datasift.push.delete subscription_id
37
+ end
38
+ rescue DataSiftError => dse
39
+ puts dse
40
+ end
41
+ end
42
+
43
+ end
44
+ PushApi.new().run
@@ -0,0 +1,56 @@
1
+ require './auth'
2
+ class PushApi < DataSiftExample
3
+ def initialize
4
+ super
5
+ end
6
+
7
+ def run
8
+ begin
9
+ puts 'Validating'
10
+ if @datasift.push.valid? @params
11
+ stream = @datasift.compile 'interaction.content contains "datasift"'
12
+ subscription = create_push(stream[:data][:hash])
13
+
14
+ subscription_id = subscription[:data][:id]
15
+ #pull a bunch of interactions from the push queue - only work if we had set the output_type above to pull
16
+ #pull @datasift.pull subscription_id
17
+
18
+ puts 'updating subscription'
19
+ # update the info we just used to create
20
+ # id, name and output_params.* are valid
21
+ puts @datasift.push.update @params.merge({:id => subscription_id, :name => 'My updated awesome name'})
22
+
23
+ puts 'getting subscription info'
24
+ # get details for a subscription also available are
25
+ # push.[get, get_by_hash,get_by_historics_id]
26
+ puts @datasift.push.get_by_subscription subscription_id
27
+
28
+ puts 'getting logs for subscription'
29
+ # get log messages for a subscription id
30
+ #also available push.logs to fetch logs for all subscriptions
31
+ puts @datasift.push.logs_for subscription_id
32
+
33
+ puts 'pausing subscription'
34
+ #pause the subscription that was created
35
+ puts @datasift.push.pause subscription_id
36
+
37
+ puts 'resuming subscription'
38
+ # resume the subscription that was just paused
39
+ puts @datasift.push.resume subscription_id
40
+
41
+ puts 'stopping subscription'
42
+ # stop the subscription
43
+ puts @datasift.push.stop subscription_id
44
+
45
+ puts 'deleting subscription'
46
+ #and delete it
47
+ puts @datasift.push.delete subscription_id
48
+ end
49
+ #rescue DataSiftError
50
+ rescue DataSiftError => dse
51
+ puts dse.message
52
+ end
53
+ end
54
+
55
+ end
56
+ PushApi.new().run
@@ -0,0 +1,23 @@
1
+ module DataSift
2
+ class ApiResource
3
+ include DataSift
4
+
5
+ def initialize (config)
6
+ @config = config
7
+ config[:api_host] = 'api.datasift.com' unless config.has_key?(:api_host)
8
+ config[:stream_host] = 'websocket.datasift.com' unless config.has_key?(:stream_host)
9
+ config[:api_version] = 'v1' unless config.has_key?(:api_version)
10
+ config[:enable_ssl] = true unless config.has_key?(:enable_ssl)
11
+ # max 320 seconds retry - http://dev.datasift.com/docs/streaming-api/reconnecting
12
+ config[:max_retry_time] = 320 unless config.has_key?(:max_retry_time)
13
+ end
14
+
15
+ def requires params
16
+ params.each { |k, v|
17
+ if v == nil || v.to_s.length == 0
18
+ raise InvalidParamError.new "#{k} is a required parameter, it cannot be nil or empty"
19
+ end
20
+ }
21
+ end
22
+ end
23
+ end
@@ -1,15 +1,288 @@
1
- #This is the base file for the DataSift API library. Require this file to get
2
- #access to the full library functionality.
3
- require 'rubygems'
4
-
5
1
  dir = File.dirname(__FILE__)
6
- require dir + '/DataSift/exceptions'
7
- require dir + '/DataSift/apiclient'
8
- require dir + '/DataSift/user'
9
- require dir + '/DataSift/definition'
10
- require dir + '/DataSift/historic'
11
- require dir + '/DataSift/push_definition'
12
- require dir + '/DataSift/push_subscription'
13
- require dir + '/DataSift/stream_consumer'
14
- require dir + '/DataSift/stream_consumer_http'
15
- require dir + '/DataSift/managed_source'
2
+
3
+ require 'uri'
4
+ require 'rest_client'
5
+ require 'multi_json'
6
+ require 'websocket_td'
7
+
8
+ require dir + '/api/api_resource'
9
+
10
+ require dir + '/errors'
11
+ require dir + '/push'
12
+ require dir + '/historics'
13
+ require dir + '/historics_preview'
14
+ require dir + '/managed_source'
15
+ require dir + '/live_stream'
16
+
17
+ require 'rbconfig'
18
+
19
+ module DataSift
20
+ #
21
+ IS_WINDOWS = (RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/)
22
+ VERSION = File.open('../VERSION').first
23
+
24
+ class Client < ApiResource
25
+
26
+ #+config+:: A hash containing configuration options for the client for e.g.
27
+ # {:username => 'some_user', :api_key => 'ds_api_key', :enable_ssl => true, :open_timeout => 30, :timeout => 30}
28
+ def initialize (config)
29
+ if config == nil
30
+ raise InvalidConfigError.new ('Config cannot be nil')
31
+ end
32
+ if config.key?(:api_key) == false || config.key?(:username) == false
33
+ raise InvalidConfigError.new('A valid username and API key are required')
34
+ end
35
+
36
+ @config = config
37
+ @historics = DataSift::Historics.new(config)
38
+ @push = DataSift::Push.new(config)
39
+ @managed_source = DataSift::ManagedSource.new(config)
40
+ @historics_preview = DataSift::HistoricsPreview.new(config)
41
+ end
42
+
43
+ attr_reader :historics, :push, :managed_source, :historics_preview
44
+
45
+ ##
46
+ # Checks if the syntax of the given CSDL is valid
47
+ def valid?(csdl)
48
+ requires({:csdl => csdl})
49
+ res= DataSift.request(:POST, 'validate', @config, {:csdl => csdl})
50
+ res[:http][:status] == 200
51
+ end
52
+
53
+ ##
54
+ # Compile CSDL code.
55
+ #+csdl+:: The CSDL you wish to compile
56
+ def compile(csdl)
57
+ requires({:csdl => csdl})
58
+ DataSift.request(:POST, 'compile', @config, {:csdl => csdl})
59
+ end
60
+
61
+ ##
62
+ # Check the number of objects processed and delivered for a given time period.
63
+ #+period+:: Can be "day", "hour", or "current", defaults to hour
64
+ def usage(period = :hour)
65
+ DataSift.request(:POST, 'usage', @config, {:period => period})
66
+ end
67
+
68
+ ##
69
+ # Calculate the DPU cost of consuming a stream.
70
+ def dpu(hash)
71
+ requires ({:hash => hash})
72
+ DataSift.request(:POST, 'dpu', @config, {:hash => hash})
73
+ end
74
+
75
+ ##
76
+ # Determine your credit balance or DPU balance.
77
+ def balance
78
+ DataSift.request(:POST, 'balance', @config, {})
79
+ end
80
+
81
+ ##
82
+ # Collect a batch of interactions from a push queue
83
+ def pull(id, size = 20971520, cursor='')
84
+ DataSift.request(:POST, 'pull', @config, {:id => id, :size => size, :cursor => cursor})
85
+ end
86
+
87
+ end
88
+
89
+
90
+ # Generates and executes an HTTP request from the params provided
91
+ # Params:
92
+ # +method+:: the HTTP method to use e.g. GET,POST
93
+ # +path+:: the DataSift path relevant to the base URL of the API
94
+ # +username+:: API username
95
+ # +api_key+:: DS api key
96
+ # +params+:: A hash representing the params to use in the request, if it's a get,head or delete request these params
97
+ # are used as query string params, if not they become form url encoded params
98
+ # +headers+:: any headers to pass to the API, Authorization header is automatically included
99
+ def self.request(method, path, config, params = {}, headers = {}, timeout=30, open_timeout=30, new_line_separated=false)
100
+ validate config
101
+ options = {}
102
+ url = build_url(path, config)
103
+ case method.to_s.downcase.to_sym
104
+ when :get, :head, :delete
105
+ url += "#{URI.parse(url).query ? '&' : '?'}#{encode params}"
106
+ payload = nil
107
+ else
108
+ payload = encode params
109
+ end
110
+
111
+ headers.update ({
112
+ :user_agent => "DataSift/#{config[:api_version]} Ruby/v#{VERSION}",
113
+ :authorization => "#{config[:username]}:#{config[:api_key]}",
114
+ :content_type => 'application/x-www-form-urlencoded'
115
+ })
116
+
117
+ options.update(
118
+ :headers => headers,
119
+ :method => method,
120
+ :open_timeout => open_timeout,
121
+ :timeout => timeout,
122
+ :payload => payload,
123
+ :url => url
124
+ )
125
+
126
+ begin
127
+ response = RestClient::Request.execute options
128
+ if response != nil && response.length > 0
129
+ if new_line_separated
130
+ res_arr = response.split("\n")
131
+ data = []
132
+ res_arr.each { |e|
133
+ interaction = MultiJson.load(e, :symbolize_keys => true)
134
+ data.push(interaction)
135
+ if params.has_key? :on_interaction
136
+ params[:on_interaction].call(interaction)
137
+ end
138
+ }
139
+ else
140
+ data = MultiJson.load(response, :symbolize_keys => true)
141
+ end
142
+ else
143
+ data = {}
144
+ end
145
+ {
146
+ :data => data,
147
+ :datasift => {
148
+ :x_ratelimit_limit => response.headers[:x_ratelimit_limit],
149
+ :x_ratelimit_remaining => response.headers[:x_ratelimit_remaining],
150
+ :x_ratelimit_cost => response.headers[:x_ratelimit_cost]
151
+ },
152
+ :http => {
153
+ :status => response.code,
154
+ :headers => response.headers
155
+ }
156
+ }
157
+ rescue MultiJson::DecodeError => de
158
+ raise DataSiftError.new response
159
+ rescue SocketError => e
160
+ process_client_error(e)
161
+ rescue RestClient::ExceptionWithResponse => e
162
+ begin
163
+ code = e.http_code
164
+ body = e.http_body
165
+ if code && body
166
+ error = MultiJson.load(body)
167
+ handle_api_error(e.http_code, error['error'] + " for URL #{url}")
168
+ else
169
+ process_client_error(e)
170
+ end
171
+ rescue MultiJson::DecodeError
172
+ process_client_error(e)
173
+ end
174
+ rescue RestClient::Exception, Errno::ECONNREFUSED => e
175
+ process_client_error (e)
176
+ end
177
+ end
178
+
179
+ def self.build_url(path, config)
180
+ 'http' + (config[:enable_ssl] ? 's' : '') + '://' + config[:api_host] + '/' + config[:api_version] + '/' + path
181
+ end
182
+
183
+ #returns true if username and api key are set
184
+ def self.is_invalid? config
185
+ !config.key?(:username) || !config.key?(:api_key)
186
+ end
187
+
188
+ def self.validate conf
189
+ if is_invalid? conf
190
+ raise InvalidConfigError.new 'A username and api_key are required'
191
+ end
192
+ end
193
+
194
+ def self.encode params
195
+ URI.escape(params.collect { |k, v| "#{k}=#{v}" }.join('&'))
196
+ end
197
+
198
+ def self.handle_api_error(code, body)
199
+ case code
200
+ when 400
201
+ raise BadRequestError.new(code, body)
202
+ when 401
203
+ raise AuthError.new(code, body)
204
+ when 404
205
+ raise ApiResourceNotFoundError.new(code, body)
206
+ else
207
+ raise DataSiftError.new(code, body)
208
+ end
209
+ end
210
+
211
+ def self.process_client_error(e)
212
+ case e
213
+ when RestClient::ServerBrokeConnection, RestClient::RequestTimeout
214
+ message = 'Unable to connect to DataSift. Please check your connection and try again'
215
+ when RestClient::SSLCertificateNotVerified
216
+ message = 'Failed to complete SSL verification'
217
+ when SocketError
218
+ message = 'Communication with DataSift failed. Are you able to resolve api.datasift.com?'
219
+ else
220
+ message = 'Unexpected error.'
221
+ end
222
+ raise ConnectionError.new(message + " (Network error: #{e.message})")
223
+ end
224
+
225
+ ##
226
+ # a Proc/lambda callback to receive delete messages
227
+ # DataSift and its customers are required to process Twitter's delete request, a delete handler must be provided
228
+ # a Proc/lambda callback to receive errors
229
+ # Because EventMachine is used errors can be raised from another thread, this method will receive any such errors
230
+ def self.new_stream(config, on_delete, on_error, on_open = nil, on_close = nil)
231
+ if on_delete == nil || on_error == nil
232
+ raise NotConfiguredError.new 'on_delete and on_error are required before you can connect'
233
+ end
234
+
235
+ begin
236
+ stream = WebsocketTD::Websocket.new('websocket.datasift.com', '/multi', "username=#{config[:username]}&api_key=#{config[:api_key]}")
237
+ connection = LiveStream.new(config, stream)
238
+
239
+ stream.on_open = lambda {
240
+ connection.connected = true
241
+ connection.retry_timeout = 0
242
+ on_open.call(connection) if on_open != nil
243
+ }
244
+
245
+ stream.on_close = lambda {
246
+ connection.connected = false
247
+ retry_connect(config, connection, on_delete, on_error, on_open, on_close, '', true)
248
+ }
249
+ stream.on_error = lambda {
250
+ connection.connected = false
251
+ on_error.call(connection) if on_close != nil
252
+ retry_connect(config, connection, on_delete, on_error, on_open, on_close)
253
+ }
254
+ stream.on_message =lambda { |msg|
255
+ data = MultiJson.load(msg.data, :symbolize_keys => true)
256
+ if data.has_key?(:deleted)
257
+ on_delete.call(connection, data)
258
+ elsif data.has_key?(:status)
259
+ connection.fire_ds_message(data)
260
+ else
261
+ connection.fire_on_message(data[:hash], data[:data])
262
+ end
263
+ }
264
+ rescue Exception => e
265
+ retry_connect(config, connection, on_delete, on_error, on_open, on_close, e.message)
266
+ end
267
+ connection
268
+ end
269
+
270
+ def self.retry_connect(config, connection, on_delete, on_error, on_open, on_close, message = '', use_closed = false)
271
+ connection.retry_timeout = connection.retry_timeout == 0 ? 10 : connection.retry_timeout * 2
272
+ if connection.retry_timeout > config[:max_retry_time]
273
+ if use_closed && on_close != nil
274
+ on_close.call(connection)
275
+ else
276
+ on_error.call ReconnectTimeoutError.new "Connecting to DataSift has failed, re-connection was attempted but
277
+ multiple consecutive failures where encountered. As a result no further
278
+ re-connection will be automatically attempted. Manually invoke connect() after
279
+ investigating the cause of the failure, be sure to observe DataSift's
280
+ re-connect policies available at http://dev.datasift.com/docs/streaming-api/reconnecting
281
+ - Error { #{message}}"
282
+ end
283
+ else
284
+ sleep connection.retry_timeout
285
+ new_stream(config, on_delete, on_error, on_open, on_close)
286
+ end
287
+ end
288
+ end