Set state values before initialization for state_machine - ruby-on-rails

I'm using state_machine to manage approvals on an ActiveRecord::Base class.
I'm using custom state values so I can store states as integers. This works well, except that I am getting the following warning (from StateMachine::Machine) on Rails startup:
# Both MyClass and its :state machine have defined a different default
# for "state". Use only one or the other for defining defaults to avoid
# unexpected behaviors.
I'm not observing any unexpected behaviors, so this isn't a big deal. I know that I could remove the :default value from my table schema (e.g., :default => 0) to make this error go away, but I would prefer to do it on the state_machine side.
Here is the state_machine code (in my_class.rb):
# ...
States = {
:pending => 0,
:approved => 1,
:rejected => 2,
}
state_machine :state, :initial => :pending do
States.each do |name, value|
state name, :value => value
end
event :approve do
transition all => :approved
end
event :reject do
transition all => :rejected
end
end
The problem is that state_machine wants to set the initial/default value to "pending" before it realizes that the default value should actually be 0.
Is it possible to define my states pre-initialization? It would be nice if I could pass State objects or a StateCollection to the Machine initializer, but it doesn't look like that is possible (looking at the source at https://github.com/pluginaweek/state_machine/blob/master/lib/state_machine/machine.rb).

Related

using Acts as State Machine, how to set a specific time period for a given state?

given the below;
aasm do
state :available, :intitial => true
state :presented
state :invited
event :present do
transitions :from => :available, :to => :presented
end
event :invite do
transitions :from => :presented, :to => :invited
end
event :provide do
transitions :from => [:presented, :invited], :to => :available
end
end
what is an optimal pattern for setting the time period that an object 'lives under' a given state ?
ie, once the 'present' event occurs, I'd like the object to maintain the 'presented' state for exactly two hours, i'm feeling like I will have to mangle the way aasm works to achieve this, any thoughts ?
extra: this aasm code is being inserted into an active record class in a rails app, postgres is the db. Thx!

state_machine validations on transitions

Using the state_machine gem, I want to have validations that run only on transitions. For example:
# Configured elsewhere:
# StateMachine::Callback.bind_to_object = true
class Walrus > ActiveRecord::Base
state_machine :life_cycle, :initial => :fetus do
state :fetus
state :child
state :adult
state :dead
event :coming_of_age do
transition :child => :adult
end
before_transition :child => :adult do
validate :coming_of_age_party_in_the_future
end
end
end
If I attach that validation to the adult state, it will fail once that date passes. But I only need it to be valid during the transition. I could add something like:
validate :coming_of_age_party_in_the_future, if: 'adult? && life_cycle_was == "child"'
but that seems to miss the point of transitions.
Also, since I am binding to the object in callbacks, how would it conditionally call 'validate', a class method? (Binding is necessary since many methods in callbacks are private.)
Have you considered using the transition if: :x feature?
It should work something like this:
event :coming_of_age do
transition :child => :adult, if: :coming_of_age_party_in_the_future
end
You can see the section 'Class definition' in 'Example' (near the top) or 'Transition context' in 'Syntax flexibility' (pretty far down) in the README

How to invoke Ruby gem AASM transition event given to and from states?

We have a Ruby on Rails application.
We're using Ruby's aasm gem to manage states of objects.
has_state
aasm do
state :created, :initial => true
state :submitted
state :rejected
state :approved
event :submit do
transitions :to => :submitted, :from => [:created]
end
event :mark_as_incomplete do
transitions :to => :created, :from => [:submitted]
end
event :approve do
transitions :to => :approved, :from => [:submitted]
end
event :reject do
transitions :to => :rejected, :from => [:submitted]
end
end
If we know an object's current state, which can be obtained using
object.aasm_current_state
and we also know the state to transition to, how can we invoke the event?
Please note, the from-state and to-state are variables, so we need to do the above
dynamically. Of course, with certain to-state and from-state combination, the transition isn't available, in that case we should detect an error.
We're also assuming between any two state combination (to-state and from-state), there's only 1 event, I think theoretically there can be more than 1.
I think this can be achieved by delving into the innards of aasm source code,
which, arguably, may not be a good practice. Any thoughts?
Just wonder if anyone has done this before.
Thanks!
There is no way provided by AASM to do this, but your own answer is getting already close enough to where you want to go. AASM is built around the assumption, that state machines allow multiple different transitions from one state to another.
If the event name is not relevant for you, you could reuse the to-state name as event name, like this:
aasm do
...
event :approved do
transitions :from => :submitted, :to => :approved
end
...
end
By this you can fire the event by just knowing the to-state name
approval_request.send(to_state)
By default, AASM raises an exception if this transition is not allowed. If you don't like the exception, set whiny_transitions to false, like this:
aasm :whiny_transitions => false do
...
end
This is the code I have. to_state and from_state are the states from and to.
ApprovalRequest.aasm_events.each do |event_key, event_obj|
if event_obj.transitions_from_state?(from_state) &&
event_obj.transitions_to_state?(to_state)
self.approval_request.send "#{event_key.to_s}!"
end
end
Any comments on this implementation?

User status field, how to map to an enum

I have a User model, and I want to have a user_status attribute.
I want this attribute to be stored as an integer in the database.
I was thinking of then creating a enum, and then mapping that enum to the integer value so I could do things like:
if user.status == MyEnum::RequiresApproval
..
..
Using active_record, at the model level, is there something I can do with the enum?
What is normally done in this kind of situation?
Enums are not very Rails like. State Machines are.
Check out the 'transitions' gem (link) (which was almost part of Rails Core)
And then you can do the following ...
#GemFile
gem "transitions", :require => ["transitions", "active_record/transitions"]
#And in your Model, do something like the following:
include ActiveRecord::Transitions
field :state, type: String
scope :active, where(state: 'active')
state_machine do
state :active
state :inactive
event :inactivate do
transitions :from => :active, :to => :inactive
end
event :activate do
transitions :from => :inactive, :to => :active
end
end
It was a transition for me too not to use enums and type tables -- but I haven't missed them

Transitions class (state machine) get a list of possible transitions

I'm using a ActiveRecord::Transitions in Rails 3 and have my state machine defines as:
state_machine do
state :initial # first one is initial state
state :active
state :disabled
event :activate do
transitions :to => :active, :from => [:initial, :disabled]
end
event :disable do
transitions :to => :disabled, :from => [:initial, :active]
end
end
How do I see a list of available transitions for a current object and state?
For example if I have a #product in state "active" it should tell me
"disabled" is the only state available, or
"disable" is the only event available
I can't see any obvious way to enumerate possible-next-states, but you can query the available events like this:
YourClass.state_machines[:default].events_for(:active)
=> [:disable]
(If you have more than one state machine there will be additional members in the YourClass.state_machines Hash)
This answer is now more relevant
Basically - you have access to #product.state_evants, #product.state_transitions and #product.state_paths

Resources