tweetstream 1.0.5 → 1.1.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

@@ -0,0 +1,82 @@
1
+ require 'multi_json'
2
+ require 'tweetstream/version'
3
+
4
+ module TweetStream
5
+ # Defines constants and methods related to configuration
6
+ module Configuration
7
+ # An array of valid keys in the options hash when configuring TweetStream.
8
+ VALID_OPTIONS_KEYS = [
9
+ :parser,
10
+ :username,
11
+ :password,
12
+ :user_agent,
13
+ :auth_method,
14
+ :consumer_key,
15
+ :consumer_secret,
16
+ :oauth_token,
17
+ :oauth_token_secret].freeze
18
+
19
+ # The parser that will be used to connect if none is set
20
+ DEFAULT_PARSER = MultiJson.default_engine
21
+
22
+ # By default, don't set a username
23
+ DEFAULT_USERNAME = nil
24
+
25
+ # By default, don't set a password
26
+ DEFAULT_PASSWORD = nil
27
+
28
+ # The user agent that will be sent to the API endpoint if none is set
29
+ DEFAULT_USER_AGENT = "TweetStream Ruby Gem #{TweetStream::VERSION}".freeze
30
+
31
+ # The default authentication method
32
+ DEFAULT_AUTH_METHOD = :oauth
33
+
34
+ VALID_FORMATS = [
35
+ :basic,
36
+ :oauth].freeze
37
+
38
+ # By default, don't set an application key
39
+ DEFAULT_CONSUMER_KEY = nil
40
+
41
+ # By default, don't set an application secret
42
+ DEFAULT_CONSUMER_SECRET = nil
43
+
44
+ # By default, don't set a user oauth token
45
+ DEFAULT_OAUTH_TOKEN = nil
46
+
47
+ # By default, don't set a user oauth secret
48
+ DEFAULT_OAUTH_TOKEN_SECRET = nil
49
+
50
+ # @private
51
+ attr_accessor *VALID_OPTIONS_KEYS
52
+
53
+ # When this module is extended, set all configuration options to their default values
54
+ def self.extended(base)
55
+ base.reset
56
+ end
57
+
58
+ # Convenience method to allow configuration options to be set in a block
59
+ def configure
60
+ yield self
61
+ end
62
+
63
+ # Create a hash of options and their values
64
+ def options
65
+ Hash[VALID_OPTIONS_KEYS.map {|key| [key, send(key)] }]
66
+ end
67
+
68
+ # Reset all configuration options to defaults
69
+ def reset
70
+ self.parser = DEFAULT_PARSER
71
+ self.username = DEFAULT_USERNAME
72
+ self.password = DEFAULT_PASSWORD
73
+ self.user_agent = DEFAULT_USER_AGENT
74
+ self.auth_method = DEFAULT_AUTH_METHOD
75
+ self.consumer_key = DEFAULT_CONSUMER_KEY
76
+ self.consumer_secret = DEFAULT_CONSUMER_SECRET
77
+ self.oauth_token = DEFAULT_OAUTH_TOKEN
78
+ self.oauth_token_secret = DEFAULT_OAUTH_TOKEN_SECRET
79
+ self
80
+ end
81
+ end
82
+ end
@@ -30,10 +30,14 @@ class TweetStream::Daemon < TweetStream::Client
30
30
  @app_name = app_name
31
31
  super(user, pass, parser)
32
32
  end
33
-
33
+
34
34
  def start(path, query_parameters = {}, &block) #:nodoc:
35
+ # Because of a change in Ruvy 1.8.7 patchlevel 249, you cannot call anymore
36
+ # super inside a block. So I assign to a variable the base class method before
37
+ # the Daemons block begins.
38
+ startmethod = super.start
35
39
  Daemons.run_proc(@app_name || 'tweetstream', :multiple => true) do
36
- super(path, query_parameters, &block)
40
+ startmethod(path, query_parameters, &block)
37
41
  end
38
42
  end
39
43
  end
@@ -0,0 +1,3 @@
1
+ module TweetStream
2
+ VERSION = '1.1.0.rc1'
3
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,10 +1,15 @@
1
+ require 'simplecov'
2
+ SimpleCov.start do
3
+ add_group 'Tweetstream', 'lib/tweetstream'
4
+ add_group 'Specs', 'spec'
5
+ end
6
+
1
7
  $LOAD_PATH.unshift(File.dirname(__FILE__))
2
8
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
9
 
4
- require 'rubygems'
5
10
  require 'tweetstream'
6
- require 'spec'
7
- require 'spec/autorun'
11
+ require 'rspec'
12
+ require 'rspec/autorun'
8
13
  require 'yajl'
9
14
  require 'json'
10
15
 
@@ -19,7 +24,3 @@ def sample_tweets
19
24
  @tweets
20
25
  end
21
26
  end
22
-
23
- Spec::Runner.configure do |config|
24
-
25
- end
@@ -1,17 +1,16 @@
1
1
  require File.dirname(__FILE__) + '/../spec_helper'
2
2
 
3
3
  describe TweetStream::Client do
4
- it 'should set the username and password from the initializers' do
5
- @client = TweetStream::Client.new('abc','def')
6
- @client.username.should == 'abc'
7
- @client.password.should == 'def'
4
+ before(:each) do
5
+ TweetStream.configure do |config|
6
+ config.username = 'abc'
7
+ config.password = 'def'
8
+ config.auth_method = :basic
9
+ end
10
+ @client = TweetStream::Client.new
8
11
  end
9
12
 
10
13
  describe '#build_uri' do
11
- before do
12
- @client = TweetStream::Client.new('abc','def')
13
- end
14
-
15
14
  it 'should return a URI' do
16
15
  @client.send(:build_uri, '').is_a?(URI).should be_true
17
16
  end
@@ -26,10 +25,6 @@ describe TweetStream::Client do
26
25
  end
27
26
 
28
27
  describe '#build_post_body' do
29
- before do
30
- @client = TweetStream::Client.new('abc','def')
31
- end
32
-
33
28
  it 'should return a blank string if passed a nil value' do
34
29
  @client.send(:build_post_body, nil).should == ''
35
30
  end
@@ -53,71 +48,73 @@ describe TweetStream::Client do
53
48
 
54
49
  describe '#start' do
55
50
  before do
56
- @stream = stub("Twitter::JSONStream",
51
+ @stream = stub("Twitter::JSONStream",
57
52
  :connect => true,
58
- :unbind => true,
59
- :each_item => true,
53
+ :unbind => true,
54
+ :each_item => true,
60
55
  :on_error => true,
61
56
  :on_max_reconnects => true,
62
57
  :connection_completed => true
63
58
  )
64
59
  EM.stub!(:run).and_yield
65
60
  Twitter::JSONStream.stub!(:connect).and_return(@stream)
66
- @client = TweetStream::Client.new('abc','def')
67
61
  end
68
-
69
- it 'should try to connect via a JSON stream' do
62
+
63
+ it 'should try to connect via a JSON stream with basic auth' do
70
64
  Twitter::JSONStream.should_receive(:connect).with(
71
- :auth => 'abc:def',
72
- :content => 'track=monday',
73
65
  :path => URI.parse('/1/statuses/filter.json'),
74
66
  :method => 'POST',
75
- :user_agent => 'TweetStream'
67
+ :user_agent => TweetStream::Configuration::DEFAULT_USER_AGENT,
68
+ :on_inited => nil,
69
+ :filters => 'monday',
70
+ :params => {},
71
+ :ssl => true,
72
+ :auth => 'abc:def'
76
73
  ).and_return(@stream)
77
-
74
+
78
75
  @client.track('monday')
79
76
  end
80
-
77
+
81
78
  describe '#each_item' do
82
79
  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({})
80
+ @client = TweetStream::Client.new
81
+ MultiJson.should_receive(:decode).and_return({})
85
82
  @stream.should_receive(:each_item).and_yield(sample_tweets[0].to_json)
86
83
  @client.track('abc','def')
87
84
  end
88
-
85
+
89
86
  it 'should yield a TweetStream::Status' do
90
87
  @stream.should_receive(:each_item).and_yield(sample_tweets[0].to_json)
91
88
  @client.track('abc'){|s| s.should be_kind_of(TweetStream::Status)}
92
89
  end
93
-
90
+
94
91
  it 'should also yield the client if a block with arity 2 is given' do
95
92
  @stream.should_receive(:each_item).and_yield(sample_tweets[0].to_json)
96
93
  @client.track('abc'){|s,c| c.should == @client}
97
94
  end
98
-
95
+
99
96
  it 'should include the proper values' do
100
97
  tweet = sample_tweets[0]
101
98
  tweet[:id] = 123
102
99
  tweet[:user][:screen_name] = 'monkey'
103
100
  tweet[:text] = "Oo oo aa aa"
104
101
  @stream.should_receive(:each_item).and_yield(tweet.to_json)
105
- @client.track('abc') do |s|
102
+ @client.track('abc') do |s|
106
103
  s[:id].should == 123
107
104
  s.user.screen_name.should == 'monkey'
108
105
  s.text.should == 'Oo oo aa aa'
109
106
  end
110
107
  end
111
-
108
+
112
109
  it 'should call the on_delete if specified' do
113
110
  delete = '{ "delete": { "status": { "id": 1234, "user_id": 3 } } }'
114
111
  @stream.should_receive(:each_item).and_yield(delete)
115
- @client.on_delete do |id, user_id|
112
+ @client.on_delete do |id, user_id|
116
113
  id.should == 1234
117
114
  user_id.should == 3
118
115
  end.track('abc')
119
116
  end
120
-
117
+
121
118
  it 'should call the on_limit if specified' do
122
119
  limit = '{ "limit": { "track": 1234 } }'
123
120
  @stream.should_receive(:each_item).and_yield(limit)
@@ -125,15 +122,22 @@ describe TweetStream::Client do
125
122
  track.should == 1234
126
123
  end.track('abc')
127
124
  end
128
-
125
+
129
126
  it 'should call on_error if a non-hash response is received' do
130
127
  @stream.should_receive(:each_item).and_yield('["favorited"]')
131
128
  @client.on_error do |message|
132
129
  message.should == 'Unexpected JSON object in stream: ["favorited"]'
133
130
  end.track('abc')
134
131
  end
132
+
133
+ it 'should call on_error if a json parse error occurs' do
134
+ @stream.should_receive(:each_item).and_yield("{'a_key':}")
135
+ @client.on_error do |message|
136
+ message.should == "MultiJson::DecodeError occured in stream: {'a_key':}"
137
+ end.track('abc')
138
+ end
135
139
  end
136
-
140
+
137
141
  describe '#on_error' do
138
142
  it 'should pass the message on to the error block' do
139
143
  @stream.should_receive(:on_error).and_yield('Uh oh')
@@ -142,7 +146,7 @@ describe TweetStream::Client do
142
146
  end.track('abc')
143
147
  end
144
148
  end
145
-
149
+
146
150
  describe '#on_max_reconnects' do
147
151
  it 'should raise a ReconnectError' do
148
152
  @stream.should_receive(:on_max_reconnects).and_yield(30, 20)
@@ -153,51 +157,52 @@ describe TweetStream::Client do
153
157
  end
154
158
  end
155
159
  end
156
-
160
+
157
161
  describe ' API methods' do
158
- before do
159
- @client = TweetStream::Client.new('abc','def')
160
- end
161
-
162
162
  %w(firehose retweet sample).each do |method|
163
163
  it "##{method} should make a call to start with \"statuses/#{method}\"" do
164
164
  @client.should_receive(:start).once.with('statuses/' + method, {})
165
165
  @client.send(method)
166
166
  end
167
167
  end
168
-
168
+
169
169
  it '#track should make a call to start with "statuses/filter" and a track query parameter' do
170
- @client.should_receive(:start).once.with('statuses/filter', :track => 'test', :method => :post)
170
+ @client.should_receive(:start).once.with('statuses/filter', :track => ['test'], :method => :post)
171
171
  @client.track('test')
172
172
  end
173
-
173
+
174
174
  it '#track should comma-join multiple arguments' do
175
- @client.should_receive(:start).once.with('statuses/filter', :track => 'foo,bar,baz', :method => :post)
175
+ @client.should_receive(:start).once.with('statuses/filter', :track => ['foo', 'bar', 'baz'], :method => :post)
176
176
  @client.track('foo', 'bar', 'baz')
177
177
  end
178
-
178
+
179
+ it '#track should comma-join an array of arguments' do
180
+ @client.should_receive(:start).once.with('statuses/filter', :track => [['foo','bar','baz']], :method => :post)
181
+ @client.track(['foo','bar','baz'])
182
+ end
183
+
179
184
  it '#follow should make a call to start with "statuses/filter" and a follow query parameter' do
180
- @client.should_receive(:start).once.with('statuses/filter', :follow => '123', :method => :post)
185
+ @client.should_receive(:start).once.with('statuses/filter', :follow => [123], :method => :post)
181
186
  @client.follow(123)
182
187
  end
183
-
188
+
184
189
  it '#follow should comma-join multiple arguments' do
185
- @client.should_receive(:start).once.with('statuses/filter', :follow => '123,456', :method => :post)
190
+ @client.should_receive(:start).once.with('statuses/filter', :follow => [123,456], :method => :post)
186
191
  @client.follow(123, 456)
187
192
  end
188
-
193
+
189
194
  it '#filter should make a call to "statuses/filter" with the query params provided' do
190
- @client.should_receive(:start).once.with('statuses/filter', :follow => '123', :method => :post)
195
+ @client.should_receive(:start).once.with('statuses/filter', :follow => 123, :method => :post)
191
196
  @client.filter(:follow => 123)
192
197
  end
198
+ it '#filter should make a call to "statuses/filter" with the query params provided longitude/latitude pairs, separated by commas ' do
199
+ @client.should_receive(:start).once.with('statuses/filter', :locations => '-122.75,36.8,-121.75,37.8,-74,40,-73,41', :method => :post)
200
+ @client.filter(:locations => '-122.75,36.8,-121.75,37.8,-74,40,-73,41')
201
+ end
193
202
  end
194
-
195
- %w(on_delete on_limit).each do |proc_setter|
203
+
204
+ %w(on_delete on_limit on_inited).each do |proc_setter|
196
205
  describe "##{proc_setter}" do
197
- before do
198
- @client = TweetStream::Client.new('abc','def')
199
- end
200
-
201
206
  it 'should set when a block is given' do
202
207
  proc = Proc.new{|a,b| puts a }
203
208
  @client.send(proc_setter, &proc)
@@ -207,27 +212,82 @@ describe TweetStream::Client do
207
212
  end
208
213
 
209
214
  describe '#track' do
210
- before do
211
- @client = TweetStream::Client.new('abc','def')
212
- end
213
-
214
215
  it 'should call #start with "statuses/filter" and the provided queries' do
215
- @client.should_receive(:start).once.with('statuses/filter', :track => 'rock', :method => :post)
216
+ @client.should_receive(:start).once.with('statuses/filter', :track => ['rock'], :method => :post)
216
217
  @client.track('rock')
217
218
  end
218
219
  end
219
220
 
221
+ describe '#locations' do
222
+ it 'should call #start with "statuses/filter" with the query params provided longitude/latitude pairs' do
223
+ @client.should_receive(:start).once.with('statuses/filter', :locations => ['-122.75,36.8,-121.75,37.8,-74,40,-73,41'], :method => :post)
224
+ @client.locations('-122.75,36.8,-121.75,37.8,-74,40,-73,41')
225
+ end
226
+
227
+ it 'should call #start with "statuses/filter" with the query params provided longitude/latitude pairs and additional filter' do
228
+ @client.should_receive(:start).once.with('statuses/filter', :locations => ['-122.75,36.8,-121.75,37.8,-74,40,-73,41'], :track => 'rock', :method => :post)
229
+ @client.locations('-122.75,36.8,-121.75,37.8,-74,40,-73,41', :track => 'rock')
230
+ end
231
+ end
232
+
220
233
  describe 'instance .stop' do
221
234
  it 'should call EventMachine::stop_event_loop' do
222
235
  EventMachine.should_receive :stop_event_loop
223
- TweetStream::Client.new('test','fake').stop.should be_nil
236
+ TweetStream::Client.new.stop.should be_nil
224
237
  end
225
-
238
+
226
239
  it 'should return the last status yielded' do
227
240
  EventMachine.should_receive :stop_event_loop
228
- client = TweetStream::Client.new('test','fake')
241
+ client = TweetStream::Client.new
229
242
  client.send(:instance_variable_set, :@last_status, {})
230
243
  client.stop.should == {}
231
244
  end
232
245
  end
246
+
247
+ describe "oauth" do
248
+ describe '#start' do
249
+ before do
250
+ TweetStream.configure do |config|
251
+ config.consumer_key = '123456789'
252
+ config.consumer_secret = 'abcdefghijklmnopqrstuvwxyz'
253
+ config.oauth_token = '123456789'
254
+ config.oauth_token_secret = 'abcdefghijklmnopqrstuvwxyz'
255
+ config.auth_method = :oauth
256
+ end
257
+ @client = TweetStream::Client.new
258
+
259
+ @stream = stub("Twitter::JSONStream",
260
+ :connect => true,
261
+ :unbind => true,
262
+ :each_item => true,
263
+ :on_error => true,
264
+ :on_max_reconnects => true,
265
+ :connection_completed => true
266
+ )
267
+ EM.stub!(:run).and_yield
268
+ Twitter::JSONStream.stub!(:connect).and_return(@stream)
269
+ end
270
+
271
+ it 'should try to connect via a JSON stream with oauth' do
272
+ Twitter::JSONStream.should_receive(:connect).with(
273
+ :path => URI.parse('/1/statuses/filter.json'),
274
+ :method => 'POST',
275
+ :user_agent => TweetStream::Configuration::DEFAULT_USER_AGENT,
276
+ :on_inited => nil,
277
+ :filters => 'monday',
278
+ :params => {},
279
+ :ssl => true,
280
+ :oauth => {
281
+ :consumer_key => '123456789',
282
+ :consumer_secret => 'abcdefghijklmnopqrstuvwxyz',
283
+ :access_key => '123456789',
284
+ :access_secret => 'abcdefghijklmnopqrstuvwxyz'
285
+ }
286
+ ).and_return(@stream)
287
+
288
+ @client.track('monday')
289
+ end
290
+ end
291
+ end
292
+
233
293
  end