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 +3 -0
- data/LICENSE +20 -0
- data/README.rdoc +105 -0
- data/Rakefile +62 -0
- data/VERSION +1 -0
- data/chirpy.gemspec +55 -0
- data/lib/chirpy.rb +681 -0
- data/test/chirpy_test.rb +36 -0
- data/test/test_helper.rb +10 -0
- metadata +92 -0
data/.gitignore
ADDED
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
|
data/test/chirpy_test.rb
ADDED
@@ -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
|
data/test/test_helper.rb
ADDED
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
|