---
# System prepended metadata

title: 12th 鐵人賽 Day_20 pundit ?
tags: [12th 鐵人賽]

---

---
title: 12th 鐵人賽 Day_20 pundit ?
tags: 12th 鐵人賽
--- 
# 12th 鐵人賽 Day_20 pundit ?

嗨！各位朋友大家好，打給後，歹嘎吼，胎尬喉，我是阿圓，一樣有請今天的one piece：
![](https://i.imgur.com/64R9C2D.jpg)
(雷利大叔！！)

鐵人賽的進度來到三分之二了呢！今天想跟各位來介紹 pundit 這個平常在專案裡面，使用程度很高的gem!

## 權限管理系統(pundit)

若今天的專案比較複雜一點，你可能會需要定義一下 user 權限，除了在controller裡寫下一大堆`if...else`判斷外，你有另一個選項是使用pundit 這個 gem。

pundit 會在資料夾中生成與 controller 同名的 policy，而你可以在 policy 去定義跟與各個 action 有關的權限。

### 基本安裝
首先無例外的記得先到 gemfile ：
`gem "pundit"`
(記得bundle)

接著，到 application controller的檔案裡面引入：
```ruby=
class ApplicationController < ActionController::Base
  include Pundit
end
```
最後，記得執行pundit的安裝：
`rails g pundit:install`

這樣，就會長出`app/policies/`的這個資料夾，設定好後，就可以在controller、view 裡使用了！

`rails g pundit:policy post`
就會幫你建立 post 的 policy 了！

### 使用方法
policy 設定方法呢，就是定義檢查登入者（user) 有沒有辦法對資料（record) 進行某種操作 (update?)，而一般來說，policy 遵從以下幾點：

1. policy 裡的類別命名，都是 modle 名稱後面接 Policy
2. policy 接收兩個參數，第一個是 user ，而預設會呼叫 `current_user` 這個方法，若沒有使用 devise 這個 gem ，請記得作一個給他！
3. 第二個參數是想要驗證能否執行操作的 model(ex:post)
4. policy 裡的方法名，一般都是使用 action 的名稱後面加個問號(ex:update?)

```ruby=
class PostPolicy
  attr_reader :user, :post

  def initialize(user, post)
    @user = user
    @post = post
  end

  def update?
    user.admin? or not post.published?
  end
end
```
以上方的例子，我們可以在 post_controller 裡使用`authorize`這個方法：
```ruby=
def update
  @post = Post.find(params[:id])
  authorize @post
  if @post.update(post_params)
    redirect_to @post
  else
    render :edit
  end
end
```
就會去執行 post_policy 內的`update?`方法。

你也可以在不同的 action 裡，要求與 action 不同名的驗證：
```ruby=
def publish
  @post = Post.find(params[:id])
  authorize @post, :update?
  @post.publish!
  redirect_to @post
end
```
也可以在view裡面呼叫他：
```html=
<% if policy(@post).update? %>
  <%= link_to "Edit post", edit_post_path(@post) %>
<% end %>
```
以上是比較基本的用法！

如果想某些 user 來做驗證，可以使用scope：
```ruby=
class PostPolicy < ApplicationPolicy
  class Scope
    def initialize(user, scope)
      @user  = user
      @scope = scope
    end

    def resolve
      if user.admin?
        scope.all
      else
        scope.where(published: true)
      end
    end

    private

    attr_reader :user, :scope
  end

  def update?
    user.admin? or not record.published?
  end
end
```

特別要說的是，pundit 也能處理 params：
```ruby=
# in policy
class PostPolicy < ApplicationPolicy
  def permitted_attributes
    if user.admin? || user.owner_of?(post)
      [:title, :body, :tag_list]
    else
      [:tag_list]
    end
  end
end

# in controller
def update
  @post = Post.find(params[:id])
  if @post.update_attributes(permitted_attributes(@post))
    redirect_to @post
  else
    render :edit
  end
end
```
也可以針對不同action，有不同的`permitted_attributes`
```ruby=
class PostPolicy < ApplicationPolicy
  def permitted_attributes_for_create
    [:title, :body]
  end

  def permitted_attributes_for_edit
    [:body]
  end
end
```
以上就是有關 pundit 的基本介紹啦！感謝各位看到這邊，若有任何建議，請各位不吝指教！我們明天見！
![](https://i.imgur.com/PPbpd2b.gif)