Synopsis
In Ruby on Rails, does the state machine gem support the use of a model instance that doesn't directly relate to the host model? If they do, how do I do it?
The conclusion I'm leaning toward is that authorization should be left to other parts of the framework, and the state machine should just be an interface defining the transition of states. That being said, I see some support for transition conditions and I was wondering if the data inside those conditions could be something NOT set on the host model, but instead passed in like a parameter.
Background
Say we have a Task
that has the states in_progress
and completed
, and in order to transition from them respectively, the current_user
(assigned in the session, access in the controller) needs to pass a check.
I understand through the documentation that in order to add a check to the transition I have to program it like this:
transition :in_progress => :completed, :if => :user_is_owner?
and define the function like:
def user_is_owner()
true
end
but let's try to implement the restriction so that the task can only be edited if the user_id
is the same as the id
of the user
that requested the task
USING dynamic data.
def user_is_owner?(user)
user.id == self.requester_id
end
Notice I don't have that user
object, how would one pass the user
object they need in?
Ruby Version: 1.9.3
Rails Version: 3.2.9
Thanks!
CodePudding user response:
The thought process behind this post was that I wanted to use the framework the way it was meant to be used, MVC. Information specific to the connection doesn't belong on a model that represents something completely independent of the connection, it's just logical.
The solution I chose for my problem was what @SergioTulentsev mentioned, A transient attribute.
My Ruby on Rails solution included setting up a transient attribute on my model, by adding an attr_accessor
attr_accessor :session_user
and a setter
# @doc Setter function for transient variable @session_user
def session_user
@session_user
end
and a function that uses the setter on my Task model
def user_is_owner?
requester == session_user
end
then I utilized that function inside of my state_machine's transition
transition :completed => :archived, :if => :user_is_owner?
The problems I see with this are that anytime you want to use the User to make authorization checks, you can't just pass it in as a parameter; it has to be on the object.
Thanks, I learned a lot. Hopefully this will be somewhat useful over the years...
CodePudding user response:
The original response is a valid approach, but I wound up going with this one. I think it's a much cleaner solution. Override the state machine events and extract the authorization.
state_machine :status, :initial => :new do
event :begin_work do
transition :new => :in_progress
end
end
def begin_work(user)
if can_begin_work?(user)
super # This calls the state transition, but only if we want.
end
end
Sources:
https://github.com/pluginaweek/state_machine/issues/193 https://www.rubydoc.info/github/pluginaweek/state_machine/StateMachine/Machine:before_transition Passing variables to Rails StateMachine gem transitions