■ Part3で構築したRailsアプリケーションに操作制限を追加実装.
■ タスク管理制御.
コントローラ内にてフィルタ機能(コールバック)でアクション処理前に任意処理が行えるよう制御.
各アクションの前に処理されるフィルダでログイン状態を確認し、ログインしていなければログイン画面にリダイレクトさせ、実質的にタスク管理機能が利用出来ないように制御.
class ApplicationController < ActionController::Base
helper_method :current_user
before_action :login_required
private
def current_user
@current_user ||= User.find_by(id: session[:user_id]) if session[:user_id]
end
# アクション処理前にユーザーのログイン状態かチェックを実施し、未ログインの場合はログイン画面へリダイレクト.
def login_required
redirect_to login_path unless current_user
end
end
ただしログイン画面の表示時には、同チェックが実行されないよう制御.
class SessionController < ApplicationController
skip_before_action :login_required
...
end
特定のコントローラにおいて、親クラスなど既に定義済みのフィルタを通らせないようにするためには、skip_before_actionを利用.
■ ログインユーザーのデータのみ扱う.
ログイン中のユーザーに紐づいたタスクデータだけを扱えるように制御.
- UserとTaskを紐付ける(tasksテーブルにuser_idカラムを追加し、タスク所有のユーザーのidが格納されるよう制御).
- Railsの「関連」を定義.
- ログインユーザーに紐づいたTaskデータを登録できるように制御.
- 一覧、詳細、変更など既存レコードを扱う機能はログインユーザーに紐づくデータのみを扱う.
bin/rails g migration AddUserIdToTasks
TaskとUserが紐づくように書き換え.
class AddUserIdToTasks < ActiveRecord::Migration[5.2]
def up
execute 'DELETE FROM tasks;'
add_reference :tasks, :user, null: false, index: true
end
def down
remove_reference :tasks, :user, index: true
end
end
Railsには「関連(Association)」という仕組みが用意されており、データベースでの紐付きを前提にモデルクラス同士の紐付けを定義することが可能で、UserとTaskの関係は1対多なので、以下のように定義できる.
class User < ApplicationRecord
has_secure_password
validates :name, presence: true
validates :email, presence: true, uniqueness: true
has_many :tasks
end
has_manyではクラス(Userクラス)のidを外部キーとして抱える他のクラス(Task)があり、そのレコードが複数件登録可能であることを表現.
class Task < ApplicationRecord
before_validation :set_nameless_name
validates :name, presence: true
validates :name, length: { maximum: 30 }
validate :validate_name_not_including_comma
belongs_to :user
private
...
end
belong_toでは、クラス(Task)が別のクラス(User)に従属し、従属先のクラスのidを外部キーとして抱えていることを表現.
class TasksController < ApplicationController
def index
@tasks = current_user.tasks
end
def show
@task = current_user.tasks.find(params[:id])
end
def new
@task = Task.new
end
def edit
@task = current_user.tasks.find(params[:id])
end
def update
task = current_user.tasks.find(params[:id])
task.update!(task_params)
redirect_to tasks_url, notice: "タスク「#{task.name}」を更新しました。"
end
def create
@task = Task.new(task_params.merge(user_id: current_user.id))
if @task.save
redirect_to @task, notice: "タスク「#{@task.name}」を登録しました。"
else
render :new
end
end
def destroy
task = current_user.tasks.find(params[:id])
task.destroy
redirect_to tasks_url, notice: "タスク「#{task.name}」を削除しました。"
end
private
def task_params
params.require(:task).permit(:name, :description)
end
end
ログイン中のユーザーを指定するように修正.
■ 管理者利用制御.
管理者権限を持つユーザーのみユーザー管理機能が使えるように実装.
doctype html
html
head
title
| Taskleaf
= csrf_meta_tags
= csp_meta_tag
= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload'
= javascript_include_tag 'application', 'data-turbolinks-track': 'reload'
body
.app-title.navbar-expamd-md.navbar-light.bg-light
.navbar-brand Taskleaf
ul.navbar-nav.ml-auto
- if current_user
li.nav-item= link_to 'タスク一覧', tasks_path, class: 'nav-link'
- if current_user.admin?
li.nav-item= link_to 'ユーザー一覧', admin_users_path, class: 'nav-link'
li.nav-item= link_to 'ログアウト', logout_path, method: :delete, class: 'nav-link'
- else
li.nav-item= link_to 'ログイン', login_path, class: 'nav-link'
.container
- if flash.notice.present?
.alert.alert-success = flash.notice
= yield
ログインユーザーが管理権限を持っている場合のみ、ナビゲーションバーにユーザー一覧リンクが表示される.
管理者権限以外は利用を禁止するフィルタとしてrequire_adminを追加.
class Admin::UsersController < ApplicationController
before_action :require_admin
...
private
...
def require_admin
redirect_to root_path unless current_user.admin?
end
end
権限がない場合はトップ画面へリダイレクトさせるよう制御.
■ 最初のユーザー作成.
Railsコンソールで最初の管理者を作成.
> User.create!(name: 'admin', email: 'admin@example.com', password: 'password', password_confirmation: 'password', admin: true)
■ 参考文献.
現場で使える Ruby on Rails 5速習実践ガイド.