Closed MincePie closed 7 years ago
This doesn't seem to be a problem with Pundit, but with whatever the return value your code is giving you for Proposal.reviewable
.
Do you know if Proposal.reviewable
is an ActiveRecord relation? I'm not familiar with how Statesman works or what else you have going on in your application, but it may be that you're getting an array of results rather than a relation. ActiveRecord won't be able to do any fancy relational algebra unless you feed it a relation.
Proposal.reviewable works fine on its own.
My proposal model defines a scope called 'reviewable' which checks the current_state of the proposal.
The problem comes when I try to define the resolve method in the proposal policy in the way that i have shown. The logic is that the index should include all of a users own records and if the user is not the record owner (that created the proposal) and the proposal is reviewable, then the current user should see the reviewable proposals.
Reviewable will return an array of results because it is a scope on the proposal model.
The trouble is that the pundit resolve method doesnt seem to accept a SQL query that includes an or statement. See the note about incompatible search structure at the end of my post.
@MincePie
Pundit scopes doesn't do anything special. When reading Pundit scopes, just mentally replace the scope
keyword with the class name and try to type exactly the same thing into your Rails console.
The error isn't specific to Pundit. It's an error related to the or
operator.
I took a peek at how statesman implements the in_state
call, and it does a join with your ProposalTransition
class.
ActiveRecord's or
operator requires the left side and the right side to be compatible.
In your case, your left-side query (Proposal.where(user: User.first)
) is incompatible with your right-side query:
Proposal.reviewable
which translates to
Proposal.in_state(:under_review)
which translates to Proposal.joins(most_recent_transition_join).where(states_where(most_recent_transition_alias, states), :under_review)
which does a LEFT OUTER JOIN
with the transition table (proposal_transitions
, I believe) [1]
To make them compatible, you'd have to apply the same join to both sides of the or
.
Since you're already making an expensive call anyway, you could do a poor man's or
by doing the following:
def resolve
proposal_ids = current_user.proposal_ids + Proposal.reviewable.pluck(:id)
Proposal.where(id: proposal_ids)
end
[1] I looked at statesman's source code: https://github.com/gocardless/statesman/blob/77958f1c2ae613ac29c6db5329c67538a5655ddf/lib/statesman/adapters/active_record_queries.rb
@rystraum above and beyond on that little investigation. Nicely done!
@damien @rystraum - thank you very much for this. It's beyond generous of you to look at this in so much detail. Thank you
@MincePie care to close this one? :)
I have just had a session on getting started with pundit scopes with codementor.io. After an hour, we stopped because it appears that it is not possible to use Pundit scopes with my state machine - which is Statesman. Apparently, Pundit can't run an SQL query where one of the query parameters is a statesman state.
The suggestion I have received is to use a different state machine. Before I do that, i just want to check if anyone has managed to find a way to use Pundit (with scopes) and Statesman state machine.
My setup is:
ApplicationPolicy
ProposalPolicy
Proposal.rb
When I try to run the query in the console, I get an error that indicates the SQL is incompatible. The current problem is:
ProposalTransition.rb
ProposalPolicy::Scope.new(User.first, Proposal).resolve.all
The insight I've just received is that the pundit scope cannot query the proposal state because statesman has a series of tables behind the scenes that manage the transitions.
Does anyone see a work around that would let me continue using statesman as well as using Pundit scopes?