dschn-twitter 0.3.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/History.txt +106 -0
  2. data/License.txt +19 -0
  3. data/Manifest.txt +71 -0
  4. data/README.txt +84 -0
  5. data/Rakefile +4 -0
  6. data/bin/twitter +15 -0
  7. data/config/hoe.rb +74 -0
  8. data/config/requirements.rb +17 -0
  9. data/examples/blocks.rb +15 -0
  10. data/examples/direct_messages.rb +28 -0
  11. data/examples/favorites.rb +20 -0
  12. data/examples/friends_followers.rb +25 -0
  13. data/examples/friendships.rb +13 -0
  14. data/examples/identica_timeline.rb +7 -0
  15. data/examples/location.rb +8 -0
  16. data/examples/posting.rb +9 -0
  17. data/examples/replies.rb +26 -0
  18. data/examples/search.rb +17 -0
  19. data/examples/sent_messages.rb +26 -0
  20. data/examples/timeline.rb +33 -0
  21. data/examples/twitter.rb +27 -0
  22. data/examples/verify_credentials.rb +13 -0
  23. data/lib/twitter.rb +21 -0
  24. data/lib/twitter/base.rb +260 -0
  25. data/lib/twitter/cli.rb +328 -0
  26. data/lib/twitter/cli/config.rb +9 -0
  27. data/lib/twitter/cli/helpers.rb +97 -0
  28. data/lib/twitter/cli/migrations/20080722194500_create_accounts.rb +13 -0
  29. data/lib/twitter/cli/migrations/20080722194508_create_tweets.rb +16 -0
  30. data/lib/twitter/cli/migrations/20080722214605_add_account_id_to_tweets.rb +9 -0
  31. data/lib/twitter/cli/migrations/20080722214606_create_configurations.rb +13 -0
  32. data/lib/twitter/cli/models/account.rb +33 -0
  33. data/lib/twitter/cli/models/configuration.rb +13 -0
  34. data/lib/twitter/cli/models/tweet.rb +20 -0
  35. data/lib/twitter/direct_message.rb +22 -0
  36. data/lib/twitter/easy_class_maker.rb +43 -0
  37. data/lib/twitter/rate_limit_status.rb +19 -0
  38. data/lib/twitter/search.rb +94 -0
  39. data/lib/twitter/status.rb +22 -0
  40. data/lib/twitter/user.rb +37 -0
  41. data/lib/twitter/version.rb +9 -0
  42. data/script/destroy +14 -0
  43. data/script/generate +14 -0
  44. data/script/txt2html +74 -0
  45. data/setup.rb +1585 -0
  46. data/spec/base_spec.rb +109 -0
  47. data/spec/cli/helper_spec.rb +35 -0
  48. data/spec/direct_message_spec.rb +35 -0
  49. data/spec/fixtures/followers.xml +706 -0
  50. data/spec/fixtures/friends.xml +609 -0
  51. data/spec/fixtures/friends_for.xml +584 -0
  52. data/spec/fixtures/friends_lite.xml +192 -0
  53. data/spec/fixtures/friends_timeline.xml +66 -0
  54. data/spec/fixtures/public_timeline.xml +148 -0
  55. data/spec/fixtures/rate_limit_status.xml +7 -0
  56. data/spec/fixtures/search_results.json +1 -0
  57. data/spec/fixtures/status.xml +25 -0
  58. data/spec/fixtures/user.xml +38 -0
  59. data/spec/fixtures/user_timeline.xml +465 -0
  60. data/spec/search_spec.rb +89 -0
  61. data/spec/spec.opts +1 -0
  62. data/spec/spec_helper.rb +12 -0
  63. data/spec/status_spec.rb +40 -0
  64. data/spec/user_spec.rb +42 -0
  65. data/tasks/deployment.rake +50 -0
  66. data/tasks/environment.rake +7 -0
  67. data/tasks/website.rake +17 -0
  68. data/twitter.gemspec +49 -0
  69. data/website/css/common.css +47 -0
  70. data/website/images/terminal_output.png +0 -0
  71. data/website/index.html +156 -0
  72. metadata +180 -0
@@ -0,0 +1,20 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
+ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
+
5
+ twitter = Twitter::Base.new(config['email'], config['password'])
6
+
7
+ puts 'FAVORITES'
8
+ twitter.favorites.each { |f| puts f.text }
9
+ puts
10
+ puts
11
+
12
+ puts 'CREATE'
13
+ puts twitter.create_favorite(865416114).text
14
+ puts
15
+ puts
16
+
17
+ puts 'DESTROY'
18
+ puts twitter.destroy_favorite(865416114).text
19
+ puts
20
+ puts
@@ -0,0 +1,25 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
+ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
+
5
+ twitter = Twitter::Base.new(config['email'], config['password'])
6
+
7
+ puts "FRIENDS"
8
+ twitter.friends.each { |f| puts f.name }
9
+ puts
10
+ puts
11
+
12
+ puts "FRIENDS FOR"
13
+ twitter.friends_for('orderedlist', :lite => true).each { |f| puts f.name }
14
+ puts
15
+ puts
16
+
17
+ puts "FOLLOWERS"
18
+ twitter.followers(:lite => true).each { |f| puts f.name }
19
+ puts
20
+ puts
21
+
22
+ puts "FOLLOWERS FOR"
23
+ twitter.followers_for('orderedlist', :lite => true).each { |f| puts f.name }
24
+ puts
25
+ puts
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
+ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
+
5
+ twitter = Twitter::Base.new(config['email'], config['password'])
6
+
7
+ puts twitter.create_friendship('orderedlist').name
8
+ puts twitter.follow('orderedlist').name
9
+ puts twitter.leave('orderedlist').name
10
+ puts twitter.destroy_friendship('orderedlist').name
11
+
12
+ puts twitter.friendship_exists?('jnunemaker', 'orderedlist').inspect
13
+ puts twitter.friendship_exists?('jnunemaker', 'ze').inspect
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
+ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
+
5
+ identica = Twitter::Base.new(config['email'], config['password'], :api_host => 'identi.ca/api')
6
+
7
+ identica.timeline(:public).each { |s| puts s.text, s.user.name, '' }
@@ -0,0 +1,8 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
+ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
+
5
+ twitter = Twitter::Base.new(config['email'], config['password'])
6
+
7
+ puts twitter.update_location('Hollywood, CA').location
8
+ puts twitter.update_delivery_device('none')
@@ -0,0 +1,9 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
+ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
+
5
+ twitter = Twitter::Base.new(config['email'], config['password'])
6
+ puts twitter.post("This is a test from the example file").inspect
7
+
8
+ # sending a direct message
9
+ # puts twitter.d('jnunemaker', 'this is a test').inspect
@@ -0,0 +1,26 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
+ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
+
5
+ twitter = Twitter::Base.new(config['email'], config['password'])
6
+
7
+ puts 'SINCE'
8
+ twitter.replies(:since => Time.now - 5.day).each do |s|
9
+ puts "- #{s.text}"
10
+ end
11
+ puts
12
+ puts
13
+
14
+ puts 'SINCE_ID'
15
+ twitter.replies(:since_id => 863081345).each do |s|
16
+ puts "- #{s.text}"
17
+ end
18
+ puts
19
+ puts
20
+
21
+ puts 'PAGE'
22
+ twitter.replies(:page => 1).each do |s|
23
+ puts "- #{s.text}"
24
+ end
25
+ puts
26
+ puts
@@ -0,0 +1,17 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
+
4
+ Twitter::Search.new('httparty').each { |r| puts r.inspect,'' }
5
+
6
+ # search = Twitter::Search.new
7
+ # search.from('jnunemaker').to('oaknd1').each { |r| puts r.inspect, '' }
8
+ # pp search.result
9
+ # search.clear
10
+
11
+ # search.from('jnunemaker').to('oaknd1').since(814529437).containing('milk').each { |r| puts r.inspect, '' }
12
+ # search.clear
13
+ #
14
+ # search.geocode('40.757929', '-73.985506', '50mi').containing('holland').each { |r| puts r.inspect, '' }
15
+ # search.clear
16
+
17
+ # pp search.from('jnunemaker').fetch()
@@ -0,0 +1,26 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
+ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
+
5
+ twitter = Twitter::Base.new(config['email'], config['password'])
6
+
7
+ puts 'SINCE'
8
+ twitter.sent_messages(:since => Time.now - 5.day).each do |s|
9
+ puts "- #{s.text}"
10
+ end
11
+ puts
12
+ puts
13
+
14
+ puts 'SINCE_ID'
15
+ twitter.sent_messages(:since_id => 33505386).each do |s|
16
+ puts "- #{s.text}"
17
+ end
18
+ puts
19
+ puts
20
+
21
+ puts 'PAGE'
22
+ twitter.sent_messages(:page => 1).each do |s|
23
+ puts "- #{s.text}"
24
+ end
25
+ puts
26
+ puts
@@ -0,0 +1,33 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
+ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
+
5
+ twitter = Twitter::Base.new(config['email'], config['password'])
6
+
7
+ puts 'SINCE'
8
+ twitter.timeline(:user, :since => Time.now - 1.day).each do |s|
9
+ puts "- #{s.text}"
10
+ end
11
+ puts
12
+ puts
13
+
14
+ puts 'SINCE_ID'
15
+ twitter.timeline(:user, :since_id => 865547074).each do |s|
16
+ puts "- #{s.text}"
17
+ end
18
+ puts
19
+ puts
20
+
21
+ puts 'COUNT'
22
+ twitter.timeline(:user, :count => 1).each do |s|
23
+ puts "- #{s.text}"
24
+ end
25
+ puts
26
+ puts
27
+
28
+ puts 'PAGE'
29
+ twitter.timeline(:user, :page => 1).each do |s|
30
+ puts "- #{s.text}"
31
+ end
32
+ puts
33
+ puts
@@ -0,0 +1,27 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
+ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
+
5
+ puts "Public Timeline", "=" * 50
6
+ Twitter::Base.new(config['email'], config['password']).timeline(:public).each do |s|
7
+ puts s.text, s.user.name
8
+ puts
9
+ end
10
+
11
+ puts '', "Friends Timeline", "=" * 50
12
+ Twitter::Base.new(config['email'], config['password']).timeline.each do |s|
13
+ puts s.text, s.user.name
14
+ puts
15
+ end
16
+
17
+ puts '', "Friends", "=" * 50
18
+ Twitter::Base.new(config['email'], config['password']).friends.each do |u|
19
+ puts u.name, u.status.text
20
+ puts
21
+ end
22
+
23
+ puts '', "Followers", "=" * 50
24
+ Twitter::Base.new(config['email'], config['password']).followers.each do |u|
25
+ puts u.name, u.status.text
26
+ puts
27
+ end
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'twitter')
3
+ config = YAML::load(open(ENV['HOME'] + '/.twitter'))
4
+
5
+ twitter = Twitter::Base.new(config['email'], config['password'])
6
+
7
+ puts twitter.verify_credentials
8
+
9
+ begin
10
+ Twitter::Base.new('asdf', 'foobar').verify_credentials
11
+ rescue => error
12
+ puts error.message
13
+ end
data/lib/twitter.rb ADDED
@@ -0,0 +1,21 @@
1
+ %w(uri cgi net/http yaml rubygems hpricot active_support).each { |f| require f }
2
+
3
+ $:.unshift(File.join(File.dirname(__FILE__)))
4
+ require 'twitter/version'
5
+ require 'twitter/easy_class_maker'
6
+ require 'twitter/base'
7
+ require 'twitter/user'
8
+ require 'twitter/search'
9
+ require 'twitter/status'
10
+ require 'twitter/direct_message'
11
+ require 'twitter/rate_limit_status'
12
+
13
+ module Twitter
14
+ class Unavailable < StandardError; end
15
+ class CantConnect < StandardError; end
16
+ class BadResponse < StandardError; end
17
+ class UnknownTimeline < ArgumentError; end
18
+ class RateExceeded < StandardError; end
19
+
20
+ SourceName = 'twittergem'
21
+ end
@@ -0,0 +1,260 @@
1
+ # This is the base class for the twitter library. It makes all the requests
2
+ # to twitter, parses the xml (using hpricot) and returns ruby objects to play with.
3
+ #
4
+ # For complete documentation on the options, check out the twitter api docs.
5
+ # http://groups.google.com/group/twitter-development-talk/web/api-documentation
6
+ module Twitter
7
+ class Base
8
+ # Initializes the configuration for making requests to twitter
9
+ # Twitter example:
10
+ # Twitter.new('email/username', 'password')
11
+ #
12
+ # Identi.ca example:
13
+ # Twitter.new('email/username', 'password', :api_host => 'identi.ca/api')
14
+ def initialize(email, password, options={})
15
+ @config, @config[:email], @config[:password] = {}, email, password
16
+ @api_host = options.delete(:api_host) || 'twitter.com'
17
+ end
18
+
19
+ def timeout=(time)
20
+ @config[:timeout] = time
21
+ end
22
+
23
+ def timeout
24
+ @config[:timeout]
25
+ end
26
+
27
+ # Returns an array of statuses for a timeline; Defaults to your friends timeline.
28
+ def timeline(which=:friends, options={})
29
+ raise UnknownTimeline unless [:friends, :public, :user].include?(which)
30
+ auth = which.to_s.include?('public') ? false : true
31
+ statuses(call("#{which}_timeline", :auth => auth, :since => options[:since], :args => parse_options(options)))
32
+ end
33
+
34
+ # Returns an array of users who are in your friends list
35
+ def friends(options={})
36
+ users(call(:friends, {:args => parse_options(options)}))
37
+ end
38
+
39
+ # Returns an array of users who are friends for the id or username passed in
40
+ def friends_for(id, options={})
41
+ friends(options.merge({:id => id}))
42
+ end
43
+
44
+ # Returns an array of users who are following you
45
+ def followers(options={})
46
+ users(call(:followers, {:args => parse_options(options)}))
47
+ end
48
+
49
+ def followers_for(id, options={})
50
+ followers(options.merge({:id => id}))
51
+ end
52
+
53
+ # Returns a single status for a given id
54
+ def status(id)
55
+ statuses(call("show/#{id}")).first
56
+ end
57
+
58
+ # returns all the profile information and the last status for a user
59
+ def user(id_or_screenname)
60
+ users(request("users/show/#{id_or_screenname}.xml", :auth => true)).first
61
+ end
62
+
63
+ # Returns an array of statuses that are replies
64
+ def replies(options={})
65
+ statuses(call(:replies, :since => options[:since], :args => parse_options(options)))
66
+ end
67
+
68
+ # Destroys a status by id
69
+ def destroy(id)
70
+ call("destroy/#{id}")
71
+ end
72
+
73
+ def rate_limit_status
74
+ RateLimitStatus.new_from_xml request("account/rate_limit_status.xml", :auth => true)
75
+ end
76
+
77
+ # waiting for twitter to correctly implement this in the api as it is documented
78
+ def featured
79
+ users(call(:featured))
80
+ end
81
+
82
+ # Returns an array of all the direct messages for the authenticated user
83
+ def direct_messages(options={})
84
+ doc = request(build_path('direct_messages.xml', parse_options(options)), {:auth => true, :since => options[:since]})
85
+ (doc/:direct_message).inject([]) { |dms, dm| dms << DirectMessage.new_from_xml(dm); dms }
86
+ end
87
+ alias :received_messages :direct_messages
88
+
89
+ # Returns direct messages sent by auth user
90
+ def sent_messages(options={})
91
+ doc = request(build_path('direct_messages/sent.xml', parse_options(options)), {:auth => true, :since => options[:since]})
92
+ (doc/:direct_message).inject([]) { |dms, dm| dms << DirectMessage.new_from_xml(dm); dms }
93
+ end
94
+
95
+ # destroys a give direct message by id if the auth user is a recipient
96
+ def destroy_direct_message(id)
97
+ DirectMessage.new_from_xml(request("direct_messages/destroy/#{id}.xml", :auth => true, :method => :post))
98
+ end
99
+
100
+ # Sends a direct message <code>text</code> to <code>user</code>
101
+ def d(user, text)
102
+ DirectMessage.new_from_xml(request('direct_messages/new.xml', :auth => true, :method => :post, :form_data => {'text' => text, 'user' => user}))
103
+ end
104
+
105
+ # Befriends id_or_screenname for the auth user
106
+ def create_friendship(id_or_screenname)
107
+ users(request("friendships/create/#{id_or_screenname}.xml", :auth => true, :method => :post)).first
108
+ end
109
+
110
+ # Defriends id_or_screenname for the auth user
111
+ def destroy_friendship(id_or_screenname)
112
+ users(request("friendships/destroy/#{id_or_screenname}.xml", :auth => true, :method => :post)).first
113
+ end
114
+
115
+ # Returns true if friendship exists, false if it doesn't.
116
+ def friendship_exists?(user_a, user_b)
117
+ doc = request(build_path("friendships/exists.xml", {:user_a => user_a, :user_b => user_b}), :auth => true)
118
+ doc.at('friends').innerHTML == 'true' ? true : false
119
+ end
120
+
121
+ # Updates your location and returns Twitter::User object
122
+ def update_location(location)
123
+ users(request(build_path('account/update_location.xml', {'location' => location}), :auth => true, :method => :post)).first
124
+ end
125
+
126
+ # Updates your deliver device and returns Twitter::User object
127
+ def update_delivery_device(device)
128
+ users(request(build_path('account/update_delivery_device.xml', {'device' => device}), :auth => true, :method => :post)).first
129
+ end
130
+
131
+ # Turns notifications by id_or_screenname on for auth user.
132
+ def follow(id_or_screenname)
133
+ users(request("notifications/follow/#{id_or_screenname}.xml", :auth => true, :method => :post)).first
134
+ end
135
+
136
+ # Turns notifications by id_or_screenname off for auth user.
137
+ def leave(id_or_screenname)
138
+ users(request("notifications/leave/#{id_or_screenname}.xml", :auth => true, :method => :post)).first
139
+ end
140
+
141
+ # Returns the most recent favorite statuses for the autenticating user
142
+ def favorites(options={})
143
+ statuses(request(build_path('favorites.xml', parse_options(options)), :auth => true))
144
+ end
145
+
146
+ # Favorites the status specified by id for the auth user
147
+ def create_favorite(id)
148
+ statuses(request("favorites/create/#{id}.xml", :auth => true, :method => :post)).first
149
+ end
150
+
151
+ # Un-favorites the status specified by id for the auth user
152
+ def destroy_favorite(id)
153
+ statuses(request("favorites/destroy/#{id}.xml", :auth => true, :method => :post)).first
154
+ end
155
+
156
+ # Blocks the user specified by id for the auth user
157
+ def block(id)
158
+ users(request("blocks/create/#{id}.xml", :auth => true, :method => :post)).first
159
+ end
160
+
161
+ # Unblocks the user specified by id for the auth user
162
+ def unblock(id)
163
+ users(request("blocks/destroy/#{id}.xml", :auth => true, :method => :post)).first
164
+ end
165
+
166
+ # Posts a new update to twitter for auth user.
167
+ def post(status, options={})
168
+ form_data = {'status' => status}
169
+ form_data.merge!({'source' => options[:source]}) if options[:source]
170
+ Status.new_from_xml(request('statuses/update.xml', :auth => true, :method => :post, :form_data => form_data))
171
+ end
172
+ alias :update :post
173
+
174
+ # Verifies the credentials for the auth user.
175
+ # raises Twitter::CantConnect on failure.
176
+ def verify_credentials
177
+ request('account/verify_credentials', :auth => true)
178
+ end
179
+
180
+ private
181
+ # Converts an hpricot doc to an array of statuses
182
+ def statuses(doc)
183
+ (doc/:status).inject([]) { |statuses, status| statuses << Status.new_from_xml(status); statuses }
184
+ end
185
+
186
+ # Converts an hpricot doc to an array of users
187
+ def users(doc)
188
+ (doc/:user).inject([]) { |users, user| users << User.new_from_xml(user); users }
189
+ end
190
+
191
+ # Calls whatever api method requested that deals with statuses
192
+ #
193
+ # ie: call(:public_timeline, :auth => false)
194
+ def call(method, options={})
195
+ options.reverse_merge!({ :auth => true, :args => {} })
196
+ # Following line needed as lite=false doesn't work in the API: http://tinyurl.com/yo3h5d
197
+ options[:args].delete(:lite) unless options[:args][:lite]
198
+ args = options.delete(:args)
199
+ request(build_path("statuses/#{method.to_s}.xml", args), options)
200
+ end
201
+
202
+ # Makes a request to twitter.
203
+ def request(path, options={})
204
+ options.reverse_merge!({
205
+ :headers => { "User-Agent" => @config[:email] },
206
+ :method => :get
207
+ })
208
+ unless options[:since].blank?
209
+ since = options[:since].kind_of?(Date) ? options[:since].strftime('%a, %d-%b-%y %T GMT') : options[:since].to_s
210
+ options[:headers]["If-Modified-Since"] = since
211
+ end
212
+
213
+ uri = URI.parse("http://#{@api_host}")
214
+
215
+ begin
216
+ response = Net::HTTP.start(uri.host, 80) do |http|
217
+ klass = Net::HTTP.const_get options[:method].to_s.downcase.capitalize
218
+ req = klass.new("#{uri.path}/#{path}", options[:headers])
219
+ req.basic_auth(@config[:email], @config[:password]) if options[:auth]
220
+ if options[:method].to_s == 'post' && options[:form_data]
221
+ req.set_form_data(options[:form_data])
222
+ end
223
+ http.read_timeout = @config[:timeout] if @config[:timeout]
224
+ http.request(req)
225
+ end
226
+ rescue => error
227
+ raise CantConnect, error.message
228
+ end
229
+
230
+ if %w[200 304].include?(response.code)
231
+ response = parse(response.body)
232
+ raise RateExceeded if (response/:hash/:error).text =~ /Rate limit exceeded/
233
+ response
234
+ elsif response.code == '503'
235
+ raise Unavailable, response.message
236
+ elsif response.code == '401'
237
+ raise CantConnect, 'Authentication failed. Check your username and password'
238
+ else
239
+ raise CantConnect, "Twitter is returning a #{response.code}: #{response.message}"
240
+ end
241
+ end
242
+
243
+ # Given a path and a hash, build a full path with the hash turned into a query string
244
+ def build_path(path, options)
245
+ path += "?#{options.to_query}" unless options.blank?
246
+ path
247
+ end
248
+
249
+ # Tries to get all the options in the correct format before making the request
250
+ def parse_options(options)
251
+ options[:since] = options[:since].kind_of?(Date) ? options[:since].strftime('%a, %d-%b-%y %T GMT') : options[:since].to_s if options[:since]
252
+ options
253
+ end
254
+
255
+ # Converts a string response into an Hpricot xml element.
256
+ def parse(response)
257
+ Hpricot.XML(response || '')
258
+ end
259
+ end
260
+ end