tweetstream 1.1.0.rc1 → 1.1.0.rc2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of tweetstream might be problematic. Click here for more details.

data/README.md CHANGED
@@ -51,6 +51,33 @@ user ids:
51
51
  The methods available to TweetStream::Client will be kept in parity
52
52
  with the methods available on the Streaming API wiki page.
53
53
 
54
+ Using the Twitter Userstream
55
+ ----------------------------
56
+
57
+ Using the Twitter userstream works similarly to the regular streaming, except you use the userstream method.
58
+
59
+ # Use 'userstream' to get message from your stream
60
+ TweetStream::Client.new.userstream do |status|
61
+ puts status.text
62
+ end
63
+
64
+
65
+ You also can use method hooks for both regular timeline statuses and direct messages.
66
+
67
+ client = TweetStream::Client.new
68
+
69
+ client.on_direct_message do |direct_message|
70
+ puts "direct message"
71
+ puts direct_message.text
72
+ end
73
+
74
+ client.on_timeline_status do |status|
75
+ puts "timeline status"
76
+ puts status.text
77
+ end
78
+
79
+ client.userstream
80
+
54
81
  Configuration and Changes in 1.1.0
55
82
  ----------------------------------
56
83
 
@@ -96,8 +123,7 @@ specify a parser during configuration:
96
123
  config.parser = :yajl
97
124
  end.
98
125
 
99
- Available options are `:yajl`, `:json_gem` (default),
100
- `:json_pure`, and `:active_support`.
126
+ Available options are `:yajl`, `:json_gem`, `:json_pure`, and `:ok_json`.
101
127
 
102
128
  Handling Deletes and Rate Limitations
103
129
  -------------------------------------
@@ -189,17 +215,16 @@ It is also possible to create a daemonized script quite easily
189
215
  using the TweetStream library:
190
216
 
191
217
  # The third argument is an optional process name
192
- TweetStream::Daemon.new('username','password', 'tracker').track('term1', 'term2') do |status|
218
+ TweetStream::Daemon.new('tracker').track('term1', 'term2') do |status|
193
219
  # do something in the background
194
220
  end
195
221
 
196
- If you put the above into a script and run the script with `ruby scriptname.rb`, you will see a list of daemonization commands such
197
- as start, stop, and run.
222
+ If you put the above into a script and run the script with `ruby scriptname.rb`,
223
+ you will see a list of daemonization commands such as start, stop, and run.
198
224
 
199
225
  TODO
200
226
  ----
201
227
 
202
- * UserStream support
203
228
  * SiteStream support
204
229
 
205
230
  Note on Patches/Pull Requests
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ #!/usr/bin/env rake
2
+
1
3
  require 'bundler'
2
4
  Bundler::GemHelper.install_tasks
3
5
 
@@ -4,11 +4,19 @@ require 'ruby-growl'
4
4
 
5
5
  if args_start = ARGV.index('--')
6
6
  username, password = ARGV[args_start + 1].split(':')
7
- tracks = ARGV[args_start + 2 .. -1]
7
+ tracks = ARGV[args_start + 2 .. -1]
8
8
  puts "Starting a GrowlTweet to track: #{tracks.inspect}"
9
9
  end
10
10
 
11
- TweetStream::Daemon.new(username,password).track(*tracks) do |status|
11
+ TweetStream.configure do |config|
12
+ config.consumer_key = 'abcdefghijklmnopqrstuvwxyz'
13
+ config.consumer_secret = '0123456789'
14
+ config.oauth_token = 'abcdefghijklmnopqrstuvwxyz'
15
+ config.oauth_token_secret = '0123456789'
16
+ config.auth_method = :oauth
17
+ end
18
+
19
+ TweetStream::Daemon.new('tracker').track(*tracks) do |status|
12
20
  g = Growl.new 'localhost', 'growltweet', ['tweet']
13
21
  g.notify 'tweet', status.user.screen_name, status.text
14
22
  end
data/examples/oauth.rb CHANGED
@@ -10,8 +10,12 @@ TweetStream.configure do |config|
10
10
  config.parser = :yajl
11
11
  end
12
12
 
13
- TweetStream::Client.new.on_error do |message|
13
+ client = TweetStream::Client.new
14
+
15
+ client.on_error do |message|
14
16
  puts message
15
- end.track("yankees") do |status|
17
+ end
18
+
19
+ client.track("yankees") do |status|
16
20
  puts "#{status.text}"
17
21
  end
@@ -0,0 +1,27 @@
1
+ require 'yajl'
2
+ require 'tweetstream'
3
+
4
+ TweetStream.configure do |config|
5
+ config.consumer_key = 'abcdefghijklmnopqrstuvwxyz'
6
+ config.consumer_secret = '0123456789'
7
+ config.oauth_token = 'abcdefghijklmnopqrstuvwxyz'
8
+ config.oauth_token_secret = '0123456789'
9
+ config.auth_method = :oauth
10
+ config.parser = :yajl
11
+ end
12
+
13
+ client = TweetStream::Client.new
14
+
15
+ client.on_error do |message|
16
+ puts message
17
+ end
18
+
19
+ client.on_direct_message do |direct_message|
20
+ puts direct_message.text
21
+ end
22
+
23
+ client.on_timeline_status do |status|
24
+ puts status.text
25
+ end
26
+
27
+ client.userstream
data/lib/tweetstream.rb CHANGED
@@ -2,41 +2,31 @@ require 'tweetstream/configuration'
2
2
  require 'tweetstream/client'
3
3
  require 'tweetstream/hash'
4
4
  require 'tweetstream/status'
5
+ require 'tweetstream/direct_message'
5
6
  require 'tweetstream/user'
7
+ require 'tweetstream/error'
6
8
  require 'tweetstream/daemon'
7
9
 
8
10
  module TweetStream
9
11
  extend Configuration
10
12
 
11
- class Terminated < ::StandardError; end
12
- class Error < ::StandardError; end
13
- class ConnectionError < TweetStream::Error; end
14
- # A ReconnectError is raised when the maximum number of retries has
15
- # failed to re-establish a connection.
16
- class ReconnectError < StandardError
17
- attr_accessor :timeout, :retries
18
- def initialize(timeout, retries)
19
- self.timeout = timeout
20
- self.retries = retries
21
- super("Failed to reconnect after #{retries} tries.")
13
+ class << self
14
+ # Alias for TweetStream::Client.new
15
+ #
16
+ # @return [TweetStream::Client]
17
+ def new(options={})
18
+ TweetStream::Client.new(options)
22
19
  end
23
- end
24
-
25
- # Alias for TweetStream::Client.new
26
- #
27
- # @return [TweetStream::Client]
28
- def self.client(options={})
29
- TweetStream::Client.new(options)
30
- end
31
20
 
32
- # Delegate to TweetStream::Client
33
- def self.method_missing(method, *args, &block)
34
- return super unless client.respond_to?(method)
35
- client.send(method, *args, &block)
36
- end
21
+ # Delegate to TweetStream::Client
22
+ def method_missing(method, *args, &block)
23
+ return super unless new.respond_to?(method)
24
+ new.send(method, *args, &block)
25
+ end
37
26
 
38
- # Delegate to TweetStream::Client
39
- def self.respond_to?(method)
40
- client.respond_to?(method) || super
27
+ # Delegate to TweetStream::Client
28
+ def respond_to?(method, include_private = false)
29
+ new.respond_to?(method, include_private) || super(method, include_private)
30
+ end
41
31
  end
42
- end
32
+ end
@@ -13,18 +13,17 @@ module TweetStream
13
13
  # methods and provide a block that will perform actions on
14
14
  # a yielded TweetStream::Status. For example:
15
15
  #
16
- # TweetStream::Client.new('user','pass').track('fail') do |status|
16
+ # TweetStream::Client.new.track('fail') do |status|
17
17
  # puts "[#{status.user.screen_name}] #{status.text}"
18
18
  # end
19
19
  #
20
20
  # For information about a daemonized TweetStream client,
21
21
  # view the TweetStream::Daemon class.
22
22
  class Client
23
- attr_accessor :username, :password
24
- attr_reader :parser
25
23
 
26
24
  # @private
27
25
  attr_accessor *Configuration::VALID_OPTIONS_KEYS
26
+ attr_accessor :timer
28
27
 
29
28
  # Creates a new API
30
29
  def initialize(options={})
@@ -39,16 +38,6 @@ module TweetStream
39
38
  parser_from(parser)
40
39
  end
41
40
 
42
- # Create a new client with the Twitter credentials
43
- # of the account you want to be using its API quota.
44
- # You may also set the JSON parsing library as specified
45
- # in the #parser= setter.
46
- # def initialize(ouser, pass, parser = :json_gem)
47
- # self.username = user
48
- # self.password = pass
49
- # self.parser = parser
50
- # end
51
-
52
41
  # Returns all public statuses. The Firehose is not a generally
53
42
  # available resource. Few applications require this level of access.
54
43
  # Creative use of a combination of other resources and various access
@@ -122,10 +111,15 @@ module TweetStream
122
111
  start('statuses/filter', query_params.merge(:method => :post), &block)
123
112
  end
124
113
 
114
+ # Make a call to the userstream api for currently authenticated user
115
+ def userstream(&block)
116
+ start('', :extra_stream_parameters => {:host => "userstream.twitter.com", :path => "/2/user.json"}, &block)
117
+ end
118
+
125
119
  # Set a Proc to be run when a deletion notice is received
126
120
  # from the Twitter stream. For example:
127
121
  #
128
- # @client = TweetStream::Client.new('user','pass')
122
+ # @client = TweetStream::Client.new
129
123
  # @client.on_delete do |status_id, user_id|
130
124
  # Tweet.delete(status_id)
131
125
  # end
@@ -146,7 +140,7 @@ module TweetStream
146
140
  # Set a Proc to be run when a rate limit notice is received
147
141
  # from the Twitter stream. For example:
148
142
  #
149
- # @client = TweetStream::Client.new('user','pass')
143
+ # @client = TweetStream::Client.new
150
144
  # @client.on_limit do |discarded_count|
151
145
  # # Make note of discarded count
152
146
  # end
@@ -168,7 +162,7 @@ module TweetStream
168
162
  # processing of the stream. Note that TweetStream will automatically
169
163
  # try to reconnect, this is for reference only. Don't panic!
170
164
  #
171
- # @client = TweetStream::Client.new('user','pass')
165
+ # @client = TweetStream::Client.new
172
166
  # @client.on_error do |message|
173
167
  # # Make note of error message
174
168
  # end
@@ -186,10 +180,73 @@ module TweetStream
186
180
  end
187
181
  end
188
182
 
183
+ # Set a Proc to be run when a direct message is encountered in the
184
+ # processing of the stream.
185
+ #
186
+ # @client = TweetStream::Client.new
187
+ # @client.on_direct_message do |direct_message|
188
+ # # do something with the direct message
189
+ # end
190
+ #
191
+ # Block must take one argument: the direct message.
192
+ # If no block is given, it will return the currently set
193
+ # direct message proc. When a block is given, the TweetStream::Client
194
+ # object is returned to allow for chaining.
195
+ def on_direct_message(&block)
196
+ if block_given?
197
+ @on_direct_message = block
198
+ self
199
+ else
200
+ @on_direct_message
201
+ end
202
+ end
203
+
204
+ # Set a Proc to be run whenever anything is encountered in the
205
+ # processing of the stream.
206
+ #
207
+ # @client = TweetStream::Client.new
208
+ # @client.on_anything do |status|
209
+ # # do something with the status
210
+ # end
211
+ #
212
+ # Block can take one or two arguments. |status (, client)|
213
+ # If no block is given, it will return the currently set
214
+ # timeline status proc. When a block is given, the TweetStream::Client
215
+ # object is returned to allow for chaining.
216
+ def on_anything(&block)
217
+ if block_given?
218
+ @on_anything = block
219
+ self
220
+ else
221
+ @on_anything
222
+ end
223
+ end
224
+
225
+ # Set a Proc to be run when a regular timeline message is encountered in the
226
+ # processing of the stream.
227
+ #
228
+ # @client = TweetStream::Client.new
229
+ # @client.on_timeline_message do |status|
230
+ # # do something with the status
231
+ # end
232
+ #
233
+ # Block can take one or two arguments. |status (, client)|
234
+ # If no block is given, it will return the currently set
235
+ # timeline status proc. When a block is given, the TweetStream::Client
236
+ # object is returned to allow for chaining.
237
+ def on_timeline_status(&block)
238
+ if block_given?
239
+ @on_timeline_status = block
240
+ self
241
+ else
242
+ @on_timeline_status
243
+ end
244
+ end
245
+
189
246
  # Set a Proc to be run when connection established.
190
247
  # Called in EventMachine::Connection#post_init
191
248
  #
192
- # @client = TweetStream::Client.new('user','pass')
249
+ # @client = TweetStream::Client.new
193
250
  # @client.on_inited do
194
251
  # puts 'Connected...'
195
252
  # end
@@ -203,27 +260,59 @@ module TweetStream
203
260
  end
204
261
  end
205
262
 
263
+ # Set a Proc to be run on a regular interval
264
+ # independent of timeline status updates
265
+ #
266
+ # @client = TweetStream::Client.new
267
+ # @client.on_interval(20) do
268
+ # # do something every 20 seconds
269
+ # end
270
+ #
271
+ def on_interval(time_interval=nil, &block)
272
+ if block_given?
273
+ @on_interval_time = time_interval
274
+ @on_interval_proc = block
275
+ self
276
+ else
277
+ [@on_interval_time, @on_interval_proc]
278
+ end
279
+ end
280
+
206
281
  def start(path, query_parameters = {}, &block) #:nodoc:
207
282
  method = query_parameters.delete(:method) || :get
208
283
  delete_proc = query_parameters.delete(:delete) || self.on_delete
209
284
  limit_proc = query_parameters.delete(:limit) || self.on_limit
210
285
  error_proc = query_parameters.delete(:error) || self.on_error
211
286
  inited_proc = query_parameters.delete(:inited) || self.on_inited
287
+ direct_message_proc = query_parameters.delete(:direct_message) || self.on_direct_message
288
+ timeline_status_proc = query_parameters.delete(:timeline_status) || self.on_timeline_status
289
+ anything_proc = query_parameters.delete(:anything) || self.on_anything
212
290
 
213
291
  params = normalize_filter_parameters(query_parameters)
214
292
 
293
+ extra_stream_parameters = query_parameters.delete(:extra_stream_parameters) || {}
294
+
215
295
  uri = method == :get ? build_uri(path, params) : build_uri(path)
216
296
 
297
+ stream_params = {
298
+ :path => uri,
299
+ :method => method.to_s.upcase,
300
+ :user_agent => user_agent,
301
+ :on_inited => inited_proc,
302
+ :filters => params.delete(:track),
303
+ :params => params,
304
+ :ssl => true
305
+ }.merge(auth_params).merge(extra_stream_parameters)
306
+
307
+ EventMachine.epoll
308
+ EventMachine.kqueue
309
+
217
310
  EventMachine::run {
218
- stream_params = {
219
- :path => uri,
220
- :method => method.to_s.upcase,
221
- :user_agent => user_agent,
222
- :on_inited => inited_proc,
223
- :filters => params.delete(:track),
224
- :params => params,
225
- :ssl => true
226
- }.merge(auth_params)
311
+ if @on_interval_proc.is_a?(Proc)
312
+ interval = @on_interval_time || Configuration::DEFAULT_TIMER_INTERVAL
313
+ proc = @on_interval_proc
314
+ @timer = EM.add_periodic_timer(interval, &proc)
315
+ end
227
316
 
228
317
  @stream = Twitter::JSONStream.connect(stream_params)
229
318
  @stream.each_item do |item|
@@ -240,23 +329,31 @@ module TweetStream
240
329
  end
241
330
 
242
331
  hash = TweetStream::Hash.new(raw_hash)
243
-
244
332
  if hash[:delete] && hash[:delete][:status]
245
333
  delete_proc.call(hash[:delete][:status][:id], hash[:delete][:status][:user_id]) if delete_proc.is_a?(Proc)
246
334
  elsif hash[:limit] && hash[:limit][:track]
247
335
  limit_proc.call(hash[:limit][:track]) if limit_proc.is_a?(Proc)
336
+
337
+ elsif hash[:direct_message]
338
+ yield_message_to direct_message_proc, TweetStream::DirectMessage.new(hash[:direct_message])
339
+
248
340
  elsif hash[:text] && hash[:user]
249
341
  @last_status = TweetStream::Status.new(hash)
250
-
251
- # Give the block the option to receive either one
252
- # or two arguments, depending on its arity.
253
- case block.arity
254
- when 1
255
- yield @last_status
256
- when 2
257
- yield @last_status, self
342
+ yield_message_to timeline_status_proc, @last_status
343
+
344
+ if block_given?
345
+ # Give the block the option to receive either one
346
+ # or two arguments, depending on its arity.
347
+ case block.arity
348
+ when 1
349
+ yield @last_status
350
+ when 2
351
+ yield @last_status, self
352
+ end
258
353
  end
259
354
  end
355
+
356
+ yield_message_to anything_proc, hash
260
357
  end
261
358
 
262
359
  @stream.on_error do |message|
@@ -292,14 +389,11 @@ module TweetStream
292
389
 
293
390
  def build_post_body(query) #:nodoc:
294
391
  return '' unless query && query.is_a?(::Hash) && query.size > 0
295
- pairs = []
296
392
 
297
- query.each_pair do |k,v|
393
+ query.map do |k, v|
298
394
  v = v.flatten.collect { |q| q.to_s }.join(',') if v.is_a?(Array)
299
- pairs << "#{k.to_s}=#{CGI.escape(v.to_s)}"
300
- end
301
-
302
- pairs.join('&')
395
+ "#{k.to_s}=#{CGI.escape(v.to_s)}"
396
+ end.join('&')
303
397
  end
304
398
 
305
399
  def normalize_filter_parameters(query_parameters = {})
@@ -326,5 +420,16 @@ module TweetStream
326
420
  }
327
421
  end
328
422
  end
423
+
424
+ def yield_message_to(procedure, message)
425
+ if procedure.is_a?(Proc)
426
+ case procedure.arity
427
+ when 1
428
+ procedure.call(message)
429
+ when 2
430
+ procedure.call(message, self)
431
+ end
432
+ end
433
+ end
329
434
  end
330
435
  end
@@ -47,6 +47,9 @@ module TweetStream
47
47
  # By default, don't set a user oauth secret
48
48
  DEFAULT_OAUTH_TOKEN_SECRET = nil
49
49
 
50
+ # Default time interval for use with on_interval
51
+ DEFAULT_TIMER_INTERVAL = 30
52
+
50
53
  # @private
51
54
  attr_accessor *VALID_OPTIONS_KEYS
52
55
 
@@ -8,7 +8,15 @@ require 'daemons'
8
8
  # require 'rubygems'
9
9
  # require 'tweetstream'
10
10
  #
11
- # TweetStream::Daemon.new('user','pass', 'tracker').track('intridea') do |status|
11
+ # TweetStream.configure do |config|
12
+ # config.consumer_key = 'abcdefghijklmnopqrstuvwxyz'
13
+ # config.consumer_secret = '0123456789'
14
+ # config.oauth_token = 'abcdefghijklmnopqrstuvwxyz'
15
+ # config.oauth_token_secret = '0123456789'
16
+ # config.auth_method = :oauth
17
+ # end
18
+ #
19
+ # TweetStream::Daemon.new('tracker').track('intridea') do |status|
12
20
  # # do something here
13
21
  # end
14
22
  #
@@ -26,9 +34,9 @@ class TweetStream::Daemon < TweetStream::Client
26
34
  # Twitter account you wish to use. The daemon has
27
35
  # an optional process name for use when querying
28
36
  # running processes.
29
- def initialize(user, pass, app_name=nil, parser=:json_gem)
37
+ def initialize(app_name=nil)
30
38
  @app_name = app_name
31
- super(user, pass, parser)
39
+ super({})
32
40
  end
33
41
 
34
42
  def start(path, query_parameters = {}, &block) #:nodoc:
@@ -0,0 +1,6 @@
1
+ class TweetStream::DirectMessage < TweetStream::Hash
2
+ def initialize(hash)
3
+ super
4
+ self[:user] = self[:sender] = TweetStream::User.new(self[:sender])
5
+ end
6
+ end
@@ -0,0 +1,15 @@
1
+ module TweetStream
2
+ class Terminated < ::StandardError; end
3
+ class Error < ::StandardError; end
4
+ class ConnectionError < TweetStream::Error; end
5
+ # A ReconnectError is raised when the maximum number of retries has
6
+ # failed to re-establish a connection.
7
+ class ReconnectError < StandardError
8
+ attr_accessor :timeout, :retries
9
+ def initialize(timeout, retries)
10
+ self.timeout = timeout
11
+ self.retries = retries
12
+ super("Failed to reconnect after #{retries} tries.")
13
+ end
14
+ end
15
+ end
@@ -5,7 +5,7 @@ class TweetStream::Status < TweetStream::Hash
5
5
  super
6
6
  self[:user] = TweetStream::User.new(self[:user])
7
7
  end
8
-
8
+
9
9
  def id
10
10
  self[:id] || super
11
11
  end
@@ -1,3 +1,3 @@
1
1
  module TweetStream
2
- VERSION = '1.1.0.rc1'
2
+ VERSION = '1.1.0.rc2'
3
3
  end
@@ -0,0 +1 @@
1
+ {"direct_message":{"created_at":"Sat Sep 24 18:59:38 +0000 2011", "id_str":"4227325281", "sender_screen_name":"coreyhaines", "sender":{"name":"Corey Haines", "profile_sidebar_fill_color":"DAECF4", "profile_sidebar_border_color":"C6E2EE", "profile_background_tile":false, "profile_image_url":"http://a0.twimg.com/profile_images/1508969901/Photo_on_2011-08-22_at_19.15__3_normal.jpg", "created_at":"Sun Dec 23 18:11:29 +0000 2007", "location":"Chicago, IL", "follow_request_sent":false, "id_str":"11458102", "is_translator":false, "profile_link_color":"1F98C7", "default_profile":false, "favourites_count":122, "contributors_enabled":false, "url":"http://www.coreyhaines.com", "id":11458102, "profile_image_url_https":"https://si0.twimg.com/profile_images/1508969901/Photo_on_2011-08-22_at_19.15__3_normal.jpg", "utc_offset":-21600, "profile_use_background_image":true, "listed_count":593, "lang":"en", "followers_count":5764, "protected":false, "profile_text_color":"663B12", "notifications":false, "description":"Software Journeyman, Coderetreat Facilitator, Cofounder of MercuryApp.com, Awesome....\r\nI make magic!", "verified":false, "profile_background_color":"C6E2EE", "geo_enabled":false, "profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme2/bg.gif", "time_zone":"Central Time (US & Canada)", "profile_background_image_url":"http://a1.twimg.com/images/themes/theme2/bg.gif", "default_profile_image":false, "friends_count":423, "statuses_count":35950, "following":false, "screen_name":"coreyhaines", "show_all_inline_media":false}, "recipient_screen_name":"coreyhainestest", "text":"waddup gain", "id":4227325281, "recipient":{"name":"Corey's Test Account", "profile_sidebar_fill_color":"DDEEF6", "profile_sidebar_border_color":"C0DEED", "profile_background_tile":false, "profile_image_url":"http://a2.twimg.com/sticky/default_profile_images/default_profile_3_normal.png", "created_at":"Sat Sep 24 13:04:56 +0000 2011", "location":null, "follow_request_sent":false, "id_str":"379145826", "is_translator":false, "profile_link_color":"0084B4", "default_profile":true, "favourites_count":0, "contributors_enabled":false, "url":null, "id":379145826, "profile_image_url_https":"https://si0.twimg.com/sticky/default_profile_images/default_profile_3_normal.png", "utc_offset":null, "profile_use_background_image":true, "listed_count":0, "lang":"en", "followers_count":1, "protected":false, "profile_text_color":"333333", "notifications":false, "description":null, "verified":false, "profile_background_color":"C0DEED", "geo_enabled":false, "profile_background_image_url_https":"https://si0.twimg.com/images/themes/theme1/bg.png", "time_zone":null, "profile_background_image_url":"http://a0.twimg.com/images/themes/theme1/bg.png", "default_profile_image":true, "friends_count":1, "statuses_count":21, "following":true, "screen_name":"coreyhainestest", "show_all_inline_media":false}, "recipient_id":379145826, "sender_id":11458102}}
data/spec/spec_helper.rb CHANGED
@@ -24,3 +24,13 @@ def sample_tweets
24
24
  @tweets
25
25
  end
26
26
  end
27
+
28
+ def sample_direct_messages
29
+ return @direct_messages if @direct_messages
30
+
31
+ @direct_messages = []
32
+ Yajl::Parser.parse(File.open(File.dirname(__FILE__) + '/data/direct_messages.json', 'r')) do |hash|
33
+ @direct_messages << hash
34
+ end
35
+ @direct_messages
36
+ end
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ require 'spec_helper'
2
2
 
3
3
  describe TweetStream::Client do
4
4
  before(:each) do
@@ -12,7 +12,7 @@ describe TweetStream::Client do
12
12
 
13
13
  describe '#build_uri' do
14
14
  it 'should return a URI' do
15
- @client.send(:build_uri, '').is_a?(URI).should be_true
15
+ @client.send(:build_uri, '').should be_kind_of(URI)
16
16
  end
17
17
 
18
18
  it 'should have the specified path with the version prefix and a json extension' do
@@ -123,6 +123,81 @@ describe TweetStream::Client do
123
123
  end.track('abc')
124
124
  end
125
125
 
126
+ context "using on_anything" do
127
+ it "yields the raw hash" do
128
+ hash = {:id => 1234}
129
+ @stream.should_receive(:each_item).and_yield(hash.to_json)
130
+ yielded_hash = nil
131
+ @client.on_anything do |hash|
132
+ yielded_hash = hash
133
+ end.track('abc')
134
+ yielded_hash.should_not be_nil
135
+ yielded_hash.id.should == 1234
136
+ end
137
+ it 'yields itself if block has an arity of 2' do
138
+ hash = {:id => 1234}
139
+ @stream.should_receive(:each_item).and_yield(hash.to_json)
140
+ yielded_client = nil
141
+ @client.on_anything do |_, client|
142
+ yielded_client = client
143
+ end.track('abc')
144
+ yielded_client.should_not be_nil
145
+ yielded_client.should == @client
146
+ end
147
+ end
148
+
149
+ context 'using on_timeline_status' do
150
+ it 'yields a Status' do
151
+ tweet = sample_tweets[0]
152
+ tweet[:id] = 123
153
+ tweet[:user][:screen_name] = 'monkey'
154
+ tweet[:text] = "Oo oo aa aa"
155
+ @stream.should_receive(:each_item).and_yield(tweet.to_json)
156
+ yielded_status = nil
157
+ @client.on_timeline_status do |status|
158
+ yielded_status = status
159
+ end.track('abc')
160
+ yielded_status.should_not be_nil
161
+ yielded_status[:id].should == 123
162
+ yielded_status.user.screen_name.should == 'monkey'
163
+ yielded_status.text.should == 'Oo oo aa aa'
164
+ end
165
+ it 'yields itself if block has an arity of 2' do
166
+ @stream.should_receive(:each_item).and_yield(sample_tweets[0].to_json)
167
+ yielded_client = nil
168
+ @client.on_timeline_status do |_, client|
169
+ yielded_client = client
170
+ end.track('abc')
171
+ yielded_client.should_not be_nil
172
+ yielded_client.should == @client
173
+ end
174
+ end
175
+
176
+ context 'using on_direct_message' do
177
+ it 'yields a DirectMessage' do
178
+ direct_message = sample_direct_messages[0]
179
+ direct_message["direct_message"]["id"] = 1234
180
+ direct_message["direct_message"]["sender"]["screen_name"] = "coder"
181
+ @stream.should_receive(:each_item).and_yield(direct_message.to_json)
182
+ yielded_dm = nil
183
+ @client.on_direct_message do |dm|
184
+ yielded_dm = dm
185
+ end.userstream
186
+ yielded_dm.should_not be_nil
187
+ yielded_dm.id.should == 1234
188
+ yielded_dm.user.screen_name.should == "coder"
189
+ end
190
+
191
+ it 'yields itself if block has an arity of 2' do
192
+ @stream.should_receive(:each_item).and_yield(sample_direct_messages[0].to_json)
193
+ yielded_client = nil
194
+ @client.on_direct_message do |_, client|
195
+ yielded_client = client
196
+ end.userstream
197
+ yielded_client.should == @client
198
+ end
199
+ end
200
+
126
201
  it 'should call on_error if a non-hash response is received' do
127
202
  @stream.should_receive(:each_item).and_yield('["favorited"]')
128
203
  @client.on_error do |message|
@@ -145,6 +220,17 @@ describe TweetStream::Client do
145
220
  m.should == 'Uh oh'
146
221
  end.track('abc')
147
222
  end
223
+
224
+ it 'should return the block when defined' do
225
+ @client.on_error do |m|
226
+ puts 'ohai'
227
+ end
228
+ @client.on_error.should be_kind_of(Proc)
229
+ end
230
+
231
+ it 'should return nil when undefined' do
232
+ @client.on_error.should be_nil
233
+ end
148
234
  end
149
235
 
150
236
  describe '#on_max_reconnects' do
@@ -208,6 +294,27 @@ describe TweetStream::Client do
208
294
  @client.send(proc_setter, &proc)
209
295
  @client.send(proc_setter).should == proc
210
296
  end
297
+
298
+ it 'should return nil when undefined' do
299
+ @client.send(proc_setter).should be_nil
300
+ end
301
+ end
302
+ end
303
+
304
+ describe '#on_interval' do
305
+ it 'should set when a block is given' do
306
+ @client.on_interval(5) { puts 'hi' }
307
+ @client.on_interval[0].should == 5
308
+ @client.on_interval[1].should be_kind_of(Proc)
309
+ end
310
+
311
+ it 'should should create a periodic timer' do
312
+ # need to figure out a better way to test this
313
+ # for now, using on_inited to stop the reactor
314
+ proc = Proc.new{ puts 'hi' }
315
+ EM.should_receive(:add_periodic_timer).once.with(5)
316
+ @client.on_inited { EM.stop }.on_interval(5, &proc)
317
+ @client.track('go')
211
318
  end
212
319
  end
213
320
 
@@ -287,6 +394,18 @@ describe TweetStream::Client do
287
394
 
288
395
  @client.track('monday')
289
396
  end
397
+
398
+ context "when calling #userstream" do
399
+ it "sends the userstream host" do
400
+ Twitter::JSONStream.should_receive(:connect).with(hash_including(:host => "userstream.twitter.com")).and_return(@stream)
401
+ @client.userstream
402
+ end
403
+
404
+ it "uses the userstream uri" do
405
+ Twitter::JSONStream.should_receive(:connect).with(hash_including(:path => "/2/user.json")).and_return(@stream)
406
+ @client.userstream
407
+ end
408
+ end
290
409
  end
291
410
  end
292
411
 
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe TweetStream::DirectMessage do
4
+ it 'modifies the :sender key into a TweetStream::User object called #user' do
5
+ @status = TweetStream::DirectMessage.new({:sender => {:screen_name => 'bob'}})
6
+ @status.user.should be_kind_of(TweetStream::User)
7
+ @status.user.screen_name.should == 'bob'
8
+ end
9
+
10
+ it 'transforms the sender into a TweetStream::User object called #sender' do
11
+ @status = TweetStream::DirectMessage.new({:sender => {:screen_name => 'bob'}})
12
+ @status.sender.should be_kind_of(TweetStream::User)
13
+ @status.sender.screen_name.should == 'bob'
14
+ end
15
+
16
+ it 'overrides the #id method for itself and the user' do
17
+ @status = TweetStream::DirectMessage.new({:id => 123, :sender => {:id => 345}})
18
+ @status.id.should == 123
19
+ @status.user.id.should == 345
20
+ end
21
+ end
@@ -1,14 +1,14 @@
1
- require File.dirname(__FILE__) + '/../spec_helper'
1
+ require 'spec_helper'
2
2
 
3
3
  describe TweetStream::Status do
4
4
  it 'should modify the :user key into a TweetStream::User object' do
5
- @status = TweetStream::Status.new(:user => {:screen_name => 'bob'})
6
- @status.user.is_a?(TweetStream::User).should be_true
5
+ @status = TweetStream::Status.new({:user => {:screen_name => 'bob'}})
6
+ @status.user.should be_kind_of(TweetStream::User)
7
7
  @status.user.screen_name.should == 'bob'
8
8
  end
9
-
9
+
10
10
  it 'should override the #id method for itself and the user' do
11
- @status = TweetStream::Status.new(:id => 123, :user => {:id => 345})
11
+ @status = TweetStream::Status.new({:id => 123, :user => {:id => 345}})
12
12
  @status.id.should == 123
13
13
  @status.user.id.should == 345
14
14
  end
@@ -22,13 +22,19 @@ describe TweetStream do
22
22
  it "should return the same results as a client" do
23
23
  MultiJson.should_receive(:decode).and_return({})
24
24
  @stream.should_receive(:each_item).and_yield(sample_tweets[0].to_json)
25
- TweetStream.track('abc','def').should == TweetStream::Client.new.track('abc','def')
25
+ TweetStream.new.track('abc','def').should == TweetStream::Client.new.track('abc','def')
26
26
  end
27
27
  end
28
28
 
29
- describe ".client" do
29
+ describe ".new" do
30
30
  it "should be a TweetStream::Client" do
31
- TweetStream.client.should be_a TweetStream::Client
31
+ TweetStream.new.should be_a TweetStream::Client
32
+ end
33
+ end
34
+
35
+ describe '.respond_to?' do
36
+ it "should take an optional argument" do
37
+ TweetStream.respond_to?(:new, true).should be_true
32
38
  end
33
39
  end
34
40
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tweetstream
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0.rc1
4
+ version: 1.1.0.rc2
5
5
  prerelease: 6
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-09-19 00:00:00.000000000Z
12
+ date: 2011-09-26 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: twitter-stream
16
- requirement: &2152826800 !ruby/object:Gem::Requirement
16
+ requirement: &2153824280 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 0.1.14
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *2152826800
24
+ version_requirements: *2153824280
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: daemons
27
- requirement: &2152826340 !ruby/object:Gem::Requirement
27
+ requirement: &2153823820 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 1.1.2
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *2152826340
35
+ version_requirements: *2153823820
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: multi_json
38
- requirement: &2152858380 !ruby/object:Gem::Requirement
38
+ requirement: &2153823360 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 1.0.3
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *2152858380
46
+ version_requirements: *2153823360
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rake
49
- requirement: &2152857920 !ruby/object:Gem::Requirement
49
+ requirement: &2153822900 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0.9'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *2152857920
57
+ version_requirements: *2153822900
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: simplecov
60
- requirement: &2152857460 !ruby/object:Gem::Requirement
60
+ requirement: &2153822440 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0.4'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *2152857460
68
+ version_requirements: *2153822440
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: yard
71
- requirement: &2152857000 !ruby/object:Gem::Requirement
71
+ requirement: &2153821980 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ~>
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0.7'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *2152857000
79
+ version_requirements: *2153821980
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: rdiscount
82
- requirement: &2152856540 !ruby/object:Gem::Requirement
82
+ requirement: &2153821520 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ~>
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '1.6'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *2152856540
90
+ version_requirements: *2153821520
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: rspec
93
- requirement: &2152856080 !ruby/object:Gem::Requirement
93
+ requirement: &2153821060 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ~>
@@ -98,10 +98,10 @@ dependencies:
98
98
  version: 2.6.0
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *2152856080
101
+ version_requirements: *2153821060
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: yajl-ruby
104
- requirement: &2152855620 !ruby/object:Gem::Requirement
104
+ requirement: &2153820600 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ~>
@@ -109,10 +109,10 @@ dependencies:
109
109
  version: '1.0'
110
110
  type: :development
111
111
  prerelease: false
112
- version_requirements: *2152855620
112
+ version_requirements: *2153820600
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: json
115
- requirement: &2152855160 !ruby/object:Gem::Requirement
115
+ requirement: &2153820140 !ruby/object:Gem::Requirement
116
116
  none: false
117
117
  requirements:
118
118
  - - ~>
@@ -120,10 +120,10 @@ dependencies:
120
120
  version: 1.5.1
121
121
  type: :development
122
122
  prerelease: false
123
- version_requirements: *2152855160
123
+ version_requirements: *2153820140
124
124
  - !ruby/object:Gem::Dependency
125
125
  name: guard-rspec
126
- requirement: &2152854700 !ruby/object:Gem::Requirement
126
+ requirement: &2153819680 !ruby/object:Gem::Requirement
127
127
  none: false
128
128
  requirements:
129
129
  - - ~>
@@ -131,7 +131,7 @@ dependencies:
131
131
  version: 0.4.3
132
132
  type: :development
133
133
  prerelease: false
134
- version_requirements: *2152854700
134
+ version_requirements: *2153819680
135
135
  description: TweetStream allows you to easily consume the Twitter Streaming API utilizing
136
136
  the YAJL Ruby gem.
137
137
  email:
@@ -153,17 +153,22 @@ files:
153
153
  - Rakefile
154
154
  - examples/growl_daemon.rb
155
155
  - examples/oauth.rb
156
+ - examples/userstream.rb
156
157
  - lib/tweetstream.rb
157
158
  - lib/tweetstream/client.rb
158
159
  - lib/tweetstream/configuration.rb
159
160
  - lib/tweetstream/daemon.rb
161
+ - lib/tweetstream/direct_message.rb
162
+ - lib/tweetstream/error.rb
160
163
  - lib/tweetstream/hash.rb
161
164
  - lib/tweetstream/status.rb
162
165
  - lib/tweetstream/user.rb
163
166
  - lib/tweetstream/version.rb
167
+ - spec/data/direct_messages.json
164
168
  - spec/data/statuses.json
165
169
  - spec/spec_helper.rb
166
170
  - spec/tweetstream/client_spec.rb
171
+ - spec/tweetstream/direct_message_spec.rb
167
172
  - spec/tweetstream/hash_spec.rb
168
173
  - spec/tweetstream/parser_spec.rb
169
174
  - spec/tweetstream/status_spec.rb
@@ -194,9 +199,11 @@ signing_key:
194
199
  specification_version: 3
195
200
  summary: TweetStream is a simple wrapper for consuming the Twitter Streaming API.
196
201
  test_files:
202
+ - spec/data/direct_messages.json
197
203
  - spec/data/statuses.json
198
204
  - spec/spec_helper.rb
199
205
  - spec/tweetstream/client_spec.rb
206
+ - spec/tweetstream/direct_message_spec.rb
200
207
  - spec/tweetstream/hash_spec.rb
201
208
  - spec/tweetstream/parser_spec.rb
202
209
  - spec/tweetstream/status_spec.rb