Posting to Twitter from a Ruby on Rails App

Similar to Tuesday's Posting to Facebook post, here is how to link a model in a Ruby on Rails app with Twitter and post to Twitter. This one does the exact same thing, but of course needs to use different code because everyone implements OAuth differently. To post to Twitter, I use the OAuth gem to connect, and the Twitter Gem to do posting. Note that this uses the 1.0 version of the Twitter gem which is not yet released, so the Gemfile pulls it from Github directly. Read The Twitter Docs, specifically the Twitter OAuth Docs. The basic flow here for the user is the same as with Facebook:
  1. User visits twitter_account/new when they want to link in Twitter
  2. They get redirected to the allow/deny page on Twitter that prompts the user to log in and accept your application
  3. Assuming they accept, they get redirected back to callback/twitter/ with an oauth_token that we use to look up the TwitterAccount (a nice feature that Facebook doesn't do!) and the oauth_verifier
  4. The application uses the OAuth gem to sends that verifier back to Twitter to retrieve an access_token, which can be used for all future posts to Twitter to act as the user
First, some Gems: (If you're using a feedreader and not looking at this on the web, the code snippets may not show up. Please click through to the original source to see them!)
gem 'oauth'
gem 'twitter', :git => 'git://github.com/jnunemaker/twitter.git'
view raw Gemfile hosted with ❤ by GitHub
Run "bundle install" and set up the routes:
resource :twitter_account
match '/callback/twitter/' => "twitter_accounts#callback", :as => :twitter_callback
view raw Routes.rb hosted with ❤ by GitHub
Note that this is simpler than the Facebook callback because Twitter includes something in responses that can be used to look up the record instead of requiring a unique callback URL. Create the table in a migration, something like:
create_table "twitter_accounts", :force => true do |t|
t.integer "user_id"
t.boolean "active", :default => false
t.text "stream_url"
t.string "oauth_token"
t.string "oauth_token_secret"
t.string "oauth_token_verifier"
t.text "oauth_authorize_url"
end
view raw table.rb hosted with ❤ by GitHub
Then set up the model which takes care of generating the authorize url, validating the token, and posting:
class TwitterAccount < ActiveRecord::Base
CONSUMER_KEY = 'key'
CONSUMER_SECRET = 'secret'
OPTIONS = {:site => "http://api.twitter.com", :request_endpoint => "http://api.twitter.com"}
belongs_to :user
def authorize_url(callback_url = '')
if self.oauth_authorize_url.blank?
# Step one, generate a request URL with a request token and secret
signing_consumer = OAuth::Consumer.new(TwitterAccount::CONSUMER_KEY, TwitterAccount::CONSUMER_SECRET, TwitterAccount::OPTIONS)
request_token = signing_consumer.get_request_token(:oauth_callback => callback_url)
self.oauth_token = request_token.token
self.oauth_token_secret = request_token.secret
self.oauth_authorize_url = request_token.authorize_url
self.save!
end
self.oauth_authorize_url
end
def validate_oauth_token(oauth_verifier, callback_url = '')
begin
signing_consumer = OAuth::Consumer.new(TwitterAccount::CONSUMER_KEY, TwitterAccount::CONSUMER_SECRET, TwitterAccount::OPTIONS)
access_token = OAuth::RequestToken.new(signing_consumer, self.oauth_token, self.oauth_token_secret).
get_access_token(:oauth_verifier => oauth_verifier)
self.oauth_token = access_token.params[:oauth_token]
self.oauth_token_secret = access_token.params[:oauth_token_secret]
self.stream_url = "http://twitter.com/#{access_token.params[:screen_name]}"
self.active = true
rescue OAuth::Unauthorized
self.errors.add(:oauth_token, "Invalid OAuth token, unable to connect to twitter")
self.active = false
end
self.save!
end
def post(message)
Twitter.configure do |config|
config.consumer_key = TwitterAccount::CONSUMER_KEY
config.consumer_secret = TwitterAccount::CONSUMER_SECRET
config.oauth_token = self.oauth_token
config.oauth_token_secret = self.oauth_token_secret
end
client = Twitter::Client.new
begin
client.update(message)
return true
rescue Exception => e
self.errors.add(:oauth_token, "Unable to send to twitter: #{e.to_s}")
return false
end
end
end
And lastly, the controller to glue it all together:
class TwitterAccountsController < ApplicationController
def new
twitter_account = TwitterAccount.create(:user => current_user)
redirect_to(twitter_account.authorize_url(twitter_callback_url))
end
def callback
if params[:denied] && !params[:denied].empty?
redirect_to(deals_url, :alert => 'Unable to connect with twitter: #{parms[:denied]}')
else
twitter_account = TwitterAccount.find_by_oauth_token(params[:oauth_token])
twitter_account.validate_oauth_token(params[:oauth_verifier], twitter_callback_url)
twitter_account.save
if twitter_account.active?
redirect_to(deals_url, :notice => 'Twitter account activated!')
else
redirect_to(deals_url, :notice => "Unable to activate twitter account.")
end
end
end
end
As with the Facebook example, you'll need to add your own views, error handling, etc, but hopefully this is useful to someone. I got things close to working with the old version of the Twitter gem and had to rewrite a lot of it for the 1.0 gem (because I needed some functionality only in the 1.0 version).