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.
- data/.yardopts +6 -0
- data/CHANGELOG.md +76 -0
- data/CONTRIBUTING.md +9 -0
- data/LICENSE.md +20 -0
- data/README.md +392 -0
- data/Rakefile +11 -0
- data/lib/tweetstream.rb +36 -0
- data/lib/tweetstream/client.rb +582 -0
- data/lib/tweetstream/configuration.rb +92 -0
- data/lib/tweetstream/daemon.rb +52 -0
- data/lib/tweetstream/site_stream_client.rb +122 -0
- data/lib/tweetstream/version.rb +3 -0
- data/spec/fixtures/delete.json +8 -0
- data/spec/fixtures/direct_messages.json +1 -0
- data/spec/fixtures/favorite.json +150 -0
- data/spec/fixtures/ids.json +30 -0
- data/spec/fixtures/info.json +18 -0
- data/spec/fixtures/limit.json +5 -0
- data/spec/fixtures/scrub_geo.json +8 -0
- data/spec/fixtures/stall_warning.json +7 -0
- data/spec/fixtures/status_withheld.json +7 -0
- data/spec/fixtures/statuses.json +1 -0
- data/spec/fixtures/user_withheld.json +6 -0
- data/spec/helper.rb +58 -0
- data/spec/tweetstream/client_authentication_spec.rb +83 -0
- data/spec/tweetstream/client_site_stream_spec.rb +145 -0
- data/spec/tweetstream/client_spec.rb +391 -0
- data/spec/tweetstream/client_userstream_spec.rb +74 -0
- data/spec/tweetstream/daemon_spec.rb +29 -0
- data/spec/tweetstream/site_stream_client_spec.rb +233 -0
- data/spec/tweetstream_spec.rb +129 -0
- data/tweetstream.gemspec +29 -0
- metadata +179 -0
@@ -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 @@
|
|
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}
|
data/spec/helper.rb
ADDED
@@ -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
|