diff --git a/README.rdoc b/README.rdoc index c68c371..5841f46 100644 --- a/README.rdoc +++ b/README.rdoc @@ -3,12 +3,12 @@ TweetStream provides simple Ruby access to Twitter's Streaming API (http://apiwiki.twitter.com/Streaming-API-Documentation). -This fork has been modified to support the site_streams API currently in Beta. +This fork has been modified to support the site_streams API currently in Beta. -We have also added support for location-based queries to the streaming API. +We have also added support for location-based queries to the streaming API. == Installation - + To install from Gemcutter: gem install tweetstream @@ -19,15 +19,15 @@ Using TweetStream is quite simple: require 'rubygems' require 'tweetstream' - + # This will pull a sample of all tweets based on # your Twitter account's Streaming API role. TweetStream::Client.new('username','password').sample do |status| - # The status object is a special Hash with + # The status object is a special Hash with # method access to its keys. puts "#{status.text}" end - + You can also use it to track keywords or follow a given set of user ids: @@ -35,12 +35,12 @@ user ids: TweetStream::Client.new('username','password').track('term1', 'term2') do |status| puts "#{status.text}" end - + # Use 'follow' to follow a group of user ids (integers, not screen names) TweetStream::Client.new('username','password').follow(14252, 53235) do |status| puts "#{status.text}" end - + The methods available to TweetStream::Client will be kept in parity with the methods available on the Streaming API wiki page. @@ -52,7 +52,7 @@ client or daemon by passing it in as the last argument: # Parse tweets using Yajl-Ruby TweetStream::Client.new('abc','def',:yajl) # ... - + Available options are :yajl, :json_gem (default), :json_pure, and :active_support. @@ -64,17 +64,17 @@ have caused some tweets not to appear in the stream. To handle these, you can use the on_delete and on_limit methods. Example: @client = TweetStream::Client.new('user','pass') - + @client.on_delete do |status_id, user_id| Tweet.delete(status_id) end - + @client.on_limit do |skip_count| # do something - end - + end + @client.track('intridea') - + The on_delete and on_limit methods can also be chained, like so: TweetStream::Client.new('user','pass').on_delete{ |status_id, user_id| @@ -84,8 +84,8 @@ The on_delete and on_limit methods can also be chained, like so: }.track('intridea') do |status| # do something with the status like normal end - -You can also provide :delete and/or :limit + +You can also provide :delete and/or :limit options when you make your method call: TweetStream::Client.new('user','pass').track('intridea', @@ -94,7 +94,7 @@ options when you make your method call: ) do |status| # do something with the status like normal end - + Twitter recommends honoring deletions as quickly as possible, and you would likely be wise to integrate this functionality into your application. @@ -115,7 +115,7 @@ down. It could be for routine maintenance, etc. end However, if the maximum number of reconnect attempts has been reached, -TweetStream will raise a TweetStream::ReconnectError with +TweetStream will raise a TweetStream::ReconnectError with information about the timeout and number of retries attempted. == Terminating a TweetStream @@ -131,7 +131,7 @@ the client itself: @statuses << status client.stop if @statuses.size >= 10 end - + When stop is called, TweetStream will return from the block the last successfully yielded status, allowing you to make note of it in your application as necessary. @@ -145,12 +145,12 @@ using the TweetStream library: TweetStream::Daemon.new('username','password', 'tracker').track('term1', 'term2') do |status| # do something in the background end - + 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 as start, stop, and run. == Note on Patches/Pull Requests - + * Fork the project. * Make your feature addition or bug fix. * Add tests for it. This is important so I don't break it in a future version unintentionally. diff --git a/examples/growl_daemon.rb b/examples/growl_daemon.rb index e760786..42cec32 100644 --- a/examples/growl_daemon.rb +++ b/examples/growl_daemon.rb @@ -4,7 +4,7 @@ if args_start = ARGV.index('--') username, password = ARGV[args_start + 1].split(':') - tracks = ARGV[args_start + 2 .. -1] + tracks = ARGV[args_start + 2 .. -1] puts "Starting a GrowlTweet to track: #{tracks.inspect}" end diff --git a/lib/tweetstream/client.rb b/lib/tweetstream/client.rb index e87b95f..a9ac29d 100644 --- a/lib/tweetstream/client.rb +++ b/lib/tweetstream/client.rb @@ -35,7 +35,7 @@ class Client def parser=(parser) @parser = parser_from(parser) end - + # Create a new client with the Twitter credentials # of the account you want to be using its API quota. # You may also set the JSON parsing library as specified @@ -47,16 +47,16 @@ def initialize(consumer_key, consumer_secret, access_key, access_secret, parser self.access_secret = access_secret self.parser = parser end - + # Returns all public statuses. The Firehose is not a generally - # available resource. Few applications require this level of access. - # Creative use of a combination of other resources and various access - # levels can satisfy nearly every application use case. + # available resource. Few applications require this level of access. + # Creative use of a combination of other resources and various access + # levels can satisfy nearly every application use case. def firehose(query_parameters = {}, &block) start('statuses/firehose', query_parameters, &block) end - - # Returns all retweets. The retweet stream is not a generally available + + # Returns all retweets. The retweet stream is not a generally available # resource. Few applications require this level of access. Creative # use of a combination of other resources and various access levels # can satisfy nearly every application use case. As of 9/11/2009, @@ -65,8 +65,8 @@ def firehose(query_parameters = {}, &block) def retweet(query_parameters = {}, &block) start('statuses/retweet', query_parameters, &block) end - - # Returns a random sample of all public statuses. The default access level + + # Returns a random sample of all public statuses. The default access level # provides a small proportion of the Firehose. The "Gardenhose" access # level provides a proportion more suitable for data mining and # research applications that desire a larger proportion to be statistically @@ -75,11 +75,11 @@ def sample(query_parameters = {}, &block) start('statuses/sample', query_parameters, &block) end - # Specify keywords to track. Queries are subject to Track Limitations, - # described in Track Limiting and subject to access roles, described in - # the statuses/filter method. Track keywords are case-insensitive logical + # Specify keywords to track. Queries are subject to Track Limitations, + # described in Track Limiting and subject to access roles, described in + # the statuses/filter method. Track keywords are case-insensitive logical # ORs. Terms are exact-matched, and also exact-matched ignoring - # punctuation. Phrases, keywords with spaces, are not supported. + # punctuation. Phrases, keywords with spaces, are not supported. # Keywords containing punctuation will only exact match tokens. # Query parameters may be passed as the last argument. def track(*keywords, &block) @@ -88,8 +88,8 @@ def track(*keywords, &block) filter(query_params.merge(:track => keywords), &block) end - # Returns public statuses from or in reply to a set of users. Mentions - # ("Hello @user!") and implicit replies ("@user Hello!" created without + # Returns public statuses from or in reply to a set of users. Mentions + # ("Hello @user!") and implicit replies ("@user Hello!" created without # pressing the reply "swoosh") are not matched. Requires integer user # IDs, not screen names. Query parameters may be passed as the last argument. def follow(*user_ids, &block) @@ -101,7 +101,7 @@ def follow(*user_ids, &block) def locations(coords, &block) filter(query_params.merge(:locations => coords), &block) end - + # Make a call to the statuses/filter method of the Streaming API, # you may provide :follow, :track or both as options # to follow the tweets of specified users or track keywords. This @@ -117,10 +117,10 @@ def filter(query_params = {}, &block) end start('statuses/filter', query_params.merge(:method => :post), &block) end - + def site_follow(*user_ids, &block) query_params ||= {:follow => user_ids} - + if query_params[:follow].is_a?(Array) query_params[:follow] = query_params[:follow].collect{|q| q.to_s}.join(',') elsif query_params[:follow] @@ -128,10 +128,10 @@ def site_follow(*user_ids, &block) end query_params[:site_streams] = query_params[:follow] query_params[:track] = ["foo"] - + start('site', query_params.merge(:method => :post, :host => 'betastream.twitter.com', :version => '2b'), &block) end - + # Set a Proc to be run when a deletion notice is received # from the Twitter stream. For example: # @@ -141,7 +141,7 @@ def site_follow(*user_ids, &block) # end # # Block must take two arguments: the status id and the user id. - # If no block is given, it will return the currently set + # If no block is given, it will return the currently set # deletion proc. When a block is given, the TweetStream::Client # object is returned to allow for chaining. def on_delete(&block) @@ -152,7 +152,7 @@ def on_delete(&block) @on_delete end end - + # Set a Proc to be run when a rate limit notice is received # from the Twitter stream. For example: # @@ -162,7 +162,7 @@ def on_delete(&block) # end # # Block must take one argument: the number of discarded tweets. - # If no block is given, it will return the currently set + # If no block is given, it will return the currently set # limit proc. When a block is given, the TweetStream::Client # object is returned to allow for chaining. def on_limit(&block) @@ -173,7 +173,7 @@ def on_limit(&block) @on_limit end end - + # Set a Proc to be run when an HTTP error is encountered in the # processing of the stream. Note that TweetStream will automatically # try to reconnect, this is for reference only. Don't panic! @@ -184,7 +184,7 @@ def on_limit(&block) # end # # Block must take one argument: the error message. - # If no block is given, it will return the currently set + # If no block is given, it will return the currently set # error proc. When a block is given, the TweetStream::Client # object is returned to allow for chaining. def on_error(&block) @@ -195,7 +195,7 @@ def on_error(&block) @on_error end end - + def start(path, query_parameters = {}, &block) #:nodoc: host = query_parameters.delete(:host) || 'stream.twitter.com' @@ -205,16 +205,16 @@ def start(path, query_parameters = {}, &block) #:nodoc: delete_proc = query_parameters.delete(:delete) || self.on_delete limit_proc = query_parameters.delete(:limit) || self.on_limit error_proc = query_parameters.delete(:error) || self.on_error - + uri = method == :get ? build_uri(path, version, query_parameters) : build_uri(path, version) - + oauth = { :consumer_key => self.consumer_key, :consumer_secret => self.consumer_secret, :access_key => self.access_key, :access_secret => self.access_secret } - + EventMachine::run { @stream = Twitter::JSONStream.connect( :path => uri, @@ -228,24 +228,24 @@ def start(path, query_parameters = {}, &block) #:nodoc: :content => (method == :post ? build_post_body(query_parameters) : ''), :user_agent => 'TweetStream' ) - + @stream.each_item do |item| raw_hash = @parser.decode(item) - + unless raw_hash.is_a?(::Hash) error_proc.call("Unexpected JSON object in stream: #{item}") next end - + hash = TweetStream::Hash.new(raw_hash) # @parser.parse(item) - + if hash[:delete] && hash[:delete][:status] delete_proc.call(hash[:delete][:status][:id], hash[:delete][:status][:user_id]) if delete_proc.is_a?(Proc) elsif hash[:limit] && hash[:limit][:track] limit_proc.call(hash[:limit][:track]) if limit_proc.is_a?(Proc) elsif hash[:text] && hash[:user] @last_status = TweetStream::Status.new(hash) - + # Give the block the option to receive either one # or two arguments, depending on its arity. case block.arity @@ -255,9 +255,9 @@ def start(path, query_parameters = {}, &block) #:nodoc: yield @last_status, self end elsif hash[:for_user] - + @last_status = TweetStream::Status.new(hash[:messages] || hash[:message]) - + # Give the block the option to receive either one # or two arguments, depending on its arity. case block.arity @@ -268,23 +268,23 @@ def start(path, query_parameters = {}, &block) #:nodoc: end end end - + @stream.on_error do |message| error_proc.call(message) if error_proc.is_a?(Proc) end - + @stream.on_max_reconnects do |timeout, retries| raise TweetStream::ReconnectError.new(timeout, retries) end } end - + # Terminate the currently running TweetStream. def stop EventMachine.stop_event_loop @last_status end - + protected def parser_from(parser) @@ -296,7 +296,7 @@ def parser_from(parser) eval("TweetStream::Parsers::#{parser.to_s.split('_').map{|s| s.capitalize}.join('')}") end end - + def build_uri(path, version = '1', query_parameters = {}) #:nodoc: URI.parse("/#{version}/#{path}.json#{build_query_parameters(query_parameters)}") end @@ -304,11 +304,11 @@ def build_uri(path, version = '1', query_parameters = {}) #:nodoc: def build_query_parameters(query) query.size > 0 ? "?#{build_post_body(query)}" : '' end - + def build_post_body(query) #:nodoc: return '' unless query && query.is_a?(::Hash) && query.size > 0 pairs = [] - + query.each_pair do |k,v| pairs << "#{k.to_s}=#{CGI.escape(v.to_s)}" end diff --git a/lib/tweetstream/daemon.rb b/lib/tweetstream/daemon.rb index 5570d7c..87c22ce 100644 --- a/lib/tweetstream/daemon.rb +++ b/lib/tweetstream/daemon.rb @@ -30,7 +30,7 @@ def initialize(consumer_key, consumer_secret, access_key, access_secret, app_nam @app_name = app_name super(consumer_key, consumer_secret, access_key, access_secret, parser) end - + def start(path, query_parameters = {}, &block) #:nodoc: Daemons.run_proc(@app_name || 'tweetstream', :multiple => true) do super(path, query_parameters, &block) diff --git a/lib/tweetstream/hash.rb b/lib/tweetstream/hash.rb index e378b02..8a734c0 100644 --- a/lib/tweetstream/hash.rb +++ b/lib/tweetstream/hash.rb @@ -6,7 +6,7 @@ def initialize(other_hash = {}) self[key.to_sym] = value end end - + def method_missing(method_name, *args) if key?(method_name.to_sym) self[method_name.to_sym] diff --git a/lib/tweetstream/status.rb b/lib/tweetstream/status.rb index e44b137..2940e88 100644 --- a/lib/tweetstream/status.rb +++ b/lib/tweetstream/status.rb @@ -5,7 +5,7 @@ def initialize(hash) super self[:user] = TweetStream::User.new(self[:user]) if self[:user] end - + def id self[:id] || super end diff --git a/spec/data/statuses.json b/spec/data/statuses.json index 7c26336..7e56cc5 100644 --- a/spec/data/statuses.json +++ b/spec/data/statuses.json @@ -1 +1 @@ -{"favorited":false,"text":"listening to Where U Headed by Universal Playaz. http://iLike.com/s/9zpOZ #musicmonday something for the ladies","in_reply_to_user_id":null,"in_reply_to_screen_name":null,"source":"iLike","truncated":false,"created_at":"Tue Sep 22 01:29:13 +0000 2009","user":{"statuses_count":378,"favourites_count":1,"profile_text_color":"666666","location":"Atlanta, Ga","profile_background_image_url":"http://a3.twimg.com/profile_background_images/36516125/Universal_Playaz.jpg","profile_link_color":"2FC2EF","description":"Paper Chaser","following":null,"verified":false,"notifications":null,"profile_sidebar_fill_color":"252429","profile_image_url":"http://a1.twimg.com/profile_images/413331530/DIESELSTATScopy_normal.jpg","url":"http://www.myspace.com/DieselDtheg","profile_sidebar_border_color":"181A1E","screen_name":"DieselD2143","profile_background_tile":true,"followers_count":75,"protected":false,"time_zone":"Eastern Time (US & Canada)","created_at":"Thu Jun 18 15:56:32 +0000 2009","name":"Diesel D","friends_count":119,"profile_background_color":"1A1B1F","id":48392351,"utc_offset":-18000},"in_reply_to_status_id":null,"id":4161231023} {"favorited":false,"text":"David Bowie and Nine Inch Nails perform \"Hurt\" http://bit.ly/AOaWG #musicmonday #nineinchnails #nin","in_reply_to_user_id":null,"in_reply_to_screen_name":null,"source":"web","truncated":false,"created_at":"Tue Sep 22 01:29:16 +0000 2009","user":{"statuses_count":668,"favourites_count":25,"profile_text_color":"445d85","location":"S\u00e3o Paulo, Brazil","profile_background_image_url":"http://a3.twimg.com/profile_background_images/38174991/GeorgeRomero-oil-400.jpg","profile_link_color":"555757","description":"You think I ain't worth a dollar, but I feel like a millionaire","following":null,"verified":false,"notifications":null,"profile_sidebar_fill_color":"a3a7ad","profile_image_url":"http://a1.twimg.com/profile_images/96034368/n1076431955_30001395_7912_normal.jpg","url":null,"profile_sidebar_border_color":"c7d1ed","screen_name":"RenatonMiranda","profile_background_tile":true,"followers_count":111,"protected":false,"time_zone":"Santiago","created_at":"Sat Mar 14 15:03:59 +0000 2009","name":"Renato Miranda","friends_count":143,"profile_background_color":"287356","id":24379310,"utc_offset":-14400},"in_reply_to_status_id":null,"id":4161232008} {"favorited":false,"text":"#musicmonday ,time to download some songs today!! :)","in_reply_to_user_id":null,"in_reply_to_screen_name":null,"source":"web","truncated":false,"created_at":"Tue Sep 22 01:29:19 +0000 2009","user":{"statuses_count":188,"favourites_count":0,"profile_text_color":"3D1957","location":"under the water","profile_background_image_url":"http://s.twimg.com/a/1253562286/images/themes/theme10/bg.gif","profile_link_color":"FF0000","description":"ask me ","following":null,"verified":false,"notifications":null,"profile_sidebar_fill_color":"7AC3EE","profile_image_url":"http://a1.twimg.com/profile_images/421281292/twit_pic_normal.jpg","url":"http://www.exploretalent.com/contest_video.php?talentnum=2053105&cm_id=3398","profile_sidebar_border_color":"65B0DA","screen_name":"julieanne11343","profile_background_tile":true,"followers_count":9,"protected":false,"time_zone":"Pacific Time (US & Canada)","created_at":"Mon Jul 20 21:08:22 +0000 2009","name":"Julieanne","friends_count":17,"profile_background_color":"642D8B","id":58591151,"utc_offset":-28800},"in_reply_to_status_id":null,"id":4161233120} {"text":"#Musicmonday \"Dont be tardy f0r the party\"","truncated":false,"source":"mobile web","in_reply_to_status_id":null,"favorited":false,"created_at":"Tue Sep 22 01:29:19 +0000 2009","user":{"verified":false,"notifications":null,"profile_sidebar_fill_color":"e0ff92","location":"Dope Girl Island","profile_sidebar_border_color":"87bc44","description":"","following":null,"profile_background_tile":false,"followers_count":29,"profile_image_url":"http://a3.twimg.com/profile_images/217487577/badbad_normal.jpg","time_zone":"Eastern Time (US & Canada)","url":null,"friends_count":65,"profile_background_color":"9ae4e8","screen_name":"SwagGirlOnDeck","protected":false,"statuses_count":847,"favourites_count":0,"created_at":"Fri May 01 16:59:15 +0000 2009","profile_text_color":"000000","name":"Mariah Reta","id":36987168,"profile_background_image_url":"http://s.twimg.com/a/1253301564/images/themes/theme1/bg.png","utc_offset":-18000,"profile_link_color":"0000ff"},"in_reply_to_user_id":null,"id":4161233317,"in_reply_to_screen_name":null} \ No newline at end of file +{"favorited":false,"text":"listening to Where U Headed by Universal Playaz. http://iLike.com/s/9zpOZ #musicmonday something for the ladies","in_reply_to_user_id":null,"in_reply_to_screen_name":null,"source":"iLike","truncated":false,"created_at":"Tue Sep 22 01:29:13 +0000 2009","user":{"statuses_count":378,"favourites_count":1,"profile_text_color":"666666","location":"Atlanta, Ga","profile_background_image_url":"http://a3.twimg.com/profile_background_images/36516125/Universal_Playaz.jpg","profile_link_color":"2FC2EF","description":"Paper Chaser","following":null,"verified":false,"notifications":null,"profile_sidebar_fill_color":"252429","profile_image_url":"http://a1.twimg.com/profile_images/413331530/DIESELSTATScopy_normal.jpg","url":"http://www.myspace.com/DieselDtheg","profile_sidebar_border_color":"181A1E","screen_name":"DieselD2143","profile_background_tile":true,"followers_count":75,"protected":false,"time_zone":"Eastern Time (US & Canada)","created_at":"Thu Jun 18 15:56:32 +0000 2009","name":"Diesel D","friends_count":119,"profile_background_color":"1A1B1F","id":48392351,"utc_offset":-18000},"in_reply_to_status_id":null,"id":4161231023} {"favorited":false,"text":"David Bowie and Nine Inch Nails perform \"Hurt\" http://bit.ly/AOaWG #musicmonday #nineinchnails #nin","in_reply_to_user_id":null,"in_reply_to_screen_name":null,"source":"web","truncated":false,"created_at":"Tue Sep 22 01:29:16 +0000 2009","user":{"statuses_count":668,"favourites_count":25,"profile_text_color":"445d85","location":"S\u00e3o Paulo, Brazil","profile_background_image_url":"http://a3.twimg.com/profile_background_images/38174991/GeorgeRomero-oil-400.jpg","profile_link_color":"555757","description":"You think I ain't worth a dollar, but I feel like a millionaire","following":null,"verified":false,"notifications":null,"profile_sidebar_fill_color":"a3a7ad","profile_image_url":"http://a1.twimg.com/profile_images/96034368/n1076431955_30001395_7912_normal.jpg","url":null,"profile_sidebar_border_color":"c7d1ed","screen_name":"RenatonMiranda","profile_background_tile":true,"followers_count":111,"protected":false,"time_zone":"Santiago","created_at":"Sat Mar 14 15:03:59 +0000 2009","name":"Renato Miranda","friends_count":143,"profile_background_color":"287356","id":24379310,"utc_offset":-14400},"in_reply_to_status_id":null,"id":4161232008} {"favorited":false,"text":"#musicmonday ,time to download some songs today!! :)","in_reply_to_user_id":null,"in_reply_to_screen_name":null,"source":"web","truncated":false,"created_at":"Tue Sep 22 01:29:19 +0000 2009","user":{"statuses_count":188,"favourites_count":0,"profile_text_color":"3D1957","location":"under the water","profile_background_image_url":"http://s.twimg.com/a/1253562286/images/themes/theme10/bg.gif","profile_link_color":"FF0000","description":"ask me ","following":null,"verified":false,"notifications":null,"profile_sidebar_fill_color":"7AC3EE","profile_image_url":"http://a1.twimg.com/profile_images/421281292/twit_pic_normal.jpg","url":"http://www.exploretalent.com/contest_video.php?talentnum=2053105&cm_id=3398","profile_sidebar_border_color":"65B0DA","screen_name":"julieanne11343","profile_background_tile":true,"followers_count":9,"protected":false,"time_zone":"Pacific Time (US & Canada)","created_at":"Mon Jul 20 21:08:22 +0000 2009","name":"Julieanne","friends_count":17,"profile_background_color":"642D8B","id":58591151,"utc_offset":-28800},"in_reply_to_status_id":null,"id":4161233120} {"text":"#Musicmonday \"Dont be tardy f0r the party\"","truncated":false,"source":"mobile web","in_reply_to_status_id":null,"favorited":false,"created_at":"Tue Sep 22 01:29:19 +0000 2009","user":{"verified":false,"notifications":null,"profile_sidebar_fill_color":"e0ff92","location":"Dope Girl Island","profile_sidebar_border_color":"87bc44","description":"","following":null,"profile_background_tile":false,"followers_count":29,"profile_image_url":"http://a3.twimg.com/profile_images/217487577/badbad_normal.jpg","time_zone":"Eastern Time (US & Canada)","url":null,"friends_count":65,"profile_background_color":"9ae4e8","screen_name":"SwagGirlOnDeck","protected":false,"statuses_count":847,"favourites_count":0,"created_at":"Fri May 01 16:59:15 +0000 2009","profile_text_color":"000000","name":"Mariah Reta","id":36987168,"profile_background_image_url":"http://s.twimg.com/a/1253301564/images/themes/theme1/bg.png","utc_offset":-18000,"profile_link_color":"0000ff"},"in_reply_to_user_id":null,"id":4161233317,"in_reply_to_screen_name":null} \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ec0debe..419d07c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -21,5 +21,5 @@ def sample_tweets end Spec::Runner.configure do |config| - + end diff --git a/spec/tweetstream/client_spec.rb b/spec/tweetstream/client_spec.rb index d99efe6..26a724b 100644 --- a/spec/tweetstream/client_spec.rb +++ b/spec/tweetstream/client_spec.rb @@ -29,7 +29,7 @@ before do @client = TweetStream::Client.new('abc','def') end - + it 'should return a blank string if passed a nil value' do @client.send(:build_post_body, nil).should == '' end @@ -53,10 +53,10 @@ describe '#start' do before do - @stream = stub("Twitter::JSONStream", + @stream = stub("Twitter::JSONStream", :connect => true, - :unbind => true, - :each_item => true, + :unbind => true, + :each_item => true, :on_error => true, :on_max_reconnects => true, :connection_completed => true @@ -65,7 +65,7 @@ Twitter::JSONStream.stub!(:connect).and_return(@stream) @client = TweetStream::Client.new('abc','def') end - + it 'should try to connect via a JSON stream' do Twitter::JSONStream.should_receive(:connect).with( :auth => 'abc:def', @@ -74,10 +74,10 @@ :method => 'POST', :user_agent => 'TweetStream' ).and_return(@stream) - + @client.track('monday') end - + describe '#each_item' do it 'should call the appropriate parser' do @client = TweetStream::Client.new('abc','def',:active_support) @@ -85,39 +85,39 @@ @stream.should_receive(:each_item).and_yield(sample_tweets[0].to_json) @client.track('abc','def') end - + it 'should yield a TweetStream::Status' do @stream.should_receive(:each_item).and_yield(sample_tweets[0].to_json) @client.track('abc'){|s| s.should be_kind_of(TweetStream::Status)} end - + it 'should also yield the client if a block with arity 2 is given' do @stream.should_receive(:each_item).and_yield(sample_tweets[0].to_json) @client.track('abc'){|s,c| c.should == @client} end - + it 'should include the proper values' do tweet = sample_tweets[0] tweet[:id] = 123 tweet[:user][:screen_name] = 'monkey' tweet[:text] = "Oo oo aa aa" @stream.should_receive(:each_item).and_yield(tweet.to_json) - @client.track('abc') do |s| + @client.track('abc') do |s| s[:id].should == 123 s.user.screen_name.should == 'monkey' s.text.should == 'Oo oo aa aa' end end - + it 'should call the on_delete if specified' do delete = '{ "delete": { "status": { "id": 1234, "user_id": 3 } } }' @stream.should_receive(:each_item).and_yield(delete) - @client.on_delete do |id, user_id| + @client.on_delete do |id, user_id| id.should == 1234 user_id.should == 3 end.track('abc') end - + it 'should call the on_limit if specified' do limit = '{ "limit": { "track": 1234 } }' @stream.should_receive(:each_item).and_yield(limit) @@ -125,7 +125,7 @@ track.should == 1234 end.track('abc') end - + it 'should call on_error if a non-hash response is received' do @stream.should_receive(:each_item).and_yield('["favorited"]') @client.on_error do |message| @@ -133,7 +133,7 @@ end.track('abc') end end - + describe '#on_error' do it 'should pass the message on to the error block' do @stream.should_receive(:on_error).and_yield('Uh oh') @@ -142,7 +142,7 @@ end.track('abc') end end - + describe '#on_max_reconnects' do it 'should raise a ReconnectError' do @stream.should_receive(:on_max_reconnects).and_yield(30, 20) @@ -153,51 +153,51 @@ end end end - + describe ' API methods' do before do @client = TweetStream::Client.new('abc','def') end - + %w(firehose retweet sample).each do |method| it "##{method} should make a call to start with \"statuses/#{method}\"" do @client.should_receive(:start).once.with('statuses/' + method, {}) @client.send(method) end end - + it '#track should make a call to start with "statuses/filter" and a track query parameter' do @client.should_receive(:start).once.with('statuses/filter', :track => 'test', :method => :post) @client.track('test') end - + it '#track should comma-join multiple arguments' do @client.should_receive(:start).once.with('statuses/filter', :track => 'foo,bar,baz', :method => :post) @client.track('foo', 'bar', 'baz') end - + it '#follow should make a call to start with "statuses/filter" and a follow query parameter' do @client.should_receive(:start).once.with('statuses/filter', :follow => '123', :method => :post) @client.follow(123) end - + it '#follow should comma-join multiple arguments' do @client.should_receive(:start).once.with('statuses/filter', :follow => '123,456', :method => :post) @client.follow(123, 456) end - + it '#filter should make a call to "statuses/filter" with the query params provided' do @client.should_receive(:start).once.with('statuses/filter', :follow => '123', :method => :post) @client.filter(:follow => 123) end end - + %w(on_delete on_limit).each do |proc_setter| describe "##{proc_setter}" do before do @client = TweetStream::Client.new('abc','def') end - + it 'should set when a block is given' do proc = Proc.new{|a,b| puts a } @client.send(proc_setter, &proc) @@ -222,7 +222,7 @@ EventMachine.should_receive :stop_event_loop TweetStream::Client.new('test','fake').stop.should be_nil end - + it 'should return the last status yielded' do EventMachine.should_receive :stop_event_loop client = TweetStream::Client.new('test','fake') diff --git a/spec/tweetstream/hash_spec.rb b/spec/tweetstream/hash_spec.rb index 6a9737f..10e4319 100644 --- a/spec/tweetstream/hash_spec.rb +++ b/spec/tweetstream/hash_spec.rb @@ -4,15 +4,15 @@ it 'should be initialized by passing in an existing hash' do TweetStream::Hash.new(:abc => 123)[:abc].should == 123 end - + it 'should symbolize incoming keys' do TweetStream::Hash.new('abc' => 123)[:abc].should == 123 end - + it 'should allow access via method calls' do TweetStream::Hash.new(:abc => 123).abc.should == 123 end - + it 'should still throw NoMethod for non-existent keys' do lambda{TweetStream::Hash.new({}).akabi}.should raise_error(NoMethodError) end diff --git a/spec/tweetstream/parser_spec.rb b/spec/tweetstream/parser_spec.rb index 24292f3..941e2c5 100644 --- a/spec/tweetstream/parser_spec.rb +++ b/spec/tweetstream/parser_spec.rb @@ -4,18 +4,18 @@ it 'should default to the JSON Gem' do TweetStream::Client.new('test','fake').parser.should == TweetStream::Parsers::JsonGem end - + [:json_gem, :yajl, :active_support, :json_pure].each do |engine| describe "#{engine} parsing" do before do @client = TweetStream::Client.new('test','fake',engine) @class_name = "TweetStream::Parsers::#{engine.to_s.split('_').map{|s| s.capitalize}.join('')}" end - + it 'should set the parser to the appropriate class' do @client.parser.to_s == @class_name end - + it 'should be settable via client.parser=' do @client.parser = nil @client.parser.should be_nil @@ -24,13 +24,13 @@ end end end - + class FakeParser def self.decode(text) {} end end - + it 'should be settable to a class' do @client = TweetStream::Client.new('abc','def') @client.parser = FakeParser diff --git a/spec/tweetstream/status_spec.rb b/spec/tweetstream/status_spec.rb index 7ce1817..b48ce16 100644 --- a/spec/tweetstream/status_spec.rb +++ b/spec/tweetstream/status_spec.rb @@ -6,7 +6,7 @@ @status.user.is_a?(TweetStream::User).should be_true @status.user.screen_name.should == 'bob' end - + it 'should override the #id method for itself and the user' do @status = TweetStream::Status.new(:id => 123, :user => {:id => 345}) @status.id.should == 123