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 CHANGED
@@ -10,7 +10,7 @@ Install the gem.
10
10
 
11
11
  Require the gem.
12
12
 
13
- require 'twitter-search'
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
@@ -0,0 +1,4 @@
1
+ ---
2
+ patch: 1
3
+ major: 0
4
+ minor: 5
@@ -34,6 +34,10 @@ module TwitterSearch
34
34
  def size
35
35
  @results.size
36
36
  end
37
+
38
+ def [](index)
39
+ @results[index]
40
+ end
37
41
  end
38
42
 
39
43
  class Client
@@ -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