Rails3.2のウェブサービスにOAuthを導入したこと。にせほボタンの解
かとりょーです。
あー。書くのに時間がかかってしまった……。
予告( http://katryo.hatenablog.com/entry/2012/05/05/122513 )通り、にせほボタンの解説です。
にせほボタンはこちら。 http://nisehobutton.heroku.com/
OAuthを導入する練習のために作りました。ついでにtwitter連携の練習にもなりました。これからOAuthやtwitter連携を始める人は、読むと参考になります。
にせほボタンの概要
詳しくはここ http://katryo.hatenablog.com/entry/2012/05/05/122513 に書きました。
GitHubはこちら。https://github.com/katryo/oauthtest
参考
Railscastの#241 simple OmniAuthを参考にしました。
http://railscasts.com/episodes/241-simple-omniauth?language=ja&view=asciicast
準備
とりあえず、 https://dev.twitter.com/ でアプリ登録します。
Access levelはRead and writeに設定します。
あとで設定は変えられるので、Callback URLも適当なURLを入れておいてください。これを書いておかないと、あとでOAuthが動いてくれません。
OmniAuth認証の仕組み
OmniAuthを使います。
Gemfileに
source 'https://rubygems.org' gem 'rails', '3.2.3' # Bundle edge Rails instead: # gem 'rails', :git => 'git://github.com/rails/rails.git' #gem 'omniauth' gem 'twitter' gem 'omniauth-twitter' # Gems used only for assets and not required # in production environments by default. group :assets do gem 'sass-rails', '~> 3.2.3' gem 'coffee-rails', '~> 3.2.1' gem 'twitter-bootstrap-rails' # See https://github.com/sstephenson/execjs#readme for more supported runtimes # gem 'therubyracer', :platform => :ruby gem 'uglifier', '>= 1.0.3' end # Heroku group :production do gem 'pg' end group :development do gem 'sqlite3' end
という風に書き加えます。
これで
$ bundle install --without production
したあと、Gemfile.lockを見てみると、oAuth関連のgemは
oauth (0.4.6) omniauth (1.1.0) hashie (~> 1.2) rack omniauth-oauth (1.0.1) oauth omniauth (~> 1.0) omniauth-twitter (0.0.11) multi_json (~> 1.3) omniauth-oauth (~> 1.0)
となりました。
種類が多くて内実がよくわからないです。もしかして、いらないもの入っている?
まず/config/initializers/omniauth.rbというファイルを作成し、そこに
Rails.application.config.middleware.use OmniAuth::Builder do provider :twitter, 'CONSUMER_KEY', 'CONSUMER_SECRET' end
と書きます。'CONSUMER_KEY'と'CONSUMER_SECRET' の''の中には、https://dev.twitter.com/ でのアプリ登録でもらえる'CONSUMER_KEY'と'CONSUMER_SECRET'を入れましょう。
この'CONSUMER_KEY'と'CONSUMER_KEY'を他人に知られると、「そのアプリのふりをする」いたずらをされる恐れがあるので、公開しないようにしてください。
で、このOAuthログインページまでユーザーを誘導するパスが"/auth/twitter"なので、にせほボタンのログインページにはこんなerbを書きました。
<%= link_to 'にせほボタンを始める', "/auth/twitter", :class => 'btn btn-large' %>
ユーザーがこのボタンをクリックするとTwitterの https://api.twitter.com/oauth/authorize に移動して、認証を許諾するとコールバックされて、にせほボタンのウェブページに戻ってきます。このとき、戻ってくる先のウェブページを設定しておく必要があります。おなじみ https://dev.twitter.com/ で、この画像のようにCallback URLを設定しておいてください。僕の場合は、"http://nisehobutton.heroku.com/contents/buttons/"に設定しました。
ユーザーがログインした状態を保つため、sessionsコントローラをまず作ります。
$ rails g controller sessions
userモデルも作ります。モデル内の要素はprovider, uid, name, と指定します。
$ rails g model user provider:string uid:string name:string
さっそくデータベースをマイグレートして、
$ rake db:migrate
"/model/user.rb"にはこう書きます。
class User < ActiveRecord::Base attr_accessible :name, :provider, :uid def self.create_with_omniauth(auth) create! do |user| user.provider = auth["provider"] user.uid = auth["uid"] user.name = auth["info"]["name"] end end end
OAuthでもらってきたauth["provider"]やauth["uid"]などのユーザーの情報を、Userテーブルに入れるというメソッドがself.create_with_omniauth(auth)ですね。
sessions_controller.rbには以下のように書きます。
#coding: utf-8 class SessionsController < ApplicationController def create auth = request.env["omniauth.auth"] user = User.find_by_provider_and_uid(auth["provider"], auth["uid"]) || User.create_with_omniauth(auth) session[:user_id] = user.id Twitter.configure do |config| config.oauth_token = auth['credentials']['token'] config.oauth_token_secret = auth['credentials']['secret'] end redirect_to '/contents/buttons', :notice => "認証しました!" end def destroy session[:user_id] = nil redirect_to '/', :notice => "認証を外しました" end end
createでセッション情報を作り、destroyで認証を外します。実際のにせほボタンでは認証を外す機能がない(2012/07/08の段階では)ので、destroyは使わないのですが一応残してあります。
userには、すでにOAuthで認証ずみの場合は認証したユーザーのprovider(さっき登場したauth["provider"]が入っている)と同じくuid(auth["uid"]が入っている)をもとに特定したユーザーが入り、認証していない場合はUser.create_with_omniauth(auth)でUserクラスのcreate_with_omniauthメソッドを使って、新たにユーザーを作成します。
これでようやくOAuthは完成です。OAuthの仕組みを使って認証・Twitter連携ができます。
Twitter連携
せっかく手に入れたOAuthなので、Twitterと連携してツイートできるようにします。
Gemfileに
gem 'twitter'
と書いてbundleしておいたので、すでに"The Twitter Ruby Gem"( http://twitter.rubyforge.org/ )は導入されています。これの機能を使って、ユーザーにツイートさせます。
config/initializers/twitter.rb というファイルを作って、
Twitter.configure do |config| config.consumer_key = 'CONSUMER_KEY' config.consumer_secret = 'CONSUMER_SECRET' end
と書きます。'CONSUMER_KEY', 'CONSUMER_SECRET' にはOAuthと同じく、 https://dev.twitter.com/ でもらった文字列を入れます。
これで、OAuth認証したユーザーのツイートを操れます。
app/controllers/contents_controller.rbはこんな風にしました。
#encoding: utf-8 class ContentsController < ApplicationController def index end def buttons if current_user @user = Twitter.user.screen_name @random_number = Time.now.sec % 4 if params[:button_1] if @random_number == 2 Twitter.update("にせほー! にせほー! そこにいるの?おしいー! おしいー! 明日があるの! 3分間\ハイ!/リプライを\ハイ!/か・ぞ・え・て・たー\人☆工☆知☆能☆/\( ゜ヮ゜)>\(゜ヮ゜)/ \(゜ヮ゜)/ <(゜ヮ^ )/ #にせほボタン http://nisehobutton.heroku.com/") else Twitter.update("@nisehorn @nisehorrn @nisehorrrn @nisehorrrrn にせほー #にせほボタン http://nisehobutton.heroku.com/") end session[:button_pushed_1] = true elsif params[:button_2] if @random_number == 3 Twitter.update("ロックスターはオワコン。時代はレッドブル #にせほボタン http://nisehobutton.heroku.com/") else Twitter.update("ロックスター・エナジードリンクなう #にせほボタン http://nisehobutton.heroku.com/") end session[:button_pushed_2] = true elsif params[:button_3] Twitter.update("ゆ #にせほボタン http://nisehobutton.heroku.com/") session[:button_pushed_3] = true elsif params[:button_4] Twitter.update("#zekitterは神 #にせほボタン http://nisehobutton.heroku.com/") session[:button_pushed_4] = true elsif params[:button_5] Twitter.update("アイエエエ!? ニンジャ!? ニンジャナンデ!? #にせほボタン http://nisehobutton.heroku.com/") session[:button_pushed_5] = true elsif params[:button_6] Twitter.update("たいぷかのんほー #にせほボタン http://nisehobutton.heroku.com/") session[:button_pushed_6] = true end @msg = 'あなたのアカウントでツイートできました。たぶん。' else redirect_to '/' end end def show end end
解説
Twitter.update("ツイートの本文")
メソッドを使って、OAuthでトークンを渡してくれたユーザーにツイートさせる、シンプルな仕組みです。
@user = Twitter.user.screen_name
は、twitterユーザーのスクリーンネーム(たとえばkatryo)を取ってきて、/views/contents/index.html.erbで
ようこそ<%= render :text => @user %>さん。 このページはあなたのために生まれました。
と書くことで、
こんなApple製品の箱を開けたあと風のページにする狙いです。
@random_number = Time.now.sec % 4
は、「1/4の確率をで特別なツイートになる」仕組みのためにやってます。現在時刻を4で割った余りが2や3のとき、別のツイートになります。
うーん、今見ると、ifじゃなくてcaseを使ったほうがrubyらしいコードになった気がする。直すのめんどいので次回に生かすことにしますー。
デプロイ
herokuにデプロイしました。herokuはPostgreSQLしか使えないので、Gemfileにdevelopmentとproductionを分けて、このように書いておいて、
group :production do gem 'pg' end group :development do gem 'sqlite3' end
デプロイ前に
$ bundle install --without development
とします。
デプロイしてから、DBのマイグレーションをheroku runコマンドでします。
$ heroku run rake db:migrate
herokuへのデプロイでハマるのは、assetsとDB周りが大半です。特にRails3.1から導入されたasset pipelineは比較的新しい機能のため、本によっては対応してないことがあるので古めの本を読んでる人は注意が必要です。
asset pipelineは要するにCSSや画像、Javascriptをこれまでのような/publicでなく/app/assetsに入れておき、デプロイ時に/assetsに圧縮して入れる仕組みです。ウェブページのレンダリング時のリクエスト回数を減らせるので高速化できるそうです。
Railscastにasset pipelineを紹介した記事があるので、見ておくといいです。 http://railscasts.com/episodes/279-understanding-the-asset-pipeline?language=ja&view=asciicast
こっちはRails公式の解説 http://guides.rubyonrails.org/asset_pipeline.html
かつてherokuはasset pipelineに標準対応していなかったのでアプリ作成時に
heroku create アプリの名前 --stack cedar
と、stack cedar(cedarはただのコードネーム。1つ前のバージョンはbambooだった)を指定しないといけなかったのですが、2012年6月でstack cedarが標準になったので、今は単純に
heroku create アプリの名前
とすれば、勝手にstack cedarのアプリを作ってくれます。
herokuの公式マニュアルに色々書いてあるので読みましょう。 https://devcenter.heroku.com/articles/cedar/
herokuの設定やデプロイ順序は頻繁に変わるので、日本語で書かれたブログを探すよりもheroku公式マニュアルを読んだほうが速いことが多いです。
デプロイしてもアプリが動かないときは、
$ heroku logs
でログを見て、プリコンパイルしたりDB設定を変えながら対応すると時間を無駄にしなくて済むと思います。
ひがしのてらこや。公式サイト( http://higashinoterakoya.herokuapp.com/ )のように、DBの内容を表示するタイプのアプリでは、DB内にレコードが1つもないとsomething went wrongが出ます。なので、とりあえずDBに何か入れましょう。
heroku run rails console
とすれば、heroku上でRuby対話環境を起動できます。
もしUserテーブルにレコードを追加したいなら、
User.create(name:'katryo', password:'secret')
とすれば、ユーザー情報が入ります。
ユーザーが入ったことを確認したいなら、このあと
User.count
とすれば、ユーザー数を表示してくれます。
ではでは!