ashrewdmint-chirpy 0.8.2

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.
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