使用pundit gem的类别策略规范的问题

时间:2018-01-05 17:20:14

标签: ruby-on-rails rspec pundit

所以我通过分类来进行类别和帖子的关联。 而我正试图用pundit gem限制访问,并使用RSpec进行测试,以便按类别显示帖子。因为有了这种关联,我有类似的东西: c = Category.first c.posts

我正按类别显示帖子。

我需要一些帮助。

my categories_controller.rb

class CategoriesController < ApplicationController
  before_action :set_category, only: [:show]

  def show
    authorize @category, :show?
  end

  private

  def set_category
    @category = Category.find(params[:id])
  end
end

category.rb

class Category < ActiveRecord::Base
  has_many :categorizations, dependent: :destroy
  has_many :posts, through: :categorizations

  validates :name,
                  presence: true,
                  length: { minimum: 3, maximum: 30 },
                  uniqueness: true
end

应用程序/视图/类别/ show.html.slim

= title("Category - #{@category.name}")
#category.text-center
  h1 Category: <small>#{@category.name}</small>

  - if @category.posts
    #category.posts
      - @category.posts.each do |post|
        .row.col-md-12
          .caption
            h2 = link_to post.title, post
            p = link_to post.subtitle, post
            small = "by : #{post.author}"
          hr

的routes.rb

Rails.application.routes.draw do

  namespace :admin do
    root 'application#index'

    resources :posts, only: [:new, :create, :destroy]
    resources :categories

    resources :users do
      member do
        patch :archive
      end
    end
  end

  devise_for :users

  root "posts#index"

  resources :posts, only: [:index, :show, :edit, :update]
  resources :categories, only: [:show]
end

category_policy.rb

class CategoryPolicy < ApplicationPolicy
  class Scope < Scope
    def resolve
      resolve
    end
  end

  def show?
    user.try(:admin?) || record.post.has_member?(user)
  end
end

category_policy_spec.rb

require 'rails_helper'

RSpec.describe CategoryPolicy do
  context "permissions" do
    subject        { CategoryPolicy.new(user, category) }
    let(:user)     { create(:user) }
    let(:post)     { create(:post) }
    let(:category) { create(:category) }

    context "for anonymous users" do
      let(:user) { nil }

      it { should_not permit_action :show }
    end

    context "for viewers of the post" do
      before { assign_role!(user, :viewer, post) }

      it { should permit_action :show }
    end

    context "for editors of the post" do
      before { assign_role!(user, :editor, post) }

      it { should permit_action :show }
    end

    context "for managers of the post" do
      before { assign_role!(user, :manager, post) }

      it { should permit_action :show }
    end

    context "for managers of other posts" do
      before do
         assign_role!(user, :manager, create(:post))
      end

      it { should_not permit_action :show }
    end

    context "for administrators" do
      let(:user) { create(:user, :admin) }

      it  { should permit_action :show }
    end
  end
end

我正在使用这个匹配器。 pundit_matcher.rb

RSpec::Matchers.define :permit_action do |action|
  match do |policy|
    policy.public_send("#{action}?")
  end

  failure_message do |policy|
    "#{policy.class} does not allow #{policy.user || "nil"} to " +
      "perform :#{action}? on #{policy.record}."
  end

  failure_message_when_negated do |policy|
    "#{policy.class} does not forbid #{policy.user || "nil"} from " +
      "performing :#{action}? on #{policy.record}."
  end
end

post.rb

class Post < ActiveRecord::Base
  belongs_to :author, class_name: "User"
  has_many :roles, dependent: :delete_all
  has_many :categorizations, dependent: :destroy
  has_many :categories, through: :categorizations

  mount_base64_uploader :attachment, AttachmentUploader

  validates :title,
              presence: true,
              length: { minimum: 10, maximum: 100 },
              uniqueness: true
  validates :subtitle,
              length: { maximum: 100 }
  validates :content,
              presence: true,
              length: { minimum: 30 }
  validates :attachment, file_size: { less_than: 1.megabytes }


  def has_member?(user)
    roles.exists?(user_id: user)
  end

  [:manager, :editor, :viewer].each do |role|
    define_method "has_#{role}?" do |user|
      roles.exists?(user_id: user, role: role)
    end
  end

end

roler.rb

class Role < ActiveRecord::Base
  belongs_to :user
  belongs_to :post

  def self.available_roles
    %w(manager editor viewer)
  end
end

当我尝试运行:

rspec spec/policies/category_policy_spec.rb

我收到此错误:

Failure/Error: user.try(:admin?) || record.post.has_member?(user)

     NoMethodError:
       undefined method `post' for #<Category:0x007fc61bfdbf40>
       Did you mean?  posts

可以在节目中按类别显示此帖子,或者我需要切换索引?

1 个答案:

答案 0 :(得分:1)

如错误所述,您的类别模型没有post方法,只有posts。这是因为Category has_many帖子。由于您的类别政策中的record.post.has_member?(user)没有任何意义(record是一个类别实例)。由于类别似乎与用户没有直接关系,因此您的角色与用户和帖子相关联。

我不清楚您的类别政策的目的是什么,除非它只允许用户查看类别,如果他们已经拥有使用该类别的帖子?如果是这种情况,那么您需要更改策略以实际检查是否存在任何这些策略。