Module: StateMachines::Machine::Configuration

Included in:
StateMachines::Machine
Defined in:
lib/state_machines/machine/configuration.rb

Instance Method Summary collapse

Instance Method Details

#attribute(name = :state) ⇒ Object

Gets the attribute name for the given machine scope.



131
132
133
# File 'lib/state_machines/machine/configuration.rb', line 131

def attribute(name = :state)
  name == :state ? @attribute : :"#{self.name}_#{name}"
end

#initial_state=(new_initial_state) ⇒ Object

Sets the initial state of the machine. This can be either the static name of a state or a lambda block which determines the initial state at creation time.



109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/state_machines/machine/configuration.rb', line 109

def initial_state=(new_initial_state)
  @initial_state = new_initial_state
  add_states([@initial_state]) unless dynamic_initial_state?

  # Update all states to reflect the new initial state
  states.each { |state| state.initial = (state.name == @initial_state) }

  # Output a warning if there are conflicting initial states for the machine's
  # attribute
  initial_state = states.detect(&:initial)
  has_owner_default = !owner_class_attribute_default.nil?
  has_conflicting_default = dynamic_initial_state? || !owner_class_attribute_default_matches?(initial_state)
  return unless has_owner_default && has_conflicting_default

  warn(
    "Both #{owner_class.name} and its #{name.inspect} machine have defined " \
    "a different default for \"#{attribute}\". Use only one or the other for " \
    'defining defaults to avoid unexpected behaviors.'
  )
end

#initialize(owner_class, *args) ⇒ Object

Initializes a new state machine with the given configuration.



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/state_machines/machine/configuration.rb', line 7

def initialize(owner_class, *args, &)
  options = args.last.is_a?(Hash) ? args.pop : {}
  
  # Find an integration that matches this machine's owner class
  @integration = if options.include?(:integration)
                   options[:integration] && StateMachines::Integrations.find_by_name(options[:integration])
                 else
                   StateMachines::Integrations.match(owner_class)
                 end
  
  # Validate options including integration-specific options
  valid_keys = [:attribute, :initial, :initialize, :action, :plural, :namespace, :integration, :messages, :use_transactions, :async]
  valid_keys += @integration.integration_options if @integration
  StateMachines::OptionsValidator.assert_valid_keys!(options, valid_keys)

  if @integration
    extend @integration
    options = (@integration.defaults || {}).merge(options)
  end

  # Add machine-wide defaults
  options = { use_transactions: true, initialize: true }.merge(options)

  # Set machine configuration
  @name = args.first || :state
  @attribute = options[:attribute] || @name
  @events = EventCollection.new(self)
  @states = StateCollection.new(self)
  @callbacks = { before: [], after: [], failure: [] }
  @namespace = options[:namespace]
  @messages = options[:messages] || {}
  @action = options[:action]
  @use_transactions = options[:use_transactions]
  @initialize_state = options[:initialize]
  @action_hook_defined = false
  @async_requested = options[:async]

  self.owner_class = owner_class

  # Merge with sibling machine configurations
  add_sibling_machine_configs

  # Define class integration
  define_helpers
  define_scopes(options[:plural])
  after_initialize

  # Evaluate DSL
  instance_eval(&) if block_given?

  # Configure async mode if requested, after owner_class is set and DSL is evaluated
  configure_async_mode!(true) if @async_requested

  self.initial_state = options[:initial] unless sibling_machines.any?
end

#initialize_copy(orig) ⇒ Object

Creates a copy of this machine in addition to copies of each associated event/states/callback, so that the modifications to those collections do not affect the original machine.



66
67
68
69
70
71
72
73
74
75
76
# File 'lib/state_machines/machine/configuration.rb', line 66

def initialize_copy(orig) # :nodoc:
  super

  @events = @events.dup
  @events.machine = self
  @states = @states.dup
  @states.machine = self
  @callbacks = { before: @callbacks[:before].dup, after: @callbacks[:after].dup, failure: @callbacks[:failure].dup }
  @async_requested = orig.instance_variable_get(:@async_requested)
  @async_mode_enabled = orig.instance_variable_get(:@async_mode_enabled)
end

#owner_class=(klass) ⇒ Object

Sets the class which is the owner of this state machine. Any methods generated by states, events, or other parts of the machine will be defined on the given owner class.



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/state_machines/machine/configuration.rb', line 81

def owner_class=(klass)
  @owner_class = klass

  # Create modules for extending the class with state/event-specific methods
  @helper_modules = helper_modules = { instance: HelperModule.new(self, :instance), class: HelperModule.new(self, :class) }
  owner_class.class_eval do
    extend helper_modules[:class]
    include helper_modules[:instance]
  end

  # Add class-/instance-level methods to the owner class for state initialization
  unless owner_class < StateMachines::InstanceMethods
    owner_class.class_eval do
      extend StateMachines::ClassMethods
      include StateMachines::InstanceMethods
    end

    define_state_initializer if @initialize_state
  end

  # Record this machine as matched to the name in the current owner class.
  # This will override any machines mapped to the same name in any superclasses.
  owner_class.state_machines[name] = self
end