dancroak-twitter-search 0.5 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/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