devfu-twitter-search-watcher 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +21 -0
- data/Rakefile +68 -0
- data/VERSION +1 -0
- data/lib/twitter-search-watcher.rb +191 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +53 -0
- data/spec/twitter_search_watcher_spec.rb +140 -0
- metadata +61 -0
data/README.rdoc
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
= TwitterSearchWatcher
|
2
|
+
|
3
|
+
Sometimes, we want to continually search Twitter for something
|
4
|
+
like a hashtag to display the latest results on our sites.
|
5
|
+
|
6
|
+
TwitterSearchWatcher is a simple gem for continually searching
|
7
|
+
Twitter for something and calling a callback whenever new
|
8
|
+
tweets are present.
|
9
|
+
|
10
|
+
== Installation
|
11
|
+
|
12
|
+
sudo gem install json
|
13
|
+
sudo gem install devfu-twitter-search-watcher -s http://gems.github.com
|
14
|
+
|
15
|
+
== Usage
|
16
|
+
|
17
|
+
TwitterSearchWatcher.watch! '#laidoffcamp', :max_pages => 1 do |tweet|
|
18
|
+
puts "#{ tweet.from_user }: #{ tweet.text }"
|
19
|
+
end
|
20
|
+
|
21
|
+
For additional usage, see RDoc at http://code.devfu.com/twitter-search-watcher or the specs at http://github.com/devfu/twitter-search-watcher/blob/master/spec/twitter_search_watcher_spec.rb
|
data/Rakefile
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
require 'spec/rake/spectask'
|
5
|
+
|
6
|
+
puts "\nGem: twitter-search-watcher\n\n"
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'jeweler'
|
10
|
+
Jeweler::Tasks.new do |s|
|
11
|
+
s.name = 'twitter-search-watcher'
|
12
|
+
s.summary = 'for watching a Twitter search'
|
13
|
+
s.email = 'remi@remitaylor.com'
|
14
|
+
s.homepage = 'http://github.com/devfu/twitter-search-watcher'
|
15
|
+
s.description = 'for watching a particular Twitter search and calling code whenever there are new tweets'
|
16
|
+
s.authors = %w( remi )
|
17
|
+
s.files = FileList['[A-Z]*', '{lib,spec,bin,examples}/**/*']
|
18
|
+
# s.add_dependency 'person-gemname'
|
19
|
+
# s.executables << 'script'
|
20
|
+
# s.rubyforge_project = 'gemname'
|
21
|
+
# s.extra_rdoc_files = %w( README.rdoc )
|
22
|
+
end
|
23
|
+
rescue LoadError
|
24
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
25
|
+
end
|
26
|
+
|
27
|
+
Spec::Rake::SpecTask.new do |t|
|
28
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
29
|
+
end
|
30
|
+
|
31
|
+
desc "Run all examples with RCov"
|
32
|
+
Spec::Rake::SpecTask.new('rcov') do |t|
|
33
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
34
|
+
t.rcov = true
|
35
|
+
end
|
36
|
+
|
37
|
+
# require 'hanna'
|
38
|
+
# require 'darkfish-rdoc'
|
39
|
+
|
40
|
+
Rake::RDocTask.new do |rdoc|
|
41
|
+
rdoc.rdoc_dir = 'rdoc'
|
42
|
+
rdoc.title = 'twitter-search-watcher'
|
43
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
44
|
+
# rdoc.options += ["--template=#{`allison --path`}"] # sudo gem install allison
|
45
|
+
# rdoc.options += %w( -f darkfish ) # sudo gem install darkfish-rdoc
|
46
|
+
# rdoc.options += %w( -T hanna ) # sudo gem install mislav-hanna
|
47
|
+
rdoc.options += %w( -m README.rdoc ) # the initial page displayed
|
48
|
+
rdoc.rdoc_files.include('README.rdoc')
|
49
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
50
|
+
end
|
51
|
+
|
52
|
+
desc 'Confirm that gemspec is $SAFE'
|
53
|
+
task :safe do
|
54
|
+
require 'yaml'
|
55
|
+
require 'rubygems/specification'
|
56
|
+
data = File.read('twitter-search-watcher.gemspec')
|
57
|
+
spec = nil
|
58
|
+
if data !~ %r{!ruby/object:Gem::Specification}
|
59
|
+
Thread.new { spec = eval("$SAFE = 3\n#{data}") }.join
|
60
|
+
else
|
61
|
+
spec = YAML.load(data)
|
62
|
+
end
|
63
|
+
spec.validate
|
64
|
+
puts spec
|
65
|
+
puts "OK"
|
66
|
+
end
|
67
|
+
|
68
|
+
task :default => :spec
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
@@ -0,0 +1,191 @@
|
|
1
|
+
%w( rubygems cgi json open-uri ostruct ).each {|lib| require lib }
|
2
|
+
|
3
|
+
class TwitterSearchWatcher
|
4
|
+
|
5
|
+
TWITTER_SEARCH_URL = 'http://search.twitter.com/search.json'
|
6
|
+
DEFAULT_USER_AGENT = 'TwitterSearchWatcher RubyGem http://github.com/devfu/twitter-search-watcher'
|
7
|
+
QUERY_STRING_ATTRIBUTES = [ :q, :to, :from, :since_id, :page, :max_id, :rpp ]
|
8
|
+
|
9
|
+
# The User-Agent header value to send along with all Twitter Search API requests
|
10
|
+
attr_accessor :user_agent
|
11
|
+
|
12
|
+
# A string you want to search twitter for
|
13
|
+
attr_accessor :q
|
14
|
+
|
15
|
+
# The username of someone you want to search replies to
|
16
|
+
attr_accessor :to
|
17
|
+
|
18
|
+
# The username of someone you want to search replies from
|
19
|
+
attr_accessor :from
|
20
|
+
|
21
|
+
# Get a particular page of Twitter search results (pagination).
|
22
|
+
# Typically used in conjunction with :max_id
|
23
|
+
attr_accessor :page
|
24
|
+
|
25
|
+
# Used for pagination, so you can get page=3 where the max_id of the first page was 1234
|
26
|
+
attr_accessor :max_id
|
27
|
+
|
28
|
+
# Only get tweets with ID's greater than this ID (useful for only getting new tweets)
|
29
|
+
attr_accessor :since_id
|
30
|
+
|
31
|
+
# Number of results per page (max 100)
|
32
|
+
attr_accessor :rpp
|
33
|
+
|
34
|
+
# The number of seconds to wait between Twitter calls. Default: 60 (seconds)
|
35
|
+
attr_accessor :check_every
|
36
|
+
|
37
|
+
# The maximum number of pages to check for tweets
|
38
|
+
#
|
39
|
+
# If nil, we'll check until there are no more pages (when :next_page isn't present)
|
40
|
+
attr_accessor :max_pages
|
41
|
+
|
42
|
+
def rpp= value
|
43
|
+
raise "The maximum rpp (Results per Page) value is 100" if value > 100
|
44
|
+
@rpp = value
|
45
|
+
end
|
46
|
+
|
47
|
+
def check_every
|
48
|
+
@check_every || 60
|
49
|
+
end
|
50
|
+
|
51
|
+
# Create a new TwitterSearchWatcher
|
52
|
+
#
|
53
|
+
# TwitterSearchWatcher.new 'string to search'
|
54
|
+
# TwitterSearchWatcher.new 'string to search', :check_every => 60
|
55
|
+
# TwitterSearchWatcher.new :to => 'barackobama', :from => 'SenJohnMcCain'
|
56
|
+
#
|
57
|
+
def initialize search_string = nil, options = nil
|
58
|
+
if search_string.is_a? Hash
|
59
|
+
options = search_string
|
60
|
+
else
|
61
|
+
self.q = search_string
|
62
|
+
end
|
63
|
+
|
64
|
+
options.each {|k,v| send "#{k}=", v } if options
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns the URL we'll use to call the Twitter Search API.
|
68
|
+
#
|
69
|
+
# Without parameters, it'll generate a URL just from this TwitterSearchWatcher instance.
|
70
|
+
#
|
71
|
+
# With parameters, it'll override the TwitterSearchWatcher instance's options with
|
72
|
+
# whatever you pass, eg.
|
73
|
+
#
|
74
|
+
# >> TwitterSearchWatcher.new( 'foo', :rpp => 15 ).search_url
|
75
|
+
# => "http://search.twitter.com/search.json?q=foo&rpp=15"
|
76
|
+
#
|
77
|
+
# >> TwitterSearchWatcher.new( 'foo', :rpp => 15 ).search_url( :rpp => 99 )
|
78
|
+
# => "http://search.twitter.com/search.json?q=foo&rpp=99"
|
79
|
+
#
|
80
|
+
def search_url additional_parameters = nil
|
81
|
+
TWITTER_SEARCH_URL + build_query_string(additional_parameters)
|
82
|
+
end
|
83
|
+
|
84
|
+
def user_agent
|
85
|
+
@user_agent || DEFAULT_USER_AGENT
|
86
|
+
end
|
87
|
+
|
88
|
+
# Performs a search. Accepts the same parameters as #search_url
|
89
|
+
def search! additional_parameters = nil
|
90
|
+
JSON.parse open( search_url(additional_parameters), 'User-Agent' => user_agent ).read
|
91
|
+
end
|
92
|
+
|
93
|
+
# Performs a search, given the response from another search.
|
94
|
+
#
|
95
|
+
# If a response if given, the search will only return tweets newer than the given response's tweets.
|
96
|
+
# If a response is not given, this performs a normal search.
|
97
|
+
#
|
98
|
+
# Accepts additional parameters (same as #search_url)
|
99
|
+
def search_newer! response = nil, additional_parameters = nil
|
100
|
+
if response
|
101
|
+
search!( (additional_parameters || {}).merge( :since_id => response['max_id'] ) )
|
102
|
+
else
|
103
|
+
search! additional_parameters
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Performs a search, given the response from another search.
|
108
|
+
#
|
109
|
+
# If the response given is paginated (ie. there are additional tweets available on additional pages),
|
110
|
+
# this will return the next page. Else, this will return nil.
|
111
|
+
#
|
112
|
+
# Accepts additional parameters (same as #search_url)
|
113
|
+
def search_more! response, additional_parameters = nil
|
114
|
+
search!( (additional_parameters || {}).merge( :page => (response['page'] + 1), :max_id => response['max_id'] ) ) if response['next_page']
|
115
|
+
end
|
116
|
+
|
117
|
+
# Instantiates a new TwitterSearchWatcher given the search_string and options and then
|
118
|
+
# calls #watch on the instance using the block given.
|
119
|
+
def self.watch! search_string, options = nil, &block
|
120
|
+
watcher = TwitterSearchWatcher.new search_string, options
|
121
|
+
watcher.watch! &block
|
122
|
+
end
|
123
|
+
|
124
|
+
# Starts watching this search in a loop.
|
125
|
+
# It will wait #check_every seconds between new requests (except requests to get additional pages).
|
126
|
+
# Every time a new tweet is found, that tweet is passed to the block given.
|
127
|
+
#
|
128
|
+
# TwitterSearchWatcher.new('foo').watch! {|tweet| puts "got tweet: #{ tweet.text }" }
|
129
|
+
#
|
130
|
+
def watch! additional_parameters = nil, &block
|
131
|
+
@max_id_found_so_far = 0
|
132
|
+
|
133
|
+
trap('INT'){ puts "\nexiting ..."; exit }
|
134
|
+
puts "Watching for tweets: #{ search_url(additional_parameters) }"
|
135
|
+
|
136
|
+
loop do
|
137
|
+
|
138
|
+
@last_response = search_newer!(@last_response, additional_parameters)
|
139
|
+
call_tweet_callbacks(@last_response, block)
|
140
|
+
update_max_id @last_response
|
141
|
+
|
142
|
+
# this is kindof icky ... but it works
|
143
|
+
if @last_response['next_page']
|
144
|
+
response = @last_response
|
145
|
+
num_pages_searched = 0
|
146
|
+
while (response = search_more!(response, additional_parameters)) && (num_pages_searched <= max_pages if max_pages)
|
147
|
+
num_pages_searched += 1
|
148
|
+
call_tweet_callbacks(response, block)
|
149
|
+
update_max_id response
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
sleep check_every
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
private
|
158
|
+
|
159
|
+
def update_max_id response
|
160
|
+
@max_id_found_so_far = response['max_id'] if response['max_id'] > @max_id_found_so_far
|
161
|
+
end
|
162
|
+
|
163
|
+
def call_tweet_callbacks response, block
|
164
|
+
response['results'].each do |tweet|
|
165
|
+
block.call OpenStruct.new(tweet)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def escape string
|
170
|
+
CGI.escape(string.to_s).gsub('%22','"').gsub(' ','+')
|
171
|
+
end
|
172
|
+
|
173
|
+
def build_query_string additional_parameters = nil
|
174
|
+
parameter_values = QUERY_STRING_ATTRIBUTES.inject({}){|all, attr|
|
175
|
+
all[attr] = send(attr) if send(attr)
|
176
|
+
all
|
177
|
+
}
|
178
|
+
|
179
|
+
# if additional parameters are passed, we override the watcher's parameters with these
|
180
|
+
if additional_parameters
|
181
|
+
additional_parameter_values = QUERY_STRING_ATTRIBUTES.inject({}){|all, attr|
|
182
|
+
all[attr] = additional_parameters[attr] if additional_parameters.keys.include?(attr)
|
183
|
+
all
|
184
|
+
}
|
185
|
+
parameter_values.merge! additional_parameter_values
|
186
|
+
end
|
187
|
+
|
188
|
+
'?' + parameter_values.map {|k,v| "#{ k }=#{ escape(v) }" if v }.compact.join('&')
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
data/spec/spec.opts
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../lib/twitter-search-watcher'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'spec'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
def readable value
|
7
|
+
OpenStruct.new({ :read => value })
|
8
|
+
end
|
9
|
+
|
10
|
+
def fake_response_for watcher
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
def fake_response options = {}
|
15
|
+
readable(
|
16
|
+
{
|
17
|
+
'max_id' => 1234,
|
18
|
+
'since_id' => 0,
|
19
|
+
'total' => 15, # doesn't appear if next_page present?
|
20
|
+
'next_page' => '?page=2&max_id=1234&q=remitaylor', # only appears if there are additional pages
|
21
|
+
'refresh_url' => '?since_id=1234&q=remitaylor',
|
22
|
+
'page' => 1,
|
23
|
+
'results_per_page' => 15,
|
24
|
+
'completed_in' => 0.1234,
|
25
|
+
'query' => 'remitaylor',
|
26
|
+
'results' => fake_tweets
|
27
|
+
}.merge(options).to_json
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def fake_tweets
|
32
|
+
[ fake_tweet, fake_tweet ]
|
33
|
+
end
|
34
|
+
|
35
|
+
def fake_tweet
|
36
|
+
{
|
37
|
+
"created_at"=>"Wed, 05 Aug 2009 03:54:03 +0000",
|
38
|
+
"profile_image_url"=>"http://s3.amazonaws.com/twitter_production/profile_images/346217637/tiltshiftdino_normal.jpg",
|
39
|
+
"from_user"=>"BaddMann",
|
40
|
+
"to_user_id"=>549282,
|
41
|
+
"text"=>"@remitaylor What did you think of the badges at defcon Did you try anything with your badge",
|
42
|
+
"id" => (rand * 1000000000).to_i,
|
43
|
+
"from_user_id"=>160309,
|
44
|
+
"to_user"=>"remitaylor",
|
45
|
+
"iso_language_code"=>"en",
|
46
|
+
"source"=>"<a href="http://twitter.com/">web</a>"}
|
47
|
+
end
|
48
|
+
|
49
|
+
Spec::Matchers.define :end_with do |expected|
|
50
|
+
match do |actual|
|
51
|
+
actual.end_with? expected
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
|
3
|
+
describe TwitterSearchWatcher do
|
4
|
+
|
5
|
+
it 'should be able to initialize with a search string' do
|
6
|
+
TwitterSearchWatcher.new('foo').search_url.should == 'http://search.twitter.com/search.json?q=foo'
|
7
|
+
TwitterSearchWatcher.new('#foo').search_url.should == 'http://search.twitter.com/search.json?q=%23foo'
|
8
|
+
TwitterSearchWatcher.new('@foo').search_url.should == 'http://search.twitter.com/search.json?q=%40foo'
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should be able to search for replies :to someone' do
|
12
|
+
TwitterSearchWatcher.new( :to => 'remitaylor' ).search_url.should end_with('search.json?to=remitaylor')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should be able to search for tweets :from someone' do
|
16
|
+
TwitterSearchWatcher.new( :from => 'remitaylor' ).search_url.should end_with('search.json?from=remitaylor')
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should be able to search for tweets :from someone that are :to someone' do
|
20
|
+
watcher = TwitterSearchWatcher.new :from => 'remitaylor', :to => 'wickd_wanda'
|
21
|
+
watcher.search_url.should include('from=remitaylor')
|
22
|
+
watcher.search_url.should include('to=wickd_wanda')
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'should have a good default User-Agent' do
|
26
|
+
watcher = TwitterSearchWatcher.new
|
27
|
+
watcher.user_agent.should == 'TwitterSearchWatcher RubyGem http://github.com/devfu/twitter-search-watcher'
|
28
|
+
watcher.user_agent = 'foo'
|
29
|
+
watcher.user_agent.should == 'foo'
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should be able to override User-Agent' do
|
33
|
+
watcher = TwitterSearchWatcher.new 'x', :user_agent => 'bar'
|
34
|
+
watcher.search_url.should == 'http://search.twitter.com/search.json?q=x'
|
35
|
+
watcher.user_agent.should == 'bar'
|
36
|
+
|
37
|
+
watcher = TwitterSearchWatcher.new :user_agent => 'bar'
|
38
|
+
watcher.search_url.should == 'http://search.twitter.com/search.json?'
|
39
|
+
watcher.user_agent.should == 'bar'
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should allow quotes in query string' do
|
43
|
+
TwitterSearchWatcher.new('"hello there"').search_url.should end_with('q="hello+there"')
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should replace all spaces in query string with + signs' do
|
47
|
+
TwitterSearchWatcher.new('hello there').search_url.should end_with('q=hello+there')
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should be able to execute search' do
|
51
|
+
watcher = TwitterSearchWatcher.new 'chunky bacon'
|
52
|
+
watcher.should_receive(:open).with(watcher.search_url, 'User-Agent' => watcher.user_agent).and_return(fake_response)
|
53
|
+
response = watcher.search!
|
54
|
+
response.should be_a_kind_of(Hash)
|
55
|
+
response['query'].should == 'remitaylor' # make sure it's getting our fake date
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should be able to request a particular :page (pagination)' do
|
59
|
+
TwitterSearchWatcher.new( 'foo', :page => 2 ).search_url.should include("page=2")
|
60
|
+
TwitterSearchWatcher.new( 'foo', :page => 2 ).search_url.should include("q=foo")
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should be able to request since a particular twitter ID (:since_id)' do
|
64
|
+
TwitterSearchWatcher.new( 'foo', :since_id => 1234 ).search_url.should include("since_id=1234")
|
65
|
+
TwitterSearchWatcher.new( 'foo', :since_id => 1234 ).search_url.should include("q=foo")
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'should be able to request with a particular :max_id (used with :page)' do
|
69
|
+
TwitterSearchWatcher.new( 'foo', :max_id => 4321 ).search_url.should include("max_id=4321")
|
70
|
+
TwitterSearchWatcher.new( 'foo', :max_id => 4321 ).search_url.should include("q=foo")
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should be able to request with a particular :rpp (results_per_page)' do
|
74
|
+
TwitterSearchWatcher.new( 'foo', :rpp => 90 ).search_url.should include("rpp=90")
|
75
|
+
TwitterSearchWatcher.new( 'foo', :rpp => 90 ).search_url.should include("q=foo")
|
76
|
+
|
77
|
+
lambda { TwitterSearchWatcher.new( 'foo', :rpp => 101 ) }.should raise_error(/100/)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'should be able to search!(:rpp => 15) ... inotherwords, should be able to pass additional parameters to search!' do
|
81
|
+
watcher = TwitterSearchWatcher.new 'foo'
|
82
|
+
|
83
|
+
watcher.should_receive(:open).with(/99/, 'User-Agent' => watcher.user_agent).and_return(fake_response)
|
84
|
+
watcher.search! :rpp => 99
|
85
|
+
|
86
|
+
watcher.should_receive(:open).with(/foo/, 'User-Agent' => watcher.user_agent).and_return(fake_response)
|
87
|
+
watcher.search! :rpp => 99
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should be able to search!(:rpp => nil) to override an existing parameter' do
|
91
|
+
watcher = TwitterSearchWatcher.new 'foo', :rpp => 50
|
92
|
+
watcher.search_url.should include('rpp=50')
|
93
|
+
watcher.search_url.should include('q=foo')
|
94
|
+
|
95
|
+
# double check that normal overrides are working properly
|
96
|
+
watcher.search_url( :rpp => 99 ).should_not include('rpp=50')
|
97
|
+
watcher.search_url( :rpp => 99 ).should include('rpp=99')
|
98
|
+
|
99
|
+
watcher.search_url( :rpp => nil ).should_not include('rpp=50')
|
100
|
+
watcher.search_url( :rpp => nil ).should_not include('rpp')
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should be able to search_newer!(result_set) to get results newer than a given result set' do
|
104
|
+
watcher = TwitterSearchWatcher.new 'chunky bacon'
|
105
|
+
watcher.should_receive(:open).with(watcher.search_url, 'User-Agent' => watcher.user_agent).and_return(fake_response(:max_id => 5555))
|
106
|
+
response = watcher.search!
|
107
|
+
|
108
|
+
watcher.should_receive(:open).with(/since_id=5555/, 'User-Agent' => watcher.user_agent).and_return(fake_response)
|
109
|
+
watcher.search_newer! response
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should be able to search_more!(result_set) to get paginated results' do
|
113
|
+
watcher = TwitterSearchWatcher.new 'chunky bacon'
|
114
|
+
watcher.should_receive(:open).with(watcher.search_url, 'User-Agent' => watcher.user_agent).and_return(fake_response(:page => 1, :max_id => 444, :next_page => '?page=2&max_id=444&q=chunky+bacon'))
|
115
|
+
response = watcher.search!
|
116
|
+
|
117
|
+
watcher.should_receive(:open).with(/page=2/, 'User-Agent' => watcher.user_agent).and_return(fake_response(:page => 2, :max_id => 445, :next_page => nil))
|
118
|
+
response2 = watcher.search_more! response
|
119
|
+
watcher.should_receive(:open).with(/max_id=444/, 'User-Agent' => watcher.user_agent).and_return(fake_response(:page => 2, :max_id => 445, :next_page => nil))
|
120
|
+
response2 = watcher.search_more! response
|
121
|
+
|
122
|
+
watcher.should_not_receive(:open)
|
123
|
+
watcher.search_more!( response2 ).should be_nil # because no next_page
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'should be able to watch! on a watcher' do
|
127
|
+
# it's a PITA to test the loop, so i'm not currently testing that check_every and max_pages are actually used correctly
|
128
|
+
watcher = TwitterSearchWatcher.new 'chunky bacon', :check_every => 120, :max_pages => 5
|
129
|
+
watcher.check_every.should == 120
|
130
|
+
watcher.max_pages.should == 5
|
131
|
+
watcher.should respond_to(:watch!)
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'default rpp should be 100 (the max)'
|
135
|
+
|
136
|
+
it 'should have a nice method to call that will make a new watcher and start watching'
|
137
|
+
|
138
|
+
it 'should be able to watch! with pagination (will search! while there is a :next_page to request)'
|
139
|
+
|
140
|
+
end
|
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: devfu-twitter-search-watcher
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- remi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-08-06 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: for watching a particular Twitter search and calling code whenever there are new tweets
|
17
|
+
email: remi@remitaylor.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.rdoc
|
24
|
+
files:
|
25
|
+
- README.rdoc
|
26
|
+
- Rakefile
|
27
|
+
- VERSION
|
28
|
+
- lib/twitter-search-watcher.rb
|
29
|
+
- spec/spec.opts
|
30
|
+
- spec/spec_helper.rb
|
31
|
+
- spec/twitter_search_watcher_spec.rb
|
32
|
+
has_rdoc: false
|
33
|
+
homepage: http://github.com/devfu/twitter-search-watcher
|
34
|
+
licenses:
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options:
|
37
|
+
- --charset=UTF-8
|
38
|
+
require_paths:
|
39
|
+
- lib
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
version: "0"
|
45
|
+
version:
|
46
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: "0"
|
51
|
+
version:
|
52
|
+
requirements: []
|
53
|
+
|
54
|
+
rubyforge_project:
|
55
|
+
rubygems_version: 1.3.5
|
56
|
+
signing_key:
|
57
|
+
specification_version: 3
|
58
|
+
summary: for watching a Twitter search
|
59
|
+
test_files:
|
60
|
+
- spec/twitter_search_watcher_spec.rb
|
61
|
+
- spec/spec_helper.rb
|