ashrewdmint-chirpy 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ /pkg
2
+ /doc
3
+ /github-test.rb
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Andrew Smith
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 ADDED
@@ -0,0 +1,105 @@
1
+ = Chirpy
2
+
3
+ == Description
4
+
5
+ Chirpy is a simple Twitter client for Ruby, written using Hpricot and RestClient.
6
+
7
+ * Github: http://github.com/ashrewdmint/chirpy
8
+ * Documentation: http://ashrewdmint.com/code/chirpy
9
+
10
+ == Caveats
11
+
12
+ Currently, Chirpy doesn't support OAuth yet. It also can't upload images to Twitter
13
+ (+profile_image_update+ and +profile_background_image_update+). However, I hope to
14
+ support both of these things in the future.
15
+
16
+ == Installation
17
+
18
+ sudo gem install ashrewdmint-chirpy
19
+
20
+ Or, if that doesn't work, try:
21
+
22
+ sudo gem install ashrewdmint-chirpy --source=http://gems.github.com
23
+
24
+ If that failed too, you can do this:
25
+
26
+ $ git clone git://github.com/ashrewdmint/chirpy.git
27
+ $ cd chirpy
28
+ $ gem build chirpy.gemspec
29
+ $ sudo gem install chirpy-x.x.x.gem
30
+
31
+ == Usage
32
+
33
+ Once you have the gem installed, you have to require it at the top of your Ruby document.
34
+
35
+ require 'rubygems'
36
+ require 'ashrewdmint-chirpy' # Could be 'chirpy' if you installed the gem manually
37
+
38
+ Everything Chirpy returns is a Hpricot object, which lets you
39
+ easily search through XML soup and find what you need. You
40
+ should familiarize yourself with Hpricot first:
41
+ http://wiki.github.com/why/hpricot
42
+
43
+ === Examples
44
+
45
+ Let's say you want to see the public timeline:
46
+
47
+ Chirpy.public_timeline.search('text').each do |text|
48
+ puts text.inner_html
49
+ end
50
+
51
+ That was easy! Note that everything after <tt>.public_timeline</tt> was just Hpricot magic.
52
+
53
+ But what if I want to search Twitter? That's simple, too:
54
+
55
+ Chirpy.search('Murray Rothbard').search('content').each do |text|
56
+ puts text.inner_html
57
+ end
58
+
59
+ Well, that was certainly painless. Unfortunately, since the search method parses an RSS feed, there's a lot of entities and links making a mess of the text. Chirpy has a method to handle annoyances like that:
60
+
61
+ puts Chirpy.remove_crap(text.inner_html)
62
+
63
+ But I'm getting ahead of myself. What if you want to post a new tweet?
64
+
65
+ chirpy = Chirpy.new('username', 'password')
66
+ chirpy.update_status("I'm posting this with Chirpy!")
67
+
68
+ ...or view a list of your friends?
69
+
70
+ chirpy.friends.search('name').each do |name|
71
+ puts name.inner_html + ' is a horrible person'
72
+ end
73
+
74
+ ...or look at peoples' favorite tweets?
75
+
76
+ chirpy.favorites # Your own favorites
77
+ chirpy.favorites('ashrewdmint') # My favorites!
78
+
79
+ But what if something goes wrong? Well, it's easy to check for an error:
80
+
81
+ response = Chirpy.public_timeline
82
+
83
+ if response.ok?
84
+ # Do something awesome
85
+ else
86
+ puts response.status.inspect
87
+ end
88
+
89
+ If anything goes wrong, you can find error details in the status attribute. Just so you know, Chirpy adds two new attributes to the Hpricot response object: status and url.
90
+
91
+ One last thing: some Twitter methods let you pass some extra GET parameters, like <tt>page</tt> or <tt>since_id</tt>. It's easy to do this with Chirpy, just pass those arguments in a hash:
92
+
93
+ chirpy.friends_timeline :page => 3
94
+
95
+ Nifty, eh? Good luck, and enjoy!
96
+
97
+ == Special thanks & credits
98
+
99
+ * Thanks to Why The Lucky Stiff for making Hpricot
100
+ * Thanks to Adam Wiggins for making RestClient
101
+ * Written by Andrew Smith [andrew.caleb.smith@gmail.com] [@ashrewdmint]
102
+
103
+ == License
104
+
105
+ Released under the MIT license. Mayest thou go forth and redistributeth to thine heart's content.
data/Rakefile ADDED
@@ -0,0 +1,62 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "chirpy"
8
+ gem.summary = "A simple Twitter client for Ruby, written using Hpricot and RestClient."
9
+ gem.description = "Lets you easily interact with Twitter's API; post status updates, search Twitter, and more!"
10
+ gem.email = "andrew.caleb.smith@gmail.com"
11
+ gem.homepage = "http://github.com/ashrewdmint/chirpy"
12
+ gem.authors = ["Andrew Smith"]
13
+
14
+ # Dependencies
15
+
16
+ gem.add_dependency('hpricot', '~> 0.8.1')
17
+ gem.add_dependency('rest-client', '~> 0.9.2')
18
+ gem.add_dependency('htmlentities', '~> 4.0.0')
19
+ end
20
+
21
+ rescue LoadError
22
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
23
+ end
24
+
25
+ require 'rake/testtask'
26
+ Rake::TestTask.new(:test) do |test|
27
+ test.libs << 'lib' << 'test'
28
+ test.pattern = 'test/**/*_test.rb'
29
+ test.verbose = true
30
+ end
31
+
32
+ begin
33
+ require 'rcov/rcovtask'
34
+ Rcov::RcovTask.new do |test|
35
+ test.libs << 'test'
36
+ test.pattern = 'test/**/*_test.rb'
37
+ test.verbose = true
38
+ end
39
+ rescue LoadError
40
+ task :rcov do
41
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
42
+ end
43
+ end
44
+
45
+
46
+ task :default => :test
47
+
48
+ require 'rake/rdoctask'
49
+ Rake::RDocTask.new do |rdoc|
50
+ if File.exist?('VERSION.yml')
51
+ config = YAML.load(File.read('VERSION.yml'))
52
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
53
+ else
54
+ version = ""
55
+ end
56
+
57
+ rdoc.rdoc_dir = 'rdoc'
58
+ rdoc.title = "chirpy #{version}"
59
+ rdoc.rdoc_files.include('README*')
60
+ rdoc.rdoc_files.include('lib/**/*.rb')
61
+ end
62
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.8.2
data/chirpy.gemspec ADDED
@@ -0,0 +1,55 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{chirpy}
5
+ s.version = "0.8.2"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Andrew Smith"]
9
+ s.date = %q{2009-05-21}
10
+ s.description = %q{Lets you easily interact with Twitter's API; post status updates, search Twitter, and more!}
11
+ s.email = %q{andrew.caleb.smith@gmail.com}
12
+ s.extra_rdoc_files = [
13
+ "LICENSE",
14
+ "README.rdoc"
15
+ ]
16
+ s.files = [
17
+ ".gitignore",
18
+ "LICENSE",
19
+ "README.rdoc",
20
+ "Rakefile",
21
+ "VERSION",
22
+ "chirpy.gemspec",
23
+ "lib/chirpy.rb",
24
+ "test/chirpy_test.rb",
25
+ "test/test_helper.rb"
26
+ ]
27
+ s.homepage = %q{http://github.com/ashrewdmint/chirpy}
28
+ s.rdoc_options = ["--charset=UTF-8"]
29
+ s.require_paths = ["lib"]
30
+ s.rubygems_version = %q{1.3.3}
31
+ s.summary = %q{A simple Twitter client for Ruby, written using Hpricot and RestClient.}
32
+ s.test_files = [
33
+ "test/chirpy_test.rb",
34
+ "test/test_helper.rb"
35
+ ]
36
+
37
+ if s.respond_to? :specification_version then
38
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
39
+ s.specification_version = 3
40
+
41
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
42
+ s.add_runtime_dependency(%q<hpricot>, ["~> 0.8.1"])
43
+ s.add_runtime_dependency(%q<rest-client>, ["~> 0.9.2"])
44
+ s.add_runtime_dependency(%q<htmlentities>, ["~> 4.0.0"])
45
+ else
46
+ s.add_dependency(%q<hpricot>, ["~> 0.8.1"])
47
+ s.add_dependency(%q<rest-client>, ["~> 0.9.2"])
48
+ s.add_dependency(%q<htmlentities>, ["~> 4.0.0"])
49
+ end
50
+ else
51
+ s.add_dependency(%q<hpricot>, ["~> 0.8.1"])
52
+ s.add_dependency(%q<rest-client>, ["~> 0.9.2"])
53
+ s.add_dependency(%q<htmlentities>, ["~> 4.0.0"])
54
+ end
55
+ end
data/lib/chirpy.rb ADDED
@@ -0,0 +1,681 @@
1
+ require 'cgi'
2
+ require 'rubygems'
3
+ require 'restclient'
4
+ require 'hpricot'
5
+ require 'htmlentities'
6
+
7
+ # Adds to_url_params and from_url_params methods to the Hash class.
8
+ # I found the code from: http://www.ruby-forum.com/topic/69428
9
+ class Hash
10
+
11
+ # Turns a hash into URL parameters, e.g. "key=value&another_key=another_value"
12
+ def to_url_params
13
+ elements = []
14
+ keys.size.times do |i|
15
+ elements << "#{keys[i]}=#{values[i]}"
16
+ end
17
+ elements.join('&')
18
+ end
19
+
20
+ # Takes a string of URL parameters and turns them into a hash
21
+ def from_url_params(url_params)
22
+ result = {}.with_indifferent_access
23
+ url_params.split('&').each do |element|
24
+ element = element.split('=')
25
+ result[element[0]] = element[1]
26
+ end
27
+ result
28
+ end
29
+ end
30
+
31
+ # Chirpy is a simple Twitter client for Ruby, written using RestClient and Hpricot.
32
+
33
+ class Chirpy
34
+ @username = nil
35
+ @password = nil
36
+
37
+ # Makes a new instance of Chirpy
38
+ #
39
+ # Example: <tt>chirpy = Chirpy.new('username', 'password')</tt>
40
+ #
41
+ # Authentication, however, is not required.
42
+ #
43
+ # Example: <tt>chirpy = Chirpy.new</tt>
44
+
45
+ def initialize(username = nil, password = nil)
46
+ authenticate(username, password) if username and password
47
+ end
48
+
49
+ #-- Authentication
50
+
51
+ # Tells Chirpy to use authentication.
52
+ #
53
+ # Example: <tt>chirpy.authenticate('username', 'password)</tt>
54
+
55
+ def authenticate(username, password)
56
+ @username = username
57
+ @password = password
58
+ end
59
+
60
+ # Turns authentication off.
61
+
62
+ def unauthenticate()
63
+ @username = nil
64
+ @password = nil
65
+ end
66
+
67
+ # Returns the username and password in a hash.
68
+
69
+ def authentication
70
+ {:username => @username, :password => @password}
71
+ end
72
+
73
+ private :authentication
74
+
75
+ #-- Utility methods
76
+
77
+ # Search results have bold tags and links in them. This removes it all.
78
+
79
+ def self.remove_crap(string)
80
+ remove_tags(decode_entities(string))
81
+ end
82
+
83
+ # Removes tags.
84
+
85
+ def self.remove_tags(string)
86
+ string.gsub(/<[^>]+>/, '')
87
+ end
88
+
89
+ # Decodes HTML entities.
90
+
91
+ def self.decode_entities(string)
92
+ HTMLEntities.new.decode(string)
93
+ end
94
+
95
+ #-- Search methods
96
+
97
+ # Searches Twitter. Supply a query and extra options in a hash (not required).
98
+ # Available options (go here for more details: http://apiwiki.twitter.com/Twitter-Search-API-Method)
99
+ # - :lang
100
+ # - :rpp
101
+ # - :page
102
+ # - :since_id
103
+ # - :geocode
104
+
105
+ def self.search(query, params = {})
106
+ get "search", params.merge({:q => query})
107
+ end
108
+
109
+ #-- Timeline methods
110
+
111
+ # Gets the public timeline. Authentication is not required for this.
112
+
113
+ def self.public_timeline
114
+ get "statuses/public_timeline"
115
+ end
116
+
117
+ # Instance method for public timeline
118
+
119
+ def public_timeline
120
+ Chirpy.public_timeline
121
+ end
122
+
123
+ # Gets the authenticated user's friends' timeline. Authentication required.
124
+ #
125
+ # Optional parameters:
126
+ # - :since_id
127
+ # - :max_id
128
+ # - :count
129
+ # - :page
130
+
131
+ def friends_timeline(params = {})
132
+ get "statuses/friends_timeline", params
133
+ end
134
+
135
+ # Gets a list of status updates from a specific user.
136
+ # If no user is supplied, the authenticated user will be used.
137
+ # you may supply a hash as the only argument.
138
+ # Authentication required.
139
+ #
140
+ # Optional parameters:
141
+ # - :user_id
142
+ # - :screen_name
143
+ # - :since_id
144
+ # - :max_id
145
+ # - :count
146
+ # - :page
147
+
148
+ def user_timeline(user = nil, params = {})
149
+ args = [user, params]
150
+ get path_from_args('statuses/user_timeline', args), params_from_args(args)
151
+ end
152
+
153
+ # Gets mentions for the authenticated user. Authentication required.
154
+ #
155
+ # Optional parameters:
156
+ # - :since_id
157
+ # - :max_id
158
+ # - :count
159
+ # - :page
160
+
161
+ def mentions(params = {})
162
+ get "statuses/mentions", params
163
+ end
164
+
165
+ #-- Status methods
166
+
167
+ # Shows a specific tweet. Authentication is only required if author is protected.
168
+
169
+ def show_status(status_id)
170
+ get "statuses/show/#{status_id}"
171
+ end
172
+
173
+ # Updates the status of the authenticated user. Authentication required, silly.
174
+
175
+ def update_status(status)
176
+ post "statuses/update", :post => {:status => status}
177
+ end
178
+
179
+ # Destroys one of the authenticated user's tweets.
180
+ #
181
+ # Authentication required.
182
+
183
+ def destroy_status(status_id)
184
+ delete "statuses/destroy/#{status_id}"
185
+ end
186
+
187
+ #-- User methods
188
+
189
+ # Shows details for a specific user. Authentication is only required if the user is protected.
190
+ # you may supply a hash as the only argument.
191
+ #
192
+ # Optional parameters:
193
+ # - :user_id
194
+ # - :screen_name
195
+
196
+ def show_user(user = nil, params = {})
197
+ args = [user, params]
198
+ get path_from_args('users/show', args), params_from_args(params)
199
+ end
200
+
201
+ # Gets a list of a user's friends.
202
+ # If no user is supplied, the authenticated user will be used.
203
+ # you may supply a hash as the only argument.
204
+ #
205
+ # Optional parameters:
206
+ # - :user_id
207
+ # - :screen_name
208
+ # - :page
209
+
210
+ def friends(user = nil, params = {})
211
+ args = [user, params]
212
+ get path_from_args('statuses/friends', args), params_from_args(args)
213
+ end
214
+
215
+ # Gets a list of a user's followers.
216
+ # If no user is supplied, the authenticated user will be used.
217
+ # However, you need to authenticate whether or not you supply the user parameter.
218
+ # Authentication required.
219
+ # You may supply a hash as the only argument.
220
+ #
221
+ # Optional parameters:
222
+ # - :user_id
223
+ # - :screen_name
224
+ # - :page
225
+
226
+ def followers(user = nil, params = {})
227
+ args = [user, params]
228
+ get path_from_args('statuses/followers', args), params_from_args(args)
229
+ end
230
+
231
+ # Gets a list of the messages sent to the authenticated user.
232
+ # Authentication required.
233
+ #
234
+ # Optional parameters:
235
+ # - :since_id
236
+ # - :max_id
237
+ # - :count
238
+ # - :page
239
+
240
+ def direct_messages(params = {})
241
+ get "direct_messages", params
242
+ end
243
+
244
+ # Gets a list of the messages sent by the authenticated user.
245
+ # Authentication required.
246
+ #
247
+ # Optional parameters:
248
+ # - :since_id
249
+ # - :max_id
250
+ # - :page
251
+
252
+ def direct_messages_sent(params = {})
253
+ get "direct_messages/sent", params
254
+ end
255
+
256
+ # Sends a direct message.
257
+ #
258
+ # Authentication required.
259
+
260
+ def direct_messages_new(recipient, text)
261
+ post_params = {:user => recipient, :text => text}
262
+ post "direct_messages/new", post_params
263
+ end
264
+
265
+ #-- Friendship methods
266
+
267
+ # Creates a friendship between authenticated user and another user.
268
+ # You may supply a hash as the only argument.
269
+ # Authentication required.
270
+ #
271
+ # Optional parameters:
272
+ # - :user_id
273
+ # - :screen_name
274
+ # - :follow (automatically set to true)
275
+
276
+ def create_friendship(user = nil, params = {})
277
+ args = [user, params]
278
+ post path_from_args('friendships/create', args), {:follow => true}.merge(params_from_args(args))
279
+ end
280
+
281
+ # Destroys a friendship between the authenticated user and another user.
282
+ # You may supply a hash as the only argument.
283
+ # Authentication required.
284
+ #
285
+ # Optional parameters:
286
+ # - :user_id
287
+ # - :screen_name
288
+
289
+ def destroy_friendship(user, params = {})
290
+ args = [user, params]
291
+ delete path_from_args('friendships/create', args), params_from_args(args)
292
+ end
293
+
294
+ # Checks if a friendship exists between two users; returns true or false if no error occured.
295
+ # If an error did occur, it returns the usual object.
296
+ #
297
+ # Authentication required.
298
+
299
+ def friendship_exists?(user_a, user_b)
300
+ response = get "friendships/exists", {:user_a => user_a, :user_b => user_b}
301
+ if response.ok?
302
+ response.data.%('friends').inner_html == 'true'
303
+ else
304
+ response
305
+ end
306
+ end
307
+
308
+ #-- Social graph methods
309
+
310
+ # Returns ids for someone's friends. You may supply a hash as the only argument.
311
+ #
312
+ # Optional parameters:
313
+ # - :user_id
314
+ # - :screen_name
315
+ # - :page
316
+
317
+ def friends_ids(user = nil, params = {})
318
+ args = [user, params]
319
+ get path_from_args('friends/ids', args), params_from_args(params)
320
+ end
321
+
322
+ # Returns ids for someone's followers. You may supply a hash as the only argument.
323
+ #
324
+ # Optional parameters:
325
+ # - :user_id
326
+ # - :screen_name
327
+ # - :page
328
+
329
+ def followers_ids(user = nil, params = {})
330
+ args = [user, params]
331
+ get path_from_args('followers/ids', args), params_from_args(params)
332
+ end
333
+
334
+ #-- Account methods
335
+
336
+ # Use this to check if a username and password are valid.
337
+ # Returns a Chirpy instance if valid, otherwise, false.
338
+
339
+ def self.verify_credentials(username, password)
340
+ chirpy = self.new(username, password)
341
+ chirpy.verify_credentials
342
+ end
343
+
344
+ # Use this to check if an instance's username and password are valid.
345
+ #
346
+ # Authentication required.
347
+
348
+ def verify_credentials
349
+ if auth_supplied?
350
+ response = get "account/verify_credentials"
351
+ response.ok? ? response : false
352
+ else
353
+ false
354
+ end
355
+ end
356
+
357
+ # Gets information on rate limiting.
358
+ # Specify <tt>:authenticate => false</tt> to see rate limiting for current ip
359
+
360
+ def rate_limit_status(params)
361
+ get "account/rate_limit_status", params
362
+ end
363
+
364
+ # Ends the session of the authenticated user
365
+
366
+ def end_session
367
+ post "account/end_session", :post => {}
368
+ end
369
+
370
+ # Updates the authenticated user's delivery device. Must be one of:
371
+ # - sms
372
+ # - im
373
+ # - none
374
+
375
+ def update_delivery_device(device)
376
+ post "account/update_delivery_device", :post => {:device => device}
377
+ end
378
+
379
+ # Updates the authenticated user's colors (on their Twitter page).
380
+ #
381
+ # Please supply a hash with hexadecimal colors (e.g. "fff" or "ffffff").
382
+ # Don't include the "#" character in front of the color code.
383
+ # Here are the different values you can customize:
384
+ # - :background_color
385
+ # - :text_color
386
+ # - :sidebar_color
387
+ # - :sidebar_fill_color
388
+ # - :sidebar_border_color
389
+
390
+ def update_profile_colors(colors)
391
+ return unless colors.is_a?(Hash)
392
+ post_data = {}
393
+
394
+ colors.each_pair do |key, value|
395
+ post_data.store("profile_#{key}", value.gsub(/#/, ''))
396
+ end
397
+
398
+ post "account/update_profile_colors", :post => post_data
399
+ end
400
+
401
+ #-- TODO: update_profile_image and update_profile_background_image
402
+ #-- Methods delayed until I can figure out how to get RestClient working with them
403
+
404
+ # Updates the user's profile information. Pass in a hash with symbols as keys.
405
+ #
406
+ # From: http://apiwiki.twitter.com/Twitter-REST-API-Method%3A-account%C2%A0update_profile
407
+ # - :name, 20 characters max.
408
+ # - :email, 40 characters max. Must be a valid email address.
409
+ # - :url, 100 characters max. Will be prepended with "http://" if not present.
410
+ # - :location, 30 characters max. The contents are not normalized or geocoded in any way.
411
+ # - :descriptionm 160 characters max.
412
+
413
+ def update_profile(params)
414
+ post 'account/update_profile', :post => params
415
+ end
416
+
417
+ #-- Favorite methods
418
+
419
+ # Gets a list of a user's favorites.
420
+ # You may supply a hash as the only argument.
421
+ # Authentication required.
422
+ #
423
+ # Optional parameters:
424
+ # - :id
425
+ # - :page
426
+
427
+ def favorites(user = nil, params = {})
428
+ args = [user, params]
429
+ get path_from_args('favorites', args), params_from_args(args)
430
+ end
431
+
432
+ # Adds a tweet to the authenticated user's favorites.
433
+ #
434
+ # Authentication required.
435
+
436
+ def create_favorite(id)
437
+ post "favorites/create/#{id}", {}
438
+ end
439
+
440
+ # Removes a tweet from the authenticated user's favorites
441
+ #
442
+ # Authentication required, Strong Bad.
443
+
444
+ def destroy_favorite(id)
445
+ delete "favorites/destroy/#{id}"
446
+ end
447
+
448
+ #-- Notification methods
449
+
450
+ # Makes the authenticated user follow a new person. Authentication required.
451
+ # Pass a username or a hash with one of the following options:
452
+ # - :user_id
453
+ # - :screen_name
454
+
455
+ def follow(user_or_params)
456
+ args = [user_or_params]
457
+ post path_from_args('notifications/follow', args), params_from_args(args).merge({:post => {}})
458
+ end
459
+
460
+ # Unfollows a person the authenticated user is following. Authentication required.
461
+ # Pass a username or a hash with one of the following options:
462
+ # - :user_id
463
+ # - :screen_name
464
+
465
+ def leave(user_or_params)
466
+ args = [user_or_params]
467
+ post path_from_args('notifications/leave', args), params_from_args(params).merge({:post => {}})
468
+ end
469
+
470
+ #-- Block methods
471
+
472
+ # Makes the authenticated user block someone.
473
+ # Authentication required.
474
+
475
+ def block(user)
476
+ post "blocks/create/#{user}"
477
+ end
478
+
479
+ # Removes the authenticated user's block.
480
+ # Authentication required.
481
+
482
+ def destroy_block(user)
483
+ delete "blocks/destroy/#{user}"
484
+ end
485
+
486
+ # Checks if the authenticated user is blocking someone.
487
+ # Pass in a username or a hash with one of the following options:
488
+ # - :user_id
489
+ # - :screen_name
490
+ #
491
+ # Authentication required.
492
+
493
+ def block_exists(user_or_params)
494
+ args = [user_or_params]
495
+ get path_from_args('block/exists', args), params_from_args(params)
496
+ end
497
+
498
+ # Returns a list of people the authenticated user is blocking.
499
+ # You can pass :page => x if you want to.
500
+ #
501
+ # Authentication required.
502
+
503
+ def blocking(params = {})
504
+ get "blocks/blocking", params
505
+ end
506
+
507
+ # Returns a list of the ids of the people the authenticated user is blocking.
508
+ #
509
+ # Authentication required.
510
+
511
+ def blocking_ids
512
+ get "blocks/blocking/ids"
513
+ end
514
+
515
+ #-- Help methods
516
+
517
+ def self.test
518
+ get "help/test"
519
+ end
520
+
521
+ private
522
+
523
+ # Concatenates the username onto the path if the former is found in the arguments.
524
+
525
+ def path_from_args(path, args)
526
+ username = nil
527
+ args.each { |arg| username = arg if arg.is_a?(String) }
528
+ username ? path + "/#{username}" : path
529
+ end
530
+
531
+ # Finds and returns the hash in the arguments, or an empty hash if nothing is found.
532
+
533
+ def params_from_args(args)
534
+ params = {}
535
+ args.each { |arg| params = arg if arg.is_a?(Hash) and ! arg.empty? }
536
+ params
537
+ end
538
+
539
+ # Calls request. By default, request will use the get method
540
+
541
+ def get(path, params = {})
542
+ Chirpy.request params.merge({:path => path, :method => 'get'}.merge(authentication))
543
+ end
544
+
545
+ # Calls request with post method
546
+
547
+ def post(path, params = {})
548
+ Chirpy.request params.merge({:path => path, :method => 'post'}.merge(authentication))
549
+ end
550
+
551
+ # Calls request with delete method
552
+
553
+ def delete(path, params = {})
554
+ Chirpy.request params.merge({:path => path, :method => 'delete'}.merge(authentication))
555
+ end
556
+
557
+ # Class method for get
558
+
559
+ def self.get(path, params = {})
560
+ request params.merge({:path => path, :method => 'get'})
561
+ end
562
+
563
+ # Constructs the correct url (including authentication), uses RestClient to call Twitter,
564
+ # parses the data with Hpricot, handles errors (both from RestClient and Twitter)
565
+ # and returns the result, for great justice!
566
+
567
+ # Resulting objects have three methods, "status", "ok?", and "data".
568
+ # Call "ok?" to check if there are errors. If there are errors, you can look inside the
569
+ # hash returned by the "status" method, which gives you information on what went wrong.
570
+ #
571
+ # The Hpricot object can be retreived by calling the "data" method.
572
+
573
+ def self.request(params)
574
+ params = organize_params({:authenticate => true}.merge(params))
575
+ url = 'twitter.com/' + params[:path] + '.xml' + params[:url_params]
576
+
577
+ if url =~ /search/
578
+ url = 'search.' + url.sub(/xml/, 'atom')
579
+ end
580
+
581
+ username = params[:username]
582
+ password = params[:password]
583
+
584
+ auth_supplied = !! username and !! password
585
+ use_authentication = params[:authenticate]
586
+
587
+ # Simple authentication
588
+
589
+ if auth_supplied and use_authentication
590
+ url = 'https://' + username + ':' + password + '@' + url
591
+ else
592
+ url = 'http://' + url
593
+ end
594
+
595
+ # Call Twitter
596
+
597
+ begin
598
+ response = case params[:method]
599
+ when 'get' then
600
+ RestClient.get(url)
601
+ when 'post' then
602
+ RestClient.post(url, params[:post])
603
+ when 'delete' then
604
+ RestClient.delete(url)
605
+ end
606
+ rescue Exception => error
607
+ status = {:ok => false, :error_message => error.message, :error_response => error.response, :exception => error.class}
608
+ end
609
+
610
+ # Parse with Hpricot and check for errors
611
+
612
+ if (response)
613
+ response = Hpricot.XML(response, :fixup_tags => true)
614
+ error = response.search('error')
615
+ if (error.length > 0)
616
+ status = {:ok => false, :error_message => error.first.inner_html.strip}
617
+ end
618
+ end
619
+
620
+ status = {:ok => true} unless status
621
+ prepare_response(response, url, status)
622
+ end
623
+
624
+ # Tacks on handy methods to the response.
625
+ # The status is a hash with error messages, if anything went wrong.
626
+ # The URL is the url requested when Twitter was called.
627
+ # Call the "ok?" method to quickly check if there was an error.
628
+
629
+ def self.prepare_response(response, url, status)
630
+ response = Hpricot('') unless response
631
+
632
+ class << response
633
+ attr_accessor :url, :status
634
+
635
+ @url = nil
636
+ @status = nil
637
+
638
+ def ok?
639
+ status[:ok]
640
+ end
641
+ end
642
+
643
+ response.url = url
644
+ response.status = status
645
+ response
646
+ end
647
+
648
+ def self.organize_params(params)
649
+ url_params_list = [
650
+ :id,
651
+ :user_id,
652
+ :screen_name,
653
+ :page,
654
+ :since_id,
655
+ :max,
656
+ :count,
657
+ :q,
658
+ :lang,
659
+ :rpp,
660
+ :geo_code,
661
+ :show_user
662
+ ]
663
+ url_params = {}
664
+
665
+ # Escape query
666
+ params[:q] = CGI.escape(params[:q]) if params[:q]
667
+
668
+ params.each_pair do |key, value|
669
+ if url_params_list.include?(key)
670
+ url_params.store(key, value)
671
+ params.delete(key)
672
+ end
673
+ end
674
+
675
+ url_params = url_params.to_url_params
676
+ url_params = '?' + url_params unless url_params == ''
677
+
678
+ params = {:method => 'get', :url_params => url_params}.merge(params)
679
+ end
680
+
681
+ end
@@ -0,0 +1,36 @@
1
+ require 'test_helper'
2
+
3
+ class ChirpyTest < Test::Unit::TestCase
4
+ @@root = "http://twitter.com/"
5
+
6
+ context "Class methods" do
7
+ should "request the public timeline URL" do
8
+ assert_equal @@root + "statuses/public_timeline.xml", Chirpy.public_timeline.url
9
+ end
10
+
11
+ should "request the test URL" do
12
+ assert_equal @@root + "help/test.xml", Chirpy.test.url
13
+ end
14
+
15
+ should "request a search URL" do
16
+ search_term = 'three blind mice'
17
+ assert_equal "http://search.twitter.com/search.atom?q=" + CGI.escape(search_term), Chirpy.search(search_term).url
18
+ end
19
+ end
20
+
21
+ context "Authenticated user" do
22
+ setup do
23
+ @username = 'testuser'
24
+ @password = 'testpass'
25
+ @chirpy = Chirpy.new(@username, @password)
26
+ end
27
+
28
+ should "send authentication in URL" do
29
+ assert_equal "https://#{@username}:#{@password}@twitter.com/statuses/user_timeline.xml", @chirpy.user_timeline.url
30
+ end
31
+
32
+ should "not send authentication in URL when specified" do
33
+ assert_equal "http://twitter.com/statuses/user_timeline.xml", @chirpy.user_timeline(:authenticate => false).url
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'chirpy'
8
+
9
+ class Test::Unit::TestCase
10
+ end
metadata ADDED
@@ -0,0 +1,92 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ashrewdmint-chirpy
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.2
5
+ platform: ruby
6
+ authors:
7
+ - Andrew Smith
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-21 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hpricot
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ~>
22
+ - !ruby/object:Gem::Version
23
+ version: 0.8.1
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: rest-client
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 0.9.2
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: htmlentities
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 4.0.0
44
+ version:
45
+ description: Lets you easily interact with Twitter's API; post status updates, search Twitter, and more!
46
+ email: andrew.caleb.smith@gmail.com
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - LICENSE
53
+ - README.rdoc
54
+ files:
55
+ - .gitignore
56
+ - LICENSE
57
+ - README.rdoc
58
+ - Rakefile
59
+ - VERSION
60
+ - chirpy.gemspec
61
+ - lib/chirpy.rb
62
+ - test/chirpy_test.rb
63
+ - test/test_helper.rb
64
+ has_rdoc: false
65
+ homepage: http://github.com/ashrewdmint/chirpy
66
+ post_install_message:
67
+ rdoc_options:
68
+ - --charset=UTF-8
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: "0"
76
+ version:
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: "0"
82
+ version:
83
+ requirements: []
84
+
85
+ rubyforge_project:
86
+ rubygems_version: 1.2.0
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: A simple Twitter client for Ruby, written using Hpricot and RestClient.
90
+ test_files:
91
+ - test/chirpy_test.rb
92
+ - test/test_helper.rb