经过多年努力学习如何在我的Rails应用程序中使用权威范围,我刚刚了解了为什么我无法使用它。显然,Pundit无法运行SQL查询,其中一个查询参数是一个政治家状态。
我收到的建议是使用不同的状态机。在我这样做之前,我把它扔到那里看看是否有人设法找到使用Pundit(带范围)和政治家状态机的方法。
我的设置是:
ApplicationPolicy
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
@user = user
@record = record
end
def index?
true
end
def show?
scope.where(:id => record.id).exists?
end
def create?
false
end
def new?
create?
end
def update?
false
end
def edit?
update?
end
def destroy?
false
end
def scope
Pundit.policy_scope!(user, record.class)
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
scope
end
end
end
ProposalPolicy
class ProposalPolicy < ApplicationPolicy
class Scope < Scope
def resolve
accum_scope = scope.where(user_id: @user.id)
# # Depends on moving off statesman
# # accum_scope = accum_scope.or(accum_scope.reviewable) if @user.has_role?(:research_management, Organisation.first)
accum_scope
end
end
# I think index isnt necessary when I have a Scope
# def index?
# true
# end
def new?
true
end
def create?
new?
end
def show?
#if its in the index results for that user
true
end
def edit?
update?
end
def update?
true if record.try(:user) == user
end
def destroy?
true if record.try(:user) == user
end
def draft?
create?
end
def under_review?
true if record.try(:user) == user
end
def approved?
@user.has_role?(:research_management, Organisation.matching)
end
def not_approved?
approved?
end
def publish_openly?
true if record.try(:user) == user && record.can_transition_to?(:publish_openly)
end
def publish_to_invitees?
true if record.try(:user) == user && record.can_transition_to?(:publish_to_invitees)
end
def publish_counterparties_only?
true if record.try(:user) == user && record.can_transition_to?(:publish_counterparties_only)
end
def remove?
true if record.try(:user) == user #|| @user.has_role? :admin
end
private
def matching
@user.organisation_id == record.user.organisation_id
end
end
Proposal.rb
class Proposal < ApplicationRecord
include Statesman::Adapters::ActiveRecordQueries
has_many :proposal_transitions, class_name: "ProposalTransition", autosave: false
scope :reviewable, -> { in_state(:under_review) }
def state_machine
@state_machine ||= ProposalStateMachine.new(self, transition_class: ProposalTransition,
association_name: :proposal_transitions)
end
delegate :can_transition_to?, :transition_to!, :transition_to, :current_state,
to: :state_machine
private
def self.transition_class
ProposalTransition
end
def self.initial_state
:draft
end
当前的问题:
ProposalPolicy::Scope.new(User.first, Proposal).resolve.all
User Load (1.0ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1]]
Organisation Load (0.7ms) SELECT "organisations".* FROM "organisations" ORDER BY "organisations"."id" ASC LIMIT $1 [["LIMIT", 1]]
(0.9ms) SELECT COUNT(*) FROM "roles" INNER JOIN "users_roles" ON "roles"."id" = "users_roles"."role_id" WHERE "users_roles"."user_id" = $1 AND "roles"."name" = $2 AND "roles"."resource_type" = $3 AND "roles"."resource_id" = $4 [["user_id", 43], ["name", :research_management], ["resource_type", "Organisation"], ["resource_id", 5]]
Proposal Load (0.3ms) SELECT "proposals".* FROM "proposals" WHERE "proposals"."user_id" = $1 [["user_id", 43]]
=> #<ActiveRecord::Relation [#<Proposal id: 17, user_id: 43, title: "asdf", description: "adsf", byline: "asdf", nda_required: true, created_at: "2016-11-16 00:28:31", updated_at: "2016-11-28 01:16:47", trl_id: 1, invitee_id: nil>]>
2.3.1p112 :012 > Proposal.where(user_id: User.first).or(Proposal.reviewable)
User Load (2.3ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1 [["LIMIT", 1]]
ArgumentError: Relation passed to #or must be structurally compatible. Incompatible values: [:joins]
我刚刚收到的见解是,专家范围无法查询提案状态,因为政治家在幕后管理转换时有一系列表格。
有没有人看到一个可以让我继续使用政治家以及使用Pundit范围的解决方案?
答案 0 :(得分:0)
还没有一个完整的答案 - 我仍在试图弄清楚如何做到这一点 - 但我在这里找到了帮助:https://github.com/elabs/pundit/issues/440。帮助解释了以下内容:
Pundit范围没有做任何特别的事情。在阅读Pundit范围时,只需在思想上将范围关键字替换为类名,并尝试在Rails控制台中输入完全相同的内容。
该错误并非特定于Pundit。这是与or运算符相关的错误。我看了一下政治家如何实现in_state调用,并且它与你的ProposalTransition类进行了连接。
ActiveRecord或操作员要求左侧和右侧兼容。在您的情况下,您的左侧查询(Proposal.where(user:User.first))与您的右侧查询不兼容:
Proposal.reviewable转换为Proposal.in_state(:under_review),转换为Proposal.joins(most_recent_transition_join).where(states_where(most_recent_transition_alias,states),:under_review)与转换表进行LEFT OUTER JOIN(proposal_transitions) ,我相信)[1]
要使它们兼容,您必须将相同的连接应用于或。
的两侧既然你已经拨打了一个昂贵的电话,你可以做一个穷人或做以下事情:
def resolve proposal_ids = current_user.proposal_ids + Proposal.reviewable.pluck(:id)Proposal.where(id:proposal_ids)end [1]我查看了政治家的源代码:https://github.com/gocardless/statesman/blob/77958f1c2ae613ac29c6db5329c67538a5655ddf/lib/statesman/adapters/active_record_queries.rb