jsl-osprey 0.0.8 → 0.0.8.2

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 Justin S. Leitgeb
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc CHANGED
@@ -9,14 +9,14 @@ by storing the ids of previous tweets in a global pool which can easily be share
9
9
 
10
10
  = Installation
11
11
 
12
- sudo gem install osprey
12
+ sudo gem install jsl-osprey
13
13
 
14
14
  = Usage
15
15
 
16
16
  Basic usage of Osprey is simple, and more advanced usage is easily accomodated by the accepted options and the
17
17
  ability to write custom backend modules.
18
18
 
19
- In more basic cases, you can use Osprey as follows:
19
+ In more basic cases, you can use Osprey::Search (the main interface to Osprey) as follows:
20
20
 
21
21
  require 'osprey'
22
22
 
@@ -35,6 +35,8 @@ Note that because this is done, in most cases "results" will be the same as "new
35
35
  guarantee that results arrive in order, however, Osprey does checking to make sure that new_results really aren't
36
36
  ones that we've seen before.
37
37
 
38
+ See the documentation for Osprey::Search for more information on providing options for the backend configuration of Osprey.
39
+
38
40
  = Processing Details
39
41
 
40
42
  Osprey implements a simple but effective algorithm for keeping track of which tweets are new and which have been seen before.
data/Rakefile ADDED
@@ -0,0 +1,42 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+
4
+ require 'rake'
5
+ require 'spec/rake/spectask'
6
+ require 'rake/rdoctask'
7
+
8
+ require 'lib/osprey'
9
+
10
+ desc 'Test the plugin.'
11
+ Spec::Rake::SpecTask.new(:spec) do |t|
12
+ t.spec_opts = ["--format", "progress", "--colour"]
13
+ t.libs << 'lib'
14
+ t.verbose = true
15
+ end
16
+
17
+ begin
18
+ require 'jeweler'
19
+ Jeweler::Tasks.new do |gemspec|
20
+ gemspec.name = "osprey"
21
+ gemspec.summary = "A Twitter API that keeps track of tweets you've seen"
22
+ gemspec.email = "justin@phq.org"
23
+ gemspec.homepage = "http://github.com/jsl/osprey"
24
+ gemspec.description = "Osprey interfaces with the Twitter search API and keeps track of tweets you've seen"
25
+ gemspec.authors = ["Justin Leitgeb"]
26
+ end
27
+ rescue LoadError
28
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
29
+ end
30
+
31
+ desc "Run all the tests"
32
+ task :default => :spec
33
+
34
+ desc 'Generate documentation'
35
+ Rake::RDocTask.new(:rdoc) do |rdoc|
36
+ rdoc.rdoc_dir = 'rdoc'
37
+ rdoc.title = 'Osprey'
38
+ rdoc.options << '--line-numbers' << '--inline-source'
39
+ rdoc.rdoc_files.include('README.rdoc')
40
+ rdoc.rdoc_files.include('lib/osprey/**/*.rb')
41
+ end
42
+
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 5
3
+ :major: 0
4
+ :minor: 0
@@ -0,0 +1,8 @@
1
+ class Array
2
+
3
+ # From Rails' ActiveSupport library
4
+ def extract_options!
5
+ last.is_a?(::Hash) ? pop : {}
6
+ end
7
+
8
+ end
@@ -0,0 +1,17 @@
1
+ class Hash
2
+ # Returns a Hash containing only input keys.
3
+ # Method from merb-core.
4
+ def except(*rejected)
5
+ reject { |k,v| rejected.include?(k) }
6
+ end
7
+
8
+ def reverse_merge(other_hash)
9
+ other_hash.merge(self)
10
+ end
11
+
12
+ # Returns a new hash containing only the input keys.
13
+ # Method from merb-core.
14
+ def only(*allowed)
15
+ reject { |k,v| !allowed.include?(k) }
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ module Osprey
2
+
3
+ # Container for sets of tweets.
4
+ class ResultSet < Array
5
+ attr_accessor :etag
6
+
7
+ # Returns all records. In most cases, this will be the same as the results
8
+ # in #new_records, since we add the id of the last tweet fetched to the query
9
+ # string automatically. If Twitter somehow sends us a Tweet that we saw in a
10
+ # previous fetch, however, this result will return all records indiscriminately
11
+ # while the method #new_records will be populated with tweets that are really
12
+ # ones that we haven't seen before.
13
+ def records
14
+ self
15
+ end
16
+
17
+ # Returns only records that are new in the most recent fetch.
18
+ def new_records
19
+ self.select{|r| r.new_record == true}
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,177 @@
1
+ module Osprey
2
+
3
+ # Primary interface to the Osprey Twitter search library.
4
+ class Search
5
+ attr_reader :term
6
+
7
+ DEFAULTS = {
8
+ :backend => {
9
+ :moneta_klass => 'Moneta::Memory',
10
+ },
11
+ :rpp => 50,
12
+ :preserved_tweet_ids => 10_000
13
+ } unless defined?(DEFAULTS)
14
+
15
+ # Initializes the Osprey::Search client.
16
+ #
17
+ # Usage (Basic):
18
+ #
19
+ # search = Osprey::Search.new(term, options)
20
+ # results = search.fetch
21
+ #
22
+ # Usage (Custom Backend):
23
+ #
24
+ # o = Osprey::Search.new('Swine Flu', { :backend => { :moneta_klass => 'Moneta::Memcache', :server => 'localhost:1978' } })
25
+ # results = o.fetch
26
+ #
27
+ # Options:
28
+ #
29
+ # The Osprey::Search library supports options for customizing the parameters that will be passed to Twitter as well
30
+ # as to the local backend for keeping track of state.
31
+ #
32
+ # - _backend_ - A hash of options which are passed directly to the Moneta class used as key-value store. The
33
+ # value for this option should be another Hash containing configuration options for the backend. Keys supported
34
+ # by the backend hash:
35
+ #
36
+ # - _moneta_backend_ - The backend used for storing the serialized representation of the last fetch of
37
+ # this query. Uses Moneta, a unified interface to key-value storage systems. This value may be a String,
38
+ # in which case the appropriate Moneta library will be automatically required, or a class constant. If a
39
+ # class constant is given, it is up to the calling user to require the appropriate Moneta library before referring
40
+ # to the constant in the initialization of Osprey::Search.
41
+ #
42
+ # - _other_options_ - Any other options given to the backend hash will be passed directly to the Moneta class
43
+ # used as key-value store for configuration. Please see the documentation for the appropriate Moneta class for
44
+ # options supported.
45
+ #
46
+ # - _preserved_tweet_ids_ - The number of Tweet ids that will be preserved. This ensures that we're able to detect if
47
+ # running different queries returns the same tweet in order to not mark that tweet as a new
48
+ # record. Choosing a higher number here will mean some additional processing time for each
49
+ # new tweet, and a small amount of increased storage. The default should be a decent compromise
50
+ # between performance needs while still not seeing duplicate tweets show up as new records.
51
+ #
52
+ # - _rpp_ - Requests per page. Determines the results to fetch from Twitter. Defaults to 50.
53
+ #
54
+ # - _since_id_ - Tells twitter to only give us results with an ID greater than the given ID. Supplied by
55
+ # default in URL string if previous results were found.
56
+ def initialize(term, options = { })
57
+ @term = term
58
+ @options = options.reverse_merge(DEFAULTS)
59
+ @backend = initialize_backend(@options[:backend][:moneta_klass], @options[:backend].except(:moneta_klass))
60
+ end
61
+
62
+ # Returns a Osprey::ResultSet object containing Osprey::Tweet objects for the tweets found for query.
63
+ def fetch
64
+ p_results = previous_results
65
+
66
+ res = Curl::Easy.perform(url(p_results))
67
+
68
+ if res.response_code == 200
69
+ parse_tweets(res, p_results)
70
+ else
71
+ $stderr.puts "Received invalid twitter response code of #{res.response_code}."
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ # Initializes the structure that keeps track of the Twitter ids that we've seen.
78
+ def initialize_backend(klass, options)
79
+ HashBack::Backend.new('Osprey', klass, options)
80
+ end
81
+
82
+ # Returns an Array of ids for the Tweets in result
83
+ def twitter_ids_from_json_result(json)
84
+ json['results'].map{|r| r['id'].to_i }
85
+ end
86
+
87
+ def twitter_ids_in(id_list)
88
+ id_list
89
+ end
90
+
91
+ # Determines the etag from the request headers. Method from Paul Dix's Feedzirra.
92
+ #
93
+ # === Parameters
94
+ # [header<String>] Raw request header returned from the request
95
+ # === Returns
96
+ # A string of the etag or nil if it cannot be found in the headers.
97
+ def etag_from_header(header)
98
+ header =~ /.*ETag:\s(.*)\r/
99
+ $1
100
+ end
101
+
102
+ def parse_tweets(curl, p_results)
103
+ json = JSON.parse(curl.body_str)
104
+
105
+ res = Osprey::ResultSet.new
106
+ id_pool = twitter_id_pool
107
+
108
+ json['results'].each do |t|
109
+ new_record = p_results.detect{|rec| rec.twitter_id == t['id'] }.nil? && !id_pool.include?(t['id'])
110
+ res << Osprey::Tweet.new(t.merge(:new_record => new_record, :twitter_id => t['id']))
111
+ end
112
+
113
+ add_to_twitter_id_pool(res.new_records)
114
+
115
+ # Store the results from this fetch, unless this set is empty and the previous set is not empty.
116
+ if res.empty? && !p_results.nil?
117
+ res = p_results
118
+ res.each{|r| r.new_record = false}
119
+ end
120
+
121
+ @backend[url_key] = res
122
+
123
+ res
124
+ end
125
+
126
+ # Returns an Array of recently-fetched Twitter IDS, or an empty Array
127
+ # if the backend doesn't have any previous results.
128
+ def twitter_id_pool
129
+ # This is a hack to deal with the fact that the backend seems to return an empty Hash when
130
+ # a Moneta::Memory store has an uninitialized value. TODO - take a closer look at this later
131
+ # and see if Moneta needs to be patched or if it's something screwy in the way we're using it.
132
+ res = @backend[twitter_id_pool_key]
133
+ res.nil? || res == { } ? [ ] : res
134
+ end
135
+
136
+ # Adds the given new tweet ids to the twitter id pool so that subsequent requests,
137
+ # even if they're for other terms, won't see these tweets as new.
138
+ def add_to_twitter_id_pool(tweets)
139
+ pool = twitter_id_pool
140
+ @backend[twitter_id_pool_key] =
141
+ ( pool | tweets.map{|t| t.twitter_id} ).sort.reverse.uniq[1..@options[:preserved_tweet_ids]]
142
+ end
143
+
144
+ def twitter_id_pool_key
145
+ %w[twitter id pool].join('-')
146
+ end
147
+
148
+ # Returns an Array of results from the previous fetch, or an empty
149
+ # Array if no previous results are detected.
150
+ def previous_results
151
+ @backend[url_key] || [ ]
152
+ end
153
+
154
+ # Returns the backend string that will be used for the storage of this term.
155
+ # Effectively namespaces the urls so that we can use the backend modules for
156
+ # storage of other things like a pool of recently-read twitter ids.
157
+ def url_key
158
+ term_hash = MD5.hexdigest(@term)
159
+ ['term', term_hash].join('-')
160
+ end
161
+
162
+ # Returns the Twitter search URL for @term. Merges the last response ID into the query string
163
+ # if we have previous results for this query. This eliminates most duplication from previous
164
+ # results and decreases transfer size.
165
+ def url(previous_response)
166
+ url = "http://search.twitter.com/search.json?rpp=#{@options[:rpp]}&q=#{CGI.escape(@term)}"
167
+
168
+ unless previous_response.nil? || previous_response.empty?
169
+ last_response_id = previous_response.records.map{|r| r.twitter_id}.sort.last
170
+ url += "&since_id=#{last_response_id}"
171
+ end
172
+
173
+ url
174
+ end
175
+
176
+ end
177
+ end
@@ -0,0 +1,7 @@
1
+ module Osprey
2
+
3
+ # Basic container for a Tweet result.
4
+ class Tweet < OpenStruct
5
+ end
6
+
7
+ end
data/lib/osprey.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require 'singleton'
3
+ require 'curb'
4
+ require 'hashback'
5
+ require 'json'
6
+ require 'md5'
7
+ require 'memcache'
8
+ require 'ostruct'
9
+ require 'cgi'
10
+
11
+ lib_dirs = [ 'core_ext', 'osprey' ].map do |d|
12
+ File.join(File.dirname(__FILE__), d)
13
+ end
14
+
15
+ lib_dirs.each do |d|
16
+ Dir[File.join(d, "*.rb")].each {|file| require file }
17
+ end
data/osprey.gemspec ADDED
@@ -0,0 +1,50 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{osprey}
5
+ s.version = "0.0.8.2"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Justin Leitgeb"]
9
+ s.date = %q{2009-05-05}
10
+ s.description = %q{Osprey interfaces with the Twitter search API and keeps track of tweets you've seen}
11
+ s.email = %q{justin@phq.org}
12
+ s.extra_rdoc_files = [
13
+ "README.rdoc"
14
+ ]
15
+
16
+ s.files = ["lib/core_ext/array.rb", "lib/core_ext/hash.rb", "lib/osprey/result_set.rb",
17
+ "lib/osprey/search.rb", "lib/osprey/tweet.rb", "lib/osprey.rb", "LICENSE",
18
+ "osprey.gemspec", "Rakefile", "README.rdoc", "spec/fixtures/swine_flu1.json", "spec/fixtures/swine_flu2.json",
19
+ "spec/fixtures/swine_flu_result_set1.yml", "spec/fixtures/swine_flu_result_set2.yml",
20
+ "spec/osprey/result_set_spec.rb", "spec/osprey/search_spec.rb", "spec/osprey_spec.rb",
21
+ "spec/spec_helper.rb", "VERSION.yml"]
22
+
23
+ s.has_rdoc = true
24
+ s.homepage = %q{http://github.com/jsl/osprey}
25
+ s.rdoc_options = ["--charset=UTF-8"]
26
+ s.require_paths = ["lib"]
27
+ s.rubygems_version = %q{1.3.1}
28
+ s.summary = %q{A Twitter API that keeps track of tweets you've seen}
29
+ s.test_files = ["spec/fixtures/swine_flu1.json", "spec/fixtures/swine_flu2.json",
30
+ "spec/fixtures/swine_flu_result_set1.yml", "spec/fixtures/swine_flu_result_set2.yml",
31
+ "spec/osprey/result_set_spec.rb", "spec/osprey/search_spec.rb", "spec/osprey_spec.rb", "spec/spec_helper.rb"]
32
+
33
+ s.extra_rdoc_files = [ "README.rdoc" ]
34
+
35
+ s.rdoc_options += [
36
+ '--title', 'Osprey',
37
+ '--main', 'README.rdoc',
38
+ '--line-numbers',
39
+ '--inline-source'
40
+ ]
41
+
42
+ %w[ curb wycats-moneta hashback json memcache-client ].each do |dep|
43
+ s.add_dependency(dep)
44
+ end
45
+
46
+ if s.respond_to? :specification_version then
47
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
48
+ s.specification_version = 2
49
+ end
50
+ end
@@ -0,0 +1 @@
1
+ {"results":[{"text":"RT @nicfish: Tracking Swine Flu with Microsoft Virtual Earth and Live Search http:\/\/bit.ly\/YqdDC --&gt; sweet!","to_user_id":null,"from_user":"tameraclark","id":1701715308,"from_user_id":386286,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/www.tweetdeck.com\/&quot;&gt;TweetDeck&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/188156012\/eye2_normal.jpg","created_at":"Tue, 05 May 2009 01:28:14 +0000"},{"text":"@keepsgettinbetr when did i worry about the swine flu","to_user_id":9477722,"to_user":"keepsgettinbetr","from_user":"Bayleeee","id":1701714956,"from_user_id":7978237,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/twitterfon.net\/&quot;&gt;TwitterFon&lt;\/a&gt;","profile_image_url":"http:\/\/static.twitter.com\/images\/default_profile_normal.png","created_at":"Tue, 05 May 2009 01:28:12 +0000"},{"text":"RT @TelegraphNews: Swine flu: Mexico starts to ease virus restrictions http:\/\/tinyurl.com\/db6jff","to_user_id":null,"from_user":"lili_one","id":1701714620,"from_user_id":4481131,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/www.tweetdeck.com\/&quot;&gt;TweetDeck&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/181901124\/31-01-07_1330_normal.jpg","created_at":"Tue, 05 May 2009 01:28:10 +0000"},{"text":"Up to the minute swine flu origins: the visual explanation!\nRT @tweetmeme Coconut Chutney: When pigs fly http:\/\/tinyurl.com\/d8cobj","to_user_id":null,"from_user":"hiddenground","id":1701714439,"from_user_id":4158379,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/66627032\/frau-freide_normal.jpg","created_at":"Tue, 05 May 2009 01:28:09 +0000"},{"text":"School is shut down! due to swine flu!! thats a good thing right?","to_user_id":null,"from_user":"moonbrella","id":1701714354,"from_user_id":14559662,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/189894422\/IMG00701_normal.JPG","created_at":"Tue, 05 May 2009 01:28:10 +0000"},{"text":"Blame it on the flu got u feelin blue, blame it on the swine got u feelin blind, blame it on the mex mex mex mex mex mexicans....","to_user_id":null,"from_user":"DJELOO","id":1701714238,"from_user_id":4513402,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/orangatame.com\/products\/twitterberry\/&quot;&gt;TwitterBerry&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/133607145\/djeloowel_normal.jpg","created_at":"Tue, 05 May 2009 01:28:07 +0000"},{"text":"Swine Flu","to_user_id":null,"from_user":"friendbartrends","id":1701713717,"from_user_id":14573136,"source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/static.twitter.com\/images\/default_profile_normal.png","created_at":"Tue, 05 May 2009 01:28:04 +0000"},{"text":"Destroying swine flu","to_user_id":null,"from_user":"premierozone","id":1701713665,"from_user_id":15592670,"iso_language_code":"da","source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/197468025\/images_normal.jpg","created_at":"Tue, 05 May 2009 01:28:04 +0000"},{"text":"@M_tm uh, finally, something more disturbing than swine flu...","to_user_id":125590,"to_user":"M_tm","from_user":"Funchilde","id":1701712780,"from_user_id":1983836,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/61959717\/DD_Oaxaca_Flower_normal.jpg","created_at":"Tue, 05 May 2009 01:27:58 +0000"},{"text":"Susie gave me a Lysol Hand Sanitizer that says &quot;Protects from Swine Flu&quot; (Haha) for the bracelet.","to_user_id":null,"from_user":"StayPositive_","id":1701712037,"from_user_id":15567837,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/197027649\/waiting_normal.jpg","created_at":"Tue, 05 May 2009 01:27:53 +0000"},{"text":"@danilogentili Swine Flu !!!!!","to_user_id":168227,"to_user":"danilogentili","from_user":"pannain","id":1701711989,"from_user_id":13123935,"iso_language_code":"eo","source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/151854910\/20070408041551_nivertia06churras07-01069_normal.jpg","created_at":"Tue, 05 May 2009 01:27:53 +0000"},{"text":"realized that after swine flu we'll get Bear pox, followed by beaver fever, and ending with Mongoose Malaria.... Be on your toes!","to_user_id":null,"from_user":"BubbaStoned85","id":1701711830,"from_user_id":1580999,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/59851479\/n31700687_7014_normal.jpg","created_at":"Tue, 05 May 2009 01:27:52 +0000"},{"text":"+ &quot;90 people get the swine flu and everyone wants to wear a face mask. Millions get AIDS and nobody wears a condom. Go figure.&quot;","to_user_id":null,"from_user":"gemmerzz","id":1701711644,"from_user_id":166576,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/88556721\/profpic2_lg_normal.png","created_at":"Tue, 05 May 2009 01:27:51 +0000"},{"text":"#swineflu 6 More Swine Flu Cases Confirmed on LI - LongIslandPress.com http:\/\/ad.vu\/bir4","to_user_id":null,"from_user":"News_SwineFlu","id":1701711527,"from_user_id":14562545,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/twitterfeed.com&quot;&gt;twitterfeed&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/189807824\/1_normal.jpg","created_at":"Tue, 05 May 2009 01:27:50 +0000"},{"text":"That darn Swine Flu is getting a tad close for comfort! A school about twenty minutes from mine closed down for a few days tonight!","to_user_id":null,"from_user":"tylergraves2009","id":1701710705,"from_user_id":8288437,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/193624367\/100_1084_normal.jpg","created_at":"Tue, 05 May 2009 01:27:45 +0000"}],"since_id":0,"max_id":1701715308,"refresh_url":"?since_id=1701715308&q=swine+flu","results_per_page":15,"next_page":"?page=2&max_id=1701715308&q=swine+flu","completed_in":0.032499,"page":1,"query":"swine+flu"}
@@ -0,0 +1 @@
1
+ {"results":[{"text":"School is shut down! due to swine flu!! thats a good thing right?","to_user_id":null,"from_user":"moonbrella","id":1701714354,"from_user_id":14559662,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/189894422\/IMG00701_normal.JPG","created_at":"Tue, 05 May 2009 01:28:10 +0000"},{"text":"Blame it on the flu got u feelin blue, blame it on the swine got u feelin blind, blame it on the mex mex mex mex mex mexicans....","to_user_id":null,"from_user":"DJELOO","id":1701714238,"from_user_id":4513402,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/orangatame.com\/products\/twitterberry\/&quot;&gt;TwitterBerry&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/133607145\/djeloowel_normal.jpg","created_at":"Tue, 05 May 2009 01:28:07 +0000"},{"text":"Swine Flu","to_user_id":null,"from_user":"friendbartrends","id":1701713717,"from_user_id":14573136,"source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/static.twitter.com\/images\/default_profile_normal.png","created_at":"Tue, 05 May 2009 01:28:04 +0000"},{"text":"Destroying swine flu","to_user_id":null,"from_user":"premierozone","id":1701713665,"from_user_id":15592670,"iso_language_code":"da","source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/197468025\/images_normal.jpg","created_at":"Tue, 05 May 2009 01:28:04 +0000"},{"text":"@M_tm uh, finally, something more disturbing than swine flu...","to_user_id":125590,"to_user":"M_tm","from_user":"Funchilde","id":1701712780,"from_user_id":1983836,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/61959717\/DD_Oaxaca_Flower_normal.jpg","created_at":"Tue, 05 May 2009 01:27:58 +0000"},{"text":"Susie gave me a Lysol Hand Sanitizer that says &quot;Protects from Swine Flu&quot; (Haha) for the bracelet.","to_user_id":null,"from_user":"StayPositive_","id":1701712037,"from_user_id":15567837,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/197027649\/waiting_normal.jpg","created_at":"Tue, 05 May 2009 01:27:53 +0000"},{"text":"@danilogentili Swine Flu !!!!!","to_user_id":168227,"to_user":"danilogentili","from_user":"pannain","id":1701711989,"from_user_id":13123935,"iso_language_code":"eo","source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/151854910\/20070408041551_nivertia06churras07-01069_normal.jpg","created_at":"Tue, 05 May 2009 01:27:53 +0000"},{"text":"realized that after swine flu we'll get Bear pox, followed by beaver fever, and ending with Mongoose Malaria.... Be on your toes!","to_user_id":null,"from_user":"BubbaStoned85","id":1701711830,"from_user_id":1580999,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/59851479\/n31700687_7014_normal.jpg","created_at":"Tue, 05 May 2009 01:27:52 +0000"},{"text":"+ &quot;90 people get the swine flu and everyone wants to wear a face mask. Millions get AIDS and nobody wears a condom. Go figure.&quot;","to_user_id":null,"from_user":"gemmerzz","id":1701711644,"from_user_id":166576,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/88556721\/profpic2_lg_normal.png","created_at":"Tue, 05 May 2009 01:27:51 +0000"},{"text":"#swineflu 6 More Swine Flu Cases Confirmed on LI - LongIslandPress.com http:\/\/ad.vu\/bir4","to_user_id":null,"from_user":"News_SwineFlu","id":1701711527,"from_user_id":14562545,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/twitterfeed.com&quot;&gt;twitterfeed&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/189807824\/1_normal.jpg","created_at":"Tue, 05 May 2009 01:27:50 +0000"},{"text":"That darn Swine Flu is getting a tad close for comfort! A school about twenty minutes from mine closed down for a few days tonight!","to_user_id":null,"from_user":"tylergraves2009","id":1701710705,"from_user_id":8288437,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/193624367\/100_1084_normal.jpg","created_at":"Tue, 05 May 2009 01:27:45 +0000"},{"text":"Explaining to the children why they don't need to be terrified of swine flu, but why they really do need to wash their hands. #swineflu","to_user_id":null,"from_user":"jojobo","id":1701710268,"from_user_id":3617869,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/71116723\/jojobo_normal.jpg","created_at":"Tue, 05 May 2009 01:27:42 +0000"},{"text":"Another day locked up with Swine Flu outside. Just went to HomeDepot and more then 50 percent of clients have masks there.","to_user_id":null,"from_user":"dennisvdheijden","id":1701709964,"from_user_id":3362361,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/twitter.com\/&quot;&gt;web&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/76497810\/picture_normal.jpg","created_at":"Tue, 05 May 2009 01:27:40 +0000"},{"text":"RT: @KYLking: Byron Center High School students celebrate 'swine break' after flu closes school for week http:\/\/tinyurl.com\/d2b3yh","to_user_id":null,"from_user":"creister","id":1701709832,"from_user_id":643746,"iso_language_code":"nl","source":"&lt;a href=&quot;http:\/\/twitterfox.net\/&quot;&gt;TwitterFox&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/79399738\/cami1_normal.jpg","created_at":"Tue, 05 May 2009 01:27:39 +0000"},{"text":"I know I said I was going to sleep but who sleeps when you can twitter Itslike an epidemic this twitter stuff it spread like the swine flu","to_user_id":null,"from_user":"chefjay1121","id":1701709797,"from_user_id":7400045,"iso_language_code":"en","source":"&lt;a href=&quot;http:\/\/iconfactory.com\/software\/twitterrific&quot;&gt;twitterrific&lt;\/a&gt;","profile_image_url":"http:\/\/s3.amazonaws.com\/twitter_production\/profile_images\/150699579\/for_my_baby_normal.jpg","created_at":"Tue, 05 May 2009 01:27:39 +0000"}],"since_id":0,"max_id":1701714354,"refresh_url":"?since_id=1701714354&q=swine+flu","results_per_page":15,"next_page":"?page=2&max_id=1701714354&q=swine+flu","completed_in":0.023306,"page":1,"query":"swine+flu"}
@@ -0,0 +1,200 @@
1
+ --- !seq:Osprey::ResultSet
2
+ - !ruby/object:Osprey::Tweet
3
+ table:
4
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/188156012/eye2_normal.jpg
5
+ :source: "&lt;a href=&quot;http://www.tweetdeck.com/&quot;&gt;TweetDeck&lt;/a&gt;"
6
+ :created_at: Tue, 05 May 2009 01:28:14 +0000
7
+ :to_user_id:
8
+ :text: "RT @nicfish: Tracking Swine Flu with Microsoft Virtual Earth and Live Search http://bit.ly/YqdDC --&gt; sweet!"
9
+ :new_record?: true
10
+ :from_user: tameraclark
11
+ :from_user_id: 386286
12
+ :twitter_id: 1701715308
13
+ :iso_language_code: en
14
+ :id: 1701715308
15
+ - !ruby/object:Osprey::Tweet
16
+ table:
17
+ :profile_image_url: http://static.twitter.com/images/default_profile_normal.png
18
+ :source: "&lt;a href=&quot;http://twitterfon.net/&quot;&gt;TwitterFon&lt;/a&gt;"
19
+ :created_at: Tue, 05 May 2009 01:28:12 +0000
20
+ :to_user_id: 9477722
21
+ :text: "@keepsgettinbetr when did i worry about the swine flu"
22
+ :new_record?: true
23
+ :from_user: Bayleeee
24
+ :from_user_id: 7978237
25
+ :twitter_id: 1701714956
26
+ :to_user: keepsgettinbetr
27
+ :iso_language_code: en
28
+ :id: 1701714956
29
+ - !ruby/object:Osprey::Tweet
30
+ table:
31
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/181901124/31-01-07_1330_normal.jpg
32
+ :source: "&lt;a href=&quot;http://www.tweetdeck.com/&quot;&gt;TweetDeck&lt;/a&gt;"
33
+ :created_at: Tue, 05 May 2009 01:28:10 +0000
34
+ :to_user_id:
35
+ :text: "RT @TelegraphNews: Swine flu: Mexico starts to ease virus restrictions http://tinyurl.com/db6jff"
36
+ :new_record?: true
37
+ :from_user: lili_one
38
+ :from_user_id: 4481131
39
+ :twitter_id: 1701714620
40
+ :iso_language_code: en
41
+ :id: 1701714620
42
+ - !ruby/object:Osprey::Tweet
43
+ table:
44
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/66627032/frau-freide_normal.jpg
45
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
46
+ :created_at: Tue, 05 May 2009 01:28:09 +0000
47
+ :to_user_id:
48
+ :text: |-
49
+ Up to the minute swine flu origins: the visual explanation!
50
+ RT @tweetmeme Coconut Chutney: When pigs fly http://tinyurl.com/d8cobj
51
+ :new_record?: true
52
+ :from_user: hiddenground
53
+ :from_user_id: 4158379
54
+ :twitter_id: 1701714439
55
+ :iso_language_code: en
56
+ :id: 1701714439
57
+ - !ruby/object:Osprey::Tweet
58
+ table:
59
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/189894422/IMG00701_normal.JPG
60
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
61
+ :created_at: Tue, 05 May 2009 01:28:10 +0000
62
+ :to_user_id:
63
+ :text: School is shut down! due to swine flu!! thats a good thing right?
64
+ :new_record?: true
65
+ :from_user: moonbrella
66
+ :from_user_id: 14559662
67
+ :twitter_id: 1701714354
68
+ :iso_language_code: en
69
+ :id: 1701714354
70
+ - !ruby/object:Osprey::Tweet
71
+ table:
72
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/133607145/djeloowel_normal.jpg
73
+ :source: "&lt;a href=&quot;http://orangatame.com/products/twitterberry/&quot;&gt;TwitterBerry&lt;/a&gt;"
74
+ :created_at: Tue, 05 May 2009 01:28:07 +0000
75
+ :to_user_id:
76
+ :text: Blame it on the flu got u feelin blue, blame it on the swine got u feelin blind, blame it on the mex mex mex mex mex mexicans....
77
+ :new_record?: true
78
+ :from_user: DJELOO
79
+ :from_user_id: 4513402
80
+ :twitter_id: 1701714238
81
+ :iso_language_code: en
82
+ :id: 1701714238
83
+ - !ruby/object:Osprey::Tweet
84
+ table:
85
+ :profile_image_url: http://static.twitter.com/images/default_profile_normal.png
86
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
87
+ :created_at: Tue, 05 May 2009 01:28:04 +0000
88
+ :to_user_id:
89
+ :text: Swine Flu
90
+ :new_record?: true
91
+ :from_user: friendbartrends
92
+ :from_user_id: 14573136
93
+ :twitter_id: 1701713717
94
+ :id: 1701713717
95
+ - !ruby/object:Osprey::Tweet
96
+ table:
97
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/197468025/images_normal.jpg
98
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
99
+ :created_at: Tue, 05 May 2009 01:28:04 +0000
100
+ :to_user_id:
101
+ :text: Destroying swine flu
102
+ :new_record?: true
103
+ :from_user: premierozone
104
+ :from_user_id: 15592670
105
+ :twitter_id: 1701713665
106
+ :iso_language_code: da
107
+ :id: 1701713665
108
+ - !ruby/object:Osprey::Tweet
109
+ table:
110
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/61959717/DD_Oaxaca_Flower_normal.jpg
111
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
112
+ :created_at: Tue, 05 May 2009 01:27:58 +0000
113
+ :to_user_id: 125590
114
+ :text: "@M_tm uh, finally, something more disturbing than swine flu..."
115
+ :new_record?: true
116
+ :from_user: Funchilde
117
+ :from_user_id: 1983836
118
+ :twitter_id: 1701712780
119
+ :to_user: M_tm
120
+ :iso_language_code: en
121
+ :id: 1701712780
122
+ - !ruby/object:Osprey::Tweet
123
+ table:
124
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/197027649/waiting_normal.jpg
125
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
126
+ :created_at: Tue, 05 May 2009 01:27:53 +0000
127
+ :to_user_id:
128
+ :text: Susie gave me a Lysol Hand Sanitizer that says &quot;Protects from Swine Flu&quot; (Haha) for the bracelet.
129
+ :new_record?: true
130
+ :from_user: StayPositive_
131
+ :from_user_id: 15567837
132
+ :twitter_id: 1701712037
133
+ :iso_language_code: en
134
+ :id: 1701712037
135
+ - !ruby/object:Osprey::Tweet
136
+ table:
137
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/151854910/20070408041551_nivertia06churras07-01069_normal.jpg
138
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
139
+ :created_at: Tue, 05 May 2009 01:27:53 +0000
140
+ :to_user_id: 168227
141
+ :text: "@danilogentili Swine Flu !!!!!"
142
+ :new_record?: true
143
+ :from_user: pannain
144
+ :from_user_id: 13123935
145
+ :twitter_id: 1701711989
146
+ :to_user: danilogentili
147
+ :iso_language_code: eo
148
+ :id: 1701711989
149
+ - !ruby/object:Osprey::Tweet
150
+ table:
151
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/59851479/n31700687_7014_normal.jpg
152
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
153
+ :created_at: Tue, 05 May 2009 01:27:52 +0000
154
+ :to_user_id:
155
+ :text: realized that after swine flu we'll get Bear pox, followed by beaver fever, and ending with Mongoose Malaria.... Be on your toes!
156
+ :new_record?: true
157
+ :from_user: BubbaStoned85
158
+ :from_user_id: 1580999
159
+ :twitter_id: 1701711830
160
+ :iso_language_code: en
161
+ :id: 1701711830
162
+ - !ruby/object:Osprey::Tweet
163
+ table:
164
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/88556721/profpic2_lg_normal.png
165
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
166
+ :created_at: Tue, 05 May 2009 01:27:51 +0000
167
+ :to_user_id:
168
+ :text: + &quot;90 people get the swine flu and everyone wants to wear a face mask. Millions get AIDS and nobody wears a condom. Go figure.&quot;
169
+ :new_record?: true
170
+ :from_user: gemmerzz
171
+ :from_user_id: 166576
172
+ :twitter_id: 1701711644
173
+ :iso_language_code: en
174
+ :id: 1701711644
175
+ - !ruby/object:Osprey::Tweet
176
+ table:
177
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/189807824/1_normal.jpg
178
+ :source: "&lt;a href=&quot;http://twitterfeed.com&quot;&gt;twitterfeed&lt;/a&gt;"
179
+ :created_at: Tue, 05 May 2009 01:27:50 +0000
180
+ :to_user_id:
181
+ :text: "#swineflu 6 More Swine Flu Cases Confirmed on LI - LongIslandPress.com http://ad.vu/bir4"
182
+ :new_record?: true
183
+ :from_user: News_SwineFlu
184
+ :from_user_id: 14562545
185
+ :twitter_id: 1701711527
186
+ :iso_language_code: en
187
+ :id: 1701711527
188
+ - !ruby/object:Osprey::Tweet
189
+ table:
190
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/193624367/100_1084_normal.jpg
191
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
192
+ :created_at: Tue, 05 May 2009 01:27:45 +0000
193
+ :to_user_id:
194
+ :text: That darn Swine Flu is getting a tad close for comfort! A school about twenty minutes from mine closed down for a few days tonight!
195
+ :new_record?: true
196
+ :from_user: tylergraves2009
197
+ :from_user_id: 8288437
198
+ :twitter_id: 1701710705
199
+ :iso_language_code: en
200
+ :id: 1701710705
@@ -0,0 +1,197 @@
1
+ --- !seq:Osprey::ResultSet
2
+ - !ruby/object:Osprey::Tweet
3
+ table:
4
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
5
+ :from_user_id: 14559662
6
+ :iso_language_code: en
7
+ :new_record: false
8
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/189894422/IMG00701_normal.JPG
9
+ :twitter_id: 1701714354
10
+ :text: School is shut down! due to swine flu!! thats a good thing right?
11
+ :created_at: Tue, 05 May 2009 01:28:10 +0000
12
+ :to_user_id:
13
+ :id: 1701714354
14
+ :from_user: moonbrella
15
+ - !ruby/object:Osprey::Tweet
16
+ table:
17
+ :source: "&lt;a href=&quot;http://orangatame.com/products/twitterberry/&quot;&gt;TwitterBerry&lt;/a&gt;"
18
+ :from_user_id: 4513402
19
+ :iso_language_code: en
20
+ :new_record: false
21
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/133607145/djeloowel_normal.jpg
22
+ :twitter_id: 1701714238
23
+ :text: Blame it on the flu got u feelin blue, blame it on the swine got u feelin blind, blame it on the mex mex mex mex mex mexicans....
24
+ :created_at: Tue, 05 May 2009 01:28:07 +0000
25
+ :to_user_id:
26
+ :id: 1701714238
27
+ :from_user: DJELOO
28
+ - !ruby/object:Osprey::Tweet
29
+ table:
30
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
31
+ :from_user_id: 14573136
32
+ :new_record: false
33
+ :profile_image_url: http://static.twitter.com/images/default_profile_normal.png
34
+ :twitter_id: 1701713717
35
+ :text: Swine Flu
36
+ :created_at: Tue, 05 May 2009 01:28:04 +0000
37
+ :to_user_id:
38
+ :id: 1701713717
39
+ :from_user: friendbartrends
40
+ - !ruby/object:Osprey::Tweet
41
+ table:
42
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
43
+ :from_user_id: 15592670
44
+ :iso_language_code: da
45
+ :new_record: false
46
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/197468025/images_normal.jpg
47
+ :twitter_id: 1701713665
48
+ :text: Destroying swine flu
49
+ :created_at: Tue, 05 May 2009 01:28:04 +0000
50
+ :to_user_id:
51
+ :id: 1701713665
52
+ :from_user: premierozone
53
+ - !ruby/object:Osprey::Tweet
54
+ table:
55
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
56
+ :from_user_id: 1983836
57
+ :iso_language_code: en
58
+ :new_record: false
59
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/61959717/DD_Oaxaca_Flower_normal.jpg
60
+ :twitter_id: 1701712780
61
+ :text: "@M_tm uh, finally, something more disturbing than swine flu..."
62
+ :created_at: Tue, 05 May 2009 01:27:58 +0000
63
+ :to_user_id: 125590
64
+ :to_user: M_tm
65
+ :id: 1701712780
66
+ :from_user: Funchilde
67
+ - !ruby/object:Osprey::Tweet
68
+ table:
69
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
70
+ :from_user_id: 15567837
71
+ :iso_language_code: en
72
+ :new_record: false
73
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/197027649/waiting_normal.jpg
74
+ :twitter_id: 1701712037
75
+ :text: Susie gave me a Lysol Hand Sanitizer that says &quot;Protects from Swine Flu&quot; (Haha) for the bracelet.
76
+ :created_at: Tue, 05 May 2009 01:27:53 +0000
77
+ :to_user_id:
78
+ :id: 1701712037
79
+ :from_user: StayPositive_
80
+ - !ruby/object:Osprey::Tweet
81
+ table:
82
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
83
+ :from_user_id: 13123935
84
+ :iso_language_code: eo
85
+ :new_record: false
86
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/151854910/20070408041551_nivertia06churras07-01069_normal.jpg
87
+ :twitter_id: 1701711989
88
+ :text: "@danilogentili Swine Flu !!!!!"
89
+ :created_at: Tue, 05 May 2009 01:27:53 +0000
90
+ :to_user_id: 168227
91
+ :to_user: danilogentili
92
+ :id: 1701711989
93
+ :from_user: pannain
94
+ - !ruby/object:Osprey::Tweet
95
+ table:
96
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
97
+ :from_user_id: 1580999
98
+ :iso_language_code: en
99
+ :new_record: false
100
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/59851479/n31700687_7014_normal.jpg
101
+ :twitter_id: 1701711830
102
+ :text: realized that after swine flu we'll get Bear pox, followed by beaver fever, and ending with Mongoose Malaria.... Be on your toes!
103
+ :created_at: Tue, 05 May 2009 01:27:52 +0000
104
+ :to_user_id:
105
+ :id: 1701711830
106
+ :from_user: BubbaStoned85
107
+ - !ruby/object:Osprey::Tweet
108
+ table:
109
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
110
+ :from_user_id: 166576
111
+ :iso_language_code: en
112
+ :new_record: false
113
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/88556721/profpic2_lg_normal.png
114
+ :twitter_id: 1701711644
115
+ :text: + &quot;90 people get the swine flu and everyone wants to wear a face mask. Millions get AIDS and nobody wears a condom. Go figure.&quot;
116
+ :created_at: Tue, 05 May 2009 01:27:51 +0000
117
+ :to_user_id:
118
+ :id: 1701711644
119
+ :from_user: gemmerzz
120
+ - !ruby/object:Osprey::Tweet
121
+ table:
122
+ :source: "&lt;a href=&quot;http://twitterfeed.com&quot;&gt;twitterfeed&lt;/a&gt;"
123
+ :from_user_id: 14562545
124
+ :iso_language_code: en
125
+ :new_record: false
126
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/189807824/1_normal.jpg
127
+ :twitter_id: 1701711527
128
+ :text: "#swineflu 6 More Swine Flu Cases Confirmed on LI - LongIslandPress.com http://ad.vu/bir4"
129
+ :created_at: Tue, 05 May 2009 01:27:50 +0000
130
+ :to_user_id:
131
+ :id: 1701711527
132
+ :from_user: News_SwineFlu
133
+ - !ruby/object:Osprey::Tweet
134
+ table:
135
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
136
+ :from_user_id: 8288437
137
+ :iso_language_code: en
138
+ :new_record: false
139
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/193624367/100_1084_normal.jpg
140
+ :twitter_id: 1701710705
141
+ :text: That darn Swine Flu is getting a tad close for comfort! A school about twenty minutes from mine closed down for a few days tonight!
142
+ :created_at: Tue, 05 May 2009 01:27:45 +0000
143
+ :to_user_id:
144
+ :id: 1701710705
145
+ :from_user: tylergraves2009
146
+ - !ruby/object:Osprey::Tweet
147
+ table:
148
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
149
+ :from_user_id: 3617869
150
+ :iso_language_code: en
151
+ :new_record: true
152
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/71116723/jojobo_normal.jpg
153
+ :twitter_id: 1701710268
154
+ :text: "Explaining to the children why they don't need to be terrified of swine flu, but why they really do need to wash their hands. #swineflu"
155
+ :created_at: Tue, 05 May 2009 01:27:42 +0000
156
+ :to_user_id:
157
+ :id: 1701710268
158
+ :from_user: jojobo
159
+ - !ruby/object:Osprey::Tweet
160
+ table:
161
+ :source: "&lt;a href=&quot;http://twitter.com/&quot;&gt;web&lt;/a&gt;"
162
+ :from_user_id: 3362361
163
+ :iso_language_code: en
164
+ :new_record: true
165
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/76497810/picture_normal.jpg
166
+ :twitter_id: 1701709964
167
+ :text: Another day locked up with Swine Flu outside. Just went to HomeDepot and more then 50 percent of clients have masks there.
168
+ :created_at: Tue, 05 May 2009 01:27:40 +0000
169
+ :to_user_id:
170
+ :id: 1701709964
171
+ :from_user: dennisvdheijden
172
+ - !ruby/object:Osprey::Tweet
173
+ table:
174
+ :source: "&lt;a href=&quot;http://twitterfox.net/&quot;&gt;TwitterFox&lt;/a&gt;"
175
+ :from_user_id: 643746
176
+ :iso_language_code: nl
177
+ :new_record: true
178
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/79399738/cami1_normal.jpg
179
+ :twitter_id: 1701709832
180
+ :text: "RT: @KYLking: Byron Center High School students celebrate 'swine break' after flu closes school for week http://tinyurl.com/d2b3yh"
181
+ :created_at: Tue, 05 May 2009 01:27:39 +0000
182
+ :to_user_id:
183
+ :id: 1701709832
184
+ :from_user: creister
185
+ - !ruby/object:Osprey::Tweet
186
+ table:
187
+ :source: "&lt;a href=&quot;http://iconfactory.com/software/twitterrific&quot;&gt;twitterrific&lt;/a&gt;"
188
+ :from_user_id: 7400045
189
+ :iso_language_code: en
190
+ :new_record: true
191
+ :profile_image_url: http://s3.amazonaws.com/twitter_production/profile_images/150699579/for_my_baby_normal.jpg
192
+ :twitter_id: 1701709797
193
+ :text: I know I said I was going to sleep but who sleeps when you can twitter Itslike an epidemic this twitter stuff it spread like the swine flu
194
+ :created_at: Tue, 05 May 2009 01:27:39 +0000
195
+ :to_user_id:
196
+ :id: 1701709797
197
+ :from_user: chefjay1121
@@ -0,0 +1,15 @@
1
+ require File.join(File.dirname(__FILE__), %w[ .. spec_helper ])
2
+
3
+ describe Osprey::ResultSet do
4
+ before do
5
+ @rs = YAML.load_file(File.join(File.dirname(__FILE__), %w[.. fixtures swine_flu_result_set2.yml]))
6
+ end
7
+
8
+ it "should return 4 results for #new_records" do
9
+ @rs.new_records.size.should == 4
10
+ end
11
+
12
+ it "should return 15 results for #records" do
13
+ @rs.records.size.should == 15
14
+ end
15
+ end
@@ -0,0 +1,126 @@
1
+ require File.join(File.dirname(__FILE__), %w[ .. spec_helper ])
2
+
3
+ describe Osprey::Search do
4
+ describe "initialization" do
5
+ it "should initialize without error" do
6
+ Osprey::Search.new('bar')
7
+ end
8
+
9
+ it "should create a new instance of the default backend" do
10
+ Osprey::Search.new('bar').instance_variable_get(:@backend).should be_a(HashBack::Backend)
11
+ end
12
+ end
13
+
14
+ it "should set the attr accessor for the term" do
15
+ tr = Osprey::Search.new('foo')
16
+ tr.term.should == 'foo'
17
+ end
18
+
19
+ describe "#fetch" do
20
+ before do
21
+ @tr = Osprey::Search.new('swine flu')
22
+ end
23
+
24
+ describe "when the backend does not contain a previous result set" do
25
+ before do
26
+ @curl = mock('curl')
27
+ @curl.expects(:response_code).returns(200)
28
+ @swine_flu_json = File.read(File.join(File.dirname(__FILE__), %w[.. fixtures swine_flu1.json]))
29
+ @curl.expects(:body_str).returns(@swine_flu_json)
30
+ Curl::Easy.expects(:perform).with('http://search.twitter.com/search.json?rpp=50&q=swine+flu').returns(@curl)
31
+ @results = @tr.fetch
32
+ end
33
+
34
+ describe "on the initial request of a term" do
35
+ it "should return a Osprey::ResultSet" do
36
+ @results.should be_a(Osprey::ResultSet)
37
+ end
38
+
39
+ it "should contain 15 results" do
40
+ @results.size.should == 15
41
+ end
42
+
43
+ it "should contain all Osprey::Tweet items" do
44
+ @results.each{|t| t.should be_a(Osprey::Tweet) }
45
+ end
46
+
47
+ it "should have all results respond affirmatively to new_record" do
48
+ @results.each{|t| t.new_record.should be_true}
49
+ end
50
+ end
51
+ end
52
+
53
+ describe "when the backend does contain a previous result set" do
54
+ before do
55
+ @curl.expects(:response_code).returns(200)
56
+ @backend = mock('backend')
57
+ @backend.expects(:[]).with('twitter-id-pool').twice
58
+ @backend.expects(:[]).with(@tr.__send__(:url_key)).returns(YAML.load_file(File.join(File.dirname(__FILE__), %w[.. fixtures swine_flu_result_set1.yml])))
59
+ @backend.expects(:[]=).twice
60
+ @tr.instance_variable_set(:@backend, @backend)
61
+ @swine_flu_json = File.read(File.join(File.dirname(__FILE__), %w[.. fixtures swine_flu2.json]))
62
+ @curl.expects(:body_str).returns(@swine_flu_json)
63
+ Curl::Easy.expects(:perform).with('http://search.twitter.com/search.json?rpp=50&q=swine+flu&since_id=1701715308').returns(@curl)
64
+ @results = @tr.fetch
65
+ end
66
+
67
+ it "should have 4 records that are not marked as new" do
68
+ @results.select{|r| r.new_record }.size.should == 4
69
+ end
70
+ end
71
+
72
+ describe "when feed ids are listed in the pool" do
73
+ before do
74
+ @curl.expects(:response_code).returns(200)
75
+ @backend = mock('backend')
76
+ @backend.expects(:[]).with('twitter-id-pool').twice.returns([1701715308, 1701714956])
77
+ @backend.expects(:[]).with(@tr.__send__(:url_key)).returns(nil)
78
+ @backend.expects(:[]=).twice
79
+ @tr.instance_variable_set(:@backend, @backend)
80
+ @swine_flu_json = File.read(File.join(File.dirname(__FILE__), %w[.. fixtures swine_flu1.json]))
81
+ @curl.expects(:body_str).returns(@swine_flu_json)
82
+ Curl::Easy.expects(:perform).with('http://search.twitter.com/search.json?rpp=50&q=swine+flu').returns(@curl)
83
+ @results = @tr.fetch
84
+ end
85
+
86
+ it "should return 13 elements in new_records" do
87
+ @results.new_records.size.should == 13
88
+ end
89
+ end
90
+ end
91
+
92
+ describe "#url_key" do
93
+ it "should create a key with the MD5 hexdigest of the term" do
94
+ term = 'swine flu'
95
+ @tr = Osprey::Search.new(term)
96
+ @tr.__send__(:url_key).should == "term-#{MD5.hexdigest(term)}"
97
+ end
98
+ end
99
+
100
+ describe "#url" do
101
+ before do
102
+ @tr = Osprey::Search.new('swine flu')
103
+ @results = YAML.load_file(File.join(File.dirname(__FILE__), %w[.. fixtures swine_flu_result_set1.yml]))
104
+ end
105
+
106
+ it "should include the greatest ID in since_if of the given set if results are present" do
107
+ url = @tr.__send__(:url, @results)
108
+ url.should =~ /since_id=1701715308/
109
+ end
110
+
111
+ it "should not contain since_id if the previous results are nil" do
112
+ url = @tr.__send__(:url, nil)
113
+ url.should_not =~ /since_id/
114
+ end
115
+
116
+ it "should not contain since_id if the previous results contain no records" do
117
+ url = @tr.__send__(:url, [])
118
+ url.should_not =~ /since_id/
119
+ end
120
+
121
+ it "should contain an option rpp (results per page) that is set to the @options[:rpp] value" do
122
+ url = @tr.__send__(:url, [])
123
+ url.should =~ /rpp=50/
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,7 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper')
2
+
3
+ describe Osprey do
4
+ it "should have loaded the twitter reader" do
5
+ Osprey::Search
6
+ end
7
+ end
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'mocha'
3
+ require 'spec'
4
+
5
+ require File.join(File.dirname(__FILE__), %w[.. lib osprey])
6
+
7
+ Spec::Runner.configure do |config|
8
+ config.mock_with(:mocha)
9
+ end
10
+
11
+ shared_examples_for "all backends" do
12
+ it "should respond to #get" do
13
+ @backend.should respond_to(:get)
14
+ end
15
+
16
+ it "should respond to #set" do
17
+ @backend.should respond_to(:set)
18
+ end
19
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsl-osprey
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.8
4
+ version: 0.0.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Leitgeb
@@ -71,7 +71,25 @@ extensions: []
71
71
  extra_rdoc_files:
72
72
  - README.rdoc
73
73
  files:
74
+ - lib/core_ext/array.rb
75
+ - lib/core_ext/hash.rb
76
+ - lib/osprey/result_set.rb
77
+ - lib/osprey/search.rb
78
+ - lib/osprey/tweet.rb
79
+ - lib/osprey.rb
80
+ - LICENSE
81
+ - osprey.gemspec
82
+ - Rakefile
74
83
  - README.rdoc
84
+ - spec/fixtures/swine_flu1.json
85
+ - spec/fixtures/swine_flu2.json
86
+ - spec/fixtures/swine_flu_result_set1.yml
87
+ - spec/fixtures/swine_flu_result_set2.yml
88
+ - spec/osprey/result_set_spec.rb
89
+ - spec/osprey/search_spec.rb
90
+ - spec/osprey_spec.rb
91
+ - spec/spec_helper.rb
92
+ - VERSION.yml
75
93
  has_rdoc: true
76
94
  homepage: http://github.com/jsl/osprey
77
95
  post_install_message:
@@ -104,5 +122,12 @@ rubygems_version: 1.2.0
104
122
  signing_key:
105
123
  specification_version: 2
106
124
  summary: A Twitter API that keeps track of tweets you've seen
107
- test_files: []
108
-
125
+ test_files:
126
+ - spec/fixtures/swine_flu1.json
127
+ - spec/fixtures/swine_flu2.json
128
+ - spec/fixtures/swine_flu_result_set1.yml
129
+ - spec/fixtures/swine_flu_result_set2.yml
130
+ - spec/osprey/result_set_spec.rb
131
+ - spec/osprey/search_spec.rb
132
+ - spec/osprey_spec.rb
133
+ - spec/spec_helper.rb