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,
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.
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.
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
gem 'twitter', '~> 5.3.1' gem 'omniauth-twitter', '~> 1.0.1'
Now run a
bundle install to install the gems.
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.
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.
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.
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.
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.
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.
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
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.
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.
<% 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 %>
<h1>Success!</h1> <p>Your tweet has been sent!</p> <%= link_to "Would you like to send another?", new_tweet_path %>
<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!