datasift 2.1.1 → 3.0.0.beta

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.
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