使用 Pundit 在 comment_policy_spec 上为 post 创建评论的权限
Permissions for create comments on comment_policy_spec for a post with Pundit
[更新:]
我正在使用 Pundit,当我尝试使用允许在 post 上具有角色(如经理)的用户创建评论时,我遇到了问题。
我正在练习以这种方式使用 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
Comment.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.
comments/_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/comments_controller.rb
class Posts::CommentsController < CommentsController
before_action :set_commentable
private
def set_commentable
@commentable = Post.find(params[:post_id])
end
end
role.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::.....
我试着把评论放在评论的地方(比如可评论的,评论),但得到了同样的错误。
我试着把 post 像: subject { CommentPolicy.new(user, post) }
和工作的,但被抱怨另一个错误:
rspec spec/policies/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,在本地主机上 运行 应用程序,并尝试创建不同用户(如管理员、经理、编辑)的评论。
正如预期的那样,应用程序工作正常。
管理员和经理能够创建评论,编辑器收到消息 "You aren't allowed to do that." 并且无法按预期创建评论。所以问题出在我还不知道的 RSpec 上。
鉴于您正在使用多态性(因为您想使用 Comment 模型关联到多个模型),您将模型 Comment 定义为:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
#etc...
end
belongs_to :commentable polymorphic: true
允许该模型属于多个模型。它将在 table:
中创建两个字段
commentable_id
commentable_type
commentable_id 将是另一个 model/table 的外键,而 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
这就是为什么您的 table 有 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(空,导致错误)和一个 commentable_id (不为空)。 Comment 模型中的 belongs_to :post
使 ActiveRecord 使用 post_id(在 record.post.has_manager?(user)
中)搜索相关的 Post,而不是使用 commentable_id。由于 post_id 为空,因此未找到 Post。只有当用户不是管理员时才会发生错误,因为如果他是管理员,user.try(:admin?)
returns true 并且不会评估句子的其余部分。将 record.post.has_manager?(user)
更改为 record.commentable.has_manager?(user)
此外,我认为您的Post政策
有误
scope.joins(:roles).where(roles: {user_id: user}
应该是
scope.joins(:roles).where(roles: {user_id: user.id}
我在使用可评论和作品创建评论时进行了更改!
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
我的决赛comment_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 的帮助!
[更新:]
我正在使用 Pundit,当我尝试使用允许在 post 上具有角色(如经理)的用户创建评论时,我遇到了问题。
我正在练习以这种方式使用 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
Comment.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.
comments/_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/comments_controller.rb
class Posts::CommentsController < CommentsController
before_action :set_commentable
private
def set_commentable
@commentable = Post.find(params[:post_id])
end
end
role.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::.....
我试着把评论放在评论的地方(比如可评论的,评论),但得到了同样的错误。
我试着把 post 像: subject { CommentPolicy.new(user, post) }
和工作的,但被抱怨另一个错误:
rspec spec/policies/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,在本地主机上 运行 应用程序,并尝试创建不同用户(如管理员、经理、编辑)的评论。
正如预期的那样,应用程序工作正常。
管理员和经理能够创建评论,编辑器收到消息 "You aren't allowed to do that." 并且无法按预期创建评论。所以问题出在我还不知道的 RSpec 上。
鉴于您正在使用多态性(因为您想使用 Comment 模型关联到多个模型),您将模型 Comment 定义为:
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
#etc...
end
belongs_to :commentable polymorphic: true
允许该模型属于多个模型。它将在 table:
commentable_id
commentable_type
commentable_id 将是另一个 model/table 的外键,而 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
这就是为什么您的 table 有 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(空,导致错误)和一个 commentable_id (不为空)。 Comment 模型中的 belongs_to :post
使 ActiveRecord 使用 post_id(在 record.post.has_manager?(user)
中)搜索相关的 Post,而不是使用 commentable_id。由于 post_id 为空,因此未找到 Post。只有当用户不是管理员时才会发生错误,因为如果他是管理员,user.try(:admin?)
returns true 并且不会评估句子的其余部分。将 record.post.has_manager?(user)
更改为 record.commentable.has_manager?(user)
此外,我认为您的Post政策
有误 scope.joins(:roles).where(roles: {user_id: user}
应该是
scope.joins(:roles).where(roles: {user_id: user.id}
我在使用可评论和作品创建评论时进行了更改!
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
我的决赛comment_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 的帮助!