ls -al

仮想通貨やプログラミングに関する事などをつらつらと書き綴ります

Rails5でOmniAuthを使ったTwitter連携(応用編)

以前こちらの記事ではOmniAuthを使ってTwitterアカウントでの認証機能をもったWebアプリを作ってみました。今回の記事ではその続きということで、認証後にTwitterのアカウントの情報を使ったWebアプリを作ってみたいと思います。

目標

以下のようなアプリの制作を目標とします。

  • / → アカウント情報表示、未ログインで/loginに遷移
  • /auth/twitter → Twitter認証実行
  • /auth/twitter/callback → Twitter認証後のコールバック先
  • /logout → ログアウト処理
  • /login → ログインを促す画面表示

ここまでは前回の記事で作成済みです。今回はこれに付け足す形で以下の機能も実装します。

  • /items/:id → 指定されたidの記事を表示
  • /items/new → 記事の新規作成ページを表示
  • /items/create → 記事を作成
  • /items/unauth → 記事の公開範囲外のユーザーが遷移される
  • 記事は公開範囲を設定できる。フォロワーのみと設定された記事はフォロワー以外には閲覧できない。

記事のモデル作成

以下のコマンドを入力して記事のモデルを作成します。

rails g model Item text:text only_followers:boolean user:references  

それぞれ

  • text → 記事の本文
  • only_followers → 公開範囲をフォロワーのみとするか
  • user → userを外部キーに設定

という役割とします。完了後にmigrateしてください。また、ItemとUserを、一対多のリレーションとしたいので、models/user.rbに以下を追記します

has_many :items,dependent: :destroy

記事のコントローラー作成

以下のコマンドを入力して記事を管理するコントローラーを作成します。

rails g controller Items new show create unauthorized

それぞれ

  • new → 記事新規作成ページ
  • show → 記事閲覧
  • create → 記事作成
  • unauthorized → 閲覧範囲外の時に遷移するページ

という役割とします。routes.rbに追記してルーティングも行います。なお、generate時に追記されているitems関連のルーティングは消してください。

routes.rb

resources :items
get '/items/unauth',to:'items#unauthorized'

コントローラーの中身も実装していきます。

controllers/items_controller.rb

class ItemsController < ApplicationController
  def new
    @item = Item.new
  end

  def create
    user = User.find(session[:user_id])
    item = user.items.new(user_params)
    if item.save
      id = item[:id]
      redirect_to "/items/"+id.to_s
    else
      render 'new'
    end
  end

  def show
    id = params[:id]
    @item = Item.find(id)
  end
  
  def unauthorized
  end

  private
    def user_params
      params.require(:item).permit(:text, :only_followers)
    end
end

至って普通の実装です。newでitemのインスタンスを取得、createで入力情報の保存に成功すればshowに飛び、失敗すれば再度newをrenderし、showで対応するidのitemのインスタンスを取得する、という流れになります。

記事のビュー作成

記事のビューを作っていきます。

views/items/new.html.erb

<%= form_for(@item, url: "/items") do |f| %>

  <%= f.label :text %>
  <%= f.text_field :text %>

  <%= f.label :only_followers %>
  <%= f.check_box :only_followers %>


  <%= f.submit "submit"%>
<% end %>

views/items/show.html.erb

<%= @item.text %>

views/items/unauthorized.html.erb

ユーザーの設定している公開範囲外です。

スタイルを一切適用していないので質素極まりない見た目になりますが、とりあえず動くものを作りました。

一旦ここまでで、ユーザーがフォームに入力することで、自分のアカウントに紐付いた記事を作成し、公開することが出来るアプリが出来ました。

gemfileの追加

ここからいよいよ、Twitterと連携した機能を作成していきます。まず、gemfileに追記します。

gem 'twitter'

追記後、bundle installしてください。これによりこのgemが導入されます。これを使うことでTwitterAPIを楽に扱うことが出来るようになります。

showメソッド改変

公開範囲を制限する機能をshowメソッドに付け加えます。以下のようにshowメソッドを書き換えてください。

  def show
    id = params[:id]
    @item = Item.find(id)
    current_user = User.find(session[:user_id])
    owner_user = User.find(@item[:user_id])
    redirect_to action:'unauthorized' unless authentication(@item[:only_follower],current_user,owner_user)
  end

現在ログインしているユーザーと、記事の所有者のユーザーを取得し、authenticationメソッドに@item[:only_follower]と一緒に渡しています。authenticationの返り値がtrueなら認証成功とし、falseなら失敗としunauthorizedに遷移する実装となっています。

authenticationメソッド実装

authenticationメソッドを実装していきます。

    def authentication(only_followers,current_user,owner_user)
      if only_followers
      	require 'twitter'
      	client = Twitter::REST::Client.new do |config|
          config.consumer_key        = Rails.application.secrets.twitter_api_key
	      config.consumer_secret     = Rails.application.secrets.twitter_api_secret
	      config.access_token        = session[:oauth_token]
	      config.access_token_secret = session[:oauth_token_secret]
        end
        current_user_screen_name = current_user[:nickname]
        owner_user_screen_name = owner_user[:nickname]
        result = client.friendship(current_user_screen_name,owner_user_screen_name)
        return result.source.following?
      else
        return true
      end
    end

このメソッドはshowメソッド内からの呼び出しにしか使用していないので、private内に実装してください。やっていることは、Twitter::REST::Clientインスタンスの作成(この中で使用しているsession[:oauth_token],session[:oauth_token_secret]については後ほど取得部分を実装します)と、それを使ったログイン中のユーザーと所有者のユーザーの関係性のチェックです。このチェックでログイン中のユーザーが所有者のユーザーのフォロワーであることが確かめられるとtrueが返されます。そもそも公開範囲がフォロワーのみに制限されていない場合もtrueとなります。

oauth_token取得処理追加

先程実装したauthenticationメソッドはoauth_tokenを必要としていますが、まだ取得する処理が書かれていないのでここで実装します。SessionsControllerのcreateメソッドを以下のように変更します。

  def create
    callback = request.env['omniauth.auth']
    user = User.find_or_create_from_auth(callback)
    session[:user_id] = user.id
    session[:oauth_token] = callback['credentials']['token']
    session[:oauth_token_secret] = callback['credentials']['secret']
    redirect_to root_path
  end

リクエストに含まれるトークンの情報をセッションに保存するようにしました。これでauthenticationメソッドが動作するようになりました。

以上で目標を達成するアプリの製作は完了です!

client.friendshipについて

result = client.friendship(A,B)

上記のとき、result.source.following?はAがBをfollowしてる時にtrueになり、result.source.followed_by?はBがAをfollowしてる時にtrueになります。

余談

このアプリを制作している最中、突然ソースコードを変更していないのにTwitterの認証に失敗することがありました。原因はほんの一秒以下の時計の誤差でした。同じようにソースコードは正しいし時間もほぼあってるのに出来ないと悩んでる方は一度時計合わせをしてみることをおすすめします。

参考

【Ruby on Rails】twitter apiを利用してoauth認証・タイムラインを表示する
Qiita - OmniAuthで認証した後に、tweetしたりfollowしたりする