jls-tweetstream 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|