davidlee-state-fu 0.3.1 → 0.10.0

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 (90) hide show
  1. data/README.textile +124 -34
  2. data/Rakefile +36 -30
  3. data/lib/no_stdout.rb +1 -1
  4. data/lib/state-fu.rb +9 -8
  5. data/lib/state_fu/active_support_lite/array/access.rb +12 -5
  6. data/lib/state_fu/active_support_lite/array/conversions.rb +10 -4
  7. data/lib/state_fu/active_support_lite/array/extract_options.rb +5 -4
  8. data/lib/state_fu/active_support_lite/array/grouping.rb +7 -4
  9. data/lib/state_fu/active_support_lite/array/random_access.rb +4 -3
  10. data/lib/state_fu/active_support_lite/array/wrapper.rb +4 -3
  11. data/lib/state_fu/active_support_lite/array.rb +3 -1
  12. data/lib/state_fu/active_support_lite/blank.rb +18 -9
  13. data/lib/state_fu/active_support_lite/cattr_reader.rb +4 -1
  14. data/lib/state_fu/active_support_lite/keys.rb +8 -3
  15. data/lib/state_fu/active_support_lite/misc.rb +6 -4
  16. data/lib/state_fu/active_support_lite/module/delegation.rb +130 -0
  17. data/lib/state_fu/active_support_lite/module.rb +1 -0
  18. data/lib/state_fu/active_support_lite/object.rb +5 -2
  19. data/lib/state_fu/active_support_lite/string.rb +6 -1
  20. data/lib/state_fu/active_support_lite/symbol.rb +2 -1
  21. data/lib/state_fu/applicable.rb +41 -0
  22. data/lib/state_fu/{helper.rb → arrays.rb} +45 -121
  23. data/lib/state_fu/binding.rb +136 -159
  24. data/lib/state_fu/core_ext.rb +78 -10
  25. data/lib/state_fu/event.rb +112 -48
  26. data/lib/state_fu/exceptions.rb +80 -34
  27. data/lib/state_fu/executioner.rb +149 -0
  28. data/lib/state_fu/has_options.rb +16 -0
  29. data/lib/state_fu/hooks.rb +21 -16
  30. data/lib/state_fu/interface.rb +80 -83
  31. data/lib/state_fu/lathe.rb +361 -148
  32. data/lib/state_fu/logger.rb +122 -45
  33. data/lib/state_fu/machine.rb +60 -32
  34. data/lib/state_fu/method_factory.rb +180 -72
  35. data/lib/state_fu/methodical.rb +17 -0
  36. data/lib/state_fu/persistence/active_record.rb +6 -1
  37. data/lib/state_fu/persistence/attribute.rb +1 -0
  38. data/lib/state_fu/persistence/base.rb +8 -6
  39. data/lib/state_fu/persistence.rb +94 -23
  40. data/lib/state_fu/sprocket.rb +26 -11
  41. data/lib/state_fu/state.rb +8 -27
  42. data/lib/state_fu/transition.rb +207 -98
  43. data/lib/state_fu/transition_query.rb +214 -0
  44. data/lib/state_fu.rb +1 -0
  45. data/lib/tasks/spec_last.rake +46 -0
  46. data/lib/tasks/state_fu.rake +57 -0
  47. data/lib/vizier.rb +61 -61
  48. data/spec/custom_formatter.rb +49 -0
  49. data/spec/features/binding_and_transition_helper_mixin_spec.rb +2 -2
  50. data/spec/features/method_missing_only_once_spec.rb +28 -0
  51. data/spec/features/not_requirements_spec.rb +83 -46
  52. data/spec/features/plotter_spec.rb +97 -0
  53. data/spec/features/shared_log_spec.rb +7 -0
  54. data/spec/features/singleton_machine_spec.rb +39 -0
  55. data/spec/features/state_and_array_options_accessor_spec.rb +1 -1
  56. data/spec/features/{transition_boolean_comparison.rb → transition_boolean_comparison_spec.rb} +29 -18
  57. data/spec/helper.rb +6 -117
  58. data/spec/integration/active_record_persistence_spec.rb +18 -4
  59. data/spec/integration/binding_extension_spec.rb +1 -1
  60. data/spec/integration/class_accessor_spec.rb +49 -59
  61. data/spec/integration/event_definition_spec.rb +20 -20
  62. data/spec/integration/example_01_document_spec.rb +13 -8
  63. data/spec/integration/example_02_string_spec.rb +3 -2
  64. data/spec/integration/instance_accessor_spec.rb +16 -19
  65. data/spec/integration/lathe_extension_spec.rb +2 -2
  66. data/spec/integration/machine_duplication_spec.rb +59 -37
  67. data/spec/integration/relaxdb_persistence_spec.rb +6 -3
  68. data/spec/integration/requirement_reflection_spec.rb +66 -57
  69. data/spec/integration/state_definition_spec.rb +72 -66
  70. data/spec/integration/transition_spec.rb +169 -173
  71. data/spec/spec.opts +5 -3
  72. data/spec/spec_helper.rb +132 -0
  73. data/spec/state_fu_spec.rb +870 -0
  74. data/spec/units/binding_spec.rb +33 -22
  75. data/spec/units/event_spec.rb +3 -22
  76. data/spec/units/exceptions_spec.rb +7 -0
  77. data/spec/units/lathe_spec.rb +7 -7
  78. data/spec/units/machine_spec.rb +67 -75
  79. data/spec/units/method_factory_spec.rb +55 -48
  80. data/spec/units/sprocket_spec.rb +5 -7
  81. data/spec/units/state_spec.rb +33 -24
  82. metadata +31 -19
  83. data/lib/state_fu/active_support_lite/inheritable_attributes.rb +0 -1
  84. data/lib/state_fu/fu_space.rb +0 -51
  85. data/lib/state_fu/mock_transition.rb +0 -38
  86. data/spec/BDD/plotter_spec.rb +0 -115
  87. data/spec/integration/dynamic_requirement_spec.rb +0 -160
  88. data/spec/integration/ex_machine_for_accounts_spec.rb +0 -79
  89. data/spec/integration/sanity_spec.rb +0 -31
  90. data/spec/units/fu_space_spec.rb +0 -95
@@ -1,5 +1,39 @@
1
1
  module StateFu
2
2
  module Interface
3
+ module SoftAlias
4
+
5
+ # define aliases that won't clobber existing methods -
6
+ # so we can be liberal with them.
7
+ def soft_alias(x)
8
+ aliases = [ x.to_a[0] ].flatten
9
+ original = aliases.shift
10
+ existing_method_names = (self.instance_methods | self.protected_instance_methods | self.private_instance_methods).map(&:to_sym)
11
+ taken, ok = aliases.partition { |a| existing_method_names.include?(a.to_sym) }
12
+ StateFu::Logger.debug("#{self.to_s} alias for ## #{original} already taken: #{taken.inspect}") unless taken.empty?
13
+ ok.each { |a| alias_method a, original}
14
+ end
15
+
16
+ end
17
+
18
+ module Aliases
19
+
20
+ def self.extended(base)
21
+ base.extend SoftAlias
22
+ base.class_eval do
23
+ # instance method aliases
24
+ soft_alias :state_fu => [:stfu, :fu, :stateful, :workflow, :engine, :machine, :context]
25
+ soft_alias :state_fu_bindings => [:bindings, :workflows, :engines, :machines, :contexts]
26
+ soft_alias :state_fu! => [:stfu!, :initialize_machines!, :initialize_state!]
27
+ class << self
28
+ extend SoftAlias
29
+ # class method aliases
30
+ soft_alias :state_fu_machine => [:stfu, :state_fu, :workflow, :stateful, :statefully, :state_machine, :engine]
31
+ soft_alias :state_fu_machines => [:stfus, :state_fus, :workflows, :engines]
32
+ end
33
+ end
34
+ end
35
+ end
36
+
3
37
  # Provides access to StateFu to your classes. Plenty of aliases are
4
38
  # provided so you can use whatever makes sense to you.
5
39
  module ClassMethods
@@ -12,9 +46,10 @@ module StateFu
12
46
  # class, creating it if it did not exist.
13
47
  #
14
48
  # Given a symbol, return the machine by that name, creating it
15
- # if it didn't exist.
49
+ # if it didn't exist, and definining it if a block is passed.
16
50
  #
17
- # Given a block, also define it with the contents of the block.
51
+ # Given a block, apply it to a StateFu::Lathe to define a
52
+ # machine, and return it.
18
53
  #
19
54
  # This can be done multiple times; changes are cumulative.
20
55
  #
@@ -24,54 +59,36 @@ module StateFu
24
59
  # # equivalent to Klass.machine(:om)
25
60
  # Klass.machine(:workflow) # another totally separate machine
26
61
  #
27
- # machine( name=:state_fu, options[:field_name], &block )
28
-
29
- def machine( *args, &block )
62
+ # recognised options are:
63
+ # :field_name - specify the field to use for persistence.
64
+ # defaults to {machine_name}_field.
65
+ #
66
+ def state_fu_machine( *args, &block )
30
67
  options = args.extract_options!.symbolize_keys!
31
- name = args[0] || StateFu::DEFAULT_MACHINE
68
+ name = args[0] || DEFAULT
32
69
  StateFu::Machine.for_class( self, name, options, &block )
33
70
  end
34
- alias_method :stfu, :machine
35
- alias_method :state_fu, :machine
36
- alias_method :workflow, :machine
37
- alias_method :stateful, :machine
38
- alias_method :statefully, :machine
39
- alias_method :state_machine, :machine
40
- alias_method :engine, :machine
41
-
42
- # return a hash of :name => StateFu::Machine for your class.
43
- def machines( *args, &block )
44
- if args.empty? && !block_given?
45
- StateFu::FuSpace.class_machines[self]
46
- else
47
- machine( *args, &block)
48
- end
71
+ alias_method :machine, :state_fu_machine
72
+
73
+ def state_fu_field_names
74
+ @_state_fu_field_names ||= {}
49
75
  end
50
- alias_method :stfus, :machines
51
- alias_method :state_fus, :machines
52
- alias_method :workflows, :machines
53
- alias_method :engines, :machines
54
-
55
- # return the list of machines names for this class
56
- def machine_names()
57
- StateFu::FuSpace.class_machines[self].keys
76
+
77
+ def state_fu_machines
78
+ @_state_fu_machines ||= {}
58
79
  end
59
- alias_method :stfu_names, :machine_names
60
- alias_method :state_fu_names, :machine_names
61
- alias_method :workflow_names, :machine_names
62
- alias_method :engine_names, :machine_names
80
+ alias_method :machines, :state_fu_machines
81
+
63
82
  end
64
83
 
65
84
  # These methods grant access to StateFu::Binding objects, which
66
85
  # are bundles of context encapsulating a StateFu::Machine, an instance
67
86
  # of a class, and its current state in the machine.
68
87
 
69
- # Again, plenty of aliases are provided so you can use whatever
70
- # makes sense to you.
71
88
  module InstanceMethods
72
- private
73
- def _state_fu
74
- @_state_fu ||= {}
89
+
90
+ def state_fu_bindings
91
+ @_state_fu_bindings ||= {}
75
92
  end
76
93
 
77
94
  # A StateFu::Binding comes into being when it is first referenced.
@@ -79,57 +96,37 @@ module StateFu
79
96
  # This is the accessor method through which an object instance (or developer)
80
97
  # can access a StateFu::Machine, the object's current state, the
81
98
  # methods which trigger event transitions, etc.
82
- public
83
- def _binding( name=StateFu::DEFAULT_MACHINE )
84
- name = name.to_sym
85
- if mach = StateFu::FuSpace.class_machines[self.class][name]
86
- _state_fu[name] ||= StateFu::Binding.new( mach, self, name )
99
+
100
+ def state_fu_binding( name = DEFAULT )
101
+ name = name.to_sym
102
+ if machine = self.class.state_fu_machines[name]
103
+ state_fu_bindings[name] ||= StateFu::Binding.new( machine, self, name )
104
+ else raise ArgumentError.new("No state machine called #{name} for #{self.class} #{self}")
87
105
  end
88
106
  end
107
+ alias_method :state_fu, :state_fu_binding
89
108
 
90
- alias_method :fu, :_binding
91
- alias_method :stfu, :_binding
92
- alias_method :state_fu, :_binding
93
- alias_method :stateful, :_binding
94
- alias_method :workflow, :_binding
95
- alias_method :engine, :_binding
96
- alias_method :machine, :_binding # not strictly accurate
97
- alias_method :context, :_binding
98
-
99
- # Gain awareness of all bindings (state contexts) this object
100
- # has contemplated into being.
101
- # Returns a Hash of { :name => <StateFu::Binding>, ... }
102
- def _bindings()
103
- _state_fu
109
+ def current_state( name = DEFAULT )
110
+ state_fu_binding(name).current_state
111
+ end
112
+
113
+ def next!(name = DEFAULT, *args, &block )
114
+ state_fu_binding(name).next! *args, &block
104
115
  end
116
+ alias_method :next_state!, :next!
117
+ alias_method :fire_next_transition!, :next!
105
118
 
106
- alias_method :fus, :_bindings
107
- alias_method :stfus, :_bindings
108
- alias_method :state_fus, :_bindings
109
- alias_method :state_foos, :_bindings
110
- alias_method :workflows, :_bindings
111
- alias_method :engines, :_bindings
112
- alias_method :bindings, :_bindings
113
- alias_method :machines, :_bindings # not strictly accurate
114
- alias_method :contexts, :_bindings
115
-
116
- # Instantiate bindings for all machines defined for this class.
119
+ # Instantiate bindings for all machines, which ensures that persistence
120
+ # fields are intialized and event methods defined.
117
121
  # It's useful to call this before_create w/
118
122
  # ActiveRecord classes, as this will cause the database field
119
123
  # to be populated with the default state name.
120
- def state_fu!( *names )
121
- if [names || [] ].flatten!.map! {|n| n.to_sym }.empty?
122
- names = self.class.machine_names()
123
- end
124
- @state_fu_initialized = true
125
- names.map { |n| _binding( n ) }
124
+
125
+ def state_fu!
126
+ MethodFactory.define_singleton_method(self, :initialize_state_fu!) { true }
127
+ self.class.state_fu_machines.keys.map { |n| state_fu_binding( n ) }
126
128
  end
127
- alias_method :fu!, :state_fu!
128
- alias_method :stfu!, :state_fu!
129
- alias_method :state_fu!, :state_fu!
130
- alias_method :init_machines!, :state_fu!
131
- alias_method :initialize_state!, :state_fu!
132
- alias_method :build_workflow!, :state_fu!
133
- end
134
- end
135
- end
129
+
130
+ end # ClassMethods
131
+ end # Interface
132
+ end # StateFu