joelind-twitter 0.9.9
Sign up to get free protection for your applications and to get access to all the features.
- data/History +290 -0
- data/License +20 -0
- data/Notes +33 -0
- data/README.rdoc +19 -0
- data/Rakefile +40 -0
- data/VERSION.yml +5 -0
- data/examples/connect.rb +30 -0
- data/examples/friendship_existence.rb +13 -0
- data/examples/helpers/config_store.rb +38 -0
- data/examples/httpauth.rb +11 -0
- data/examples/ids.rb +13 -0
- data/examples/lists.rb +11 -0
- data/examples/oauth.rb +27 -0
- data/examples/search.rb +15 -0
- data/examples/timeline.rb +19 -0
- data/examples/tumblr.rb +9 -0
- data/examples/unauthorized.rb +16 -0
- data/examples/update.rb +11 -0
- data/examples/user.rb +5 -0
- data/lib/twitter/base.rb +390 -0
- data/lib/twitter/geo.rb +25 -0
- data/lib/twitter/httpauth.rb +53 -0
- data/lib/twitter/local_trends.rb +30 -0
- data/lib/twitter/oauth.rb +85 -0
- data/lib/twitter/request.rb +71 -0
- data/lib/twitter/search.rb +163 -0
- data/lib/twitter/trends.rb +55 -0
- data/lib/twitter.rb +156 -0
- data/test/fixtures/blocking.json +1632 -0
- data/test/fixtures/firehose.json +1 -0
- data/test/fixtures/follower_ids.json +1 -0
- data/test/fixtures/followers.json +1 -0
- data/test/fixtures/friend_ids.json +1 -0
- data/test/fixtures/friends_timeline.json +1 -0
- data/test/fixtures/friendship.json +1 -0
- data/test/fixtures/friendship_exists.json +1 -0
- data/test/fixtures/geo_place.json +1 -0
- data/test/fixtures/geo_reverse_geocode.json +1 -0
- data/test/fixtures/geo_reverse_geocode_granularity.json +1 -0
- data/test/fixtures/geo_reverse_geocode_limit.json +1 -0
- data/test/fixtures/geo_search.json +1 -0
- data/test/fixtures/geo_search_ip_address.json +1 -0
- data/test/fixtures/geo_search_query.json +1 -0
- data/test/fixtures/home_timeline.json +1 -0
- data/test/fixtures/ids.json +1 -0
- data/test/fixtures/list.json +1 -0
- data/test/fixtures/list_statuses.json +1 -0
- data/test/fixtures/list_statuses_1_1.json +1 -0
- data/test/fixtures/list_statuses_2_1.json +1 -0
- data/test/fixtures/list_subscriptions.json +1 -0
- data/test/fixtures/list_users.json +1 -0
- data/test/fixtures/lists.json +1 -0
- data/test/fixtures/memberships.json +1 -0
- data/test/fixtures/mentions.json +1 -0
- data/test/fixtures/not_found.json +1 -0
- data/test/fixtures/people_search.json +39 -0
- data/test/fixtures/rate_limit_exceeded.json +1 -0
- data/test/fixtures/report_spam.json +41 -0
- data/test/fixtures/retweet.json +1 -0
- data/test/fixtures/retweeted_by_me.json +1 -0
- data/test/fixtures/retweeted_to_me.json +1 -0
- data/test/fixtures/retweeters_of_tweet.json +166 -0
- data/test/fixtures/retweets.json +1 -0
- data/test/fixtures/retweets_of_me.json +1 -0
- data/test/fixtures/sample-image.png +0 -0
- data/test/fixtures/saved_search.json +7 -0
- data/test/fixtures/saved_searches.json +16 -0
- data/test/fixtures/search.json +1 -0
- data/test/fixtures/search_from_jnunemaker.json +1 -0
- data/test/fixtures/status.json +1 -0
- data/test/fixtures/status_show.json +1 -0
- data/test/fixtures/trends_available.json +253 -0
- data/test/fixtures/trends_current.json +1 -0
- data/test/fixtures/trends_current_exclude.json +1 -0
- data/test/fixtures/trends_daily.json +1925 -0
- data/test/fixtures/trends_daily_date.json +1 -0
- data/test/fixtures/trends_daily_exclude.json +1 -0
- data/test/fixtures/trends_location.json +57 -0
- data/test/fixtures/trends_weekly.json +1 -0
- data/test/fixtures/trends_weekly_date.json +1 -0
- data/test/fixtures/trends_weekly_exclude.json +1 -0
- data/test/fixtures/unauthorized.json +1 -0
- data/test/fixtures/update_profile_background_image.json +1 -0
- data/test/fixtures/update_profile_image.json +1 -0
- data/test/fixtures/user.json +1 -0
- data/test/fixtures/user_timeline.json +710 -0
- data/test/fixtures/users.json +1 -0
- data/test/test_helper.rb +47 -0
- data/test/twitter/base_test.rb +426 -0
- data/test/twitter/geo_test.rb +79 -0
- data/test/twitter/httpauth_test.rb +86 -0
- data/test/twitter/oauth_test.rb +127 -0
- data/test/twitter/request_test.rb +217 -0
- data/test/twitter/search_test.rb +208 -0
- data/test/twitter/trends_test.rb +112 -0
- data/test/twitter_test.rb +106 -0
- metadata +305 -0
@@ -0,0 +1 @@
|
|
1
|
+
[{"created_at":"Mon Jul 16 12:59:01 +0000 2007","friends_count":50,"description":"Captain","statuses_count":1948,"profile_text_color":"333333","screen_name":"sferik","status":{"created_at":"Thu Mar 18 18:13:19 +0000 2010","truncated":false,"in_reply_to_status_id":null,"source":"<a href=\"http://www.atebits.com/\" rel=\"nofollow\">Tweetie</a>","in_reply_to_screen_name":null,"favorited":false,"in_reply_to_user_id":null,"id":10682865729,"text":"If the earth was square, what shape would web browser icons be? #rhetorical"},"following":false,"profile_background_image_url":"http://a3.twimg.com/profile_background_images/58220609/gem.png","favourites_count":494,"contributors_enabled":false,"profile_link_color":"BF1238","url":null,"geo_enabled":true,"profile_background_tile":false,"profile_background_color":"000000","location":"California","verified":false,"profile_sidebar_fill_color":"EFEFEF","protected":true,"name":"Erik Michaels-Ober","notifications":false,"time_zone":"Pacific Time (US & Canada)","profile_sidebar_border_color":"FFFFFF","followers_count":799,"id":7505382,"lang":"en","utc_offset":-28800,"profile_image_url":"http://a1.twimg.com/profile_images/323331048/me_normal.jpg"},{"created_at":"Tue Dec 12 06:39:24 +0000 2006","friends_count":39,"description":"Full-time inventor.\r\nI don't do drugs. I AM drugs.","statuses_count":2157,"profile_text_color":"333333","screen_name":"jm3","status":{"created_at":"Thu Mar 18 21:32:31 +0000 2010","in_reply_to_status_id":null,"truncated":false,"source":"<a href=\"http://foursquare.com\" rel=\"nofollow\">foursquare</a>","in_reply_to_screen_name":null,"in_reply_to_user_id":null,"id":10690325085,"favorited":false,"text":"\"Social media is the new porn.\" Not sure I agree. (@ OMMA Global SF w/ 21 others) http://4sq.com/aURoO4"},"following":true,"profile_background_image_url":"http://a1.twimg.com/profile_background_images/67143062/drucker.jpg","favourites_count":4179,"contributors_enabled":false,"profile_link_color":"038543","url":"http://jm3.net","geo_enabled":true,"profile_background_tile":true,"profile_background_color":"ACDED6","location":"SF Yay Area","verified":false,"profile_sidebar_fill_color":"F6F6F6","protected":false,"name":"John Manoogian III","notifications":true,"time_zone":"Pacific Time (US & Canada)","profile_sidebar_border_color":"EEEEEE","followers_count":477,"id":59593,"lang":"en","utc_offset":-28800,"profile_image_url":"http://a3.twimg.com/profile_images/689856229/jm3-stripes_normal.jpg"},{"created_at":"Thu Feb 15 16:28:04 +0000 2007","friends_count":223,"description":"Internet researcher & open source hacker. I created Know Your Meme and taught the Internet Famous class","contributors_enabled":false,"profile_text_color":"000000","screen_name":"jamiew","status":{"created_at":"Tue Mar 16 02:00:21 +0000 2010","favorited":false,"in_reply_to_user_id":null,"in_reply_to_status_id":null,"source":"web","in_reply_to_screen_name":null,"id":10547290021,"truncated":false,"text":"Chat Roulette Piano Improv http://www.youtube.com/watch?v=32vpgNiAH60 A++, I'm in love"},"following":true,"profile_background_image_url":"http://a1.twimg.com/profile_background_images/83556904/fat_NBC_diagram_cooper_40.jpg","favourites_count":496,"profile_link_color":"061e6f","url":"http://jamiedubs.com","geo_enabled":false,"profile_background_tile":true,"profile_background_color":"ffffff","location":"San Francisco","verified":false,"profile_sidebar_fill_color":"ededed","protected":false,"name":"Jamie Wilkinson","notifications":false,"time_zone":"Pacific Time (US & Canada)","profile_sidebar_border_color":"e3c4c9","followers_count":1135,"id":774010,"lang":"en","statuses_count":918,"utc_offset":-28800,"profile_image_url":"http://a1.twimg.com/profile_images/698063756/jamie_beards-big_normal.jpg"}]
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require "test/unit"
|
2
|
+
require "pathname"
|
3
|
+
require "shoulda"
|
4
|
+
require "matchy"
|
5
|
+
require "mocha"
|
6
|
+
require "fakeweb"
|
7
|
+
require "redgreen"
|
8
|
+
|
9
|
+
FakeWeb.allow_net_connect = false
|
10
|
+
|
11
|
+
dir = (Pathname(__FILE__).dirname + "../lib").expand_path
|
12
|
+
require dir + "twitter"
|
13
|
+
|
14
|
+
class Test::Unit::TestCase
|
15
|
+
end
|
16
|
+
|
17
|
+
def sample_image(filename)
|
18
|
+
File.expand_path(File.dirname(__FILE__) + "/fixtures/" + filename)
|
19
|
+
end
|
20
|
+
|
21
|
+
def fixture_file(filename)
|
22
|
+
return "" if filename == ""
|
23
|
+
file_path = File.expand_path(File.dirname(__FILE__) + "/fixtures/" + filename)
|
24
|
+
File.read(file_path)
|
25
|
+
end
|
26
|
+
|
27
|
+
def twitter_url(url)
|
28
|
+
url =~ /^http/ ? url : "http://api.twitter.com:80#{url}"
|
29
|
+
end
|
30
|
+
|
31
|
+
def stub_get(url, filename, status=nil)
|
32
|
+
options = {:body => fixture_file(filename)}
|
33
|
+
options.merge!({:status => status}) unless status.nil?
|
34
|
+
FakeWeb.register_uri(:get, twitter_url(url), options)
|
35
|
+
end
|
36
|
+
|
37
|
+
def stub_post(url, filename)
|
38
|
+
FakeWeb.register_uri(:post, twitter_url(url), :body => fixture_file(filename))
|
39
|
+
end
|
40
|
+
|
41
|
+
def stub_put(url, filename)
|
42
|
+
FakeWeb.register_uri(:put, twitter_url(url), :body => fixture_file(filename))
|
43
|
+
end
|
44
|
+
|
45
|
+
def stub_delete(url, filename)
|
46
|
+
FakeWeb.register_uri(:delete, twitter_url(url), :body => fixture_file(filename))
|
47
|
+
end
|
@@ -0,0 +1,426 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class BaseTest < Test::Unit::TestCase
|
4
|
+
context "base" do
|
5
|
+
setup do
|
6
|
+
oauth = Twitter::OAuth.new("token", "secret")
|
7
|
+
@access_token = OAuth::AccessToken.new(oauth.consumer, "atoken", "asecret")
|
8
|
+
oauth.stubs(:access_token).returns(@access_token)
|
9
|
+
@twitter = Twitter::Base.new(oauth)
|
10
|
+
end
|
11
|
+
|
12
|
+
context "initialize" do
|
13
|
+
should "require a client" do
|
14
|
+
@twitter.client.should respond_to(:get)
|
15
|
+
@twitter.client.should respond_to(:post)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
should "delegate get to the client" do
|
20
|
+
@access_token.expects(:get).with("/foo").returns(nil)
|
21
|
+
@twitter.get("/foo")
|
22
|
+
end
|
23
|
+
|
24
|
+
should "delegate post to the client" do
|
25
|
+
@access_token.expects(:post).with("/foo", {:bar => "baz"}).returns(nil)
|
26
|
+
@twitter.post("/foo", {:bar => "baz"})
|
27
|
+
end
|
28
|
+
|
29
|
+
context "hitting the api" do
|
30
|
+
should "be able to get home timeline" do
|
31
|
+
stub_get("/statuses/home_timeline.json", "home_timeline.json")
|
32
|
+
timeline = @twitter.home_timeline
|
33
|
+
timeline.size.should == 20
|
34
|
+
first = timeline.first
|
35
|
+
first.source.should == '<a href="http://www.atebits.com/software/tweetie/">Tweetie</a>'
|
36
|
+
first.user.name.should == "John Nunemaker"
|
37
|
+
first.user.url.should == "http://railstips.org/about"
|
38
|
+
first.id.should == 1441588944
|
39
|
+
first.favorited.should be(false)
|
40
|
+
end
|
41
|
+
|
42
|
+
should "be able to get friends timeline" do
|
43
|
+
stub_get("/statuses/friends_timeline.json", "friends_timeline.json")
|
44
|
+
timeline = @twitter.friends_timeline
|
45
|
+
timeline.size.should == 20
|
46
|
+
first = timeline.first
|
47
|
+
first.source.should == '<a href="http://www.atebits.com/software/tweetie/">Tweetie</a>'
|
48
|
+
first.user.name.should == "John Nunemaker"
|
49
|
+
first.user.url.should == "http://railstips.org/about"
|
50
|
+
first.id.should == 1441588944
|
51
|
+
first.favorited.should be(false)
|
52
|
+
end
|
53
|
+
|
54
|
+
should "be able to get user timeline" do
|
55
|
+
stub_get("/statuses/user_timeline.json", "user_timeline.json")
|
56
|
+
timeline = @twitter.user_timeline
|
57
|
+
timeline.size.should == 20
|
58
|
+
first = timeline.first
|
59
|
+
first.text.should == "Colder out today than expected. Headed to the Beanery for some morning wakeup drink. Latte or coffee...hmmm..."
|
60
|
+
first.user.name.should == "John Nunemaker"
|
61
|
+
end
|
62
|
+
|
63
|
+
should "be able to get a status" do
|
64
|
+
stub_get("/statuses/show/1441588944.json", "status.json")
|
65
|
+
status = @twitter.status(1441588944)
|
66
|
+
status.user.name.should == "John Nunemaker"
|
67
|
+
status.id.should == 1441588944
|
68
|
+
end
|
69
|
+
|
70
|
+
should "be able to update status" do
|
71
|
+
stub_post("/statuses/update.json", "status.json")
|
72
|
+
status = @twitter.update("Rob Dyrdek is the funniest man alive. That is all.")
|
73
|
+
status.user.name.should == "John Nunemaker"
|
74
|
+
status.text.should == "Rob Dyrdek is the funniest man alive. That is all."
|
75
|
+
end
|
76
|
+
|
77
|
+
should "be able to retweet a status" do
|
78
|
+
stub_post("/statuses/retweet/6235127466.json", "retweet.json")
|
79
|
+
status = @twitter.retweet(6235127466)
|
80
|
+
status.user.name.should == "Michael D. Ivey"
|
81
|
+
status.text.should == "RT @jstetser: I'm not actually awake. My mind's on autopilot for food and I managed to take a detour along the way."
|
82
|
+
status.retweeted_status.user.screen_name.should == "jstetser"
|
83
|
+
status.retweeted_status.text.should == "I'm not actually awake. My mind's on autopilot for food and I managed to take a detour along the way."
|
84
|
+
end
|
85
|
+
|
86
|
+
should "be able to get retweets of a status" do
|
87
|
+
stub_get("/statuses/retweets/6192831130.json", "retweets.json")
|
88
|
+
retweets = @twitter.retweets(6192831130)
|
89
|
+
retweets.size.should == 6
|
90
|
+
first = retweets.first
|
91
|
+
first.user.name.should == "josephholsten"
|
92
|
+
first.text.should == "RT @Moltz: Personally, I won't be satisfied until a Buddhist monk lights himself on fire for web standards."
|
93
|
+
end
|
94
|
+
|
95
|
+
should "be able to get mentions" do
|
96
|
+
stub_get("/statuses/mentions.json", "mentions.json")
|
97
|
+
mentions = @twitter.mentions
|
98
|
+
mentions.size.should == 19
|
99
|
+
first = mentions.first
|
100
|
+
first.user.name.should == "-oAk-"
|
101
|
+
first.text.should == "@jnunemaker cold out today. cold yesterday. even colder today."
|
102
|
+
end
|
103
|
+
|
104
|
+
should "be able to get retweets by me" do
|
105
|
+
stub_get("/statuses/retweeted_by_me.json", "retweeted_by_me.json")
|
106
|
+
retweeted_by_me = @twitter.retweeted_by_me
|
107
|
+
retweeted_by_me.size.should == 20
|
108
|
+
first = retweeted_by_me.first.retweeted_status
|
109
|
+
first.user.name.should == "Troy Davis"
|
110
|
+
first.text.should == "I'm the mayor of win a free MacBook Pro with promo code Cyber Monday RT for a good time"
|
111
|
+
end
|
112
|
+
|
113
|
+
should "be able to get retweets to me" do
|
114
|
+
stub_get("/statuses/retweeted_to_me.json", "retweeted_to_me.json")
|
115
|
+
retweeted_to_me = @twitter.retweeted_to_me
|
116
|
+
retweeted_to_me.size.should == 20
|
117
|
+
first = retweeted_to_me.first.retweeted_status
|
118
|
+
first.user.name.should == "Cloudvox"
|
119
|
+
first.text.should == "Testing counts with voice apps too:\n\"the voice told residents to dial 'nine hundred eleven' rather than '9-1-1'\" \342\200\224 http://j.mp/7mqe2B"
|
120
|
+
end
|
121
|
+
|
122
|
+
should "be able to get retweets of me" do
|
123
|
+
stub_get("/statuses/retweets_of_me.json", "retweets_of_me.json")
|
124
|
+
retweets_of_me = @twitter.retweets_of_me
|
125
|
+
retweets_of_me.size.should == 11
|
126
|
+
first = retweets_of_me.first
|
127
|
+
first.user.name.should == "Michael D. Ivey"
|
128
|
+
first.text.should == "Trying out geotweets in Birdfeed. No \"new RT\" support, though. Any iPhone client with RTs yet?"
|
129
|
+
end
|
130
|
+
|
131
|
+
should "be able to get users who retweeted a tweet" do
|
132
|
+
stub_get("/statuses/9021932472/retweeted_by.json", "retweeters_of_tweet.json")
|
133
|
+
retweeters = @twitter.retweeters_of("9021932472")
|
134
|
+
retweeters.size.should == 4
|
135
|
+
first = retweeters.first
|
136
|
+
first.screen_name.should == "bryanl"
|
137
|
+
end
|
138
|
+
|
139
|
+
should "be able to get ids of users who retweeted a tweet" do
|
140
|
+
stub_get("/statuses/9021932472/retweeted_by/ids.json", "ids.json")
|
141
|
+
retweeters = @twitter.retweeters_of("9021932472", :ids_only => true)
|
142
|
+
retweeters.first.should == 61940910
|
143
|
+
end
|
144
|
+
|
145
|
+
should "be able to get follower ids" do
|
146
|
+
stub_get("/followers/ids.json", "follower_ids.json")
|
147
|
+
follower_ids = @twitter.follower_ids
|
148
|
+
follower_ids.size.should == 1252
|
149
|
+
follower_ids.first.should == 613
|
150
|
+
end
|
151
|
+
|
152
|
+
should "be able to get friend ids" do
|
153
|
+
stub_get("/friends/ids.json", "friend_ids.json")
|
154
|
+
friend_ids = @twitter.friend_ids
|
155
|
+
friend_ids.size.should == 161
|
156
|
+
friend_ids.first.should == 15323
|
157
|
+
end
|
158
|
+
|
159
|
+
should "correctly hash statuses" do
|
160
|
+
stub_get("/statuses/friends_timeline.json", "friends_timeline.json")
|
161
|
+
hashes = @twitter.friends_timeline.map{ |s| s.hash }
|
162
|
+
hashes.should == @twitter.friends_timeline.map{ |s| s.hash }
|
163
|
+
end
|
164
|
+
|
165
|
+
should "be able to test whether a friendship exists" do
|
166
|
+
stub_get("/friendships/exists.json?user_a=pengwynn&user_b=sferik", "friendship_exists.json")
|
167
|
+
@twitter.friendship_exists?("pengwynn", "sferik").should == true
|
168
|
+
end
|
169
|
+
|
170
|
+
should "be able to get a friendship" do
|
171
|
+
stub_get("/friendships/show.json?source_screen_name=dcrec1&target_screen_name=pengwynn", "friendship.json")
|
172
|
+
@twitter.friendship_show(:source_screen_name => "dcrec1", :target_screen_name => "pengwynn").relationship.target.followed_by == false
|
173
|
+
end
|
174
|
+
|
175
|
+
should "be able to lookup a user" do
|
176
|
+
stub_get("/users/show/4243.json", "user.json")
|
177
|
+
user = @twitter.user(4243)
|
178
|
+
user.screen_name.should == "jnunemaker"
|
179
|
+
end
|
180
|
+
|
181
|
+
should "be able to lookup users in bulk" do
|
182
|
+
stub_get("/users/lookup.json?screen_name=sferik&user_id=59593,774010", "users.json")
|
183
|
+
users = @twitter.users("sferik", 59593, 774010)
|
184
|
+
users.first.screen_name.should == "sferik"
|
185
|
+
end
|
186
|
+
|
187
|
+
should "be able to search people" do
|
188
|
+
stub_get("/users/search.json?q=Wynn%20Netherland", "people_search.json")
|
189
|
+
people = @twitter.user_search("Wynn Netherland")
|
190
|
+
people.first.screen_name.should == "pengwynn"
|
191
|
+
end
|
192
|
+
|
193
|
+
should "be able to get followers' stauses" do
|
194
|
+
stub_get("/statuses/followers.json", "followers.json")
|
195
|
+
followers = @twitter.followers
|
196
|
+
followers.should == @twitter.followers
|
197
|
+
end
|
198
|
+
|
199
|
+
should "be able to get blocked users' IDs" do
|
200
|
+
stub_get("/blocks/blocking/ids.json", "ids.json")
|
201
|
+
blocked = @twitter.blocked_ids
|
202
|
+
blocked.should == @twitter.blocked_ids
|
203
|
+
end
|
204
|
+
|
205
|
+
should "be able to get an array of blocked users" do
|
206
|
+
stub_get("/blocks/blocking.json", "blocking.json")
|
207
|
+
blocked = @twitter.blocking
|
208
|
+
blocked.last.screen_name.should == "euciavkvyplx"
|
209
|
+
end
|
210
|
+
|
211
|
+
should "report a spammer" do
|
212
|
+
stub_post("/report_spam.json", "report_spam.json")
|
213
|
+
spammer = @twitter.report_spam(:screen_name => 'lucaasvaz00')
|
214
|
+
spammer.screen_name.should == "lucaasvaz00"
|
215
|
+
end
|
216
|
+
|
217
|
+
should "upload a profile image" do
|
218
|
+
stub_post("/account/update_profile_image.json", "update_profile_image.json")
|
219
|
+
user = @twitter.update_profile_image(File.new(sample_image("sample-image.png")))
|
220
|
+
user.name.should == "John Nunemaker" # update_profile_image responds with the user
|
221
|
+
end
|
222
|
+
|
223
|
+
should "upload a background image" do
|
224
|
+
stub_post("/account/update_profile_background_image.json", "update_profile_background_image.json")
|
225
|
+
user = @twitter.update_profile_background(File.new(sample_image("sample-image.png")))
|
226
|
+
user.name.should == "John Nunemaker" # update_profile_background responds with the user
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
context "when using saved searches" do
|
231
|
+
should "be able to retrieve my saved searches" do
|
232
|
+
stub_get('/saved_searches.json', 'saved_searches.json')
|
233
|
+
searches = @twitter.saved_searches
|
234
|
+
searches[0].query.should == "great danes"
|
235
|
+
searches[1].query.should == "rubyconf OR railsconf"
|
236
|
+
end
|
237
|
+
|
238
|
+
should "be able to retrieve a saved search by id" do
|
239
|
+
stub_get('/saved_searches/show/7095598.json', 'saved_search.json')
|
240
|
+
search = @twitter.saved_search(7095598)
|
241
|
+
search.query.should == "great danes"
|
242
|
+
end
|
243
|
+
|
244
|
+
should "be able to create a saved search" do
|
245
|
+
stub_post('/saved_searches/create.json', 'saved_search.json')
|
246
|
+
search = @twitter.saved_search_create('great danes')
|
247
|
+
search.query.should == "great danes"
|
248
|
+
end
|
249
|
+
|
250
|
+
should "be able to delete a saved search" do
|
251
|
+
stub_delete('/saved_searches/destroy/7095598.json', 'saved_search.json')
|
252
|
+
search = @twitter.saved_search_destroy(7095598)
|
253
|
+
search.query.should == "great danes"
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
context "when using lists" do
|
258
|
+
|
259
|
+
should "be able to create a new list" do
|
260
|
+
stub_post("/pengwynn/lists.json", "list.json")
|
261
|
+
list = @twitter.list_create("pengwynn", {:name => "Rubyists"})
|
262
|
+
list.name.should == "Rubyists"
|
263
|
+
list.slug.should == "rubyists"
|
264
|
+
list.mode.should == "public"
|
265
|
+
end
|
266
|
+
|
267
|
+
should "be able to update a list" do
|
268
|
+
stub_put("/pengwynn/lists/rubyists.json", "list.json")
|
269
|
+
list = @twitter.list_update("pengwynn", "rubyists", {:name => "Rubyists"})
|
270
|
+
list.name.should == "Rubyists"
|
271
|
+
list.slug.should == "rubyists"
|
272
|
+
list.mode.should == "public"
|
273
|
+
end
|
274
|
+
|
275
|
+
should "be able to delete a list" do
|
276
|
+
stub_delete("/pengwynn/lists/rubyists.json", "list.json")
|
277
|
+
list = @twitter.list_delete("pengwynn", "rubyists")
|
278
|
+
list.name.should == "Rubyists"
|
279
|
+
list.slug.should == "rubyists"
|
280
|
+
list.mode.should == "public"
|
281
|
+
end
|
282
|
+
|
283
|
+
should "be able to view lists to which a user belongs" do
|
284
|
+
stub_get("/pengwynn/lists/memberships.json", "memberships.json")
|
285
|
+
lists = @twitter.memberships("pengwynn").lists
|
286
|
+
lists.size.should == 16
|
287
|
+
lists.first.name.should == "web-dev"
|
288
|
+
lists.first.member_count.should == 38
|
289
|
+
end
|
290
|
+
|
291
|
+
should "be able to view lists for the authenticated user" do
|
292
|
+
stub_get("/pengwynn/lists.json", "lists.json")
|
293
|
+
lists = @twitter.lists("pengwynn").lists
|
294
|
+
lists.size.should == 1
|
295
|
+
lists.first.name.should == "Rubyists"
|
296
|
+
lists.first.slug.should == "rubyists"
|
297
|
+
end
|
298
|
+
|
299
|
+
should "be able to view the user owned lists without passing the username" do
|
300
|
+
stub_get('/lists.json', 'lists.json')
|
301
|
+
lists = @twitter.lists().lists
|
302
|
+
lists.size.should == 1
|
303
|
+
lists.first.name.should == 'Rubyists'
|
304
|
+
lists.first.slug.should == 'rubyists'
|
305
|
+
end
|
306
|
+
|
307
|
+
should "be able to view lists for the authenticated user by passing in a cursor" do
|
308
|
+
stub_get('/pengwynn/lists.json?cursor=-1', 'lists.json')
|
309
|
+
lists = @twitter.lists('pengwynn', :cursor => -1).lists
|
310
|
+
lists.size.should == 1
|
311
|
+
lists.first.name.should == 'Rubyists'
|
312
|
+
lists.first.slug.should == 'rubyists'
|
313
|
+
end
|
314
|
+
|
315
|
+
should "be able to view the user owned lists without passing the username and passing in a cursor" do
|
316
|
+
stub_get('/lists.json?cursor=-1', 'lists.json')
|
317
|
+
lists = @twitter.lists(:cursor => -1).lists
|
318
|
+
lists.size.should == 1
|
319
|
+
lists.first.name.should == 'Rubyists'
|
320
|
+
lists.first.slug.should == 'rubyists'
|
321
|
+
end
|
322
|
+
|
323
|
+
should "be able to view list details" do
|
324
|
+
stub_get("/pengwynn/lists/rubyists.json", "list.json")
|
325
|
+
list = @twitter.list("pengwynn", "rubyists")
|
326
|
+
list.name.should == "Rubyists"
|
327
|
+
list.subscriber_count.should == 0
|
328
|
+
end
|
329
|
+
|
330
|
+
should "be able to view list timeline" do
|
331
|
+
stub_get("/pengwynn/lists/rubyists/statuses.json", "list_statuses.json")
|
332
|
+
tweets = @twitter.list_timeline("pengwynn", "rubyists")
|
333
|
+
tweets.size.should == 20
|
334
|
+
tweets.first.id.should == 5272535583
|
335
|
+
tweets.first.user.name.should == "John Nunemaker"
|
336
|
+
end
|
337
|
+
|
338
|
+
should "be able to limit number of tweets in list timeline" do
|
339
|
+
stub_get("/pengwynn/lists/rubyists/statuses.json?per_page=1", "list_statuses_1_1.json")
|
340
|
+
tweets = @twitter.list_timeline("pengwynn", "rubyists", :per_page => 1)
|
341
|
+
tweets.size.should == 1
|
342
|
+
tweets.first.id.should == 5272535583
|
343
|
+
tweets.first.user.name.should == "John Nunemaker"
|
344
|
+
end
|
345
|
+
|
346
|
+
should "be able to paginate through the timeline" do
|
347
|
+
stub_get("/pengwynn/lists/rubyists/statuses.json?page=1&per_page=1", "list_statuses_1_1.json")
|
348
|
+
stub_get("/pengwynn/lists/rubyists/statuses.json?page=2&per_page=1", "list_statuses_2_1.json")
|
349
|
+
tweets = @twitter.list_timeline("pengwynn", "rubyists", { :page => 1, :per_page => 1 })
|
350
|
+
tweets.size.should == 1
|
351
|
+
tweets.first.id.should == 5272535583
|
352
|
+
tweets.first.user.name.should == "John Nunemaker"
|
353
|
+
tweets = @twitter.list_timeline("pengwynn", "rubyists", { :page => 2, :per_page => 1 })
|
354
|
+
tweets.size.should == 1
|
355
|
+
tweets.first.id.should == 5264324712
|
356
|
+
tweets.first.user.name.should == "John Nunemaker"
|
357
|
+
end
|
358
|
+
|
359
|
+
should "be able to view list members" do
|
360
|
+
stub_get("/pengwynn/rubyists/members.json", "list_users.json")
|
361
|
+
members = @twitter.list_members("pengwynn", "rubyists").users
|
362
|
+
members.size.should == 2
|
363
|
+
members.first.name.should == "John Nunemaker"
|
364
|
+
members.first.screen_name.should == "jnunemaker"
|
365
|
+
end
|
366
|
+
|
367
|
+
should "be able to add a member to a list" do
|
368
|
+
stub_post("/pengwynn/rubyists/members.json", "user.json")
|
369
|
+
user = @twitter.list_add_member("pengwynn", "rubyists", 4243)
|
370
|
+
user.screen_name.should == "jnunemaker"
|
371
|
+
end
|
372
|
+
|
373
|
+
should "be able to remove a member from a list" do
|
374
|
+
stub_delete("/pengwynn/rubyists/members.json?id=4243", "user.json")
|
375
|
+
user = @twitter.list_remove_member("pengwynn", "rubyists", 4243)
|
376
|
+
user.screen_name.should == "jnunemaker"
|
377
|
+
end
|
378
|
+
|
379
|
+
should "be able to check if a user is member of a list" do
|
380
|
+
stub_get("/pengwynn/rubyists/members/4243.json", "user.json")
|
381
|
+
@twitter.is_list_member?("pengwynn", "rubyists", 4243).should == true
|
382
|
+
end
|
383
|
+
|
384
|
+
should "be able to view list subscribers" do
|
385
|
+
stub_get("/pengwynn/rubyists/subscribers.json", "list_users.json")
|
386
|
+
subscribers = @twitter.list_subscribers("pengwynn", "rubyists").users
|
387
|
+
subscribers.size.should == 2
|
388
|
+
subscribers.first.name.should == "John Nunemaker"
|
389
|
+
subscribers.first.screen_name.should == "jnunemaker"
|
390
|
+
end
|
391
|
+
|
392
|
+
should "be able to subscribe to a list" do
|
393
|
+
stub_post("/pengwynn/rubyists/subscribers.json", "user.json")
|
394
|
+
user = @twitter.list_subscribe("pengwynn", "rubyists")
|
395
|
+
user.screen_name.should == "jnunemaker"
|
396
|
+
end
|
397
|
+
|
398
|
+
should "be able to unsubscribe from a list" do
|
399
|
+
stub_delete("/pengwynn/rubyists/subscribers.json", "user.json")
|
400
|
+
user = @twitter.list_unsubscribe("pengwynn", "rubyists")
|
401
|
+
user.screen_name.should == "jnunemaker"
|
402
|
+
end
|
403
|
+
|
404
|
+
should "be able to view a members list subscriptions" do
|
405
|
+
stub_get('/pengwynn/lists/subscriptions.json', 'list_subscriptions.json')
|
406
|
+
subscriptions = @twitter.subscriptions('pengwynn').lists
|
407
|
+
subscriptions.size.should == 1
|
408
|
+
subscriptions.first.full_name.should == "@chriseppstein/sass-users"
|
409
|
+
subscriptions.first.slug.should == "sass-users"
|
410
|
+
end
|
411
|
+
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
context "when using a non-twitter service" do
|
416
|
+
setup do
|
417
|
+
@twitter = Twitter::Base.new(Twitter::HTTPAuth.new("wynn@example.com", "mypass", :api_endpoint => "tumblr.com"))
|
418
|
+
end
|
419
|
+
|
420
|
+
should "get the home timeline" do
|
421
|
+
stub_get("http://wynn%40example.com:mypass@tumblr.com/1/statuses/home_timeline.json", "home_timeline.json")
|
422
|
+
timeline = @twitter.home_timeline
|
423
|
+
timeline.size.should == 20
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class GeoTest < Test::Unit::TestCase
|
4
|
+
include Twitter
|
5
|
+
|
6
|
+
context "Geographic place lookup" do
|
7
|
+
|
8
|
+
should "work" do
|
9
|
+
stub_get 'http://api.twitter.com/1/geo/id/ea76a36c5bc2bdff.json', 'geo_place.json'
|
10
|
+
place = Geo.place('ea76a36c5bc2bdff')
|
11
|
+
place.country.should == 'The United States of America'
|
12
|
+
place.full_name.should == 'Ballantyne West, Charlotte'
|
13
|
+
place.geometry.coordinates.should be_kind_of(Array)
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
context "Geographic search" do
|
19
|
+
|
20
|
+
should "work" do
|
21
|
+
stub_get 'http://api.twitter.com:80/1/geo/search.json?lat=35.061161&long=-80.854568', 'geo_search.json'
|
22
|
+
places = Geo.search(:lat => 35.061161, :long => -80.854568)
|
23
|
+
places.size.should == 3
|
24
|
+
places[0].full_name.should eql('Ballantyne West, Charlotte')
|
25
|
+
places[0].name.should eql('Ballantyne West')
|
26
|
+
end
|
27
|
+
|
28
|
+
should "be able to search with free form text" do
|
29
|
+
stub_get 'http://api.twitter.com/1/geo/search.json?query=princeton%20record%20exchange', 'geo_search_query.json'
|
30
|
+
places = Geo.search(:query => 'princeton record exchange')
|
31
|
+
places.size.should == 1
|
32
|
+
places[0].name.should eql('Princeton Record Exchange')
|
33
|
+
places[0].place_type.should eql('poi')
|
34
|
+
places[0].attributes.street_address.should eql('20 S Tulane St')
|
35
|
+
end
|
36
|
+
|
37
|
+
should "be able to search by ip address" do
|
38
|
+
stub_get 'http://api.twitter.com/1/geo/search.json?ip=74.125.19.104', 'geo_search_ip_address.json'
|
39
|
+
places = Geo.search(:ip => '74.125.19.104')
|
40
|
+
places.size.should == 4
|
41
|
+
places[0].full_name.should eql("Mountain View, CA")
|
42
|
+
places[0].name.should eql("Mountain View")
|
43
|
+
places[1].full_name.should eql("Sunnyvale, CA")
|
44
|
+
places[1].name.should eql('Sunnyvale')
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
context "Geographic reverse_geocode" do
|
50
|
+
|
51
|
+
should "work" do
|
52
|
+
stub_get 'http://api.twitter.com:80/1/geo/reverse_geocode.json?lat=35.061161&long=-80.854568', 'geo_reverse_geocode.json'
|
53
|
+
places = Geo.reverse_geocode(:lat => 35.061161, :long => -80.854568)
|
54
|
+
places.size.should == 4
|
55
|
+
places[0].full_name.should eql('Ballantyne West, Charlotte')
|
56
|
+
places[0].name.should eql('Ballantyne West')
|
57
|
+
end
|
58
|
+
|
59
|
+
should "be able to limit the number of results returned" do
|
60
|
+
stub_get 'http://api.twitter.com/1/geo/reverse_geocode.json?lat=35.061161&max_results=2&long=-80.854568', 'geo_reverse_geocode_limit.json'
|
61
|
+
places = Geo.reverse_geocode(:lat => 35.061161, :long => -80.854568, :max_results => 2)
|
62
|
+
places.size.should == 2
|
63
|
+
places[0].full_name.should eql('Ballantyne West, Charlotte')
|
64
|
+
places[0].name.should eql('Ballantyne West')
|
65
|
+
end
|
66
|
+
|
67
|
+
should "be able to lookup with granularity" do
|
68
|
+
stub_get 'http://api.twitter.com/1/geo/reverse_geocode.json?lat=35.061161&long=-80.854568&granularity=city', 'geo_reverse_geocode_granularity.json'
|
69
|
+
places = Geo.reverse_geocode(:lat => 35.061161, :long => -80.854568, :granularity => 'city')
|
70
|
+
places.size.should == 3
|
71
|
+
places[0].full_name.should eql('Charlotte, NC')
|
72
|
+
places[0].name.should eql('Charlotte')
|
73
|
+
places[1].full_name.should eql('North Carolina, US')
|
74
|
+
places[1].name.should eql('North Carolina')
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class HTTPAuthTest < Test::Unit::TestCase
|
4
|
+
context "Creating new instance" do
|
5
|
+
should "should take user and password" do
|
6
|
+
twitter = Twitter::HTTPAuth.new('username', 'password')
|
7
|
+
twitter.username.should == 'username'
|
8
|
+
twitter.password.should == 'password'
|
9
|
+
end
|
10
|
+
|
11
|
+
should "accept options" do
|
12
|
+
twitter = Twitter::HTTPAuth.new('username', 'password', :ssl => true)
|
13
|
+
twitter.options.should == {:ssl => true}
|
14
|
+
end
|
15
|
+
|
16
|
+
should "default ssl to false" do
|
17
|
+
twitter = Twitter::HTTPAuth.new('username', 'password')
|
18
|
+
twitter.options[:ssl].should be(false)
|
19
|
+
end
|
20
|
+
|
21
|
+
should "use https if ssl is true" do
|
22
|
+
Twitter::HTTPAuth.expects(:base_uri).with('https://api.twitter.com/' + Twitter::API_VERSION)
|
23
|
+
twitter = Twitter::HTTPAuth.new('username', 'password', :ssl => true)
|
24
|
+
end
|
25
|
+
|
26
|
+
should "use http if ssl is false" do
|
27
|
+
Twitter::HTTPAuth.expects(:base_uri).with('http://api.twitter.com/' + Twitter::API_VERSION)
|
28
|
+
twitter = Twitter::HTTPAuth.new('username', 'password', :ssl => false)
|
29
|
+
end
|
30
|
+
|
31
|
+
should "use api version if provided" do
|
32
|
+
Twitter::HTTPAuth.expects(:base_uri).with('http://api.twitter.com/2')
|
33
|
+
twitter = Twitter::HTTPAuth.new('username', 'password', {:ssl => false, :api_version => 2})
|
34
|
+
end
|
35
|
+
|
36
|
+
should "not use api versioning if api_version is false " do
|
37
|
+
Twitter::HTTPAuth.expects(:base_uri).with('http://api.twitter.com')
|
38
|
+
twitter = Twitter::HTTPAuth.new('username', 'password', {:ssl => false, :api_version => false})
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "Client methods" do
|
43
|
+
setup do
|
44
|
+
@twitter = Twitter::HTTPAuth.new('username', 'password')
|
45
|
+
end
|
46
|
+
|
47
|
+
should "not throw error when accessing response message" do
|
48
|
+
stub_get('http://api.twitter.com:80/1/statuses/user_timeline.json', 'user_timeline.json')
|
49
|
+
response = @twitter.get('/statuses/user_timeline.json')
|
50
|
+
response.message.should == 'OK'
|
51
|
+
end
|
52
|
+
|
53
|
+
should "be able to get" do
|
54
|
+
stub_get('http://username:password@api.twitter.com:80/1/statuses/user_timeline.json', 'user_timeline.json')
|
55
|
+
response = @twitter.get('/statuses/user_timeline.json')
|
56
|
+
response.should == fixture_file('user_timeline.json')
|
57
|
+
end
|
58
|
+
|
59
|
+
should "be able to get with headers" do
|
60
|
+
@twitter.class.expects(:get).with(
|
61
|
+
'/statuses/user_timeline.json', {
|
62
|
+
:basic_auth => {:username => 'username', :password => 'password'},
|
63
|
+
:headers => {'Foo' => 'Bar'}
|
64
|
+
}
|
65
|
+
).returns(fixture_file('user_timeline.json'))
|
66
|
+
@twitter.get('/statuses/user_timeline.json', {'Foo' => 'Bar'})
|
67
|
+
end
|
68
|
+
|
69
|
+
should "be able to post" do
|
70
|
+
stub_post('http://username:password@api.twitter.com:80/1/statuses/update.json', 'status.json')
|
71
|
+
response = @twitter.post('/statuses/update.json', :text => 'My update.')
|
72
|
+
response.should == fixture_file('status.json')
|
73
|
+
end
|
74
|
+
|
75
|
+
should "be able to post with headers" do
|
76
|
+
@twitter.class.expects(:post).with(
|
77
|
+
'/statuses/update.json', {
|
78
|
+
:headers => {'Foo' => 'Bar'},
|
79
|
+
:body => {:text => 'My update.'},
|
80
|
+
:basic_auth => {:username => 'username', :password => 'password'}
|
81
|
+
}
|
82
|
+
).returns(fixture_file('status.json'))
|
83
|
+
@twitter.post('/statuses/update.json', {:text => 'My update.'}, {'Foo' => 'Bar'})
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|