Load resource in cancancan

cancancan中load_resource的用法小结。

正文:

最基本的用法:

1
2
3
4
5
class TasksController < ApplicationController
load_resource
end

#### 等同于 @task = Task.find(params[:id]), @task = Task.new, @tasks = Task.all

options 众多,来一一捋捋。【借鉴cancancan的wiki,以下代码多以 task,project 为例。】

  • only / except

    同before_action中的only / except,指明load_resource在哪些action前调用。

    only:

    1
    2
    3
    4
    5
    class TasksController < ApplicationController
    load_resource only: [:index, :show]
    end

    ### 仅在 index,show action 前调用load_resource

    except:

    1
    2
    3
    4
    5
    class TasksController < ApplicationController
    load_resource except: [:sent]
    end

    ### 在除 sent action 外的其他actions前调用load_resource
  • through

    通过A,B之间的关联来load某一方。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Project < ActiveRecord
    has_many :tasks
    end

    class TasksController < ApplicationController
    load_resource :project
    load_resource :task, through: :project ## 这里也可以省略 :task
    end

    #### 等同于: @project = Project.find(params[:project_id])
    #### @task = @project.tasks.find(params[:id])

    也可以通过method,比如:current_user,也就是wiki中所说的nested through method:

    1
    2
    3
    4
    5
    class TasksController < ApplicationController
    load_resource through: :current_user
    end

    #### 等同于: @task = current_user.tasks.find(params[:id])
  • through_association

    与through的不同在于,project中,如果给tasks起了“别名”,不是,has_many tasks, 而是长这样:

    1
    2
    3
    class Project < ActiveRecord
    has_many :events, class_name: 'Task'
    end

    则在taskController中需要通过through_association来指明:

    1
    2
    3
    4
    5
    6
    7
    class TasksController < ApplicationController
    load_resource :project
    load_resource :task, through: :project, through_association: :events
    end

    #### 等同于:@project = Project.find(params[:project_id]),
    #### @task = @project.events.find(params[:id])
  • shallow

    shallow是跟着through的,设置为true则允许parent resource为 nil,设置为false则不允许nil,默认为false。

    1
    2
    3
    4
    5
    6
    7
    8
    class TasksController < ApplicationController
    load_resource :project
    load_resource :task, through: :project, shallow: true
    end

    #### 等同于:
    #### @project存在,则@task = @project.events.find(params[:id])
    #### @project不存在,则@task = Task.find(params[:id])
  • singleton

    当project与task存在一对一关系:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    class Project < ActiveRecord
    has_one :task
    end

    class TasksController < ApplicationController
    load_resource :project
    load_resource :task, through: :project, singleton: true
    end

    #### 等同于: @project = Project.find(params[:project_id])
    #### @task = @project.task
  • parent

    根据这个resource是不是一个parent resource来决定,当resource的name与controller无法匹配时,则默认为是parent resource。比如:

    1
    2
    3
    4
    class TasksController < ApplicationController
    load_resource :project
    load_resource :task, through: :project
    end

    这里,project在task的controller中,就是一个parent resource。

  • class

    指定类名。多用于当controller与model无法匹配时。

    常见的是个人中心这样的ProfilesController,对应的类名是User:

    1
    2
    3
    4
    5
    class ProfilesController < ApplicationController
    load_resource :profile, class: User
    end

    #### 等同于: @profile = User.find(params[:id])
  • find_by

    指定find_by_[attribute]。比如:

    1
    2
    3
    4
    5
    class PersonationsController < ApplicationController
    load_resource find_by: :token, class: User
    end

    #### 等同于: @personation = User.find_by_token(params[:id])

    注意,此处是params[:id], 不是params[:token],如果需要设置params fetch的attribute,可以用id_params.

  • Id_params

    默认是id,可以改为其他的param key,比如上面的例子:

    1
    2
    3
    4
    5
    class PersonationsController < ApplicationController
    load_resource find_by: :token,, class: User, id_params: :token
    end

    #### 等同于: @personation = User.find_by_token(params[:token])

    可以将through,find_by, id_params 结合运用:

    以常见的一个订阅为例, 用户订阅项目:

    1
    2
    3
    4
    5
    6
    7
    class SubscriptionsController < ApplicationController
    before_action { @user = current_user }
    load_resource through: :user, find_by: :project_id, id_param: :project_id
    end

    #### 等同于:
    #### @subscription = @user.subscriptions.find_by_project_id(params[:project_id])
  • new

    指定除默认的new,create之外,哪些action需要new resource。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class TasksController < ApplicationController
    load_resource new: :build

    def build
    .....
    end
    end

    #### 等同于: 在调用build action前,执行 @task = Task.new
  • prepend

    值为true或者false,默认false。如果设置为true,则会调用prepend_before_filter 而不是 before_filter。

参考

cancancan

cancancan load_resource