jls-tweetstream 2.5.0

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.
@@ -0,0 +1,18 @@
1
+ {
2
+ "info":
3
+ {
4
+ "users":
5
+ [
6
+ {
7
+ "id":119476949,
8
+ "name":"oauth_dancer",
9
+ "dm":false
10
+ }
11
+ ],
12
+ "delimited":"none",
13
+ "include_followings_activity":false,
14
+ "include_user_changes":false,
15
+ "replies":"none",
16
+ "with":"user"
17
+ }
18
+ }
@@ -0,0 +1,5 @@
1
+ {
2
+ "limit": {
3
+ "track": 1234
4
+ }
5
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "scrub_geo": {
3
+ "user_id": 1234,
4
+ "user_id_str": "1234",
5
+ "up_to_status_id":9876,
6
+ "up_to_status_id_string": "9876"
7
+ }
8
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "warning":{
3
+ "code":"FALLING_BEHIND",
4
+ "message":"Your connection is falling behind and messages are being queued for delivery to you. Your queue is now over 60% full. You will be disconnected when the queue is full.",
5
+ "percent_full": 60
6
+ }
7
+ }
@@ -0,0 +1,7 @@
1
+ {
2
+ "status_withheld":{
3
+ "id":1234567890,
4
+ "user_id":123456,
5
+ "withheld_in_countries":["de", "ar"]
6
+ }
7
+ }
@@ -0,0 +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":"<a href=\"http://www.iLike.com\" rel=\"nofollow\">iLike</a>","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":"<a href=\"http://twitterhelp.blogspot.com/2008/05/twitter-via-mobile-web-mtwittercom.html\" rel=\"nofollow\">mobile web</a>","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}
@@ -0,0 +1,6 @@
1
+ {
2
+ "user_withheld":{
3
+ "id":123456,
4
+ "withheld_in_countries":["de","ar"]
5
+ }
6
+ }
@@ -0,0 +1,58 @@
1
+ require 'simplecov'
2
+ require 'coveralls'
3
+
4
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
5
+ SimpleCov::Formatter::HTMLFormatter,
6
+ Coveralls::SimpleCov::Formatter
7
+ ]
8
+ SimpleCov.start
9
+
10
+ require 'tweetstream'
11
+ require 'tweetstream/site_stream_client'
12
+ require 'json'
13
+ require 'rspec'
14
+ require 'webmock/rspec'
15
+ require 'yajl'
16
+
17
+ WebMock.disable_net_connect!(:allow => 'coveralls.io')
18
+
19
+ RSpec.configure do |config|
20
+ config.expect_with :rspec do |c|
21
+ c.syntax = :expect
22
+ end
23
+
24
+ config.after(:each) do
25
+ TweetStream.reset
26
+ end
27
+ end
28
+
29
+ def samples(fixture)
30
+ samples = []
31
+ Yajl::Parser.parse(fixture(fixture), :symbolize_keys => true) do |hash|
32
+ samples << hash
33
+ end
34
+ samples
35
+ end
36
+
37
+ def sample_tweets
38
+ return @tweets if @tweets
39
+ @tweets = samples('statuses.json')
40
+ end
41
+
42
+ def sample_direct_messages
43
+ return @direct_messages if @direct_messages
44
+ @direct_messages = samples('direct_messages.json')
45
+ end
46
+
47
+ def fixture_path
48
+ File.expand_path("../fixtures", __FILE__)
49
+ end
50
+
51
+ def fixture(file)
52
+ File.new(fixture_path + '/' + file)
53
+ end
54
+
55
+ FakeHttp = Class.new do
56
+ def callback; end
57
+ def errback; end
58
+ end
@@ -0,0 +1,83 @@
1
+ require 'helper'
2
+
3
+ describe TweetStream::Client do
4
+ before do
5
+ @stream = stub("EM::Twitter::Client",
6
+ :connect => true,
7
+ :unbind => true,
8
+ :each => true,
9
+ :on_error => true,
10
+ :on_max_reconnects => true,
11
+ :on_reconnect => true,
12
+ :connection_completed => true,
13
+ :on_no_data_received => true,
14
+ :on_unauthorized => true,
15
+ :on_enhance_your_calm => true
16
+ )
17
+ EM.stub!(:run).and_yield
18
+ EM::Twitter::Client.stub!(:connect).and_return(@stream)
19
+ end
20
+
21
+ describe "basic auth" do
22
+ before do
23
+ TweetStream.configure do |config|
24
+ config.username = 'tweetstream'
25
+ config.password = 'rubygem'
26
+ config.auth_method = :basic
27
+ end
28
+
29
+ @client = TweetStream::Client.new
30
+ end
31
+
32
+ it "tries to connect via a JSON stream with basic auth" do
33
+ EM::Twitter::Client.should_receive(:connect).with(
34
+ :path => '/1.1/statuses/filter.json',
35
+ :method => 'POST',
36
+ :user_agent => TweetStream::Configuration::DEFAULT_USER_AGENT,
37
+ :on_inited => nil,
38
+ :params => {:track => 'monday'},
39
+ :basic => {
40
+ :username => 'tweetstream',
41
+ :password => 'rubygem'
42
+ },
43
+ :proxy => nil
44
+ ).and_return(@stream)
45
+
46
+ @client.track('monday')
47
+ end
48
+ end
49
+
50
+ describe "oauth" do
51
+ before do
52
+ TweetStream.configure do |config|
53
+ config.consumer_key = '123456789'
54
+ config.consumer_secret = 'abcdefghijklmnopqrstuvwxyz'
55
+ config.oauth_token = '123456789'
56
+ config.oauth_token_secret = 'abcdefghijklmnopqrstuvwxyz'
57
+ config.auth_method = :oauth
58
+ end
59
+
60
+ @client = TweetStream::Client.new
61
+ end
62
+
63
+ it "tries to connect via a JSON stream with oauth" do
64
+ EM::Twitter::Client.should_receive(:connect).with(
65
+ :path => '/1.1/statuses/filter.json',
66
+ :method => 'POST',
67
+ :user_agent => TweetStream::Configuration::DEFAULT_USER_AGENT,
68
+ :on_inited => nil,
69
+ :params => {:track => 'monday'},
70
+ :oauth => {
71
+ :consumer_key => '123456789',
72
+ :consumer_secret => 'abcdefghijklmnopqrstuvwxyz',
73
+ :token => '123456789',
74
+ :token_secret => 'abcdefghijklmnopqrstuvwxyz'
75
+ },
76
+ :proxy => nil
77
+ ).and_return(@stream)
78
+
79
+ @client.track('monday')
80
+ end
81
+ end
82
+
83
+ end
@@ -0,0 +1,145 @@
1
+ require 'helper'
2
+
3
+ describe TweetStream::Client do
4
+ before(:each) do
5
+ TweetStream.configure do |config|
6
+ config.consumer_key = 'abc'
7
+ config.consumer_secret = 'def'
8
+ config.oauth_token = '123'
9
+ config.oauth_token_secret = '456'
10
+ end
11
+ @client = TweetStream::Client.new
12
+
13
+ @stream = stub("EM::Twitter::Client",
14
+ :connect => true,
15
+ :unbind => true,
16
+ :each => true,
17
+ :on_error => true,
18
+ :on_max_reconnects => true,
19
+ :on_reconnect => true,
20
+ :connection_completed => true,
21
+ :on_no_data_received => true,
22
+ :on_unauthorized => true,
23
+ :on_enhance_your_calm => true
24
+ )
25
+ EM.stub!(:run).and_yield
26
+ EM::Twitter::Client.stub!(:connect).and_return(@stream)
27
+ end
28
+
29
+ describe "Site Stream support" do
30
+ context "when calling #sitestream" do
31
+ it "sends the sitestream host" do
32
+ EM::Twitter::Client.should_receive(:connect).with(hash_including(:host => "sitestream.twitter.com")).and_return(@stream)
33
+ @client.sitestream
34
+ end
35
+
36
+ it "uses the sitestream uri" do
37
+ @client.should_receive(:start).once.with('/1.1/site.json', an_instance_of(Hash)).and_return(@stream)
38
+ @client.sitestream
39
+ end
40
+
41
+ it "supports :followings => true" do
42
+ @client.should_receive(:start).once.with('/1.1/site.json', hash_including(:with => 'followings')).and_return(@stream)
43
+ @client.sitestream(['115192457'], :followings => true)
44
+ end
45
+
46
+ it "supports :with => 'followings'" do
47
+ @client.should_receive(:start).once.with('/1.1/site.json', hash_including(:with => 'followings')).and_return(@stream)
48
+ @client.sitestream(['115192457'], :with => 'followings')
49
+ end
50
+
51
+ it "supports :with => 'user'" do
52
+ @client.should_receive(:start).once.with('/1.1/site.json', hash_including(:with => 'user')).and_return(@stream)
53
+ @client.sitestream(['115192457'], :with => 'user')
54
+ end
55
+
56
+ it "supports :replies => 'all'" do
57
+ @client.should_receive(:start).once.with('/1.1/site.json', hash_including(:replies => 'all')).and_return(@stream)
58
+ @client.sitestream(['115192457'], :replies => 'all')
59
+ end
60
+
61
+ describe "control management" do
62
+ before do
63
+ @control_response = {"control" =>
64
+ {
65
+ "control_uri" =>"/1.1/site/c/01_225167_334389048B872A533002B34D73F8C29FD09EFC50"
66
+ }
67
+ }
68
+ end
69
+ it "assigns the control_uri" do
70
+ @stream.should_receive(:each).and_yield(@control_response.to_json)
71
+ @client.sitestream
72
+
73
+ expect(@client.control_uri).to eq("/1.1/site/c/01_225167_334389048B872A533002B34D73F8C29FD09EFC50")
74
+ end
75
+
76
+ it "instantiates a SiteStreamClient" do
77
+ @stream.should_receive(:each).and_yield(@control_response.to_json)
78
+ @client.sitestream
79
+
80
+ expect(@client.control).to be_kind_of(TweetStream::SiteStreamClient)
81
+ end
82
+
83
+ it "passes the client's on_error to the SiteStreamClient" do
84
+ called = false
85
+ @client.on_error { |err| called = true }
86
+ @stream.should_receive(:each).and_yield(@control_response.to_json)
87
+ @client.sitestream
88
+
89
+ @client.control.on_error.call
90
+
91
+ expect(called).to be_true
92
+ end
93
+ end
94
+
95
+ describe "data handling" do
96
+ context "messages" do
97
+ before do
98
+ @ss_message = {'for_user' => '12345', 'message' => {'id' => 123, 'user' => {'screen_name' => 'monkey'}, 'text' => 'Oo oo aa aa'}}
99
+ end
100
+
101
+ it "yields a site stream message" do
102
+ @stream.should_receive(:each).and_yield(@ss_message.to_json)
103
+ yielded_status = nil
104
+ @client.sitestream do |message|
105
+ yielded_status = message
106
+ end
107
+ expect(yielded_status).not_to be_nil
108
+ expect(yielded_status[:for_user]).to eq('12345')
109
+ expect(yielded_status[:message][:user][:screen_name]).to eq('monkey')
110
+ expect(yielded_status[:message][:text]).to eq('Oo oo aa aa')
111
+ end
112
+ it "yields itself if block has an arity of 2" do
113
+ @stream.should_receive(:each).and_yield(@ss_message.to_json)
114
+ yielded_client = nil
115
+ @client.sitestream do |_, client|
116
+ yielded_client = client
117
+ end
118
+ expect(yielded_client).not_to be_nil
119
+ expect(yielded_client).to eq(@client)
120
+ end
121
+ end
122
+
123
+ context "friends list" do
124
+ before do
125
+ @friends_list = { 'friends' => [123, 456] }
126
+ end
127
+
128
+ it "yields a friends list array" do
129
+ @stream.should_receive(:each).and_yield(@friends_list.to_json)
130
+ yielded_list = nil
131
+ @client.on_friends do |friends|
132
+ yielded_list = friends
133
+ end
134
+ @client.sitestream
135
+
136
+ expect(yielded_list).not_to be_nil
137
+ expect(yielded_list).to be_an(Array)
138
+ expect(yielded_list.first).to eq(123)
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ end
@@ -0,0 +1,391 @@
1
+ require 'helper'
2
+
3
+ describe TweetStream::Client do
4
+ before(:each) do
5
+ TweetStream.configure do |config|
6
+ config.consumer_key = 'abc'
7
+ config.consumer_secret = 'def'
8
+ config.oauth_token = '123'
9
+ config.oauth_token_secret = '456'
10
+ end
11
+ @client = TweetStream::Client.new
12
+ end
13
+
14
+ describe "#start" do
15
+ before do
16
+ @stream = stub("EM::Twitter::Client",
17
+ :connect => true,
18
+ :unbind => true,
19
+ :each => true,
20
+ :on_error => true,
21
+ :on_max_reconnects => true,
22
+ :on_reconnect => true,
23
+ :connection_completed => true,
24
+ :on_no_data_received => true,
25
+ :on_unauthorized => true,
26
+ :on_enhance_your_calm => true
27
+ )
28
+ EM.stub!(:run).and_yield
29
+ EM::Twitter::Client.stub!(:connect).and_return(@stream)
30
+ end
31
+
32
+ it "connects if the reactor is already running" do
33
+ EM.stub!(:reactor_running?).and_return(true)
34
+ @client.should_receive(:connect)
35
+ @client.track('abc')
36
+ end
37
+
38
+ it "starts the reactor if not already running" do
39
+ EM.should_receive(:run).once
40
+ @client.track('abc')
41
+ end
42
+
43
+ it "warns when callbacks are passed as options" do
44
+ @stream.stub(:each).and_yield(nil)
45
+ Kernel.should_receive(:warn).with(/Passing callbacks via the options hash is deprecated and will be removed in TweetStream 3.0/)
46
+ @client.track('abc', :inited => Proc.new { })
47
+ end
48
+
49
+ describe 'proxy usage' do
50
+ it 'connects with a proxy' do
51
+ @client = TweetStream::Client.new(:proxy => { :uri => 'http://someproxy:8081'})
52
+ EM::Twitter::Client.should_receive(:connect).
53
+ with(hash_including(:proxy => { :uri => 'http://someproxy:8081'})).and_return(@stream)
54
+ @stream.should_receive(:each).and_return
55
+ @client.track('abc')
56
+ end
57
+ end
58
+
59
+ describe "#each" do
60
+ it "calls the appropriate parser" do
61
+ @client = TweetStream::Client.new
62
+ Yajl::Parser.should_receive(:parse).twice.and_return({})
63
+ @stream.should_receive(:each).and_yield(sample_tweets[0].to_json)
64
+ @client.track('abc','def')
65
+ end
66
+
67
+ it "yields a Twitter::Tweet" do
68
+ @stream.should_receive(:each).and_yield(sample_tweets[0].to_json)
69
+ @client.track('abc'){|s| expect(s).to be_kind_of(Twitter::Tweet)}
70
+ end
71
+
72
+ it "yields the client if a block with arity 2 is given" do
73
+ @stream.should_receive(:each).and_yield(sample_tweets[0].to_json)
74
+ @client.track('abc'){|s,c| expect(c).to eq(@client)}
75
+ end
76
+
77
+ it "includes the proper values" do
78
+ tweet = sample_tweets[0]
79
+ tweet[:id] = 123
80
+ tweet[:user][:screen_name] = 'monkey'
81
+ tweet[:text] = "Oo oo aa aa"
82
+ @stream.should_receive(:each).and_yield(tweet.to_json)
83
+ @client.track('abc') do |s|
84
+ expect(s[:id]).to eq(123)
85
+ expect(s.user.screen_name).to eq('monkey')
86
+ expect(s.text).to eq('Oo oo aa aa')
87
+ end
88
+ end
89
+
90
+ it "calls the on_stall_warning callback if specified" do
91
+ @stream.should_receive(:each).and_yield(fixture('stall_warning.json'))
92
+ @client.on_stall_warning do |warning|
93
+ expect(warning[:code]).to eq('FALLING_BEHIND')
94
+ end.track('abc')
95
+ end
96
+
97
+ it "calls the on_scrub_geo callback if specified" do
98
+ @stream.should_receive(:each).and_yield(fixture('scrub_geo.json'))
99
+ @client.on_scrub_geo do |up_to_status_id, user_id|
100
+ expect(up_to_status_id).to eq(9876)
101
+ expect(user_id).to eq(1234)
102
+ end.track('abc')
103
+ end
104
+
105
+ it "calls the on_delete callback" do
106
+ @stream.should_receive(:each).and_yield(fixture('delete.json'))
107
+ @client.on_delete do |id, user_id|
108
+ expect(id).to eq(1234)
109
+ expect(user_id).to eq(3)
110
+ end.track('abc')
111
+ end
112
+
113
+ it "calls the on_limit callback" do
114
+ limit = nil
115
+ @stream.should_receive(:each).and_yield(fixture('limit.json'))
116
+ @client.on_limit do |l|
117
+ limit = l
118
+ end.track('abc')
119
+
120
+ expect(limit).to eq(1234)
121
+ end
122
+
123
+ it "calls the on_status_withheld callback" do
124
+ status = nil
125
+ @stream.should_receive(:each).and_yield(fixture('status_withheld.json'))
126
+ @client.on_status_withheld do |s|
127
+ status = s
128
+ end.track('abc')
129
+
130
+ expect(status[:user_id]).to eq(123456)
131
+ end
132
+
133
+ it "calls the on_user_withheld callback" do
134
+ status = nil
135
+ @stream.should_receive(:each).and_yield(fixture('user_withheld.json'))
136
+ @client.on_user_withheld do |s|
137
+ status = s
138
+ end.track('abc')
139
+
140
+ expect(status[:id]).to eq(123456)
141
+ end
142
+
143
+ context "using on_anything" do
144
+ it "yields the raw hash" do
145
+ hash = {:id => 1234}
146
+ @stream.should_receive(:each).and_yield(hash.to_json)
147
+ yielded_hash = nil
148
+ @client.on_anything do |h|
149
+ yielded_hash = h
150
+ end.track('abc')
151
+
152
+ expect(yielded_hash).not_to be_nil
153
+ expect(yielded_hash[:id]).to eq(1234)
154
+ end
155
+ it "yields itself if block has an arity of 2" do
156
+ hash = {:id => 1234}
157
+ @stream.should_receive(:each).and_yield(hash.to_json)
158
+ yielded_client = nil
159
+ @client.on_anything do |_, client|
160
+ yielded_client = client
161
+ end.track('abc')
162
+ expect(yielded_client).not_to be_nil
163
+ expect(yielded_client).to eq(@client)
164
+ end
165
+ end
166
+
167
+ context "using on_timeline_status" do
168
+ it "yields a Status" do
169
+ tweet = sample_tweets[0]
170
+ tweet[:id] = 123
171
+ tweet[:user][:screen_name] = 'monkey'
172
+ tweet[:text] = "Oo oo aa aa"
173
+ @stream.should_receive(:each).and_yield(tweet.to_json)
174
+ yielded_status = nil
175
+ @client.on_timeline_status do |status|
176
+ yielded_status = status
177
+ end.track('abc')
178
+ expect(yielded_status).not_to be_nil
179
+ expect(yielded_status[:id]).to eq(123)
180
+ expect(yielded_status.user.screen_name).to eq('monkey')
181
+ expect(yielded_status.text).to eq('Oo oo aa aa')
182
+ end
183
+ it "yields itself if block has an arity of 2" do
184
+ @stream.should_receive(:each).and_yield(sample_tweets[0].to_json)
185
+ yielded_client = nil
186
+ @client.on_timeline_status do |_, client|
187
+ yielded_client = client
188
+ end.track('abc')
189
+ expect(yielded_client).not_to be_nil
190
+ expect(yielded_client).to eq(@client)
191
+ end
192
+ end
193
+
194
+ context "using on_direct_message" do
195
+ it "yields a DirectMessage" do
196
+ direct_message = sample_direct_messages[0]
197
+ direct_message[:direct_message][:id] = 1234
198
+ direct_message[:direct_message][:sender][:screen_name] = "coder"
199
+ @stream.should_receive(:each).and_yield(direct_message.to_json)
200
+ yielded_dm = nil
201
+ @client.on_direct_message do |dm|
202
+ yielded_dm = dm
203
+ end.userstream
204
+ expect(yielded_dm).not_to be_nil
205
+ expect(yielded_dm.id).to eq(1234)
206
+ expect(yielded_dm.sender.screen_name).to eq("coder")
207
+ end
208
+
209
+ it "yields itself if block has an arity of 2" do
210
+ @stream.should_receive(:each).and_yield(sample_direct_messages[0].to_json)
211
+ yielded_client = nil
212
+ @client.on_direct_message do |_, client|
213
+ yielded_client = client
214
+ end.userstream
215
+ expect(yielded_client).to eq(@client)
216
+ end
217
+ end
218
+
219
+ it "calls on_error if a non-hash response is received" do
220
+ @stream.should_receive(:each).and_yield('["favorited"]')
221
+ @client.on_error do |message|
222
+ expect(message).to eq('Unexpected JSON object in stream: ["favorited"]')
223
+ end.track('abc')
224
+ end
225
+
226
+ it "calls on_error if a json parse error occurs" do
227
+ @stream.should_receive(:each).and_yield("{'a_key':}")
228
+ @client.on_error do |message|
229
+ expect(message).to eq("Yajl::ParseError occured in stream: {'a_key':}")
230
+ end.track('abc')
231
+ end
232
+ end
233
+
234
+ describe "#on_error" do
235
+ it "passes the message on to the error block" do
236
+ @stream.should_receive(:on_error).and_yield('Uh oh')
237
+ @client.on_error do |m|
238
+ expect(m).to eq('Uh oh')
239
+ end.track('abc')
240
+ end
241
+
242
+ it "returns the block when defined" do
243
+ @client.on_error { |m| true; }
244
+ expect(@client.on_error).to be_kind_of(Proc)
245
+ end
246
+
247
+ it "returns nil when undefined" do
248
+ expect(@client.on_error).to be_nil
249
+ end
250
+ end
251
+
252
+ describe "#on_max_reconnects" do
253
+ it "raises a ReconnectError" do
254
+ @stream.should_receive(:on_max_reconnects).and_yield(30, 20)
255
+ expect(lambda{ @client.track('abc') }).to raise_error(TweetStream::ReconnectError, "Failed to reconnect after 20 tries.")
256
+ end
257
+ end
258
+ end
259
+
260
+ describe "API methods" do
261
+ %w(firehose retweet sample links).each do |method|
262
+ it "##{method} should make a call to start with \"statuses/#{method}\"" do
263
+ @client.should_receive(:start).once.with('/1.1/statuses/' + method + '.json', {})
264
+ @client.send(method)
265
+ end
266
+ end
267
+
268
+ describe "#filter" do
269
+ it "makes a call to 'statuses/filter' with the query params provided" do
270
+ @client.should_receive(:start).once.with('/1.1/statuses/filter.json', :follow => 123, :method => :post)
271
+ @client.filter(:follow => 123)
272
+ end
273
+ it "makes a call to 'statuses/filter' with the query params provided longitude/latitude pairs, separated by commas " do
274
+ @client.should_receive(:start).once.with('/1.1/statuses/filter.json', :locations => '-122.75,36.8,-121.75,37.8,-74,40,-73,41', :method => :post)
275
+ @client.filter(:locations => '-122.75,36.8,-121.75,37.8,-74,40,-73,41')
276
+ end
277
+ end
278
+
279
+ describe "#follow" do
280
+ it "makes a call to start with 'statuses/filter' and a follow query parameter" do
281
+ @client.should_receive(:start).once.with('/1.1/statuses/filter.json', :follow => [123], :method => :post)
282
+ @client.follow(123)
283
+ end
284
+
285
+ it "comma-joins multiple arguments" do
286
+ @client.should_receive(:start).once.with('/1.1/statuses/filter.json', :follow => [123,456], :method => :post)
287
+ @client.follow(123, 456)
288
+ end
289
+ end
290
+
291
+ describe "#locations" do
292
+ it "calls #start with 'statuses/filter' with the query params provided longitude/latitude pairs" do
293
+ @client.should_receive(:start).once.with('/1.1/statuses/filter.json', :locations => ['-122.75,36.8,-121.75,37.8,-74,40,-73,41'], :method => :post)
294
+ @client.locations('-122.75,36.8,-121.75,37.8,-74,40,-73,41')
295
+ end
296
+
297
+ it "calls #start with 'statuses/filter' with the query params provided longitude/latitude pairs and additional filter" do
298
+ @client.should_receive(:start).once.with('/1.1/statuses/filter.json', :locations => ['-122.75,36.8,-121.75,37.8,-74,40,-73,41'], :track => 'rock', :method => :post)
299
+ @client.locations('-122.75,36.8,-121.75,37.8,-74,40,-73,41', :track => 'rock')
300
+ end
301
+ end
302
+
303
+ describe "#track" do
304
+ it "makes a call to start with 'statuses/filter' and a track query parameter" do
305
+ @client.should_receive(:start).once.with('/1.1/statuses/filter.json', :track => ['test'], :method => :post)
306
+ @client.track('test')
307
+ end
308
+
309
+ it "comma-joins multiple arguments" do
310
+ @client.should_receive(:start).once.with('/1.1/statuses/filter.json', :track => ['foo', 'bar', 'baz'], :method => :post)
311
+ @client.track('foo', 'bar', 'baz')
312
+ end
313
+
314
+ it "comma-joins an array of arguments" do
315
+ @client.should_receive(:start).once.with('/1.1/statuses/filter.json', :track => [['foo','bar','baz']], :method => :post)
316
+ @client.track(['foo','bar','baz'])
317
+ end
318
+
319
+ it "calls #start with 'statuses/filter' and the provided queries" do
320
+ @client.should_receive(:start).once.with('/1.1/statuses/filter.json', :track => ['rock'], :method => :post)
321
+ @client.track('rock')
322
+ end
323
+ end
324
+ end
325
+
326
+ %w(on_delete on_limit on_inited on_reconnect on_no_data_received on_unauthorized on_enhance_your_calm).each do |proc_setter|
327
+ describe "##{proc_setter}" do
328
+ it "sets when a block is given" do
329
+ proc = Proc.new{|a,b| puts a }
330
+ @client.send(proc_setter, &proc)
331
+ expect(@client.send(proc_setter)).to eq(proc)
332
+ end
333
+
334
+ it "returns nil when undefined" do
335
+ expect(@client.send(proc_setter)).to be_nil
336
+ end
337
+ end
338
+ end
339
+
340
+ describe "#stop" do
341
+ it "calls EventMachine::stop_event_loop" do
342
+ EventMachine.should_receive :stop_event_loop
343
+ expect(TweetStream::Client.new.stop).to be_nil
344
+ end
345
+
346
+ it "returns the last status yielded" do
347
+ EventMachine.should_receive :stop_event_loop
348
+ client = TweetStream::Client.new
349
+ client.send(:instance_variable_set, :@last_status, {})
350
+ expect(client.stop).to eq({})
351
+ end
352
+ end
353
+
354
+ describe "#close_connection" do
355
+ it "does not call EventMachine::stop_event_loop" do
356
+ EventMachine.should_not_receive :stop_event_loop
357
+ expect(TweetStream::Client.new.close_connection).to be_nil
358
+ end
359
+ end
360
+
361
+ describe "#stop_stream" do
362
+ before(:each) do
363
+ @stream = stub("EM::Twitter::Client",
364
+ :connect => true,
365
+ :unbind => true,
366
+ :each => true,
367
+ :on_error => true,
368
+ :on_max_reconnects => true,
369
+ :on_reconnect => true,
370
+ :connection_completed => true,
371
+ :on_no_data_received => true,
372
+ :on_unauthorized => true,
373
+ :on_enhance_your_calm => true,
374
+ :stop => true
375
+ )
376
+ EM::Twitter::Client.stub!(:connect).and_return(@stream)
377
+ @client = TweetStream::Client.new
378
+ @client.connect('/')
379
+ end
380
+
381
+ it "calls stream.stop to cleanly stop the current stream" do
382
+ @stream.should_receive(:stop)
383
+ @client.stop_stream
384
+ end
385
+
386
+ it "does not stop eventmachine" do
387
+ EventMachine.should_not_receive :stop_event_loop
388
+ @client.stop_stream
389
+ end
390
+ end
391
+ end