module StateMachine
module Integrations #:nodoc:
module Mongoid
include ActiveModel
# The default options to use for state machines using this integration
@defaults = {:action => :save, :use_transactions => false}
# Should this integration be used for state machines in the given class?
# Classes that include Mongoid::Document will automatically use
# the Mongoid integration.
def self.matches?(klass)
defined?(::Mongoid::Document) && klass <= ::Mongoid::Document
end
protected
# Never add observer support
def supports_observers?
false
end
# Always adds validation support
def supports_validations?
true
end
# Only runs validations on the action if using :save
def runs_validations_on_action?
action == :save
end
# Only adds dirty tracking support if ActiveRecord supports it
def supports_dirty_tracking?(object)
defined?(::Mongoid::Dirty) && object.respond_to?("#{attribute}_changed?") || super
end
# Always uses the :mongoid translation scope
def i18n_scope
:mongoid
end
# Only allows translation of I18n is available
def translate(klass, key, value)
if defined?(I18n)
super
else
value ? value.to_s.humanize.downcase : 'nil'
end
end
# Defines an initialization hook into the owner class for setting the
# initial state of the machine *before* any attributes are set on the
# object
def define_state_initializer
@instance_helper_module.class_eval <<-end_eval, __FILE__, __LINE__
# Ensure that the attributes setter gets used to force initialization
# of the state machines
def initialize(attributes = nil, *args)
attributes ||= {}
super
end
# Hooks in to attribute initialization to set the states *prior*
# to the attributes being set
def attributes=(new_attributes, *args)
if new_record? && !@initialized_state_machines
@initialized_state_machines = true
ignore = if new_attributes
attributes = new_attributes.dup
attributes.stringify_keys!
sanitize_for_mass_assignment(attributes).keys
else
[]
end
initialize_state_machines(:dynamic => false, :ignore => ignore)
super
initialize_state_machines(:dynamic => true, :ignore => ignore)
else
super
end
end
end_eval
end
# Adds support for defining the attribute predicate, while providing
# compatibility with the default predicate which determines whether
# *anything* is set for the attribute's value
def define_state_predicate
name = self.name
# Still use class_eval here instance of define_instance_method since
# we need to be able to call +super+
@instance_helper_module.class_eval do
define_method("#{name}?") do |*args|
args.empty? ? super(*args) : self.class.state_machine(name).states.matches?(self, *args)
end
end
end
end
end
end