Ruby Tuesday #8 Part 1 : Tweeting from Ruby
This week I aimed to tweet from Ruby. The first thing I noticed was that the code that had worked last week had stopped working. Twitter seems to be requiring requests for the friends timeline to be authenticated. So I needed to make a couple of adjustments to cater for this change. Firstly I created a new method called get_response. It looks like this:
def get_response(request, url)
begin
res = Net::HTTP.start(url.host, url.port) {|http| http.request(request)}
case res
when Net::HTTPSuccess, Net::HTTPRedirection
if res.body.empty?
puts "Empty Body"
else
res.body
end
else
puts res.to_s
end
res.body
rescue SocketError
puts "SocketError"
end
end
I’ve put in some very rudimentary exception handling – this way I can see what’s going on – the begin / rescue is the error handling structure. If you have a C# or Java background, beware: throw catch exists in Ruby, but it’s different. Read this for a fuller explanation of exception handling in Ruby. Next I needed to hook this method into the get_url method:
def get_url(base_address, path, username, password) url = form_url(base_address, path) req = Net::HTTP::Get.new(url.path) init_request req, username, password get_response(req, url) end
You may have spotted a couple of other changes in that method. I’m asking for a username and password and I’ve added a couple of convenience methods that’ll help me write a post_url method. The form_url method is simple:
def form_url(base_address, path) URI.parse(base_address + path) end
The init_request method looks like this:
def init_request(request, username, password) if (username && password) request.basic_auth(username, password) end end
This method takes advantage of the fact that only nil and false equate to nil in Ruby. So, now I can update my download_friends_timeline method like this:
def download_friends_timeline(username, password)
download_timeline("/friends_timeline/#{username}.xml", username, password)
end
and the download_timeline method like this:
def download_timeline(path, username=nil, password=nil) get_url(BASE_ADDRESS, path, username, password) end
Notice that I’ve supplied default values for username and password, which makes them optional (from the caller’s perspective.) Since this method is called from a couple of methods where no authentication is required, making these arguments optional seems like a good idea. A quick update to the code in main, and that all worked. Great. Now time to tweet. A tweet requires a post, so we need a method to do that. I decided to call the method post_url:
def post_url(base_address, path, username, password, form_data) url = form_url(base_address, path) req = Net::HTTP::Post.new(url.path) init_request req, username, password req.set_form_data(form_data) get_response(req, url) end
Looks a lot like get_url. Hmm. There must be a way to improve that – but let’s leave it for now. Next we need a method on the Client class called Update:
def update(update_text, username, password)
post_url(BASE_ADDRESS, "/update.xml", username, password, {'status' => update_text})
end
All that’s remaining is to call that from main:
require 'Twitter'
$KCODE = "u"
username = 'Put your username here'
password= 'Put your password here'
translator = Translator.new
client = Client.new
client.update('Tweeting from Ruby as part of Ruby Tuesday', username, password)
translator.xml_to_tweets(client.download_public_timeline).each do |tweet|
puts "#{tweet.user.screen_name} says #{tweet.text}"
end
translator.xml_to_tweets(client.download_friends_timeline(username, password)).each do |tweet|
puts "#{tweet.user.screen_name} says #{tweet.text}"
end
translator.xml_to_tweets(client.download_user_timeline('blooders')).each do |tweet|
puts "#{tweet.user.screen_name} says #{tweet.text}"
end
It all worked first time. So, I can tweet from Ruby. There’s probably some improvements that can be made to the code. To make this usable, I need to add some sort of interface. That should keep me busy next week.