Introduction

When building a website, sometimes we might want to send a tweet on a user's behalf to Twitter. For example, lets say we are building a blog and want to send out a tweet to Twitter every time we publish a new post. Fortunately, this is easy using Ruby on Rails and two gems, omniauth-twitter and twitter. The omniauth-twitter gem handles authentication, while the twitter gem actually handles the interactions with twitter.

Twitter Application Setup

Before we build our app, we will need to set up a new application on the Twitter developers website. In order to do this, simply click here to create a new application and fill out the details. Name, description, and website can be anything you want it to be. The callback url does not accept localhost for a URL, so we will need to use something else. Fortunately, VMWare owns a domain called vcap.me that resolves to 127.0.0.1. For development purposes this is perfect. To utilize this url, simply enter http://vcap.me:3000/auth/twitter/callback for the callback url. Remember that prior to production you will need to change this url or create another application in order to use this code on production.

Twitter Application Setup Screenshot

Once you have created the app, you will need to change a few settings. By default the ability to sign in with Twitter is disabled and access privileges are set to read only. This means you can't tweet as another user. Fortunately, this is easy to change. On the Twitter apps page simply click your application and then click settings. On the settings page, choose the radio button that says "Read and Write" under application type, then check the box that says "Allow this application to be used to Sign in with Twitter" and finally click the "Update this Twitter application's settings" button.

Twitter Application Settings Screenshot

Once you have saved these settings, click the details tab at the top and note the values next to the Consumer key and Consumer secret labels. You will need these values later.

Rails Application Setup

Now that we have created our application on Twitter we can begin building our Rails app. The first thing we need to do is include the omniauth-twitter and twitter gems in our Gemfile. Open up your gemfile and add in the lines listed below.

Gemfile:
gem 'twitter', '~> 5.3.1'
gem 'omniauth-twitter', '~> 1.0.1'

Now run a bundle install to install the gems.

Terminal Commands:
bundle install

Next we need to create one model and three controllers. The model, User, will store information on our users, including two very important pieces of information needed to send tweets as that user. The first controller, Home, simply provides a landing page for users. The second controller, Sessions implements login/logout functionality. The final controller, Tweets, takes information from the user and uses it to send a tweet. Run the commands listed below to create these items now.

Terminal Commands:
rails g model user provider uid name oauth_token oauth_secret
rake db:migrate
rails g controller home show
rails g controller sessions new create destroy
rails g controller tweets new create

Next we need to edit our routes file. The first two routes are used to implement the omniauth login functionality. The remaining routes are used for our application. Open up your routes file and modify it so that it looks like the code listed below. Be sure not to overwrite your own application's name on the first line with the one in this example.

config/routes.rb:
TwitterExample::Application.routes.draw do
  get 'auth/:provider/callback', to: 'sessions#create'
  get 'auth/failure', to: redirect('/')
  
  get 'signout', to: 'sessions#destroy', as: 'signout'
  resources :tweets, only: [:new, :create]
  resources :sessions, only: [:create, :destroy]
  resource :home, only: [:show]
 
  root to: 'home#show'
end

Next we need to create a few application configuration entries to store our twitter consumer key and consumer secret. We can accomplish this in a number of different ways, including storing it in YAML files, environment variables (this is the recommended way), or even directly in our development config. For simplicity's sake we will store the keys right in our development config. It is recommended that you use a different method in your production apps, as the config files get checked into source control. Open up your development config file and add in the code listed below, but be sure to replace YOUR_CONSUMER_KEY with your twitter consumer key, and YOUR_CONSUMER_SECRET with your twitter consumer secret that you noted earlier in the tutorial. Also be sure not to overwrite your applications name on line one of your config file.

config/environments/development.rb:
TwitterExample::Application.configure do
  config.cache_classes = false
  config.eager_load = false
  config.consider_all_requests_local       = true
  config.action_controller.perform_caching = false
  config.action_mailer.raise_delivery_errors = false
  config.active_support.deprecation = :log
  config.active_record.migration_error = :page_load
  config.assets.debug = true
  config.twitter_key = "YOUR_CONSUMER_KEY"
  config.twitter_secret = "YOUR_CONSUMER_SECRET"
end

Now we need to create an initializer. This initializer sets up omniauth using the config values we just created. Create an omniauth initializer called 'omniauth_twitter.rb' and paste in the code listed below.

config/initializers/omniauth_twitter.rb:
Rails.application.config.middleware.use OmniAuth::Builder do
  provider :twitter, Rails.application.config.twitter_key, Rails.application.config.twitter_secret
end

Great, now we need to add some code to our model. The first method, from_omniauth, takes and parses out data passed back by omniauth. The second method, tweet, is the code that is actually responsible for sending our tweet. Open up your user model and add in the code listed below.

app/models/user.rb:
class User < ActiveRecord::Base
  def self.from_omniauth(auth)
    where(auth.slice(:provider, :uid)).first_or_initialize.tap do |user|
      user.provider = auth.provider
      user.uid = auth.uid
      user.name = auth.info.name
      user.oauth_token = auth.credentials.token
      user.oauth_secret = auth.credentials.secret
      user.save!
    end
  end

  def tweet(tweet)
    client = Twitter::REST::Client.new do |config|
      config.consumer_key        = Rails.application.config.twitter_key
      config.consumer_secret     = Rails.application.config.twitter_secret
      config.access_token        = oauth_token
      config.access_token_secret = oauth_secret
    end
    
    client.update(tweet)
  end
end

Great, now lets add some code to our controllers. First let's add in code to our sessions controller to allow it to take the data passed by omniauth and do something useful with it. The code contained within the new method will find or create the user that omniauth returns and then store that user's id in the session. The destroy method merely logs the user out. Open up your sessions controller and add in the code listed below.

app/controllers/sessions_controller.rb:
class SessionsController < ApplicationController
  def create
    user = User.from_omniauth(env["omniauth.auth"])
    session[:user_id] = user.id
    redirect_to root_path
  end
 
  def destroy
    session[:user_id] = nil
    redirect_to root_path
  end
end

Advertisement
Great, now lets add some code to our tweets controller. The create method will take input from the user and use that information to send a tweet. Open up your tweets controller and add in the code listed below. app/controllers/tweets_controller.rb:
class TweetsController < ApplicationController
  def new
  end

  def create
    current_user.tweet(twitter_params[:message])
  end

  def twitter_params
    params.require(:tweet).permit(:message)
  end
end

Almost done with the controllers, the last thing we need to do is modify our application controller and add a method called current_user. This method will return the currently logged in user or nil if the user isn't logged in. This method will also be provided as a helper method so that views can use it. open up your application controller and add in the code listed below.

app/controllers/tweets_controller.rb:
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  helper_method :current_user
 
  def current_user
    @current_user ||= User.find(session[:user_id]) if session[:user_id]
  end
end

Now we need to create our views. Add in the code for the listed views below.

app/views/home/show.html.erb:
<% if !current_user.nil? %>
    <p>Click The link below to send a tweet.</p>
    <%= link_to "Send a Tweet", new_tweet_path %>
<% else %>
    <p>You must log in to send a tweet.</p>
<% end %>
app/views/layouts/application.html.erb:
<!DOCTYPE html>
<html>
  <head>
    <title>Twitter Auth Example App</title>
    <%= stylesheet_link_tag    "application", media: "all", "data-turbolinks-track" => true %>
    <%= javascript_include_tag "application", "data-turbolinks-track" => true %>
    <%= csrf_meta_tags %>
  </head>
  <body>
    <div>
      <% if current_user %>
        Signed in as <strong><%= current_user.name %></strong>!
        <%= link_to "Sign out", signout_path, id: "sign_out" %>
      <% else %>
        <%= link_to "Sign in with Twitter", "/auth/twitter", id: "sign_in" %>
      <% end %>
    </div>
    <div>
    <%= yield %>
    </div>
  </body>
</html>
app/views/tweets/create.html.erb:
<h1>Success!</h1>
<p>Your tweet has been sent!</p>
<%= link_to "Would you like to send another?", new_tweet_path %>
app/views/tweets/new.html.erb:
<p>
  <%= form_for :tweet, url: tweets_path, method: :post do |f| %>
    <%= f.text_field :message %>
    <%= f.submit "Send Tweet" %>
  <% end %>
</p>

SSL Setup (Optional)

Great, now that the views are done, we are almost ready to run the app. Before we start our Rails server there is one more step we should perform. While this is optional, if you have any problems running the app you should go back and perform these steps.

The first thing we need to do is download this cert file and place it in the root directory of our Rails application. We will load this file in place of our default SSL certificates.

The next thing we need to do is create an initializer called ssl_fix.rb. Open up this initializer and add in the code listed below.

app/initializers/ssl_fix.rb:
require 'open-uri'
require 'net/https'

module Net
  class HTTP
    alias_method :original_use_ssl=, :use_ssl=

    def use_ssl=(flag)
      self.ca_file = Rails.root.join("cacert.pem").to_s
      self.original_use_ssl = flag
    end
  end
end

This code monkey patches Ruby to load the certificates from our cert file in our Rails file instead of the (potentially outdated or non-existent) one on our system.

Now we are all set. Start your rails server and navigate to http://localhost:3000. You will see a 'Sign in with Twitter' link. Click it and you will be redirected to a twitter authorization screen. Clicking 'Authorize app' will send you back to your application. You can then click 'Send a tweet' to send a tweet. That's all there is to it! Thanks for reading!