tweetstream 0.3.0 → 1.0.0

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.rdoc CHANGED
@@ -4,16 +4,9 @@ TweetStream provides simple Ruby access to Twitter's Streaming API
4
4
  (http://apiwiki.twitter.com/Streaming-API-Documentation).
5
5
 
6
6
  == Installation
7
-
8
- The TweetStream gem is available on GitHub and Gemcutter. To get the
9
- latest gem from GitHub:
10
-
11
- gem sources -a http://gems.github.com/
12
- gem install intridea-tweetstream
13
7
 
14
8
  To install from Gemcutter:
15
9
 
16
- gem sources -a http://gemcutter.org/
17
10
  gem install tweetstream
18
11
 
19
12
  == Usage
@@ -47,6 +40,18 @@ user ids:
47
40
  The methods available to TweetStream::Client will be kept in parity
48
41
  with the methods available on the Streaming API wiki page.
49
42
 
43
+ == Swappable JSON Parsing
44
+
45
+ As of version 1.0, TweetStream supports swappable JSON backends for
46
+ parsing the Tweets. These are specified when you initialize the
47
+ client or daemon by passing it in as the last argument:
48
+
49
+ # Parse tweets using Yajl-Ruby
50
+ TweetStream::Client.new('abc','def',:yajl) # ...
51
+
52
+ Available options are <tt>:yajl</tt>, <tt>:json_gem</tt> (default),
53
+ <tt>:json_pure</tt>, and <tt>:active_support</tt>.
54
+
50
55
  == Handling Deletes and Rate Limitations
51
56
 
52
57
  Sometimes the Streaming API will send messages other than statuses.
@@ -90,18 +95,37 @@ Twitter recommends honoring deletions as quickly as possible, and
90
95
  you would likely be wise to integrate this functionality into your
91
96
  application.
92
97
 
98
+ == Errors and Reconnecting
99
+
100
+ TweetStream uses EventMachine to connect to the Twitter Streaming
101
+ API, and attempts to honor Twitter's guidelines in terms of automatic
102
+ reconnection. When Twitter becomes unavailable, the block specified
103
+ by you in <tt>on_error</tt> will be called. Note that this does not
104
+ indicate something is actually wrong, just that Twitter is momentarily
105
+ down. It could be for routine maintenance, etc.
106
+
107
+ TweetStream::Client.new('abc','def').on_error do |message|
108
+ # Log your error message somewhere
109
+ end.track('term') do |status|
110
+ # Do things when nothing's wrong
111
+ end
112
+
113
+ However, if the maximum number of reconnect attempts has been reached,
114
+ TweetStream will raise a <tt>TweetStream::ReconnectError</tt> with
115
+ information about the timeout and number of retries attempted.
116
+
93
117
  == Terminating a TweetStream
94
118
 
95
119
  It is often the case that you will need to change the parameters of your
96
120
  track or follow tweet streams. In the case that you need to terminate
97
- a stream, simply call <tt>TweetStream::Client.stop</tt> from within your
98
- loop:
121
+ a stream, you may add a second argument to your block that will yield
122
+ the client itself:
99
123
 
100
124
  # Stop after collecting 10 statuses
101
125
  @statuses = []
102
- TweetStream::Client.new('username','password').track('term1', 'term2') do |status|
126
+ TweetStream::Client.new('username','password').sample do |status, client|
103
127
  @statuses << status
104
- TweetStream::Client.stop if @statuses.size >= 10
128
+ client.stop if @statuses.size >= 10
105
129
  end
106
130
 
107
131
  When <tt>stop</tt> is called, TweetStream will return from the block
@@ -0,0 +1,5 @@
1
+ == Version 1.0.0
2
+
3
+ * Swappable JSON backend support
4
+ * Switches to use EventMachine instead of Yajl for the HTTP Stream
5
+ * Support reconnect and on_error
data/Rakefile CHANGED
@@ -10,9 +10,9 @@ begin
10
10
  gem.email = "michael@intridea.com"
11
11
  gem.homepage = "http://github.com/intridea/tweetstream"
12
12
  gem.authors = ["Michael Bleigh"]
13
- gem.files = FileList["[A-Z]*", "{lib,spec}/**/*"] - FileList["**/*.log"]
13
+ gem.files = FileList["[A-Z]*", "{lib,spec,examples}/**/*"] - FileList["**/*.log"]
14
14
  gem.add_development_dependency "rspec"
15
- gem.add_dependency 'yajl-ruby', '>= 0.6.6'
15
+ gem.add_dependency 'twitter-stream'
16
16
  gem.add_dependency 'daemons'
17
17
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
18
18
  end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.0
1
+ 1.0.0
@@ -0,0 +1,14 @@
1
+ require 'rubygems'
2
+ require 'tweetstream'
3
+ require 'ruby-growl'
4
+
5
+ if args_start = ARGV.index('--')
6
+ username, password = ARGV[args_start + 1].split(':')
7
+ tracks = ARGV[args_start + 2 .. -1]
8
+ puts "Starting a GrowlTweet to track: #{tracks.inspect}"
9
+ end
10
+
11
+ TweetStream::Daemon.new(username,password).track(*tracks) do |status|
12
+ g = Growl.new 'localhost', 'growltweet', ['tweet']
13
+ g.notify 'tweet', status.user.screen_name, status.text
14
+ end
@@ -1,7 +1,9 @@
1
1
  require 'uri'
2
2
  require 'cgi'
3
3
  require 'yajl'
4
- require 'yajl/http_stream'
4
+ require 'eventmachine'
5
+ require 'twitter/json_stream'
6
+ require 'json'
5
7
 
6
8
  module TweetStream
7
9
  # Provides simple access to the Twitter Streaming API (http://apiwiki.twitter.com/Streaming-API-Documentation)
@@ -20,12 +22,29 @@ module TweetStream
20
22
  # view the TweetStream::Daemon class.
21
23
  class Client
22
24
  attr_accessor :username, :password
25
+ attr_reader :parser
26
+
27
+ # Set the JSON Parser for this client. Acceptable options are:
28
+ #
29
+ # <tt>:json_gem</tt>:: Parse using the JSON gem.
30
+ # <tt>:json_pure</tt>:: Parse using the pure-ruby implementation of the JSON gem.
31
+ # <tt>:active_support</tt>:: Parse using ActiveSupport::JSON.decode
32
+ # <tt>:yajl</tt>:: Parse using <tt>yajl-ruby</tt>.
33
+ #
34
+ # You may also pass a class that will return a hash with symbolized
35
+ # keys when <tt>YourClass.parse</tt> is called with a JSON string.
36
+ def parser=(parser)
37
+ @parser = parser_from(parser)
38
+ end
23
39
 
24
40
  # Create a new client with the Twitter credentials
25
- # of the account you want to be using its API quota.
26
- def initialize(user, pass)
41
+ # of the account you want to be using its API quota.
42
+ # You may also set the JSON parsing library as specified
43
+ # in the #parser= setter.
44
+ def initialize(user, pass, parser = :json_gem)
27
45
  self.username = user
28
46
  self.password = pass
47
+ self.parser = parser
29
48
  end
30
49
 
31
50
  # Returns all public statuses. The Firehose is not a generally
@@ -136,49 +155,96 @@ module TweetStream
136
155
  end
137
156
  end
138
157
 
158
+ # Set a Proc to be run when an HTTP error is encountered in the
159
+ # processing of the stream. Note that TweetStream will automatically
160
+ # try to reconnect, this is for reference only. Don't panic!
161
+ #
162
+ # @client = TweetStream::Client.new('user','pass')
163
+ # @client.on_error do |message|
164
+ # # Make note of error message
165
+ # end
166
+ #
167
+ # Block must take one argument: the error message.
168
+ # If no block is given, it will return the currently set
169
+ # error proc. When a block is given, the TweetStream::Client
170
+ # object is returned to allow for chaining.
171
+ def on_error(&block)
172
+ if block_given?
173
+ @on_limit = block
174
+ self
175
+ else
176
+ @on_limit
177
+ end
178
+ end
179
+
139
180
  def start(path, query_parameters = {}, &block) #:nodoc:
140
181
  method = query_parameters.delete(:method) || :get
141
182
  delete_proc = query_parameters.delete(:delete) || self.on_delete
142
183
  limit_proc = query_parameters.delete(:limit) || self.on_limit
184
+ error_proc = query_parameters.delete(:error) || self.on_error
143
185
 
144
186
  uri = method == :get ? build_uri(path, query_parameters) : build_uri(path)
145
187
 
146
- args = [uri]
147
- args << build_post_body(query_parameters) if method == :post
148
- args << {:symbolize_keys => true}
149
-
150
- @stream = Yajl::HttpStream.new
151
-
152
- @stream.send(method, *args) do |hash|
153
- if hash[:delete] && hash[:delete][:status]
154
- delete_proc.call(hash[:delete][:status][:id], hash[:delete][:status][:user_id]) if delete_proc.is_a?(Proc)
155
- elsif hash[:limit] && hash[:limit][:track]
156
- limit_proc.call(hash[:limit][:track]) if limit_proc.is_a?(Proc)
157
- elsif hash[:text] && hash[:user]
158
- @last_status = TweetStream::Status.new(hash)
159
- yield @last_status
188
+ EventMachine::run {
189
+ @stream = Twitter::JSONStream.connect(
190
+ :path => uri,
191
+ :auth => "#{URI.encode self.username}:#{URI.encode self.password}",
192
+ :method => method.to_s.upcase,
193
+ :content => (method == :post ? build_post_body(query_parameters) : ''),
194
+ :user_agent => 'TweetStream'
195
+ )
196
+
197
+ @stream.each_item do |item|
198
+ hash = TweetStream::Hash.new(@parser.decode(item)) # @parser.parse(item)
199
+
200
+ if hash[:delete] && hash[:delete][:status]
201
+ delete_proc.call(hash[:delete][:status][:id], hash[:delete][:status][:user_id]) if delete_proc.is_a?(Proc)
202
+ elsif hash[:limit] && hash[:limit][:track]
203
+ limit_proc.call(hash[:limit][:track]) if limit_proc.is_a?(Proc)
204
+ elsif hash[:text] && hash[:user]
205
+ @last_status = TweetStream::Status.new(hash)
206
+
207
+ # Give the block the option to receive either one
208
+ # or two arguments, depending on its arity.
209
+ case block.arity
210
+ when 1
211
+ yield @last_status
212
+ when 2
213
+ yield @last_status, self
214
+ end
215
+ end
160
216
  end
161
- end
162
- rescue TweetStream::Terminated
163
- return @last_status
164
- rescue Yajl::HttpStream::InvalidContentType
165
- raise TweetStream::ConnectionError, "There was an error connecting to the Twitter streaming service. Please check your credentials and the current status of the Streaming API."
217
+
218
+ @stream.on_error do |message|
219
+ error_proc.call(message) if error_proc.is_a?(Proc)
220
+ end
221
+
222
+ @stream.on_max_reconnects do |timeout, retries|
223
+ raise TweetStream::ReconnectError.new(timeout, retries)
224
+ end
225
+ }
166
226
  end
167
227
 
168
- # Terminate the currently running TweetStream.
169
- def self.stop
170
- raise TweetStream::Terminated
171
- end
172
-
173
228
  # Terminate the currently running TweetStream.
174
229
  def stop
175
- @stream.terminate unless @stream.nil?
230
+ EventMachine.stop_event_loop
231
+ @last_status
176
232
  end
177
233
 
178
234
  protected
179
235
 
236
+ def parser_from(parser)
237
+ case parser
238
+ when Class
239
+ parser
240
+ when Symbol
241
+ require "tweetstream/parsers/#{parser.to_s}"
242
+ eval("TweetStream::Parsers::#{parser.to_s.split('_').map(&:capitalize).join('')}")
243
+ end
244
+ end
245
+
180
246
  def build_uri(path, query_parameters = {}) #:nodoc:
181
- URI.parse("http://#{URI.encode self.username}:#{URI.encode self.password}@stream.twitter.com/1/#{path}.json#{build_query_parameters(query_parameters)}")
247
+ URI.parse("/1/#{path}.json#{build_query_parameters(query_parameters)}")
182
248
  end
183
249
 
184
250
  def build_query_parameters(query)
@@ -26,9 +26,9 @@ class TweetStream::Daemon < TweetStream::Client
26
26
  # Twitter account you wish to use. The daemon has
27
27
  # an optional process name for use when querying
28
28
  # running processes.
29
- def initialize(user, pass, app_name=nil)
29
+ def initialize(user, pass, app_name=nil, parser=:json_gem)
30
30
  @app_name = app_name
31
- super(user, pass)
31
+ super(user, pass, parser)
32
32
  end
33
33
 
34
34
  def start(path, query_parameters = {}, &block) #:nodoc:
@@ -1,10 +1,12 @@
1
1
  class TweetStream::Hash < ::Hash #:nodoc: all
2
2
  def initialize(other_hash = {})
3
3
  other_hash.keys.each do |key|
4
- self[key.to_sym] = other_hash[key]
4
+ value = other_hash[key]
5
+ value = TweetStream::Hash.new(value) if value.is_a?(::Hash)
6
+ self[key.to_sym] = value
5
7
  end
6
8
  end
7
-
9
+
8
10
  def method_missing(method_name, *args)
9
11
  if key?(method_name.to_sym)
10
12
  self[method_name.to_sym]
@@ -0,0 +1,11 @@
1
+ require 'active_support/json' unless defined?(::ActiveSupport::JSON)
2
+
3
+ module TweetStream
4
+ module Parsers
5
+ class ActiveSupport
6
+ def self.decode(string)
7
+ ::ActiveSupport::JSON.decode(string)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'json' unless defined?(JSON)
2
+
3
+ module TweetStream
4
+ module Parsers
5
+ class JsonGem
6
+ def self.decode(string)
7
+ ::JSON.parse(string)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'json/pure' unless defined?(::JSON)
2
+
3
+ module TweetStream
4
+ module Parsers
5
+ class JsonPure
6
+ def self.decode(string)
7
+ ::JSON.parse(string)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'yajl' unless defined?(Yajl)
2
+
3
+ module TweetStream
4
+ module Parsers
5
+ class Yajl
6
+ def self.decode(string)
7
+ ::Yajl::Parser.new(:symbolize_keys => true).parse(string)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -5,4 +5,8 @@ class TweetStream::Status < TweetStream::Hash
5
5
  super
6
6
  self[:user] = TweetStream::User.new(self[:user])
7
7
  end
8
+
9
+ def id
10
+ self[:id] || super
11
+ end
8
12
  end
@@ -1,5 +1,7 @@
1
1
  # A simple Hash wrapper that gives you method-based
2
2
  # access to user properties returned by the streamer.
3
3
  class TweetStream::User < TweetStream::Hash
4
-
4
+ def id
5
+ self[:id] || super
6
+ end
5
7
  end
data/lib/tweetstream.rb CHANGED
@@ -8,4 +8,14 @@ module TweetStream
8
8
  class Terminated < ::StandardError; end
9
9
  class Error < ::StandardError; end
10
10
  class ConnectionError < TweetStream::Error; end
11
+ # A ReconnectError is raised when the maximum number of retries has
12
+ # failed to re-establish a connection.
13
+ class ReconnectError < StandardError
14
+ attr_accessor :timeout, :retries
15
+ def initialize(timeout, retries)
16
+ self.timeout = timeout
17
+ self.retries = retries
18
+ super("Failed to reconnect after #{retries} tries.")
19
+ end
20
+ end
11
21
  end
data/spec/spec_helper.rb CHANGED
@@ -6,6 +6,7 @@ require 'tweetstream'
6
6
  require 'spec'
7
7
  require 'spec/autorun'
8
8
  require 'yajl'
9
+ require 'json'
9
10
 
10
11
  def sample_tweets
11
12
  if @tweets
@@ -16,11 +16,6 @@ describe TweetStream::Client do
16
16
  @client.send(:build_uri, '').is_a?(URI).should be_true
17
17
  end
18
18
 
19
- it 'should contain the auth information from the client' do
20
- @client.send(:build_uri, '').user.should == 'abc'
21
- @client.send(:build_uri, '').password.should == 'def'
22
- end
23
-
24
19
  it 'should have the specified path with the version prefix and a json extension' do
25
20
  @client.send(:build_uri, 'awesome').path.should == '/1/awesome.json'
26
21
  end
@@ -58,52 +53,96 @@ describe TweetStream::Client do
58
53
 
59
54
  describe '#start' do
60
55
  before do
56
+ @stream = stub("Twitter::JSONStream",
57
+ :connect => true,
58
+ :unbind => true,
59
+ :each_item => true,
60
+ :on_error => true,
61
+ :on_max_reconnects => true,
62
+ :connection_completed => true
63
+ )
64
+ EM.stub!(:run).and_yield
65
+ Twitter::JSONStream.stub!(:connect).and_return(@stream)
61
66
  @client = TweetStream::Client.new('abc','def')
62
67
  end
63
-
64
- it 'should make a call to Yajl::HttpStream' do
65
- Yajl::HttpStream.should_receive(:new).and_return(y = mock("Yajl::HttpStream"))
66
- y.should_receive(:get).once.with(URI.parse('http://abc:def@stream.twitter.com/1/cool.json'), :symbolize_keys => true).and_return({})
67
- @client.start('cool')
68
+
69
+ it 'should try to connect via a JSON stream' do
70
+ Twitter::JSONStream.should_receive(:connect).with(
71
+ :auth => 'abc:def',
72
+ :content => 'track=monday',
73
+ :path => URI.parse('/1/statuses/filter.json'),
74
+ :method => 'POST',
75
+ :user_agent => 'TweetStream'
76
+ ).and_return(@stream)
77
+
78
+ @client.track('monday')
68
79
  end
69
-
70
- it 'should yield a TwitterStream::Status for each update' do
71
- Yajl::HttpStream.should_receive(:new).and_return(y = mock("Yajl::HttpStream"))
72
- y.should_receive(:post).once.with(URI.parse('http://abc:def@stream.twitter.com/1/statuses/filter.json'), 'track=musicmonday', :symbolize_keys => true).and_yield(sample_tweets[0])
73
- @client.track('musicmonday') do |status|
74
- status.is_a?(TweetStream::Status).should be_true
75
- @yielded = true
80
+
81
+ describe '#each_item' do
82
+ it 'should call the appropriate parser' do
83
+ @client = TweetStream::Client.new('abc','def',:active_support)
84
+ TweetStream::Parsers::ActiveSupport.should_receive(:decode).and_return({})
85
+ @stream.should_receive(:each_item).and_yield(sample_tweets[0].to_json)
86
+ @client.track('abc','def')
87
+ end
88
+
89
+ it 'should yield a TweetStream::Status' do
90
+ @stream.should_receive(:each_item).and_yield(sample_tweets[0].to_json)
91
+ @client.track('abc'){|s| s.should be_kind_of(TweetStream::Status)}
92
+ end
93
+
94
+ it 'should also yield the client if a block with arity 2 is given' do
95
+ @stream.should_receive(:each_item).and_yield(sample_tweets[0].to_json)
96
+ @client.track('abc'){|s,c| c.should == @client}
97
+ end
98
+
99
+ it 'should include the proper values' do
100
+ tweet = sample_tweets[0]
101
+ tweet[:id] = 123
102
+ tweet[:user][:screen_name] = 'monkey'
103
+ tweet[:text] = "Oo oo aa aa"
104
+ @stream.should_receive(:each_item).and_yield(tweet.to_json)
105
+ @client.track('abc') do |s|
106
+ s[:id].should == 123
107
+ s.user.screen_name.should == 'monkey'
108
+ s.text.should == 'Oo oo aa aa'
109
+ end
110
+ end
111
+
112
+ it 'should call the on_delete if specified' do
113
+ delete = '{ "delete": { "status": { "id": 1234, "user_id": 3 } } }'
114
+ @stream.should_receive(:each_item).and_yield(delete)
115
+ @client.on_delete do |id, user_id|
116
+ id.should == 1234
117
+ user_id.should == 3
118
+ end.track('abc')
119
+ end
120
+
121
+ it 'should call the on_limit if specified' do
122
+ limit = '{ "limit": { "track": 1234 } }'
123
+ @stream.should_receive(:each_item).and_yield(limit)
124
+ @client.on_limit do |track|
125
+ track.should == 1234
126
+ end.track('abc')
76
127
  end
77
- @yielded.should be_true
78
128
  end
79
129
 
80
- it 'should wrap Yajl errors in TweetStream errors' do
81
- Yajl::HttpStream.should_receive(:new).and_return(y = mock("Yajl::HttpStream"))
82
- y.should_receive(:get).once.with(URI.parse('http://abc:def@stream.twitter.com/1/cool.json'), :symbolize_keys => true).and_raise(Yajl::HttpStream::InvalidContentType)
83
- lambda{@client.start('cool')}.should raise_error(TweetStream::ConnectionError)
130
+ describe '#on_error' do
131
+ it 'should pass the message on to the error block' do
132
+ @stream.should_receive(:on_error).and_yield('Uh oh')
133
+ @client.on_error do |m|
134
+ m.should == 'Uh oh'
135
+ end.track('abc')
136
+ end
84
137
  end
85
138
 
86
- {
87
- :delete => {:delete => {:status => {:id => 1234, :user_id => 3}}},
88
- :limit => {:limit => {:track => 1234}}
89
- }.each_pair do |special_method, special_object|
90
- it "should make a call to the #{special_method} proc if a #{special_method} object is given" do
91
- @called = false
92
- @proc = Proc.new{|*args| @called = true }
93
- @client.send("on_#{special_method}", &@proc)
94
- Yajl::HttpStream.should_receive(:new).and_return(y = mock("Yajl::HttpStream"))
95
- y.should_receive(:post).once.with(URI.parse('http://abc:def@stream.twitter.com/1/statuses/filter.json'), "track=musicmonday", :symbolize_keys => true).and_yield(special_object)
96
- @client.track('musicmonday')
97
- @called.should == true
98
- end
99
-
100
- it "should accept a proc on a :#{special_method} option if a #{special_method} object is given" do
101
- @called = false
102
- @proc = Proc.new{|*args| @called = true }
103
- Yajl::HttpStream.should_receive(:new).and_return(y = mock("Yajl::HttpStream"))
104
- y.should_receive(:post).once.with(URI.parse('http://abc:def@stream.twitter.com/1/statuses/filter.json'), "track=musicmonday", :symbolize_keys => true).and_yield(special_object)
105
- @client.track('musicmonday', special_method => @proc)
106
- @called.should == true
139
+ describe '#on_max_reconnects' do
140
+ it 'should raise a ReconnectError' do
141
+ @stream.should_receive(:on_max_reconnects).and_yield(30, 20)
142
+ lambda{@client.track('abc')}.should raise_error(TweetStream::ReconnectError) do |e|
143
+ e.timeout.should == 30
144
+ e.retries.should == 20
145
+ end
107
146
  end
108
147
  end
109
148
  end
@@ -170,34 +209,18 @@ describe TweetStream::Client do
170
209
  @client.track('rock')
171
210
  end
172
211
  end
173
-
174
- describe '.stop' do
175
- it 'should raise a TweetStream::Terminated error' do
176
- lambda{ TweetStream::Client.stop }.should raise_error(TweetStream::Terminated)
177
- end
178
-
179
- it 'should not cause a TweetStream to crash with a real exception' do
180
- @client = TweetStream::Client.new('abc','def')
181
- @statuses = []
182
- Yajl::HttpStream.should_receive(:new).and_return(y = mock("Yajl::HttpStream"))
183
- y.should_receive(:post).once.with(URI.parse('http://abc:def@stream.twitter.com/1/statuses/filter.json'), 'track=musicmonday', :symbolize_keys => true).and_yield(sample_tweets[0])
184
- @client.track('musicmonday') do |status|
185
- @statuses << status
186
- TweetStream::Client.stop
187
- end.should == @statuses.first
188
- @statuses.size.should == 1
189
- end
190
- end
191
212
 
192
213
  describe 'instance .stop' do
193
- it 'should stop to receive the stream' do
194
- @client = TweetStream::Client.new('abc','def')
195
- Yajl::HttpStream.should_receive(:new).and_return(y = mock('Yajl::HttpStream'))
196
- y.should_receive(:post)
197
- y.should_receive(:terminate).once
198
-
199
- @client.follow('10')
200
- @client.stop
214
+ it 'should call EventMachine::stop_event_loop' do
215
+ EventMachine.should_receive :stop_event_loop
216
+ TweetStream::Client.new('test','fake').stop.should be_nil
217
+ end
218
+
219
+ it 'should return the last status yielded' do
220
+ EventMachine.should_receive :stop_event_loop
221
+ client = TweetStream::Client.new('test','fake')
222
+ client.send(:instance_variable_set, :@last_status, {})
223
+ client.stop.should == {}
201
224
  end
202
225
  end
203
226
  end
@@ -0,0 +1,39 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe 'TweetStream JSON Parsers' do
4
+ it 'should default to the JSON Gem' do
5
+ TweetStream::Client.new('test','fake').parser.should == TweetStream::Parsers::JsonGem
6
+ end
7
+
8
+ [:json_gem, :yajl, :active_support, :json_pure].each do |engine|
9
+ describe "#{engine} parsing" do
10
+ before do
11
+ @client = TweetStream::Client.new('test','fake',engine)
12
+ @class_name = "TweetStream::Parsers::#{engine.to_s.split('_').map(&:capitalize).join('')}"
13
+ end
14
+
15
+ it 'should set the parser to the appropriate class' do
16
+ @client.parser.to_s == @class_name
17
+ end
18
+
19
+ it 'should be settable via client.parser=' do
20
+ @client.parser = nil
21
+ @client.parser.should be_nil
22
+ @client.parser = engine
23
+ @client.parser.to_s.should == @class_name
24
+ end
25
+ end
26
+ end
27
+
28
+ class FakeParser
29
+ def self.decode(text)
30
+ {}
31
+ end
32
+ end
33
+
34
+ it 'should be settable to a class' do
35
+ @client = TweetStream::Client.new('abc','def')
36
+ @client.parser = FakeParser
37
+ @client.parser.should == FakeParser
38
+ end
39
+ end
@@ -6,4 +6,10 @@ describe TweetStream::Status do
6
6
  @status.user.is_a?(TweetStream::User).should be_true
7
7
  @status.user.screen_name.should == 'bob'
8
8
  end
9
+
10
+ it 'should override the #id method for itself and the user' do
11
+ @status = TweetStream::Status.new(:id => 123, :user => {:id => 345})
12
+ @status.id.should == 123
13
+ @status.user.id.should == 345
14
+ end
9
15
  end
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: 0.3.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michael Bleigh
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-12-03 00:00:00 -05:00
12
+ date: 2010-01-25 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -23,14 +23,14 @@ dependencies:
23
23
  version: "0"
24
24
  version:
25
25
  - !ruby/object:Gem::Dependency
26
- name: yajl-ruby
26
+ name: twitter-stream
27
27
  type: :runtime
28
28
  version_requirement:
29
29
  version_requirements: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: 0.6.6
33
+ version: "0"
34
34
  version:
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: daemons
@@ -54,12 +54,18 @@ extra_rdoc_files:
54
54
  files:
55
55
  - LICENSE
56
56
  - README.rdoc
57
+ - RELEASE_NOTES.rdoc
57
58
  - Rakefile
58
59
  - VERSION
60
+ - examples/growl_daemon.rb
59
61
  - lib/tweetstream.rb
60
62
  - lib/tweetstream/client.rb
61
63
  - lib/tweetstream/daemon.rb
62
64
  - lib/tweetstream/hash.rb
65
+ - lib/tweetstream/parsers/active_support.rb
66
+ - lib/tweetstream/parsers/json_gem.rb
67
+ - lib/tweetstream/parsers/json_pure.rb
68
+ - lib/tweetstream/parsers/yajl.rb
63
69
  - lib/tweetstream/status.rb
64
70
  - lib/tweetstream/user.rb
65
71
  - spec/data/statuses.json
@@ -67,6 +73,7 @@ files:
67
73
  - spec/spec_helper.rb
68
74
  - spec/tweetstream/client_spec.rb
69
75
  - spec/tweetstream/hash_spec.rb
76
+ - spec/tweetstream/parser_spec.rb
70
77
  - spec/tweetstream/status_spec.rb
71
78
  - spec/tweetstream_spec.rb
72
79
  has_rdoc: true
@@ -101,5 +108,7 @@ test_files:
101
108
  - spec/spec_helper.rb
102
109
  - spec/tweetstream/client_spec.rb
103
110
  - spec/tweetstream/hash_spec.rb
111
+ - spec/tweetstream/parser_spec.rb
104
112
  - spec/tweetstream/status_spec.rb
105
113
  - spec/tweetstream_spec.rb
114
+ - examples/growl_daemon.rb