【问题标题】:Permissions for create comments on comment_policy_spec for a post with Pundit为 Pundit 的帖子在 comment_policy_spec 上创建评论的权限
【发布时间】:2018-08-01 11:10:55
【问题描述】:

[更新:]

我正在使用 Pundit,当我尝试使用允许在帖子中具有角色(如经理)的用户创建评论时遇到问题。

我正在练习以这种方式使用 RSpec 和多态关联进行测试,我想知道我是否正确,以及如何通过此错误。 我正在使用的执行多态关联的示例类似于gorails 教程。

我有这个:

class CreateComments < ActiveRecord::Migration
  def change
    create_table :comments do |t|
      t.text :content
      t.references :commentable, polymorphic: true, index: true
      t.references :author, index: true

      t.timestamps null: false
    end

    add_foreign_key :comments, :users, column: :author_id
  end
end

评论.rb

class Comment < ActiveRecord::Base
  belongs_to :author, class_name: "User"
  belongs_to :commentable, polymorphic: true

  validates :content, presence: true

  default_scope -> { order(created_at: "desc") }
  scope :persisted, lambda { where.not(id: nil) }

end

评论控制器

class CommentsController < ApplicationController
  before_action :set_post

  def create
    @comment = @commentable.comments.new(comment_params)
    @comment.author = current_user
    authorize @comment, :create?

    if @comment.save
     flash[:notice] = "Comment has been created."
     redirect_to @commentable
    else
     flash.now[:alert] = "Comment has not been created."
     render "posts/show"
    end
 end


  private

  def set_post
    @post = Post.find(params[:post_id])
  end

  def comment_params
    params.require(:comment).permit(:content)
  end
end

app/views/posts/show.html.slim

.
.
.
#comments
  = render partial: "comments/form", locals: {commentable: @post}

  - if @post.comments.persisted.any?
    h4
      = t(:available_comments, count: @post.comments.count)
    = render partial: "comments/comments", locals: {commentable: @post}
  - else
    p
      There are no comments for this post.

cmets/_form.html.slim

    .header
      h3 New Comment

    = simple_form_for [commentable, Comment.new] do |f|
      .form-group
        = f.input :content, label: "Comment", placeholder: "Add a comment", input_html: { rows: 6 }
      = f.submit class: "btn btn-primary"
    <br>

路线

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

  resources :posts, only: [] do
    resources :comments, only: [:create], module: :posts
  end

app/controllers/post/cmets_controller.rb

class Posts::CommentsController < CommentsController
  before_action :set_commentable

  private

  def set_commentable
    @commentable = Post.find(params[:post_id])
  end
end

角色.rb

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

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

spec/factories/comment_factory.rb

FactoryGirl.define do
  factory :comment do
    content { "A comment!" }

    trait :post_comment do
      association :commentable, factory: :post
      #commentable_type 'Post'
      association :author_id, factory: :user
    end
  end
end

评论政策

    class CommentPolicy < ApplicationPolicy
  class Scope < Scope
   def resolve
     scope
   end
  end

 def create?
    user.try(:admin?) || record.commentable.has_manager?(user)
 end
end

spec/policies/comment_policy_spec.rb

    require 'rails_helper'    
    RSpec.describe CommentPolicy do
      context "permissions" do
        subject { CommentPolicy.new(user, comment) }

        let(:user) { create(:user) }
        let(:post) { create(:post)}
        let(:comment) { create(:comment, post: post)}

        context "for anonymous users" do
          let(:user) { nil }
          it { should_not permit_action :create }
        end

        context "for viewers of the post_comment" do
          before { assign_role!(user, :viewer, post) }
          it { should_not permit_action :create }
        end

        context "for editors of the post" do
          before { assign_role!(user, :editor, post) }
          it { should permit_action :create }
        end

        context "for managers of the post" do
          before { assign_role!(user, :manager, post) }
          it { should permit_action :create }
        end

        context "for managers of other post" do
          before do
            assign_role!(user, :manager, create(:post))
          end
          it { should_not permit_action :create }
        end

        context "for administrators" do
          let(:user) { create(:user, :admin) }
          it { should permit_action :create }
        end
      end

    end

当我跑步时,我有:

`rspec spec/policies/comment_policy_spec.rb`
Run options: exclude {:slow=>true}
FFFFFF

Failure/Error: subject { CommentPolicy.new(user, comment) }

     NameError:
       undefined local variable or method `comment' for #<RSpec::ExampleGroups::CommentPolicy::Permissions::.....

我尝试放置评论(如可评论,cmets)并得到相同的错误。 我尝试发布类似:subject { CommentPolicy.new(user, post) } 和工作的帖子,但被投诉另一个错误: rspec 规范/策略/comment_policy_spec.rb

Run options: exclude {:slow=>true}
FFFFF.
Failure/Error: user.try(:admin?) || record.commentable.has_manager?(user)

     NoMethodError:
       undefined method `commentable' for #<Post:0x007f9cc3dba328>
       Did you mean?  comments

我把 subject { CommentPolicy.new(user, comment) } 放在 CommentPolicy 上,然后在 localhost 上运行应用程序,并尝试使用不同的用户(如管理员、经理、编辑器)创建评论。 正如预期的那样,应用程序工作正常。

管理员和经理能够创建评论,而编辑收到消息“您不能这样做”。并且无法按预期创建评论。所以问题出在我还不知道的 RSpec 上。

【问题讨论】:

  • 你能添加你的帖子模型吗?
  • 我把更新,帖子模型。

标签: ruby-on-rails ruby-on-rails-4 roles pundit


【解决方案1】:

假设您正在使用多态性(因为您希望使用 Comment 模型与多个模型关联),您将模型 Comment 定义为:

class Comment < ActiveRecord::Base
  belongs_to :commentable, polymorphic: true
  #etc...
end

belongs_to :commentable polymorphic: true 允许此模型属于多个模型。它将在表中创建两个字段:

  1. commentable_id

  2. commentable_type

commentable_id 将是另一个模型/表的外键,commentable_type 将是模型名称。这允许评论帖子或您可以添加的任何其他模型。

此定义由相关模型 Post 补充,使用:

class Post < ActiveRecord::Base
  has_many :comments, as: :commentable, dependent: :destroy
  #etc...
end

has_many :comments, as: :commentable 让 ActiveRecord 知道这是使用多态与 Comment 关联的模型之一。

但是,在您的 Comment 模型中,您通过两种方式将评论与 Post 相关联:

belongs_to :post
belongs_to :commentable, polymorphic: true

这就是为什么你的表有post_id(因为第一行)和commentable_id(因为第二行)。创建记录时仅设置commentable_id,因为它是通过以下方式完成的:

@post.comments.build
#this will set comment.commentable_id = @post.id, and comment.commentable_type = "Post"
#but will not set comment.post_id

总而言之,您有一个 post_id(null,导致错误)和一个 commentable_id(非 null)。 Comment 模型中的 belongs_to :post 使 ActiveRecord 使用 post_id(在 record.post.has_manager?(user) 中)而不是使用 commentable_id 搜索相关 Post。由于 post_id 为空,因此找不到 Post。仅当用户不是管理员时才会发生该错误,因为如果他是管理员,user.try(:admin?) 返回 true 并且不会评估句子的其余部分。将record.post.has_manager?(user) 更改为record.commentable.has_manager?(user)

另外,我认为你在 PostPolicy 中有一个错误

  scope.joins(:roles).where(roles: {user_id: user}

应该是

  scope.joins(:roles).where(roles: {user_id: user.id}

【讨论】:

  • 你好@Pablo 不是这个。谢谢回复!
  • 您好@Pablo,我删除了 Comment 模型中的 belongs_to :post 并且 post_id 仍然保持为空值。我正在尝试以这种方式进行实践以及未来将使用这种类型的多态关联的模型。感谢您的帮助!
  • 但我也在想这篇文章首先会有两种类型的 cmets:一种用于comment_reviews(谁将使用经理和编辑或管理员),然后当帖子发布时用户。
  • post_is 保持为空,因为它没有被使用。当您创建评论时,它使用commentable_id 链接到帖子。这是唯一需要的字段。如果评论属于一个帖子,commentable_id 将代表一个 post_id(并且 commentable_type 将是“帖子”)。如果你注释其他模型(使用多态),commentable_type 将是其他模型名称,commentable_id 将代表另一个表的 id。
  • 是的,我明白了。我在那里删除了 post_id,并决定重命名内容的列文本。我现在这样做就像gorails 的教程一样,我喜欢那里的示例代码并且我正在使用它。
【解决方案2】:

我在使用commentable 和work's 创建评论时发生了变化!

comment_policy_spec.rb

require 'rails_helper'

RSpec.describe CommentPolicy do
  context "permissions" do
    subject { CommentPolicy.new(user, comment) }

    let(:user) { create(:user) }
    let(:post) { create(:post)}
    let(:comment) { create(:comment, commentable: post)}
    #let(:post_comment) { create(:post_comment)}

    context "for anonymous users" do
      let(:user) { nil }
      it { should_not permit_action :create }
    end

    context "for viewers of the post_comment" do
      before { assign_role!(user, :viewer, post) }
      it { should_not permit_action :create }
    end

    context "for editors of the post" do
      before { assign_role!(user, :editor, post) }
      it { should permit_action :create }
    end

    context "for managers of the post" do
      before { assign_role!(user, :manager, post) }
      it { should permit_action :create }
    end

    context "for managers of other post" do
      before do
        assign_role!(user, :manager, create(:post))
      end
      it { should_not permit_action :create }
    end

    context "for administrators" do
      let(:user) { create(:user, :admin) }
      it { should permit_action :create }
    end
  end

end

我的最后评论_policy.rb

class CommentPolicy < ApplicationPolicy
  class Scope < Scope
   def resolve
     scope
   end
  end

 def create?
    user.try(:admin?) || record.commentable.has_manager?(user) || record.commentable.has_editor?(user) 
 end
end

再次感谢您@Pablo 的帮助!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-02
    • 2012-08-14
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 2011-11-09
    相关资源
    最近更新 更多