ultra-smart-sys 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/state_machines-0.201.0/LICENSE.txt +23 -0
  3. data/state_machines-0.201.0/README.md +1024 -0
  4. data/state_machines-0.201.0/lib/state_machines/async_mode/async_event_extensions.rb +49 -0
  5. data/state_machines-0.201.0/lib/state_machines/async_mode/async_events.rb +282 -0
  6. data/state_machines-0.201.0/lib/state_machines/async_mode/async_machine.rb +60 -0
  7. data/state_machines-0.201.0/lib/state_machines/async_mode/async_transition_collection.rb +136 -0
  8. data/state_machines-0.201.0/lib/state_machines/async_mode/thread_safe_state.rb +47 -0
  9. data/state_machines-0.201.0/lib/state_machines/async_mode.rb +64 -0
  10. data/state_machines-0.201.0/lib/state_machines/branch.rb +246 -0
  11. data/state_machines-0.201.0/lib/state_machines/callback.rb +223 -0
  12. data/state_machines-0.201.0/lib/state_machines/core.rb +43 -0
  13. data/state_machines-0.201.0/lib/state_machines/core_ext/class/state_machine.rb +5 -0
  14. data/state_machines-0.201.0/lib/state_machines/core_ext.rb +4 -0
  15. data/state_machines-0.201.0/lib/state_machines/error.rb +115 -0
  16. data/state_machines-0.201.0/lib/state_machines/eval_helpers.rb +227 -0
  17. data/state_machines-0.201.0/lib/state_machines/event.rb +247 -0
  18. data/state_machines-0.201.0/lib/state_machines/event_collection.rb +149 -0
  19. data/state_machines-0.201.0/lib/state_machines/extensions.rb +150 -0
  20. data/state_machines-0.201.0/lib/state_machines/helper_module.rb +19 -0
  21. data/state_machines-0.201.0/lib/state_machines/integrations/base.rb +49 -0
  22. data/state_machines-0.201.0/lib/state_machines/integrations.rb +111 -0
  23. data/state_machines-0.201.0/lib/state_machines/machine/action_hooks.rb +53 -0
  24. data/state_machines-0.201.0/lib/state_machines/machine/async_extensions.rb +88 -0
  25. data/state_machines-0.201.0/lib/state_machines/machine/callbacks.rb +333 -0
  26. data/state_machines-0.201.0/lib/state_machines/machine/class_methods.rb +95 -0
  27. data/state_machines-0.201.0/lib/state_machines/machine/configuration.rb +128 -0
  28. data/state_machines-0.201.0/lib/state_machines/machine/event_methods.rb +436 -0
  29. data/state_machines-0.201.0/lib/state_machines/machine/helper_generators.rb +125 -0
  30. data/state_machines-0.201.0/lib/state_machines/machine/integration.rb +92 -0
  31. data/state_machines-0.201.0/lib/state_machines/machine/parsing.rb +77 -0
  32. data/state_machines-0.201.0/lib/state_machines/machine/rendering.rb +17 -0
  33. data/state_machines-0.201.0/lib/state_machines/machine/scoping.rb +44 -0
  34. data/state_machines-0.201.0/lib/state_machines/machine/state_methods.rb +398 -0
  35. data/state_machines-0.201.0/lib/state_machines/machine/utilities.rb +86 -0
  36. data/state_machines-0.201.0/lib/state_machines/machine/validation.rb +39 -0
  37. data/state_machines-0.201.0/lib/state_machines/machine.rb +615 -0
  38. data/state_machines-0.201.0/lib/state_machines/machine_collection.rb +105 -0
  39. data/state_machines-0.201.0/lib/state_machines/macro_methods.rb +522 -0
  40. data/state_machines-0.201.0/lib/state_machines/matcher.rb +124 -0
  41. data/state_machines-0.201.0/lib/state_machines/matcher_helpers.rb +56 -0
  42. data/state_machines-0.201.0/lib/state_machines/node_collection.rb +226 -0
  43. data/state_machines-0.201.0/lib/state_machines/options_validator.rb +72 -0
  44. data/state_machines-0.201.0/lib/state_machines/path.rb +123 -0
  45. data/state_machines-0.201.0/lib/state_machines/path_collection.rb +91 -0
  46. data/state_machines-0.201.0/lib/state_machines/state.rb +314 -0
  47. data/state_machines-0.201.0/lib/state_machines/state_collection.rb +113 -0
  48. data/state_machines-0.201.0/lib/state_machines/state_context.rb +134 -0
  49. data/state_machines-0.201.0/lib/state_machines/stdio_renderer.rb +74 -0
  50. data/state_machines-0.201.0/lib/state_machines/syntax_validator.rb +54 -0
  51. data/state_machines-0.201.0/lib/state_machines/test_helper.rb +775 -0
  52. data/state_machines-0.201.0/lib/state_machines/transition.rb +593 -0
  53. data/state_machines-0.201.0/lib/state_machines/transition_collection.rb +310 -0
  54. data/state_machines-0.201.0/lib/state_machines/version.rb +5 -0
  55. data/state_machines-0.201.0/lib/state_machines.rb +6 -0
  56. data/ultra-smart-sys.gemspec +12 -0
  57. metadata +96 -0
@@ -0,0 +1,615 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'options_validator'
4
+ require_relative 'machine/class_methods'
5
+ require_relative 'machine/utilities'
6
+ require_relative 'machine/parsing'
7
+ require_relative 'machine/validation'
8
+ require_relative 'machine/helper_generators'
9
+ require_relative 'machine/action_hooks'
10
+ require_relative 'machine/scoping'
11
+ require_relative 'machine/configuration'
12
+ require_relative 'machine/state_methods'
13
+ require_relative 'machine/event_methods'
14
+ require_relative 'machine/callbacks'
15
+ require_relative 'machine/rendering'
16
+ require_relative 'machine/integration'
17
+ require_relative 'machine/async_extensions'
18
+ require_relative 'syntax_validator'
19
+
20
+ module StateMachines
21
+ # Represents a state machine for a particular attribute. State machines
22
+ # consist of states, events and a set of transitions that define how the
23
+ # state changes after a particular event is fired.
24
+ #
25
+ # A state machine will not know all of the possible states for an object
26
+ # unless they are referenced *somewhere* in the state machine definition.
27
+ # As a result, any unused states should be defined with the +other_states+
28
+ # or +state+ helper.
29
+ #
30
+ # == Actions
31
+ #
32
+ # When an action is configured for a state machine, it is invoked when an
33
+ # object transitions via an event. The success of the event becomes
34
+ # dependent on the success of the action. If the action is successful, then
35
+ # the transitioned state remains persisted. However, if the action fails
36
+ # (by returning false), the transitioned state will be rolled back.
37
+ #
38
+ # For example,
39
+ #
40
+ # class Vehicle
41
+ # attr_accessor :fail, :saving_state
42
+ #
43
+ # state_machine :initial => :parked, :action => :save do
44
+ # event :ignite do
45
+ # transition :parked => :idling
46
+ # end
47
+ #
48
+ # event :park do
49
+ # transition :idling => :parked
50
+ # end
51
+ # end
52
+ #
53
+ # def save
54
+ # @saving_state = state
55
+ # fail != true
56
+ # end
57
+ # end
58
+ #
59
+ # vehicle = Vehicle.new # => #<Vehicle:0xb7c27024 @state="parked">
60
+ # vehicle.save # => true
61
+ # vehicle.saving_state # => "parked" # The state was "parked" was save was called
62
+ #
63
+ # # Successful event
64
+ # vehicle.ignite # => true
65
+ # vehicle.saving_state # => "idling" # The state was "idling" when save was called
66
+ # vehicle.state # => "idling"
67
+ #
68
+ # # Failed event
69
+ # vehicle.fail = true
70
+ # vehicle.park # => false
71
+ # vehicle.saving_state # => "parked"
72
+ # vehicle.state # => "idling"
73
+ #
74
+ # As shown, even though the state is set prior to calling the +save+ action
75
+ # on the object, it will be rolled back to the original state if the action
76
+ # fails. *Note* that this will also be the case if an exception is raised
77
+ # while calling the action.
78
+ #
79
+ # === Indirect transitions
80
+ #
81
+ # In addition to the action being run as the _result_ of an event, the action
82
+ # can also be used to run events itself. For example, using the above as an
83
+ # example:
84
+ #
85
+ # vehicle = Vehicle.new # => #<Vehicle:0xb7c27024 @state="parked">
86
+ #
87
+ # vehicle.state_event = 'ignite'
88
+ # vehicle.save # => true
89
+ # vehicle.state # => "idling"
90
+ # vehicle.state_event # => nil
91
+ #
92
+ # As can be seen, the +save+ action automatically invokes the event stored in
93
+ # the +state_event+ attribute (<tt>:ignite</tt> in this case).
94
+ #
95
+ # One important note about using this technique for running transitions is
96
+ # that if the class in which the state machine is defined *also* defines the
97
+ # action being invoked (and not a superclass), then it must manually run the
98
+ # StateMachine hook that checks for event attributes.
99
+ #
100
+ # For example, in ActiveRecord, DataMapper, Mongoid, MongoMapper, and Sequel,
101
+ # the default action (+save+) is already defined in a base class. As a result,
102
+ # when a state machine is defined in a model / resource, StateMachine can
103
+ # automatically hook into the +save+ action.
104
+ #
105
+ # On the other hand, the Vehicle class from above defined its own +save+
106
+ # method (and there is no +save+ method in its superclass). As a result, it
107
+ # must be modified like so:
108
+ #
109
+ # def save
110
+ # self.class.state_machines.transitions(self, :save).perform do
111
+ # @saving_state = state
112
+ # fail != true
113
+ # end
114
+ # end
115
+ #
116
+ # This will add in the functionality for firing the event stored in the
117
+ # +state_event+ attribute.
118
+ #
119
+ # == Callbacks
120
+ #
121
+ # Callbacks are supported for hooking before and after every possible
122
+ # transition in the machine. Each callback is invoked in the order in which
123
+ # it was defined. See StateMachines::Machine#before_transition and
124
+ # StateMachines::Machine#after_transition for documentation on how to define
125
+ # new callbacks.
126
+ #
127
+ # *Note* that callbacks only get executed within the context of an event. As
128
+ # a result, if a class has an initial state when it's created, any callbacks
129
+ # that would normally get executed when the object enters that state will
130
+ # *not* get triggered.
131
+ #
132
+ # For example,
133
+ #
134
+ # class Vehicle
135
+ # state_machine initial: :parked do
136
+ # after_transition all => :parked do
137
+ # raise ArgumentError
138
+ # end
139
+ # ...
140
+ # end
141
+ # end
142
+ #
143
+ # vehicle = Vehicle.new # => #<Vehicle id: 1, state: "parked">
144
+ # vehicle.save # => true (no exception raised)
145
+ #
146
+ # If you need callbacks to get triggered when an object is created, this
147
+ # should be done by one of the following techniques:
148
+ # * Use a <tt>before :create</tt> or equivalent hook:
149
+ #
150
+ # class Vehicle
151
+ # before :create, :track_initial_transition
152
+ #
153
+ # state_machine do
154
+ # ...
155
+ # end
156
+ # end
157
+ #
158
+ # * Set an initial state and use the correct event to create the
159
+ # object with the proper state, resulting in callbacks being triggered and
160
+ # the object getting persisted (note that the <tt>:pending</tt> state is
161
+ # actually stored as nil):
162
+ #
163
+ # class Vehicle
164
+ # state_machine initial: :pending
165
+ # after_transition pending: :parked, do: :track_initial_transition
166
+ #
167
+ # event :park do
168
+ # transition pending: :parked
169
+ # end
170
+ #
171
+ # state :pending, value: nil
172
+ # end
173
+ # end
174
+ #
175
+ # vehicle = Vehicle.new
176
+ # vehicle.park
177
+ #
178
+ # * Use a default event attribute that will automatically trigger when the
179
+ # configured action gets run (note that the <tt>:pending</tt> state is
180
+ # actually stored as nil):
181
+ #
182
+ # class Vehicle < ActiveRecord::Base
183
+ # state_machine initial: :pending
184
+ # after_transition pending: :parked, do: :track_initial_transition
185
+ #
186
+ # event :park do
187
+ # transition pending: :parked
188
+ # end
189
+ #
190
+ # state :pending, value: nil
191
+ # end
192
+ #
193
+ # def initialize(*)
194
+ # super
195
+ # self.state_event = 'park'
196
+ # end
197
+ # end
198
+ #
199
+ # vehicle = Vehicle.new
200
+ # vehicle.save
201
+ #
202
+ # === Canceling callbacks
203
+ #
204
+ # Callbacks can be canceled by throwing :halt at any point during the
205
+ # callback. For example,
206
+ #
207
+ # ...
208
+ # throw :halt
209
+ # ...
210
+ #
211
+ # If a +before+ callback halts the chain, the associated transition and all
212
+ # later callbacks are canceled. If an +after+ callback halts the chain,
213
+ # the later callbacks are canceled, but the transition is still successful.
214
+ #
215
+ # These same rules apply to +around+ callbacks with the exception that any
216
+ # +around+ callback that doesn't yield will essentially result in :halt being
217
+ # thrown. Any code executed after the yield will behave in the same way as
218
+ # +after+ callbacks.
219
+ #
220
+ # *Note* that if a +before+ callback fails and the bang version of an event
221
+ # was invoked, an exception will be raised instead of returning false. For
222
+ # example,
223
+ #
224
+ # class Vehicle
225
+ # state_machine :initial => :parked do
226
+ # before_transition any => :idling, :do => lambda {|vehicle| throw :halt}
227
+ # ...
228
+ # end
229
+ # end
230
+ #
231
+ # vehicle = Vehicle.new
232
+ # vehicle.park # => false
233
+ # vehicle.park! # => StateMachines::InvalidTransition: Cannot transition state via :park from "idling"
234
+ #
235
+ # == Observers
236
+ #
237
+ # Observers, in the sense of external classes and *not* Ruby's Observable
238
+ # mechanism, can hook into state machines as well. Such observers use the
239
+ # same callback api that's used internally.
240
+ #
241
+ # Below are examples of defining observers for the following state machine:
242
+ #
243
+ # class Vehicle
244
+ # state_machine do
245
+ # event :park do
246
+ # transition idling: :parked
247
+ # end
248
+ # ...
249
+ # end
250
+ # ...
251
+ # end
252
+ #
253
+ # Event/Transition behaviors:
254
+ #
255
+ # class VehicleObserver
256
+ # def self.before_park(vehicle, transition)
257
+ # logger.info "#{vehicle} instructed to park... state is: #{transition.from}, state will be: #{transition.to}"
258
+ # end
259
+ #
260
+ # def self.after_park(vehicle, transition, result)
261
+ # logger.info "#{vehicle} instructed to park... state was: #{transition.from}, state is: #{transition.to}"
262
+ # end
263
+ #
264
+ # def self.before_transition(vehicle, transition)
265
+ # logger.info "#{vehicle} instructed to #{transition.event}... #{transition.attribute} is: #{transition.from}, #{transition.attribute} will be: #{transition.to}"
266
+ # end
267
+ #
268
+ # def self.after_transition(vehicle, transition)
269
+ # logger.info "#{vehicle} instructed to #{transition.event}... #{transition.attribute} was: #{transition.from}, #{transition.attribute} is: #{transition.to}"
270
+ # end
271
+ #
272
+ # def self.around_transition(vehicle, transition)
273
+ # logger.info Benchmark.measure { yield }
274
+ # end
275
+ # end
276
+ #
277
+ # Vehicle.state_machine do
278
+ # before_transition :on => :park, :do => VehicleObserver.method(:before_park)
279
+ # before_transition VehicleObserver.method(:before_transition)
280
+ #
281
+ # after_transition :on => :park, :do => VehicleObserver.method(:after_park)
282
+ # after_transition VehicleObserver.method(:after_transition)
283
+ #
284
+ # around_transition VehicleObserver.method(:around_transition)
285
+ # end
286
+ #
287
+ # One common callback is to record transitions for all models in the system
288
+ # for auditing/debugging purposes. Below is an example of an observer that
289
+ # can easily automate this process for all models:
290
+ #
291
+ # class StateMachineObserver
292
+ # def self.before_transition(object, transition)
293
+ # Audit.log_transition(object.attributes)
294
+ # end
295
+ # end
296
+ #
297
+ # [Vehicle, Switch, Project].each do |klass|
298
+ # klass.state_machines.each do |attribute, machine|
299
+ # machine.before_transition StateMachineObserver.method(:before_transition)
300
+ # end
301
+ # end
302
+ #
303
+ # Additional observer-like behavior may be exposed by the various integrations
304
+ # available. See below for more information on integrations.
305
+ #
306
+ # == Overriding instance / class methods
307
+ #
308
+ # Hooking in behavior to the generated instance / class methods from the
309
+ # state machine, events, and states is very simple because of the way these
310
+ # methods are generated on the class. Using the class's ancestors, the
311
+ # original generated method can be referred to via +super+. For example,
312
+ #
313
+ # class Vehicle
314
+ # state_machine do
315
+ # event :park do
316
+ # ...
317
+ # end
318
+ # end
319
+ #
320
+ # def park(*args)
321
+ # logger.info "..."
322
+ # super
323
+ # end
324
+ # end
325
+ #
326
+ # In the above example, the +park+ instance method that's generated on the
327
+ # Vehicle class (by the associated event) is overridden with custom behavior.
328
+ # Once this behavior is complete, the original method from the state machine
329
+ # is invoked by simply calling +super+.
330
+ #
331
+ # The same technique can be used for +state+, +state_name+, and all other
332
+ # instance *and* class methods on the Vehicle class.
333
+ #
334
+ # == Method conflicts
335
+ #
336
+ # By default state_machine does not redefine methods that exist on
337
+ # superclasses (*including* Object) or any modules (*including* Kernel) that
338
+ # were included before it was defined. This is in order to ensure that
339
+ # existing behavior on the class is not broken by the inclusion of
340
+ # state_machine.
341
+ #
342
+ # If a conflicting method is detected, state_machine will generate a warning.
343
+ # For example, consider the following class:
344
+ #
345
+ # class Vehicle
346
+ # state_machine do
347
+ # event :open do
348
+ # ...
349
+ # end
350
+ # end
351
+ # end
352
+ #
353
+ # In the above class, an event named "open" is defined for its state machine.
354
+ # However, "open" is already defined as an instance method in Ruby's Kernel
355
+ # module that gets included in every Object. As a result, state_machine will
356
+ # generate the following warning:
357
+ #
358
+ # Instance method "open" is already defined in Object, use generic helper instead or set StateMachines::Machine.ignore_method_conflicts = true.
359
+ #
360
+ # Even though you may not be using Kernel's implementation of the "open"
361
+ # instance method, state_machine isn't aware of this and, as a result, stays
362
+ # safe and just skips redefining the method.
363
+ #
364
+ # As with almost all helpers methods defined by state_machine in your class,
365
+ # there are generic methods available for working around this method conflict.
366
+ # In the example above, you can invoke the "open" event like so:
367
+ #
368
+ # vehicle = Vehicle.new # => #<Vehicle:0xb72686b4 @state=nil>
369
+ # vehicle.fire_events(:open) # => true
370
+ #
371
+ # # This will not work
372
+ # vehicle.open # => NoMethodError: private method `open' called for #<Vehicle:0xb72686b4 @state=nil>
373
+ #
374
+ # If you want to take on the risk of overriding existing methods and just
375
+ # ignore method conflicts altogether, you can do so by setting the following
376
+ # configuration:
377
+ #
378
+ # StateMachines::Machine.ignore_method_conflicts = true
379
+ #
380
+ # This will allow you to define events like "open" as described above and
381
+ # still generate the "open" instance helper method. For example:
382
+ #
383
+ # StateMachines::Machine.ignore_method_conflicts = true
384
+ #
385
+ # class Vehicle
386
+ # state_machine do
387
+ # event :open do
388
+ # ...
389
+ # end
390
+ # end
391
+ #
392
+ # vehicle = Vehicle.new # => #<Vehicle:0xb72686b4 @state=nil>
393
+ # vehicle.open # => true
394
+ #
395
+ # By default, state_machine helps prevent you from making mistakes and
396
+ # accidentally overriding methods that you didn't intend to. Once you
397
+ # understand this and what the consequences are, setting the
398
+ # +ignore_method_conflicts+ option is a perfectly reasonable workaround.
399
+ #
400
+ # == Integrations
401
+ #
402
+ # By default, state machines are library-agnostic, meaning that they work
403
+ # on any Ruby class and have no external dependencies. However, there are
404
+ # certain libraries which expose additional behavior that can be taken
405
+ # advantage of by state machines.
406
+ #
407
+ # This library is built to work out of the box with a few popular Ruby
408
+ # libraries that allow for additional behavior to provide a cleaner and
409
+ # smoother experience. This is especially the case for objects backed by a
410
+ # database that may allow for transactions, persistent storage,
411
+ # search/filters, callbacks, etc.
412
+ #
413
+ # When a state machine is defined for classes using any of the above libraries,
414
+ # it will try to automatically determine the integration to use (Agnostic,
415
+ # ActiveModel, ActiveRecord, DataMapper, Mongoid, MongoMapper, or Sequel)
416
+ # based on the class definition. To see how each integration affects the
417
+ # machine's behavior, refer to all constants defined under the
418
+ # StateMachines::Integrations namespace.
419
+ class Machine
420
+ extend ClassMethods
421
+ include EvalHelpers
422
+ include MatcherHelpers
423
+ include Utilities
424
+ include Parsing
425
+ include Validation
426
+ include HelperGenerators
427
+ include ActionHooks
428
+ include Scoping
429
+ include Configuration
430
+ include StateMethods
431
+ include EventMethods
432
+ include Callbacks
433
+ include Rendering
434
+ include Integration
435
+
436
+ # Whether to ignore any conflicts that are detected for helper methods that
437
+ # get generated for a machine's owner class. Default is false.
438
+ # Thread-safe via atomic reference updates
439
+ @ignore_method_conflicts = false
440
+
441
+ # The class that the machine is defined in
442
+ attr_reader :owner_class
443
+
444
+ # The name of the machine, used for scoping methods generated for the
445
+ # machine as a whole (not states or events)
446
+ attr_reader :name
447
+
448
+ # The events that trigger transitions. These are sorted, by default, in
449
+ # the order in which they were defined.
450
+ attr_reader :events
451
+
452
+ # A list of all of the states known to this state machine. This will pull
453
+ # states from the following sources:
454
+ # * Initial state
455
+ # * State behaviors
456
+ # * Event transitions (:to, :from, and :except_from options)
457
+ # * Transition callbacks (:to, :from, :except_to, and :except_from options)
458
+ # * Unreferenced states (using +other_states+ helper)
459
+ #
460
+ # These are sorted, by default, in the order in which they were referenced.
461
+ attr_reader :states
462
+
463
+ # The callbacks to invoke before/after a transition is performed
464
+ #
465
+ # Maps :before => callbacks and :after => callbacks
466
+ attr_reader :callbacks
467
+
468
+ # The action to invoke when an object transitions
469
+ attr_reader :action
470
+
471
+ # An identifier that forces all methods (including state predicates and
472
+ # event methods) to be generated with the value prefixed or suffixed,
473
+ # depending on the context.
474
+ attr_reader :namespace
475
+
476
+ # Whether the machine will use transactions when firing events
477
+ attr_reader :use_transactions
478
+
479
+ # Defines a new helper method in an instance or class scope with the given
480
+ # name. If the method is already defined in the scope, then this will not
481
+ # override it.
482
+ #
483
+ # If passing in a block, there are two side effects to be aware of
484
+ # 1. The method cannot be chained, meaning that the block cannot call +super+
485
+ # 2. If the method is already defined in an ancestor, then it will not get
486
+ # overridden and a warning will be output.
487
+ #
488
+ # Example:
489
+ #
490
+ # # Instance helper
491
+ # machine.define_helper(:instance, :state_name) do |machine, object|
492
+ # machine.states.match(object).name
493
+ # end
494
+ #
495
+ # # Class helper
496
+ # machine.define_helper(:class, :state_machine_name) do |machine, klass|
497
+ # "State"
498
+ # end
499
+ #
500
+ # You can also define helpers using string evaluation like so:
501
+ #
502
+ # # Instance helper
503
+ # machine.define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
504
+ # def state_name
505
+ # self.class.state_machine(:state).states.match(self).name
506
+ # end
507
+ # end_eval
508
+ #
509
+ # # Class helper
510
+ # machine.define_helper :class, <<-end_eval, __FILE__, __LINE__ + 1
511
+ # def state_machine_name
512
+ # "State"
513
+ # end
514
+ # end_eval
515
+ def define_helper(scope, method, *, **, &block)
516
+ helper_module = @helper_modules.fetch(scope)
517
+
518
+ if block_given?
519
+ if !self.class.ignore_method_conflicts && (conflicting_ancestor = owner_class_ancestor_has_method?(scope, method))
520
+ ancestor_name = conflicting_ancestor.name && !conflicting_ancestor.name.empty? ? conflicting_ancestor.name : conflicting_ancestor.to_s
521
+ warn "#{scope == :class ? 'Class' : 'Instance'} method \"#{method}\" is already defined in #{ancestor_name}, use generic helper instead or set StateMachines::Machine.ignore_method_conflicts = true."
522
+ else
523
+ name = self.name
524
+ helper_module.class_eval do
525
+ define_method(method) do |*args, **kwargs|
526
+ block.call((scope == :instance ? self.class : self).state_machine(name), self, *args, **kwargs)
527
+ end
528
+ end
529
+ end
530
+ else
531
+ helper_module.class_eval(method, __FILE__, __LINE__)
532
+ end
533
+ end
534
+
535
+ # Marks the given object as invalid with the given message.
536
+ #
537
+ # By default, this is a no-op.
538
+ def invalidate(_object, _attribute, _message, _values = []); end
539
+
540
+ # Gets a description of the errors for the given object. This is used to
541
+ # provide more detailed information when an InvalidTransition exception is
542
+ # raised.
543
+ def errors_for(_object)
544
+ ''
545
+ end
546
+
547
+ # Resets any errors previously added when invalidating the given object.
548
+ #
549
+ # By default, this is a no-op.
550
+ def reset(_object); end
551
+
552
+ # Generates the message to use when invalidating the given object after
553
+ # failing to transition on a specific event
554
+ def generate_message(name, values = [])
555
+ message = @messages[name] || self.class.default_messages[name]
556
+
557
+ # Check whether there are actually any values to interpolate to avoid
558
+ # any warnings
559
+ if message.scan(/%./).any? { |match| match != '%%' }
560
+ message % values.map(&:last)
561
+ else
562
+ message
563
+ end
564
+ end
565
+
566
+ # Runs a transaction, rolling back any changes if the yielded block fails.
567
+ #
568
+ # This is only applicable to integrations that involve databases. By
569
+ # default, this will not run any transactions since the changes aren't
570
+ # taking place within the context of a database.
571
+ def within_transaction(object, &)
572
+ if use_transactions
573
+ transaction(object, &)
574
+ else
575
+ yield
576
+ end
577
+ end
578
+
579
+ def renderer
580
+ self.class.renderer
581
+ end
582
+
583
+ def draw(**)
584
+ renderer.draw_machine(self, **)
585
+ end
586
+
587
+ # Determines whether an action hook was defined for firing attribute-based
588
+ # event transitions when the configured action gets called.
589
+ def action_hook?(self_only = false)
590
+ @action_hook_defined || (!self_only && owner_class.state_machines.any? { |_name, machine| machine.action == action && machine != self && machine.action_hook?(true) })
591
+ end
592
+
593
+ protected
594
+
595
+ # Runs additional initialization hooks. By default, this is a no-op.
596
+ def after_initialize; end
597
+
598
+ # Always yields
599
+ def transaction(_object)
600
+ yield
601
+ end
602
+
603
+ # Gets the initial attribute value defined by the owner class (outside of
604
+ # the machine's definition). By default, this is always nil.
605
+ def owner_class_attribute_default
606
+ nil
607
+ end
608
+
609
+ # Checks whether the given state matches the attribute default specified
610
+ # by the owner class
611
+ def owner_class_attribute_default_matches?(state)
612
+ state.matches?(owner_class_attribute_default)
613
+ end
614
+ end
615
+ end