dancroak-twitter-search 0.5 → 0.5.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +1 -1
- data/Rakefile +71 -0
- data/TODO.markdown +7 -0
- data/VERSION.yml +4 -0
- data/lib/twitter_search.rb +4 -0
- data/test/test_helper.rb +40 -0
- data/test/twitter_search_test.rb +307 -0
- data/test/yaml/arabic.yaml +100 -0
- data/test/yaml/beer_minus_root.yaml +98 -0
- data/test/yaml/english.yaml +98 -0
- data/test/yaml/flight_negative_tude.yaml +98 -0
- data/test/yaml/from_alexiskold.yaml +98 -0
- data/test/yaml/ftw_until.yaml +98 -0
- data/test/yaml/happy_hour_exact.yaml +98 -0
- data/test/yaml/happy_hour_near_sf.yaml +9 -0
- data/test/yaml/hashtag_haiku.yaml +98 -0
- data/test/yaml/hilarious_links.yaml +98 -0
- data/test/yaml/movie_positive_tude.yaml +98 -0
- data/test/yaml/obama.yaml +98 -0
- data/test/yaml/obama_or_hillary.yaml +98 -0
- data/test/yaml/reference_mashable.yaml +98 -0
- data/test/yaml/results_per_page.yaml +192 -0
- data/test/yaml/superhero_since.yaml +98 -0
- data/test/yaml/to_techcrunch.yaml +98 -0
- data/test/yaml/traffic_question.yaml +98 -0
- data/test/yaml/twitter_search.yaml +98 -0
- data/test/yaml/twitter_search_and.yaml +98 -0
- data/test/yaml/within_15mi_nyc.yaml +9 -0
- metadata +35 -10
- data/twitter-search.gemspec +0 -15
data/README.markdown
CHANGED
@@ -10,7 +10,7 @@ Install the gem.
|
|
10
10
|
|
11
11
|
Require the gem.
|
12
12
|
|
13
|
-
require '
|
13
|
+
require 'twitter_search'
|
14
14
|
|
15
15
|
Set up a TwitterSearch::Client. Name your client (a.k.a. 'user agent') to something meaningful, such as your app's name. This helps Twitter Search answer any questions about your use of the API.
|
16
16
|
|
data/Rakefile
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
test_files_pattern = 'test/rails_root/test/{unit,functional,other}/**/*_test.rb'
|
6
|
+
Rake::TestTask.new do |t|
|
7
|
+
t.libs << 'lib'
|
8
|
+
t.pattern = test_files_pattern
|
9
|
+
t.verbose = false
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Run the test suite"
|
13
|
+
task :default => :test
|
14
|
+
|
15
|
+
spec = Gem::Specification.new do |s|
|
16
|
+
s.name = "twitter-search"
|
17
|
+
s.summary = "Ruby client for Twitter Search."
|
18
|
+
s.email = "dcroak@thoughtbot.com"
|
19
|
+
s.homepage = "http://github.com/dancroak/twitter-search"
|
20
|
+
s.description = "Ruby client for Twitter Search."
|
21
|
+
s.authors = ["Dustin Sallings", "Dan Croak"]
|
22
|
+
s.files = FileList["[A-Z]*", "{lib,test}/**/*"]
|
23
|
+
s.add_dependency('json', '>= 1.1.2')
|
24
|
+
end
|
25
|
+
|
26
|
+
begin
|
27
|
+
require 'rubygems'
|
28
|
+
require 'jeweler'
|
29
|
+
Jeweler.gemspec = spec
|
30
|
+
rescue LoadError
|
31
|
+
puts "Jeweler not available. sudo gem install technicalpickles-jeweler --source=http://gems.github.com"
|
32
|
+
end
|
33
|
+
|
34
|
+
require File.expand_path('lib/twitter_search', File.dirname(__FILE__))
|
35
|
+
require 'rubygems'
|
36
|
+
require 'yaml'
|
37
|
+
|
38
|
+
namespace :yaml do
|
39
|
+
desc "Write Twitter Search results to yaml file so API is not hit every test."
|
40
|
+
task :write do
|
41
|
+
write_yaml :tweets => 'Obama', :file => 'obama'
|
42
|
+
write_yaml :tweets => 'twitter search', :file => 'twitter_search'
|
43
|
+
write_yaml :tweets => {:q => 'twitter search'}, :file => 'twitter_search_and'
|
44
|
+
write_yaml :tweets => {:q => '"happy hour"'}, :file => 'happy_hour_exact'
|
45
|
+
write_yaml :tweets => {:q => 'obama OR hillary'}, :file => 'obama_or_hillary'
|
46
|
+
write_yaml :tweets => {:q => 'beer -root'}, :file => 'beer_minus_root'
|
47
|
+
write_yaml :tweets => {:q => '#haiku'}, :file => 'hashtag_haiku'
|
48
|
+
write_yaml :tweets => {:q => 'from:alexiskold'}, :file => 'from_alexiskold'
|
49
|
+
write_yaml :tweets => {:q => 'to:techcrunch'}, :file => 'to_techcrunch'
|
50
|
+
write_yaml :tweets => {:q => '@mashable'}, :file => 'reference_mashable'
|
51
|
+
write_yaml :tweets => {:q => '"happy hour" near:"san francisco"'}, :file => 'happy_hour_near_sf'
|
52
|
+
write_yaml :tweets => {:q => 'near:NYC within:15mi'}, :file => 'within_15mi_nyc'
|
53
|
+
write_yaml :tweets => {:q => 'superhero since:2008-05-01'}, :file => 'superhero_since'
|
54
|
+
write_yaml :tweets => {:q => 'ftw until:2008-05-03'}, :file => 'ftw_until'
|
55
|
+
write_yaml :tweets => {:q => 'movie -scary :)'}, :file => 'movie_positive_tude'
|
56
|
+
write_yaml :tweets => {:q => 'flight :('}, :file => 'flight_negative_tude'
|
57
|
+
write_yaml :tweets => {:q => 'traffic ?'}, :file => 'traffic_question'
|
58
|
+
write_yaml :tweets => {:q => 'hilarious filter:links'}, :file => 'hilarious_links'
|
59
|
+
write_yaml :tweets => {:q => 'congratulations', :lang => 'en'}, :file => 'english'
|
60
|
+
write_yaml :tweets => {:q => 'با', :lang => 'ar'}, :file => 'arabic'
|
61
|
+
write_yaml :tweets => {:q => 'Boston Celtics', :rpp => '30'}, :file => 'results_per_page'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def write_yaml(opts = {})
|
66
|
+
@client = TwitterSearch::Client.new 'twitter-search'
|
67
|
+
tweets = @client.query(opts[:tweets])
|
68
|
+
File.open(File.join(File.dirname(__FILE__), 'test', 'yaml', "#{opts[:file]}.yaml"), 'w+') do |file|
|
69
|
+
file.puts tweets.to_yaml
|
70
|
+
end
|
71
|
+
end
|
data/TODO.markdown
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Add tests for:
|
2
|
+
|
3
|
+
* since_id: returns tweets with status ids greater than the given id.
|
4
|
+
* geocode: returns tweets by users located within a given radius of the given latitude/longitude, where the user's location is taken from their Twitter profile.
|
5
|
+
* show_user: when "true", adds "<user>:" to the beginning of the tweet. This is useful for readers that do not display Atom's author field. The default is "false".
|
6
|
+
* callback: if supplied, the response will use the JSONP format with a callback of the given name. E.g., http://search.twitter.com/search.json?callback=foo&q=twitter
|
7
|
+
|
data/VERSION.yml
ADDED
data/lib/twitter_search.rb
CHANGED
data/test/test_helper.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.expand_path('../lib/twitter_search', File.dirname(__FILE__))
|
2
|
+
require 'test/unit'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'shoulda'
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
class Test::Unit::TestCase
|
8
|
+
|
9
|
+
def self.should_have_default_search_behaviors
|
10
|
+
should_find_tweets
|
11
|
+
should_have_text_for_all_tweets
|
12
|
+
should_return_page 1
|
13
|
+
should_return_tweets_in_sets_of 15
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.should_find_tweets
|
17
|
+
should 'find tweets' do
|
18
|
+
assert @tweets.any?
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.should_have_text_for_all_tweets
|
23
|
+
should 'have text for all tweets' do
|
24
|
+
assert @tweets.all? { |tweet| tweet.text.size > 0 }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.should_return_page(number)
|
29
|
+
should "return page #{number}" do
|
30
|
+
assert_equal number, @tweets.page
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.should_return_tweets_in_sets_of(number)
|
35
|
+
should "return tweets in sets of #{number}" do
|
36
|
+
assert_equal number, @tweets.results_per_page
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,307 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), 'test_helper')
|
4
|
+
|
5
|
+
class TwitterSearchTest < Test::Unit::TestCase # :nodoc:
|
6
|
+
|
7
|
+
context "@client.query 'Obama'" do
|
8
|
+
setup do
|
9
|
+
@tweets = read_yaml :file => 'obama'
|
10
|
+
end
|
11
|
+
|
12
|
+
should_have_default_search_behaviors
|
13
|
+
|
14
|
+
should 'find tweets containing the single word "Obama"' do
|
15
|
+
assert @tweets.all? { |tweet| tweet.text =~ /obama/i }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "@client.query 'twitter search'" do
|
20
|
+
setup do
|
21
|
+
@tweets = read_yaml :file => 'twitter_search'
|
22
|
+
end
|
23
|
+
|
24
|
+
should_have_default_search_behaviors
|
25
|
+
|
26
|
+
should 'find tweets containing both "twitter" and "search"' do
|
27
|
+
assert @tweets.all?{ |t| t.text =~ /twitter/i && t.text =~ /search/i }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context "@client.query :q => 'twitter search'" do
|
32
|
+
setup do
|
33
|
+
@tweets = read_yaml :file => 'twitter_search_and'
|
34
|
+
end
|
35
|
+
|
36
|
+
should_have_default_search_behaviors
|
37
|
+
|
38
|
+
should 'find tweets containing both "twitter" and "search"' do
|
39
|
+
assert @tweets.all?{ |t| t.text =~ /twitter/i && t.text =~ /search/i }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# TWITTER SEARCH OPERATORS
|
44
|
+
|
45
|
+
context '@client.query :q => \'"happy hour"\'' do
|
46
|
+
setup do
|
47
|
+
@tweets = read_yaml :file => 'happy_hour_exact'
|
48
|
+
end
|
49
|
+
|
50
|
+
should_have_default_search_behaviors
|
51
|
+
|
52
|
+
should 'find tweets containing the exact phrase "happy hour"' do
|
53
|
+
assert @tweets.all?{ |t| t.text =~ /happy hour/i }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "@client.query :q => 'obama OR hillary'" do
|
58
|
+
setup do
|
59
|
+
@tweets = read_yaml :file => 'obama_or_hillary'
|
60
|
+
end
|
61
|
+
|
62
|
+
should_have_default_search_behaviors
|
63
|
+
|
64
|
+
should 'find tweets containing either "obama" or "hillary" (or both)' do
|
65
|
+
assert @tweets.all?{ |t| t.text =~ /obama/i || t.text =~ /hillary/i }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "@client.query :q => 'beer -root'" do
|
70
|
+
setup do
|
71
|
+
@tweets = read_yaml :file => 'beer_minus_root'
|
72
|
+
end
|
73
|
+
|
74
|
+
should_have_default_search_behaviors
|
75
|
+
|
76
|
+
should 'find tweets containing "beer" but not "root"' do
|
77
|
+
assert @tweets.all?{ |t| t.text =~ /beer/i || t.text !~ /root/i }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "@client.query :q => '#haiku'" do
|
82
|
+
setup do
|
83
|
+
@tweets = read_yaml :file => 'hashtag_haiku'
|
84
|
+
end
|
85
|
+
|
86
|
+
should_have_default_search_behaviors
|
87
|
+
|
88
|
+
should 'find tweets containing the hashtag "haiku"' do
|
89
|
+
assert @tweets.all?{ |t| t.text =~ /#haiku/i }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
context "@client.query :q => 'from: alexiskold'" do
|
94
|
+
setup do
|
95
|
+
@tweets = read_yaml :file => 'from_alexiskold'
|
96
|
+
end
|
97
|
+
|
98
|
+
should_have_default_search_behaviors
|
99
|
+
|
100
|
+
should 'find tweets sent from person "alexiskold"' do
|
101
|
+
assert @tweets.all?{ |t| t.from_user == 'alexiskold' }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context "@client.query :q => 'to:techcrunch'" do
|
106
|
+
setup do
|
107
|
+
@tweets = read_yaml :file => 'to_techcrunch'
|
108
|
+
end
|
109
|
+
|
110
|
+
should_have_default_search_behaviors
|
111
|
+
|
112
|
+
should 'find tweets sent to person "techcrunch"' do
|
113
|
+
assert @tweets.all?{ |t| t.text =~ /^@techcrunch/i }
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context "@client.query :q => '@mashable'" do
|
118
|
+
setup do
|
119
|
+
@tweets = read_yaml :file => 'reference_mashable'
|
120
|
+
end
|
121
|
+
|
122
|
+
should_have_default_search_behaviors
|
123
|
+
|
124
|
+
should 'find tweets referencing person "mashable"' do
|
125
|
+
assert @tweets.all?{ |t| t.text =~ /@mashable/i }
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context "@client.query :q => '\"happy hour\" near:\"san francisco\"'" do
|
130
|
+
setup do
|
131
|
+
@tweets = read_yaml :file => 'happy_hour_near_sf'
|
132
|
+
end
|
133
|
+
|
134
|
+
# The Twitter Search API makes you use the geocode parameter for location searching
|
135
|
+
should 'not find tweets using the near operator' do
|
136
|
+
assert ! @tweets.any?
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context "@client.query :q => 'near:NYC within:15mi'" do
|
141
|
+
setup do
|
142
|
+
@tweets = read_yaml :file => 'within_15mi_nyc'
|
143
|
+
end
|
144
|
+
|
145
|
+
# The Twitter Search API makes you use the geocode parameter for location searching
|
146
|
+
should 'not find tweets using the near operator' do
|
147
|
+
assert ! @tweets.any?
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context "@client.query :q => 'superhero since:2008-05-01'" do
|
152
|
+
setup do
|
153
|
+
@tweets = read_yaml :file => 'superhero_since'
|
154
|
+
end
|
155
|
+
|
156
|
+
should_have_default_search_behaviors
|
157
|
+
|
158
|
+
should 'find tweets containing "superhero" and sent since date "2008-05-01" (year-month-day)' do
|
159
|
+
assert @tweets.all?{ |t| t.text =~ /superhero/i && convert_date(t.created_at) > DateTime.new(2008, 5, 1) }
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context "@client.query :q => 'ftw until:2008-05-03'" do
|
164
|
+
setup do
|
165
|
+
@tweets = read_yaml :file => 'ftw_until'
|
166
|
+
end
|
167
|
+
|
168
|
+
should_have_default_search_behaviors
|
169
|
+
|
170
|
+
should 'find tweets containing "ftw" and sent up to date "2008-05-03"' do
|
171
|
+
assert @tweets.all?{ |t| t.text =~ /ftw/i && convert_date(t.created_at) < DateTime.new(2008, 5, 3, 11, 59) }
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
context "@client.query :q => 'movie -scary :)'" do
|
176
|
+
setup do
|
177
|
+
@tweets = read_yaml :file => 'movie_positive_tude'
|
178
|
+
end
|
179
|
+
|
180
|
+
should_have_default_search_behaviors
|
181
|
+
|
182
|
+
should 'find tweets containing "movie", but not "scary", and with a positive attitude' do
|
183
|
+
assert @tweets.all?{ |t| t.text =~ /movie/i && t.text !~ /scary/i && positive_attitude?(t.text) }
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
context "@client.query :q => 'flight :('" do
|
188
|
+
setup do
|
189
|
+
@tweets = read_yaml :file => 'flight_negative_tude'
|
190
|
+
end
|
191
|
+
|
192
|
+
should_have_default_search_behaviors
|
193
|
+
|
194
|
+
should 'find tweets containing "flight" and with a negative attitude' do
|
195
|
+
assert @tweets.all?{ |t| t.text =~ /flight/i && negative_attitude?(t.text) }
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
context "@client.query :q => 'traffic ?'" do
|
200
|
+
setup do
|
201
|
+
@tweets = read_yaml :file => 'traffic_question'
|
202
|
+
end
|
203
|
+
|
204
|
+
should_have_default_search_behaviors
|
205
|
+
|
206
|
+
should 'find tweets containing "traffic" and asking a question' do
|
207
|
+
assert @tweets.all?{ |t| t.text =~ /traffic/i && t.text.include?('?') }
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
context "@client.query :q => 'hilarious filter:links'" do
|
212
|
+
setup do
|
213
|
+
@tweets = read_yaml :file => 'hilarious_links'
|
214
|
+
end
|
215
|
+
|
216
|
+
should_have_default_search_behaviors
|
217
|
+
|
218
|
+
should 'find tweets containing "hilarious" and linking to URLs' do
|
219
|
+
assert @tweets.all?{ |t| t.text =~ /hilarious/i && hyperlinks?(t.text) }
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
# FOREIGN LANGUAGES
|
224
|
+
|
225
|
+
context "@client.query :q => 'congratulations', :lang => 'en'" do
|
226
|
+
setup do
|
227
|
+
@tweets = read_yaml :file => 'english'
|
228
|
+
end
|
229
|
+
|
230
|
+
should_have_default_search_behaviors
|
231
|
+
|
232
|
+
should 'find tweets containing "congratulations" and are in English' do
|
233
|
+
assert @tweets.all?{ |t| t.text =~ /congratulation/i && t.language == 'en' }
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
context "@client.query :q => 'با', :lang => 'ar'" do
|
238
|
+
setup do
|
239
|
+
@tweets = read_yaml :file => 'arabic'
|
240
|
+
end
|
241
|
+
|
242
|
+
should_have_default_search_behaviors
|
243
|
+
|
244
|
+
should 'find tweets containing "با" and are in Arabic' do
|
245
|
+
assert @tweets.all?{ |t| t.text.include?('با') && t.language == 'ar' }
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
# PAGINATION
|
250
|
+
|
251
|
+
context "@client.query :q => 'Boston Celtics', :rpp => '30'" do
|
252
|
+
setup do
|
253
|
+
@tweets = read_yaml :file => 'results_per_page'
|
254
|
+
end
|
255
|
+
|
256
|
+
should_find_tweets
|
257
|
+
should_have_text_for_all_tweets
|
258
|
+
should_return_page 1
|
259
|
+
should_return_tweets_in_sets_of 30
|
260
|
+
end
|
261
|
+
|
262
|
+
# HELPERS
|
263
|
+
|
264
|
+
context "@tweets[2]" do
|
265
|
+
setup do
|
266
|
+
@tweets = read_yaml :file => 'reference_mashable'
|
267
|
+
end
|
268
|
+
|
269
|
+
should_have_default_search_behaviors
|
270
|
+
|
271
|
+
should 'return the third tweet' do
|
272
|
+
assert_equal 859152168, @tweets[2].id
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
protected
|
277
|
+
|
278
|
+
def convert_date(date)
|
279
|
+
date = date.split(' ')
|
280
|
+
DateTime.new(date[3].to_i, convert_month(date[2]), date[1].to_i)
|
281
|
+
end
|
282
|
+
|
283
|
+
def convert_month(str)
|
284
|
+
months = { 'Jan' => 1, 'Feb' => 2, 'Mar' => 3, 'Apr' => 4,
|
285
|
+
'May' => 5, 'Jun' => 6, 'Jul' => 7, 'Aug' => 8,
|
286
|
+
'Sep' => 9, 'Oct' => 10, 'Nov' => 11, 'Dec' => 12 }
|
287
|
+
months[str]
|
288
|
+
end
|
289
|
+
|
290
|
+
def positive_attitude?(str)
|
291
|
+
str.include?(':)') || str.include?('=)') || str.include?(':-)') || str.include?(':D')
|
292
|
+
end
|
293
|
+
|
294
|
+
def negative_attitude?(str)
|
295
|
+
str.include?(':(') || str.include?('=(') || str.include?(':-(') || str.include?(':P')
|
296
|
+
end
|
297
|
+
|
298
|
+
def hyperlinks?(str)
|
299
|
+
str.include?('http://') || str.include?('https://')
|
300
|
+
end
|
301
|
+
|
302
|
+
def read_yaml(opts = {})
|
303
|
+
return if opts[:file].nil?
|
304
|
+
YAML.load_file File.join(File.dirname(__FILE__), 'yaml', "#{opts[:file]}.yaml")
|
305
|
+
end
|
306
|
+
|
307
|
+
end
|