State-Pattern using Modules
Posted by michael.schaerfer on 22-Jul-08 at 21:20
In the last weeks, we tried to implement a state-pattern for our orders, simply put: different states means different functionality.
We tried various approaches, like simple state-symbols, state-classes (to hold the state-specifc methods and return new states), state-associations and the very nice AASM plugin by Scott Barron.
But all this techniques felt very clumsy for our simple needs and not so ... 'ruby-like'.
Then i stumbled across Jay Fields Post and really liked the approach of using just state-modules and delegating to the instance_method ('cause all modules included in a class are just ancestors of this class!).
Here a brief overview of our solution:
1 # the order-class 2 class Order 3 include StateLogic 4 5 #states 6 include Finished 7 attr_accessor :state 8 endThe StateLogic Module (defines the 'fire_event' method to delegate to the state-module).
1 module StateLogic 2 def fire_event(name, *args, &block) 3 state_module = self.state.classify.constantize 4 5 if state_module.instance_methods(false).include?(name) 6 state_module.instance_method(m).bind( self ).call( *args, &block ) 7 else 8 puts "event '#{name}' not defined for state '#{state}'" 9 return false 10 end 11 end 12 endA State-Module
1 module Finished 2 def self.included(base) 3 self.instance_methods(false).each do |m| 4 base.class_eval do 5 define_method(m) { |*args| fire_event(m,*args) } 6 end 7 end 8 end 9 10 #an event 11 def say_something 12 puts "now in state '#{state}'" 13 end 14 15 endAs you can see, we override every state-module instance_method in the receiver-class to call the fire_event method. And in this method, we check if the current state-module defines such an instance_method, bind it to the order-instance and call it (or printing an error message, if no such method is defined in the current state-module.)
Pretty simple!

Comments
There are 0 comments on this post. Post yours →
Post a comment
Required fields in bold.