Home > database >  Can you cross model data in the seuros state machine?
Can you cross model data in the seuros state machine?

Time:10-07

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

  • Related