Reputation: 1764
I am writing an e-commerce platform on rails 4 similar to kickstarter or indiegogo. What state a product is in is highly dependent on various conditions like if there are enough orders. So for example if I were to use the gem state_machine
my code might look something like this.
class Product < ActiveRecord::Base
has_many :orders
state_machine :initial => :prelaunch do
event :launch do
transition :prelaunch => :pending, :if => lambda {|p| p.launch_at <= Time.now }
end
event :fund do
transition :pending => :funded, :if => :has_enough_orders?
end
end
def has_enough_orders?
if orders.count > 10
end
end
Then I would probably create a model observer so that every time an order is placed I check product.has_enough_orders?
and if that returns true
I would call product.fund!
. So has_enough_orders?
is being checked multiple times. That just doesn't seem very efficient.
Additionally product.launch!
has a similar issue. The best way I can think to implement it is to use something like sidekiq
and have a job that checks if any pre-launched products are passed their launch_at
time. However this seems equally dirty.
Am I just over thinking it or is this how you normally would use a state machine?
Upvotes: 1
Views: 1467
Reputation: 3741
I just modified your state machine to better way to handle conditions.
You can use after_transition
or before_transition
methods
class Product < ActiveRecord::Base
has_many :orders
state_machine :initial => :prelaunch do
after_transition :prelaunch, :do => :check_launch
after_transition :pending, :do => :has_enough_orders?
event :launch do
transition :prelaunch => :pending
end
event :fund do
transition :pending => :funded
end
end
def check_launch
if launch_at <= Time.now
self.launch # call event :launch
else
# whatever you want
end
end
def has_enough_orders?
if orders.count > 10
self.fund # call event :fund
else
# whatever you want
end
end
end
Upvotes: 5