学習記録

アウトプット用に作りました

sorceryを使ってログイン機能を実装する

sorceryとは

Railsに認証機能を実装するためのライブラリです。
認証機能とは、システムへのログインやユーザの有効性確認を行うための機能のことを言います。
同じように認証機能を提供してくれているものとしてdeviseなどがあります。

sorceryの使い方

まずgemfileに以下を追加して、$bundle installを行います。

gem 'sorcery', '0.14.0'


以下のコマンドでsorceryに必須のカラムやモデルを追加。

$ rails g sorcery:install
$ rails db:migrate

このコマンドで通常で言うところの、$ rails g model の時のようにmodelとかカラムを作ってくれます。
config/initializers/sorcery.rb
models/users.rb
db/migrate/00000_sorcery_core.rb
が生成されます。


scafford_controllerを使って、userコントローラとビューファイルを作ります。

$ rails generate scafford_controller user  email:string crypted_password:string salt:string

app/controller/users_controller.rb
app/views/users
が生成されます。ここで生成された要らないビューファイルを消去します。


Userモデルにlast_nameとfirst_nameのカラムを追加します。
ここで生成されたマイグレーションファイルに、null制約をつけます。null: false

$ rails generate migration AddNameToUsers last_name:string first_name:string
$ rails db:migrate


Userコントローラのuser_paramsのコードを書き換えます。
:crypted_passwordと:saltが書いてありますが、:password, :password_confirmationに書き換えます。
新規登録画面でformから送られてくる値をpermitに書きます。

def user_params
    params.require(:user).permit(:email, :password, :password_confirmation, :last_name, :first_name)
end

※ここでコントローラの不要なコードも削除して、スッキリさせました。

ビューファイルにもcrypted_passwordとsaltが書いてあるので、passwordとpassword_confirmationに書き換えます。


Userモデルのバリデーションを追加します。

class User < ApplicationRecord
  authenticates_with_sorcery!    

  validates :password, length: { minimum: 3 }, if: -> { new_record? || changes[:crypted_password] }
  validates :password, confirmation: true, if: -> { new_record? || changes[:crypted_password] }
  validates :password_confirmation, presence: true, if: -> { new_record? || changes[:crypted_password] }
  validates :email, uniqueness: true, presence: true
  validates :last_name, presence: true, length: { maximum: 255 }
  validates :first_name, presence: true, length: { maximum: 255 }
end

ここで使われてるコードの難しいところを下記でまとめてみました。

sorceryが準備しているメソッド。
Userモデルで必要なクラスメソッドとインスタンスメソッドを得ることができます。

authenticates_with_sorcery!

登録したユーザーがパスワード以外のプロフィール項目を更新したいときに、
パスワードの入力を省略できるようになります。

 if: -> { new_record? || changes[:crypted_password] }


ログイン機能を作るために、新しいコントローラを作成します。

$ rails generate controller UserSessions new create destroy

生成されたSessionsコントローラを下記のように書きます。

class UserSessionsController < ApplicationController
  
  def new; end

  def create
    @user = login(params[:email], params[:password])
    if @user
      redirect_back_or_to root_path, notice: 'Login successful'
    else
      flash.now[:alert] = 'Login failed'
      render :new
    end
  end

  def destroy
    logout
    redirect_to root_path, notice: 'Logged out!'
  end
end

redirect_back_or_toメソッドはSorceryが提供しているメソッドです。
SorceryがURLを保存している場合は、
コードで指定したURLではなく保存されたURLにリダイレクトします。
これで便利なのは、あるページにアクセスしたユーザをまずログインさせたいという場合に、
Sorceryがそのユーザをログインページに誘導し、
ログインが成功したら最初に訪れようとしていたページに戻してくれる点です。
今回のコードでは、root_pathが指定したURLになります。


ユーザーの新規登録画面を作成します。

<%= form_with model: @user, local: true do |f| %>

ログイン画面を作成します。

<%= form_with url: login_path, local: true do |f| %>


ルーティングを変更します。

  get 'login', to: 'user_sessions#new', as: :login
  post 'login', to: 'user_sessions#create'
  delete 'logout', to: 'user_sessions#destroy', as: :logout
  resources :users, only: %i[new create]


sorceryで使えるようになるメソッド

  • require_login

  • logout

  • logged_in?

  • current_user

  • redirect_back_or_to

  • not_authenticated

などなど....

RailsでBootstrapを導入する

Bootstrapとは

Railsにはデフォルトで特定のデザインを含んでいません。
そのため見栄えの良いアプリケーションを作るには、一から自分でデザインしなければいけません。
Bootstrapというフロントエンドワークを使えば、手軽に見栄えの良いページを作ることができるようになります。

Bootstrapの使用方法

gemfileにbootstrapとjqueryのgemを追加します。 BootstrapはjQueryに依存するため、jquery-railsもGemfileに追記する必要があります。

gem 'bootstrap', '~> 4.1.1'
gem 'jquery-rails'

※gemを追加するときコメント等で区切りのない段落では、gemの記載はアルファベット順にします。


assets/stylesheets/application.cssファイルに下記を追加します。

@import 'bootstrap';
@import 'font-awesome-sprockets';
@import 'font-awesome';

*= requireなどの記載が残っていると、@importで定義した内容がうまく読み込めずbootstrapが反映されないので注意が必要です。


assets/javascripts/application.jsファイルに下記に記載があるか確認します。
//= require_tree . は一番下の行に記載するようにします。

//= require jquery3
//= require popper
//= require bootstrap

アセットパイプライン

アセットパイプラインとは

JavaScriptCSS、画像のリソース(アセットとよぶ)を最小化または圧縮して効率よく扱うための仕組みです。
アセットパイプラインはsprockets-railsというgemによって提供される機能で、デフォルトで有効になっています。


高級言語コンパイル→②アセットの連結→③アセットの最小化→④ダイジェストの付与を行います。

高級言語コンパイル

Coffee Script, SCSS, ERB, Slimなどで書かれた高級言語
ブラウザで認識できるJavaScript, CSSファイルに変換します。

アセットの連結

複数生成された.cssとか.jsファイルを連結して1つのファイルにします。
連結することで読み込み時間を短縮することができます。

アセットの最小化

そのまとめたファイルを圧縮して、サイズを小さくします。
リクエスト時の通信料を更に削減することが出来ます。

ダイジェストの付与

圧縮したファイル名にダイジェストを付与します。
ダイジェストのおかげで、ファイルが更新された場合は、
別名のファイルが新しく作成されて読み込むため、
古いキャッシュの情報が使われることがなくなります。

マニフェストファイル

マニフェストファイルとは

どのファイルを読み込むのかをまとめたファイルのことをいいます。

HTMLでは、そのページで必要となる読み込みファイルを、
application.html.erbファイル内のに記載します。

<head>
    <%= stylesheet_link_tag    'application', media: 'all' %>     # CSSファイル
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>    # JSファイル
</head>

このように記述して、application.scss、application.jsのファイルを読み込みます。
この二つのようなファイルのことをマニフェストファイルといいます。

application.scss、application.jsの中身をみてみると、

  • application.scss
*= require_self    # 自分自身を読み込む
 *= require_tree .    # 階層に存在している全てのファイルを読み込む
  • application.js
//= require_tree .

このような記載があります。
require_tree .を使うと、同ディレクトリのファイルを全て読み込んでしまうので、
ファイルを読み込む順番がわからなくなります。
それを防ぐために@importを使って必要なファイルのみ読み込んでファイル管理を安全に行うようにします。

application.scssからtop.scssを読み込むときは、

@import 'top';

を *= require_tree . よりも上に記述します。

layoutファイル

layoutファイル

アプリケーション全体で変わらないものを記述しておくファイルです。
変わるものは個別のファイルに記述し、
表示するときにはその二つのファイルを足し合わせて出力するというのがrailsの基本です。

通常このファイルは、app/views/layoutsの配下に配置されている
application.html.erbファイルのことを指しています。


通化する

ヘッダーやフッターのコードは、全てのページから出力させたいので
パーシャルテンプレート(部分テンプレート)として作成し、renderを使って、
application.html.erbファイルから読み込むようにします。
この場合、パーシャルテンプレートは、sharedディレクトに置いておきます。

<%= render 'shared/header %>

rails generateで生成されるファイルの設定

rails generateコマンド

rails newコマンドで作成したrailsのアプリケーションの中に、
必要な関連ファイルを自動生成してくれるコマンドです。

このコマンドを実行した後に不要なファイルを一つずつ消去していくのは面倒なので、
最初から指定ファイルを自動生成しない設定にすることができます。

不要なファイルを生成しない方法

assets、testファイル、ルーティングが生成されないようにしていこうと思います。

config/application.rbファイルに以下を記述します。

config.generators do |g|
  g.skip_routes true    # trueならroutes.rb変更せず、falseなら変更する
  g.assets false    # CSS, JavaScriptファイル生成しない
  g.test_framework false    # testファイル生成しない
end

g.assetsはg.javascript、g.stylesheetと分けて記述することもできます。

この状態でrails generateコマンドを実行すると、
assets、testファイル、ルーティングが生成されないため、
作成されるファイルを減らすことができます。

Active Recode(自分用メモ)

基礎編前の課題で、忘れていたこと、知らなかったことなどのまとめ。


複数レコードを一括削除したい場合は、destroy_byまたはdestroy_allを使える。

# Davidという名前のユーザーを検索してすべて削除
User.destroy_by(name: 'David')

# 全ユーザーを削除
User.destroy_all


Active Recordのnew_record?インスタンスメソッドを使うと、
オブジェクトが既にデータベース上にあるかどうかを確認できる。

$ bin/rails console
>> p = Person.new(name: "Taro")
=> #<Person id: nil, name: "Taro", created_at: nil, updated_at: nil>
>> p.new_record?
=> true
>> p.save
=> true
>> p.new_record?
=> false


6文字以上20文字以下のパスワード入力のバリデーション

validates :password, length: { in: 6..20 }


バリデーションを設定しても、データベースには制約がかかっていないため、
SQLを直接実行するとアプリケーションで作成できない形式のデータが作成できてしまう。
すでに作ってあるテーブルのカラムに後から制約をつけるときは、
マイグレーションファイルを自分で作るか、②既存のマイグレーションファイルを直接編集する。

(例) Not Null制約 (nullをデータベースに格納しない)

rails g migration ChangeTasksNameNotNull

# 生成したファイルの中身を書き換える
change_column_null :tasks, :name, false

# 既存のマイグレーションファイル
t.string :name, null: false


アソシエーションの機能を使うと、2つのモデルの間に繋がりを作ることができる。
投稿とコメントを結びつけてみる。1対多。

class Post < ApplicationRecord
  # dependent: :destroy を使う。投稿を削除したらコメントも削除される。
  has_many :comment, dependent: :destroy
end

class Comment < ApplicationRecord
  belongs_to :post
end