動かざることバグの如し

近づきたいよ 君の理想に

deviseとBootstrapで簡単登録フォームの実装

いつもの

rails new passport -TB --skip-turbolinks

bundlegemを追加しておく

gem "twitter-bootstrap-rails"
gem "slim-rails"
gem 'devise'

rails generate bootstrap:install static忘れずに

deviseのセットアップ

rails g devise:install

すると以下が表示される

mPro:passport thr3a$ rails g devise:install
      create  config/initializers/devise.rb
      create  config/locales/devise.en.yml
===============================================================================

Some setup you must do manually if you haven't yet:

  1. Ensure you have defined default url options in your environments files. Here
     is an example of default_url_options appropriate for a development environment
     in config/environments/development.rb:

       config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

     In production, :host should be set to the actual host of your application.

  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:

       root to: "home#index"

  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:

       <p class="notice"><%= notice %></p>
       <p class="alert"><%= alert %></p>

  4. If you are deploying on Heroku with Rails 3.2 only, you may want to set:

       config.assets.initialize_on_precompile = false

     On config/application.rb forcing your application to not access the DB
     or load models when precompiling your assets.

  5. You can copy Devise views (for customization) to your app by running:

       rails g devise:views

===============================================================================

コントローラーの作成

とりあえずサイト全体のindexを作成

rails g controller home index

モデルの作成

rails g devise User

ちなみに以下のマイグレーションが生成される

class DeviseCreateUsers < ActiveRecord::Migration
  def change
    create_table(:users) do |t|
      ## Database authenticatable
      t.string :email,              null: false, default: ""
      t.string :encrypted_password, null: false, default: ""

      ## Recoverable
      t.string   :reset_password_token
      t.datetime :reset_password_sent_at

      ## Rememberable
      t.datetime :remember_created_at

      ## Trackable
      t.integer  :sign_in_count, default: 0, null: false
      t.datetime :current_sign_in_at
      t.datetime :last_sign_in_at
      t.string   :current_sign_in_ip
      t.string   :last_sign_in_ip

      ## Confirmable
      # t.string   :confirmation_token
      # t.datetime :confirmed_at
      # t.datetime :confirmation_sent_at
      # t.string   :unconfirmed_email # Only if using reconfirmable

      ## Lockable
      # t.integer  :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts
      # t.string   :unlock_token # Only if unlock strategy is :email or :both
      # t.datetime :locked_at


      t.timestamps null: false
    end

    add_index :users, :email,                unique: true
    add_index :users, :reset_password_token, unique: true
    # add_index :users, :confirmation_token,   unique: true
    # add_index :users, :unlock_token,         unique: true
  end
end

モデルも以下

class User < ActiveRecord::Base
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
end

それぞれどんな機能持っているかの確認はここが詳しい。とりあえずデフォルトで

ビューの作成

app/views/home/index.html.slimを以下に変更

- if user_signed_in?
    p = current_user.email
    =link_to "Settings", edit_user_registration_path
    =link_to "Logout", destroy_user_session_path, method: :delete
- else
    =link_to "Sign up", new_user_registration_path
    =link_to "Login", new_user_session_path

大元のerbもapp/views/layouts/application.html.slimに変更してメッセージを追加

doctype html
html
  head
    title My App
    meta name="viewport" content="width=device-width, initial-scale=1.0"
    = stylesheet_link_tag    "application", media: 'all'
    = javascript_include_tag "application"
    = csrf_meta_tags

  body
    - if notice
      p.alert.alert-success = notice
    - if alert
      p.alert.alert-danger = alert
    = yield

ルーティングの設定

config/route.rbを以下に変更 deviseはリダイレクトにroot_path使ってるので指定しないと死ぬ

  devise_for :users
  get 'home/index'
  root 'home#index'

以上でユーザー登録機能までの一通りが実装される。スゲェ簡単

試しに登録してみるとこんな感じでDBに入る

 => #<ActiveRecord::Relation [#<User id: 1, email: "a@a.com", encrypted_password: "$2a$10$DuA68xuU25/C8/cS5.7bVekwr/DV3N.4/DLutd3gndR...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 2, current_sign_in_at: "2015-11-06 05:46:14", last_sign_in_at: "2015-11-06 05:33:05", current_sign_in_ip: "::1", last_sign_in_ip: "::1", created_at: "2015-11-06 05:33:05", updated_at: "2015-11-06 05:46:40">]> 

パスワードリセットのメールが送信できない

ふぇえ

Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true

ちゃんと説明読もうな

config/environments/development.rbに以下を例に設定してあげる。以下はActionmailerを利用したGmail経由の設定

  config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
  config.action_mailer.delivery_method = :smtp
  #config.action_mailer.raise_delivery_errors = true
  config.action_mailer.smtp_settings = {
    :enable_starttls_auto => true,
    :address => 'smtp.gmail.com',
    :port => '587',
    :domain => 'smtp.gmail.com',
    :authentication => 'plain',
    :user_name => '<Gmailアカウント>@gmail.com',
    :password => '<Gmailパスワード>'
  }

ただしGoogleアカウント側にも設定が必要なので注意

日本語化

https://raw.githubusercontent.com/tigrish/devise-i18n/master/rails/locales/ja.ymlを適当にdevise.ja.ymlをconfig/localesにブチ込む

あとja.ymlに以下を追記

ja:
  activerecord:
    attributes:
      user:
         name: 名前
         password: パスワード
         password_confirmation: パスワード確認
         email: メールアドレス

無心で実行

rails g devise:views

deviseはerb以外のビューに対応していない。そこでerb2slimを用いて変換する

erb2slim -d app/views/devise/

ちなみに以下のようなビューが生成されるらしい)

  • app/views/devise/shared/_links.html.erb (リンク用パーシャル)
  • app/views/devise/confirmations/new.html.erb (認証メールの再送信画面)
  • app/views/devise/passwords/edit.html.erb (パスワード変更画面)
  • app/views/devise/passwords/new.html.erb (パスワードを忘れた際、メールを送る画面)
  • app/views/devise/registrations/edit.html.erb (ユーザー情報変更画面)
  • app/views/devise/registrations/new.html.erb (ユーザー登録画面)
  • app/views/devise/sessions/new.html.erb (ログイン画面)
  • app/views/devise/unlocks/new.html.erb (ロック解除メール再送信画面)
  • app/views/devise/mailer/confirmation_instructions.html.erb (メール用アカウント認証文)
  • app/views/devise/mailer/reset_password_instructions.html.erb (メール用パスワードリセット文)
  • app/views/devise/mailer/unlock_instructions.html.erb (メール用ロック解除文)

メールアドレスだけじゃなくて名前も登録したい

登録項目を増やすにはまず普通にマイグレーションを追加

rails g migration AddNameToUsers name

マイグレーションにてnameカラムオプションにnull: false, default: ""を追加

class AddNameToUsers < ActiveRecord::Migration
  def change
    add_column :users, :name, :string, null: false, default: ""
  end
end

必須項目にするならモデルにバリデーションを追加しておく

validates :name, presence: true, length: { maximum: 10 }

rake db:migrate:resetしたのちにビューに追加 以下は例

  .field
    = f.label :name
    = f.text_field :name, autofocus: true

残念ながらこれだけでは終わらない。Railsたん最強罠であるStrong Parametersがnameパラメータを阻むからである。

そこで公式READMEを参考に以下を追加

class ApplicationController < ActionController::Base
  before_action :configure_permitted_parameters, if: :devise_controller?

  protected
  def configure_permitted_parameters
    devise_parameter_sanitizer.for(:sign_up) << :name
    devise_parameter_sanitizer.for(:sign_in) << :name
    devise_parameter_sanitizer.for(:account_update) << :name
  end
end

indexページ等特定のページでは認証を必要としない

app/controllers/application_controller.rbにて

before_action :authenticate_user!, except: [:show, :index]

/users/sign_inを/users/loginに変更したい

ルーティングの変更はroutes.rbで行うことができる

デフォルトのルーティング

mPro:passport thr3a$ rake routes
                  Prefix Verb   URI Pattern                    Controller#Action
        new_user_session GET    /users/sign_in(.:format)       devise/sessions#new
            user_session POST   /users/sign_in(.:format)       devise/sessions#create
    destroy_user_session DELETE /users/sign_out(.:format)      devise/sessions#destroy
           user_password POST   /users/password(.:format)      devise/passwords#create
       new_user_password GET    /users/password/new(.:format)  devise/passwords#new
      edit_user_password GET    /users/password/edit(.:format) devise/passwords#edit
                         PATCH  /users/password(.:format)      devise/passwords#update
                         PUT    /users/password(.:format)      devise/passwords#update
cancel_user_registration GET    /users/cancel(.:format)        devise/registrations#cancel
       user_registration POST   /users(.:format)               devise/registrations#create
   new_user_registration GET    /users/sign_up(.:format)       devise/registrations#new
  edit_user_registration GET    /users/edit(.:format)          devise/registrations#edit
                         PATCH  /users(.:format)               devise/registrations#update
                         PUT    /users(.:format)               devise/registrations#update
                         DELETE /users(.:format)               devise/registrations#destroy
              home_index GET    /home/index(.:format)          home#index
                    root GET    /                              home#index

例えば

  • /users/sign_inから/users/login
  • users/sign_outから/users/logout

に変更したい場合は以下のようにする

devise_for :users, path_names: { sign_in: "login", sign_out: "logout"}

プロフィール画面欲しい

deviseは一発でそれっぽく生成してくれるがindexとshow(に相当するページ)は作らない。のでそこは自分でこしらえる。

コントローラー生成

rails g controller users index show

適当に作る

class UsersController < ApplicationController
  def index
    @users = User.all
  end

  def show
    @user = User.find(params[:id])
  end
end

ルーティングの設定 devise_forの後に書かないと死ぬ

Rails.application.routes.draw do
  devise_for :users, path_names: { sign_in: "login", sign_out: "logout"}
  root 'home#index'
→resources :users, :only => [:index, :show]

index.html.slim

ul
    - @users.each do |user|
        li = user.email

show.html.slim

h1 = @user.name
p = @user.email

これでおk

編集後のリダイレクトを変えたい

編集後、デフォルトだとTOPページ(root_path)に飛ぶけどそのまま編集ページに戻りたい場合だってあると思うの

どうもオーバーライドすることで実現できる。 app/controllers/users/registrations_controller.rbを新規作成して以下

class RegistrationsController < Devise::RegistrationsController

  protected

    def after_update_path_for(resource)
      edit_user_registration_path
    end
end

あとはroutes.rbに以下を追記するだけ

  devise_for :users, :controllers => {
    :registrations => "registrations"
  }

こことか多くのサイトusers/registrationsしてたんだけど何が違うんだろう(無理だった