dschn-twitter 0.3.7.1

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