認可の習作: Action Policy: 準備

外観

目的
認可の習作にAction Policyを用いたいと思います。
記事が長くなったので二回に分けました。

1. 準備(アプリを作成します)
2. 入門(Action Policyを導入します)

環境
macOS 10.15.5
Ruby 2.7.1
Rails 6.0.3.1
Yarn 1.22.4
Node 13.12.0
action_policy 0.4.3

参照

リポジトリ
https://github.com/usutani/try_action_policy

画像1

準備

Rails new
テストは選択肢として残しておきます。
rails new -M --skip-active-storage try_action_policy
cd try_action_policy

Gemfile
jbuilderは使いません。

# gem 'jbuilder', '~> 2.7'

bundle install

config/routes.rb
ルーティングの骨格を決めます。

Rails.application.routes.draw do
  resources :users, only: %i[index show]
  resources :posts do
    resources :comments, only: %i[create destroy]
  end
  root to: 'users#index'
end

ロールの追加
ロールを持つユーザを作成します。
bin/rails g scaffold User name role:integer

db/migrate/*_create_users.rb

      t.string :name, null: false
      t.integer :role, null: false, default: 0

bin/rails db:migrate

app/models/user.rb

class User < ApplicationRecord
  validates :name, presence: true
  enum role: { general: 0, admin: 1 }
end

db/seeds.rb

admin = User.create! name: 'Administrator', role: 1
foo = User.create! name: 'Foo'
bar = User.create! name: 'Bar'

bin/rails db:seed:replant

bin/rails c
User.first.admin?
User.second.admin?

擬似認証
ユーザ一覧でクリックしたユーザを認証します。
app/controllers/application_controller.rb

class ApplicationController < ActionController::Base
  before_action :ensure_authenticated_user

  private

  def authenticate_user(user_id)
    if authenticated_user = User.find_by(id: user_id)
      cookies.encrypted[:user_id] ||= user_id
      @current_user = authenticated_user
    else
      @current_user = nil
      cookies.delete(:user_id)
      nil
    end
  end

  def ensure_authenticated_user
    authenticate_user(cookies.encrypted[:user_id]) || redirect_to(root_url)
  end
end

Typoしていないかを確認しておきます。
bin/rails s

画像4

app/controllers/users_controller.rb

  skip_before_action :ensure_authenticated_user

  # ...

  def show
    authenticate_user(params[:id])
    redirect_to posts_url
  end

authenticate_userの動作を確認しておきます。posts_urlは未実装なのでエラーになります。
bin/rails s

この時のようにauthenticate_userをAuthentication concernとしても良いと思います。

ユーザ名の表示
app/views/layouts/application.html.erb

  <body>
    <div>
      <% if @current_user %>
        Signed in as <strong><%= @current_user.name %></strong>!
      <% end %>
    </div>
    <%= yield %>
  </body>

bin/rails s

PostのCURD
bin/rails g scaffold Post title content:text user:belongs_to
bin/rails db:migrate

db/seeds.rb

admin = User.create! name: 'Administrator', role: 1
foo = User.create! name: 'Foo'
bar = User.create! name: 'Bar'
Post.create! title: 'Admin post', content: "Administrator's post.", user: admin
Post.create! title: 'Foo post', content: "foo's post.", user: foo
Post.create! title: 'Bar post', content: "bar post.", user: bar

bin/rails db:seed:replant

bin/rails g model Comment content:text user:belongs_to post:belongs_to
bin/rails db:migrate

db/seeds.rb

admin = User.create! name: 'Administrator', role: 1
foo = User.create! name: 'Foo'
bar = User.create! name: 'Bar'
p1 = Post.create! title: 'Admin post', content: "Administrator's post.", user: admin
p2 = Post.create! title: 'Foo post', content: "foo's post.", user: foo
p3 = Post.create! title: 'Bar post', content: "bar's post.", user: bar
[p1, p2, p3].each do |post|
  Comment.create! content: "Administrator's comment.", user: admin, post: post
  Comment.create! content: "Foo's comment.", user: foo, post: post
  Comment.create! content: "Bar's comment.", user: bar, post: post
end

bin/rails db:seed:replant

app/models/post.rb

class Post < ApplicationRecord
  belongs_to :user
  has_many :comments, dependent: :destroy
end

Commentの表示と作成
mkdir app/views/comments
touch app/views/comments/_comments.html.erb
touch app/views/comments/_comment.html.erb
touch app/views/comments/_new.html.erb

<section id="comments">
<%= render post.comments %>
</section>
<%= render 'comments/new', post: post %>
<article>
  <h3>Comment by <%= comment.user.name %></h3>
  <p><%= comment.content %></p>
</article>
<%= form_with model: [post, Comment.new], id: "new_comment" do |form| %>
  <%= form.text_area :content %><br>
  <%= form.submit 'Post comment' %>
<% end %>

app/views/posts/show.html.erb

...
<br>

<%= render 'comments/comments', post: @post %>

bin/rails s
open http://localhost:3000/posts

画像2

画像3

Commentの作成
bin/rails g controller Comments

app/controllers/comments_controller.rb

class CommentsController < ApplicationController
  def create
    @post = Post.find(params[:post_id])
    @comment = Comment.create! content: params[:comment][:content],
                               post: @post, user: @current_user
  end
end

touch app/views/comments/create.js.erb

(() => {
  const el = document.getElementById('comments')
  el.insertAdjacentHTML('beforeend', '<%=j render @comment %>')
  const new_comment = document.getElementById('new_comment')
  new_comment.outerHTML = '<%=j render "comments/new", post: @post %>'
})()

app/views/comments/_comment.html.erb

<article id="comment-<%= comment.id %>">
  <h3>Comment by <%= comment.user.name %></h3>
  <p><%= comment.content %>
    <%= link_to 'Destroy', post_comment_path(comment.post, comment), method: :delete, data: { confirm: 'Are you sure?' }, remote: true %>
  </p>
</article>

コメントを投稿できることを確認します。
bin/rails s

画像5

Commentの削除
app/controllers/comments_controller.rb

  def destroy
    @comment = Comment.find(params[:id])
    @comment.destroy
  end

touch app/views/comments/destroy.js.erb

(() => {
  const el = document.getElementById('comment-<%= @comment.id %>')
  el.remove()
})()

コメントを削除できることを確認します。
bin/rails s

以上です。


この記事が気に入ったらサポートをしてみませんか?