state_machine 1.1.2 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (240) hide show
  1. data/.gitignore +7 -11
  2. data/.travis.yml +49 -7
  3. data/Appraisals +255 -87
  4. data/CHANGELOG.md +30 -0
  5. data/README.md +142 -21
  6. data/Rakefile +1 -11
  7. data/examples/Gemfile +5 -0
  8. data/examples/Gemfile.lock +14 -0
  9. data/examples/auto_shop.rb +2 -0
  10. data/examples/car.rb +2 -0
  11. data/examples/doc/AutoShop.html +2856 -0
  12. data/examples/doc/AutoShop_state.png +0 -0
  13. data/examples/doc/Car.html +919 -0
  14. data/examples/doc/Car_state.png +0 -0
  15. data/examples/doc/TrafficLight.html +2230 -0
  16. data/examples/doc/TrafficLight_state.png +0 -0
  17. data/examples/doc/Vehicle.html +7921 -0
  18. data/examples/doc/Vehicle_state.png +0 -0
  19. data/examples/doc/_index.html +136 -0
  20. data/examples/doc/class_list.html +47 -0
  21. data/examples/doc/css/common.css +1 -0
  22. data/examples/doc/css/full_list.css +55 -0
  23. data/examples/doc/css/style.css +322 -0
  24. data/examples/doc/file_list.html +46 -0
  25. data/examples/doc/frames.html +13 -0
  26. data/examples/doc/index.html +136 -0
  27. data/examples/doc/js/app.js +205 -0
  28. data/examples/doc/js/full_list.js +173 -0
  29. data/examples/doc/js/jquery.js +16 -0
  30. data/examples/doc/method_list.html +734 -0
  31. data/examples/doc/top-level-namespace.html +105 -0
  32. data/examples/rails-rest/migration.rb +1 -5
  33. data/examples/rails-rest/view__form.html.erb +34 -0
  34. data/examples/rails-rest/view_edit.html.erb +2 -21
  35. data/examples/rails-rest/view_index.html.erb +6 -4
  36. data/examples/rails-rest/view_new.html.erb +2 -11
  37. data/examples/rails-rest/view_show.html.erb +5 -3
  38. data/examples/traffic_light.rb +2 -0
  39. data/examples/vehicle.rb +2 -0
  40. data/gemfiles/active_model-3.0.0.gemfile.lock +9 -6
  41. data/gemfiles/active_model-3.0.5.gemfile.lock +10 -7
  42. data/gemfiles/active_model-3.1.1.gemfile.lock +12 -10
  43. data/gemfiles/{active_model-3.2.0.gemfile → active_model-3.2.1.gemfile} +1 -1
  44. data/gemfiles/{graphviz-0.9.0.gemfile → active_model-3.2.12.gemfile} +1 -1
  45. data/gemfiles/active_model-3.2.12.gemfile.lock +36 -0
  46. data/gemfiles/{active_record-3.2.0.gemfile → active_model-3.2.13.rc1.gemfile} +1 -2
  47. data/gemfiles/active_model-3.2.13.rc1.gemfile.lock +36 -0
  48. data/gemfiles/active_model-4.0.0.gemfile +9 -0
  49. data/gemfiles/active_model-4.0.0.gemfile.lock +78 -0
  50. data/gemfiles/active_record-2.0.0.gemfile +2 -1
  51. data/gemfiles/active_record-2.0.0.gemfile.lock +15 -6
  52. data/gemfiles/active_record-2.0.5.gemfile +2 -1
  53. data/gemfiles/active_record-2.0.5.gemfile.lock +15 -6
  54. data/gemfiles/active_record-2.1.0.gemfile +2 -1
  55. data/gemfiles/active_record-2.1.0.gemfile.lock +15 -6
  56. data/gemfiles/active_record-2.1.2.gemfile +2 -1
  57. data/gemfiles/active_record-2.1.2.gemfile.lock +15 -6
  58. data/gemfiles/active_record-2.2.3.gemfile +2 -1
  59. data/gemfiles/active_record-2.2.3.gemfile.lock +15 -6
  60. data/gemfiles/active_record-2.3.12.gemfile +2 -1
  61. data/gemfiles/active_record-2.3.12.gemfile.lock +15 -6
  62. data/gemfiles/active_record-2.3.5.gemfile +9 -0
  63. data/gemfiles/active_record-2.3.5.gemfile.lock +39 -0
  64. data/gemfiles/active_record-3.0.0.gemfile +2 -1
  65. data/gemfiles/active_record-3.0.0.gemfile.lock +18 -11
  66. data/gemfiles/active_record-3.0.5.gemfile +2 -1
  67. data/gemfiles/active_record-3.0.5.gemfile.lock +19 -12
  68. data/gemfiles/active_record-3.1.1.gemfile +2 -1
  69. data/gemfiles/active_record-3.1.1.gemfile.lock +22 -16
  70. data/gemfiles/active_record-3.2.12.gemfile +9 -0
  71. data/gemfiles/active_record-3.2.12.gemfile.lock +51 -0
  72. data/gemfiles/active_record-3.2.13.rc1.gemfile +9 -0
  73. data/gemfiles/active_record-3.2.13.rc1.gemfile.lock +51 -0
  74. data/gemfiles/active_record-4.0.0.gemfile +11 -0
  75. data/gemfiles/active_record-4.0.0.gemfile.lock +83 -0
  76. data/gemfiles/data_mapper-0.10.2.gemfile +1 -0
  77. data/gemfiles/data_mapper-0.10.2.gemfile.lock +13 -9
  78. data/gemfiles/data_mapper-0.9.11.gemfile +1 -0
  79. data/gemfiles/data_mapper-0.9.11.gemfile.lock +31 -7
  80. data/gemfiles/data_mapper-0.9.4.gemfile.lock +25 -14
  81. data/gemfiles/data_mapper-0.9.7.gemfile +1 -0
  82. data/gemfiles/data_mapper-0.9.7.gemfile.lock +27 -15
  83. data/gemfiles/data_mapper-1.0.0.gemfile.lock +20 -17
  84. data/gemfiles/data_mapper-1.0.1.gemfile.lock +20 -17
  85. data/gemfiles/data_mapper-1.0.2.gemfile.lock +20 -17
  86. data/gemfiles/data_mapper-1.1.0.gemfile.lock +19 -16
  87. data/gemfiles/data_mapper-1.2.0.gemfile.lock +19 -16
  88. data/gemfiles/default.gemfile.lock +8 -5
  89. data/gemfiles/graphviz-0.9.17.gemfile +7 -0
  90. data/gemfiles/graphviz-0.9.17.gemfile.lock +29 -0
  91. data/gemfiles/graphviz-0.9.21.gemfile.lock +7 -4
  92. data/gemfiles/graphviz-1.0.0.gemfile.lock +7 -4
  93. data/gemfiles/graphviz-1.0.3.gemfile +7 -0
  94. data/gemfiles/graphviz-1.0.3.gemfile.lock +29 -0
  95. data/gemfiles/graphviz-1.0.8.gemfile +7 -0
  96. data/gemfiles/graphviz-1.0.8.gemfile.lock +29 -0
  97. data/gemfiles/mongo_mapper-0.10.0.gemfile +1 -0
  98. data/gemfiles/mongo_mapper-0.10.0.gemfile.lock +14 -11
  99. data/gemfiles/mongo_mapper-0.11.1.gemfile +7 -0
  100. data/gemfiles/mongo_mapper-0.11.1.gemfile.lock +44 -0
  101. data/gemfiles/mongo_mapper-0.11.2.gemfile +9 -0
  102. data/gemfiles/mongo_mapper-0.11.2.gemfile.lock +48 -0
  103. data/gemfiles/mongo_mapper-0.12.0.gemfile +9 -0
  104. data/gemfiles/mongo_mapper-0.12.0.gemfile.lock +48 -0
  105. data/gemfiles/mongo_mapper-0.5.5.gemfile.lock +7 -4
  106. data/gemfiles/mongo_mapper-0.5.8.gemfile.lock +7 -4
  107. data/gemfiles/mongo_mapper-0.6.0.gemfile.lock +7 -4
  108. data/gemfiles/mongo_mapper-0.6.10.gemfile.lock +7 -4
  109. data/gemfiles/mongo_mapper-0.7.0.gemfile.lock +7 -4
  110. data/gemfiles/mongo_mapper-0.7.5.gemfile.lock +7 -4
  111. data/gemfiles/mongo_mapper-0.8.0.gemfile.lock +7 -4
  112. data/gemfiles/mongo_mapper-0.8.3.gemfile.lock +7 -4
  113. data/gemfiles/mongo_mapper-0.8.4.gemfile.lock +7 -4
  114. data/gemfiles/mongo_mapper-0.8.6.gemfile.lock +7 -4
  115. data/gemfiles/mongo_mapper-0.9.0.gemfile.lock +7 -4
  116. data/gemfiles/mongoid-2.0.0.gemfile +2 -0
  117. data/gemfiles/mongoid-2.0.0.gemfile.lock +22 -18
  118. data/gemfiles/mongoid-2.1.4.gemfile +2 -0
  119. data/gemfiles/mongoid-2.1.4.gemfile.lock +21 -17
  120. data/gemfiles/mongoid-2.2.4.gemfile +2 -0
  121. data/gemfiles/mongoid-2.2.4.gemfile.lock +21 -17
  122. data/gemfiles/mongoid-2.3.3.gemfile +2 -0
  123. data/gemfiles/mongoid-2.3.3.gemfile.lock +21 -17
  124. data/gemfiles/mongoid-2.4.0.gemfile +9 -0
  125. data/gemfiles/mongoid-2.4.0.gemfile.lock +47 -0
  126. data/gemfiles/mongoid-2.4.10.gemfile +9 -0
  127. data/gemfiles/mongoid-2.4.10.gemfile.lock +47 -0
  128. data/gemfiles/mongoid-2.5.2.gemfile +9 -0
  129. data/gemfiles/mongoid-2.5.2.gemfile.lock +47 -0
  130. data/gemfiles/mongoid-2.6.0.gemfile +9 -0
  131. data/gemfiles/mongoid-2.6.0.gemfile.lock +47 -0
  132. data/gemfiles/mongoid-3.0.0.gemfile +8 -0
  133. data/gemfiles/mongoid-3.0.0.gemfile.lock +45 -0
  134. data/gemfiles/mongoid-3.0.22.gemfile +8 -0
  135. data/gemfiles/mongoid-3.0.22.gemfile.lock +45 -0
  136. data/gemfiles/mongoid-3.1.0.gemfile +8 -0
  137. data/gemfiles/mongoid-3.1.0.gemfile.lock +45 -0
  138. data/gemfiles/sequel-2.11.0.gemfile +2 -1
  139. data/gemfiles/sequel-2.11.0.gemfile.lock +11 -6
  140. data/gemfiles/sequel-2.12.0.gemfile +2 -1
  141. data/gemfiles/sequel-2.12.0.gemfile.lock +11 -6
  142. data/gemfiles/sequel-2.8.0.gemfile +2 -1
  143. data/gemfiles/sequel-2.8.0.gemfile.lock +11 -6
  144. data/gemfiles/sequel-3.0.0.gemfile +2 -1
  145. data/gemfiles/sequel-3.0.0.gemfile.lock +11 -6
  146. data/gemfiles/sequel-3.10.0.gemfile +9 -0
  147. data/gemfiles/sequel-3.10.0.gemfile.lock +33 -0
  148. data/gemfiles/sequel-3.13.0.gemfile +2 -1
  149. data/gemfiles/sequel-3.13.0.gemfile.lock +11 -6
  150. data/gemfiles/sequel-3.14.0.gemfile +2 -1
  151. data/gemfiles/sequel-3.14.0.gemfile.lock +11 -6
  152. data/gemfiles/sequel-3.23.0.gemfile +2 -1
  153. data/gemfiles/sequel-3.23.0.gemfile.lock +11 -6
  154. data/gemfiles/sequel-3.24.0.gemfile +2 -1
  155. data/gemfiles/sequel-3.24.0.gemfile.lock +11 -6
  156. data/gemfiles/sequel-3.29.0.gemfile +2 -1
  157. data/gemfiles/sequel-3.29.0.gemfile.lock +11 -6
  158. data/gemfiles/sequel-3.34.0.gemfile +9 -0
  159. data/gemfiles/sequel-3.34.0.gemfile.lock +33 -0
  160. data/gemfiles/sequel-3.35.0.gemfile +9 -0
  161. data/gemfiles/sequel-3.35.0.gemfile.lock +33 -0
  162. data/gemfiles/sequel-3.4.0.gemfile +9 -0
  163. data/gemfiles/sequel-3.4.0.gemfile.lock +33 -0
  164. data/gemfiles/sequel-3.44.0.gemfile +9 -0
  165. data/gemfiles/sequel-3.44.0.gemfile.lock +33 -0
  166. data/lib/state_machine.rb +6 -0
  167. data/lib/state_machine/branch.rb +9 -8
  168. data/lib/state_machine/callback.rb +2 -2
  169. data/lib/state_machine/core.rb +10 -0
  170. data/lib/state_machine/core_ext.rb +1 -0
  171. data/lib/state_machine/eval_helpers.rb +5 -3
  172. data/lib/state_machine/event.rb +17 -6
  173. data/lib/state_machine/graph.rb +92 -0
  174. data/lib/state_machine/integrations.rb +13 -1
  175. data/lib/state_machine/integrations/active_model.rb +14 -20
  176. data/lib/state_machine/integrations/active_model/observer.rb +3 -3
  177. data/lib/state_machine/integrations/active_model/observer_update.rb +42 -0
  178. data/lib/state_machine/integrations/active_record.rb +52 -25
  179. data/lib/state_machine/integrations/active_record/locale.rb +1 -1
  180. data/lib/state_machine/integrations/active_record/versions.rb +1 -17
  181. data/lib/state_machine/integrations/base.rb +15 -6
  182. data/lib/state_machine/integrations/data_mapper.rb +98 -35
  183. data/lib/state_machine/integrations/data_mapper/versions.rb +46 -8
  184. data/lib/state_machine/integrations/mongo_mapper.rb +39 -12
  185. data/lib/state_machine/integrations/mongo_mapper/locale.rb +1 -1
  186. data/lib/state_machine/integrations/mongo_mapper/versions.rb +3 -20
  187. data/lib/state_machine/integrations/mongoid.rb +52 -14
  188. data/lib/state_machine/integrations/mongoid/locale.rb +1 -1
  189. data/lib/state_machine/integrations/mongoid/versions.rb +52 -26
  190. data/lib/state_machine/integrations/sequel.rb +82 -33
  191. data/lib/state_machine/integrations/sequel/versions.rb +19 -44
  192. data/lib/state_machine/machine.rb +99 -59
  193. data/lib/state_machine/machine_collection.rb +1 -2
  194. data/lib/state_machine/macro_methods.rb +29 -0
  195. data/lib/state_machine/node_collection.rb +1 -1
  196. data/lib/state_machine/state.rb +18 -10
  197. data/lib/state_machine/state_context.rb +2 -2
  198. data/lib/state_machine/transition.rb +8 -1
  199. data/lib/state_machine/transition_collection.rb +2 -1
  200. data/lib/state_machine/version.rb +1 -1
  201. data/lib/state_machine/yard.rb +8 -0
  202. data/lib/state_machine/yard/handlers.rb +12 -0
  203. data/lib/state_machine/yard/handlers/base.rb +32 -0
  204. data/lib/state_machine/yard/handlers/event.rb +25 -0
  205. data/lib/state_machine/yard/handlers/machine.rb +344 -0
  206. data/lib/state_machine/yard/handlers/state.rb +25 -0
  207. data/lib/state_machine/yard/handlers/transition.rb +47 -0
  208. data/lib/state_machine/yard/templates.rb +3 -0
  209. data/lib/state_machine/yard/templates/default/class/html/setup.rb +30 -0
  210. data/lib/state_machine/yard/templates/default/class/html/state_machines.erb +12 -0
  211. data/lib/tasks/state_machine.rb +2 -1
  212. data/lib/yard-state_machine.rb +2 -0
  213. data/state_machine.gemspec +4 -3
  214. data/test/files/switch.rb +4 -0
  215. data/test/test_helper.rb +5 -0
  216. data/test/unit/branch_test.rb +117 -36
  217. data/test/unit/callback_test.rb +5 -2
  218. data/test/unit/eval_helpers_test.rb +49 -1
  219. data/test/unit/event_collection_test.rb +3 -1
  220. data/test/unit/event_test.rb +182 -12
  221. data/test/unit/graph_test.rb +98 -0
  222. data/test/unit/integrations/active_model_test.rb +82 -12
  223. data/test/unit/integrations/active_record_test.rb +393 -37
  224. data/test/unit/integrations/base_test.rb +7 -2
  225. data/test/unit/integrations/data_mapper_test.rb +326 -72
  226. data/test/unit/integrations/mongo_mapper_test.rb +338 -44
  227. data/test/unit/integrations/mongoid_test.rb +606 -98
  228. data/test/unit/integrations/sequel_test.rb +429 -102
  229. data/test/unit/integrations_test.rb +28 -6
  230. data/test/unit/machine_collection_test.rb +6 -2
  231. data/test/unit/machine_test.rb +134 -82
  232. data/test/unit/node_collection_test.rb +2 -2
  233. data/test/unit/path_test.rb +1 -1
  234. data/test/unit/state_test.rb +65 -21
  235. data/test/unit/transition_collection_test.rb +43 -23
  236. data/test/unit/transition_test.rb +8 -2
  237. metadata +303 -221
  238. data/gemfiles/active_model-3.2.0.gemfile.lock +0 -32
  239. data/gemfiles/active_record-3.2.0.gemfile.lock +0 -43
  240. data/gemfiles/graphviz-0.9.0.gemfile.lock +0 -26
@@ -0,0 +1,12 @@
1
+ module StateMachine
2
+ module YARD
3
+ # YARD custom handlers for integrating the state_machine DSL with the
4
+ # YARD documentation system
5
+ module Handlers
6
+ end
7
+ end
8
+ end
9
+
10
+ Dir["#{File.dirname(__FILE__)}/handlers/*.rb"].sort.each do |path|
11
+ require "state_machine/yard/handlers/#{File.basename(path)}"
12
+ end
@@ -0,0 +1,32 @@
1
+ module StateMachine
2
+ module YARD
3
+ module Handlers
4
+ # Handles and processes nodes
5
+ class Base < ::YARD::Handlers::Ruby::Base
6
+ private
7
+ # Extracts the value from the node as either a string or symbol
8
+ def extract_node_name(ast)
9
+ case ast.type
10
+ when :symbol_literal
11
+ ast.jump(:ident).source.to_sym
12
+ when :string_literal
13
+ ast.jump(:tstring_content).source
14
+ else
15
+ nil
16
+ end
17
+ end
18
+
19
+ # Extracts the values from the node as either strings or symbols.
20
+ # If the node isn't an array, it'll be converted to an array.
21
+ def extract_node_names(ast, convert_to_array = true)
22
+ if [nil, :array].include?(ast.type)
23
+ ast.children.map {|child| extract_node_name(child)}
24
+ else
25
+ node_name = extract_node_name(ast)
26
+ convert_to_array ? [node_name] : node_name
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,25 @@
1
+ module StateMachine
2
+ module YARD
3
+ module Handlers
4
+ # Handles and processes #event
5
+ class Event < Base
6
+ handles method_call(:event)
7
+
8
+ def process
9
+ if owner.is_a?(StateMachine::Machine)
10
+ handler = self
11
+ statement = self.statement
12
+ names = extract_node_names(statement.parameters(false))
13
+
14
+ names.each do |name|
15
+ owner.event(name) do
16
+ # Parse the block
17
+ handler.parse_block(statement.last.last, :owner => self)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,344 @@
1
+ require 'tempfile'
2
+
3
+ module StateMachine
4
+ module YARD
5
+ module Handlers
6
+ # Handles and processes #state_machine
7
+ class Machine < Base
8
+ handles method_call(:state_machine)
9
+ namespace_only
10
+
11
+ # The generated state machine
12
+ attr_reader :machine
13
+
14
+ def process
15
+ # Cross-file storage for state machines
16
+ globals.state_machines ||= Hash.new {|h, k| h[k] = {}}
17
+ namespace['state_machines'] ||= {}
18
+
19
+ # Create new machine
20
+ klass = inherited_machine ? Class.new(inherited_machine.owner_class) : Class.new { extend StateMachine::MacroMethods }
21
+ @machine = klass.state_machine(name, options) {}
22
+
23
+ # Track the state machine
24
+ globals.state_machines[namespace.name][name] = machine
25
+ namespace['state_machines'][name] = {:name => name, :description => statement.docstring}
26
+
27
+ # Parse the block
28
+ parse_block(statement.last.last, :owner => machine)
29
+
30
+ # Draw the machine for reference in the template
31
+ file = Tempfile.new(['state_machine', '.png'])
32
+ begin
33
+ if machine.draw(:name => File.basename(file.path, '.png'), :path => File.dirname(file.path), :orientation => 'landscape')
34
+ namespace['state_machines'][name][:image] = file.read
35
+ end
36
+ ensure
37
+ # Clean up tempfile
38
+ file.close
39
+ file.unlink
40
+ end
41
+
42
+ # Define auto-generated methods
43
+ define_macro_methods
44
+ define_state_methods
45
+ define_event_methods
46
+ end
47
+
48
+ protected
49
+ # Extracts the machine name's
50
+ def name
51
+ @name ||= begin
52
+ ast = statement.parameters.first
53
+ if ast && [:symbol_literal, :string_literal].include?(ast.type)
54
+ extract_node_name(ast)
55
+ else
56
+ :state
57
+ end
58
+ end
59
+ end
60
+
61
+ # Extracts the machine options. Note that this will only extract a
62
+ # subset of the options supported.
63
+ def options
64
+ @options ||= begin
65
+ options = {}
66
+ ast = statement.parameters(false).last
67
+
68
+ if !inherited_machine && ast && ![:symbol_literal, :string_literal].include?(ast.type)
69
+ ast.children.each do |assoc|
70
+ # Only extract important options
71
+ key = extract_node_name(assoc[0])
72
+ next unless [:initial, :attribute, :namespace, :action].include?(key)
73
+
74
+ value = extract_node_name(assoc[1])
75
+ options[key] = value
76
+ end
77
+ end
78
+
79
+ options
80
+ end
81
+ end
82
+
83
+ # Gets the machine that was inherited from a superclass. This also
84
+ # ensures each ancestor has been loaded prior to looking up their definitions.
85
+ def inherited_machine
86
+ @inherited_machine ||= begin
87
+ namespace.inheritance_tree.each do |ancestor|
88
+ begin
89
+ ensure_loaded!(ancestor)
90
+ rescue ::YARD::Handlers::NamespaceMissingError
91
+ # Ignore: just means that we can't access an ancestor
92
+ end
93
+ end
94
+
95
+ # Find the first ancestor that has the machine
96
+ loaded_superclasses.detect do |superclass|
97
+ if superclass != namespace
98
+ machine = globals.state_machines[superclass.name][name]
99
+ break machine if machine
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ # Gets members of this class's superclasses have already been loaded
106
+ # by YARD
107
+ def loaded_superclasses
108
+ namespace.inheritance_tree.select {|ancestor| ancestor.is_a?(::YARD::CodeObjects::ClassObject)}
109
+ end
110
+
111
+ # Gets a list of all attributes for the current class, including those
112
+ # that are inherited
113
+ def instance_attributes
114
+ attributes = {}
115
+ loaded_superclasses.each {|superclass| attributes.merge!(superclass.instance_attributes)}
116
+ attributes
117
+ end
118
+
119
+ # Gets the type of ORM integration being used based on the list of
120
+ # ancestors (including mixins)
121
+ def integration
122
+ @integration ||= Integrations.match_ancestors(namespace.inheritance_tree(true).map {|ancestor| ancestor.path})
123
+ end
124
+
125
+ # Gets the class type being used to define states. Default is "Symbol".
126
+ def state_type
127
+ @state_type ||= machine.states.any? ? machine.states.map {|state| state.name}.compact.first.class.to_s : 'Symbol'
128
+ end
129
+
130
+ # Gets the class type being used to define events. Default is "Symbol".
131
+ def event_type
132
+ @event_type ||= machine.events.any? ? machine.events.first.name.class.to_s : 'Symbol'
133
+ end
134
+
135
+ # Defines auto-generated macro methods for the given machine
136
+ def define_macro_methods
137
+ return if inherited_machine
138
+
139
+ # Human state name lookup
140
+ register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "human_#{machine.attribute(:name)}", :class))
141
+ m.docstring = [
142
+ "Gets the humanized name for the given state.",
143
+ "@param [#{state_type}] state The state to look up",
144
+ "@return [String] The human state name"
145
+ ]
146
+ m.parameters = ["state"]
147
+
148
+ # Human event name lookup
149
+ register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "human_#{machine.attribute(:event_name)}", :class))
150
+ m.docstring = [
151
+ "Gets the humanized name for the given event.",
152
+ "@param [#{event_type}] event The event to look up",
153
+ "@return [String] The human event name"
154
+ ]
155
+ m.parameters = ["event"]
156
+
157
+ # Only register attributes when the accessor isn't explicitly defined
158
+ # by the class / superclass *and* isn't defined by inference from the
159
+ # ORM being used
160
+ unless integration || instance_attributes.include?(machine.attribute.to_sym)
161
+ attribute = machine.attribute
162
+ namespace.attributes[:instance][attribute] = {}
163
+
164
+ # Machine attribute getter
165
+ register(m = ::YARD::CodeObjects::MethodObject.new(namespace, attribute))
166
+ namespace.attributes[:instance][attribute][:read] = m
167
+ m.docstring = [
168
+ "Gets the current attribute value for the machine",
169
+ "@return The attribute value"
170
+ ]
171
+
172
+ # Machine attribute setter
173
+ register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "#{attribute}="))
174
+ namespace.attributes[:instance][attribute][:write] = m
175
+ m.docstring = [
176
+ "Sets the current value for the machine",
177
+ "@param new_#{attribute} The new value to set"
178
+ ]
179
+ m.parameters = ["new_#{attribute}"]
180
+ end
181
+
182
+ if integration && integration.defaults[:action] && !options.include?(:action) || options[:action]
183
+ attribute = "#{machine.name}_event"
184
+ namespace.attributes[:instance][attribute] = {}
185
+
186
+ # Machine event attribute getter
187
+ register(m = ::YARD::CodeObjects::MethodObject.new(namespace, attribute))
188
+ namespace.attributes[:instance][attribute][:read] = m
189
+ m.docstring = [
190
+ "Gets the current event attribute value for the machine",
191
+ "@return The event attribute value"
192
+ ]
193
+
194
+ # Machine event attribute setter
195
+ register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "#{attribute}="))
196
+ namespace.attributes[:instance][attribute][:write] = m
197
+ m.docstring = [
198
+ "Sets the current value for the machine",
199
+ "@param new_#{attribute} The new value to set"
200
+ ]
201
+ m.parameters = ["new_#{attribute}"]
202
+ end
203
+
204
+ # Presence query
205
+ register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "#{machine.name}?"))
206
+ m.docstring = [
207
+ "Checks the given state name against the current state.",
208
+ "@param [#{state_type}] state_name The name of the state to check",
209
+ "@return [Boolean] True if they are the same state, otherwise false",
210
+ "@raise [IndexError] If the state name is invalid"
211
+ ]
212
+ m.parameters = ["state_name"]
213
+
214
+ # Internal state name
215
+ register(m = ::YARD::CodeObjects::MethodObject.new(namespace, machine.attribute(:name)))
216
+ m.docstring = [
217
+ "Gets the internal name of the state for the current value.",
218
+ "@return [#{state_type}] The internal name of the state"
219
+ ]
220
+
221
+ # Human state name
222
+ register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "human_#{machine.attribute(:name)}"))
223
+ m.docstring = [
224
+ "Gets the human-readable name of the state for the current value.",
225
+ "@return [String] The human-readable state name"
226
+ ]
227
+
228
+ # Available events
229
+ register(m = ::YARD::CodeObjects::MethodObject.new(namespace, machine.attribute(:events)))
230
+ m.docstring = [
231
+ "Gets the list of events that can be fired on the current #{machine.name} (uses the *unqualified* event names)",
232
+ "@param [Hash] requirements The transition requirements to test against",
233
+ "@option requirements [#{state_type}] :from (the current state) One or more initial states",
234
+ "@option requirements [#{state_type}] :to One or more target states",
235
+ "@option requirements [#{event_type}] :on One or more events that fire the transition",
236
+ "@option requirements [Boolean] :guard Whether to guard transitions with conditionals",
237
+ "@return [Array<#{event_type}>] The list of event names"
238
+ ]
239
+ m.parameters = [["requirements", "{}"]]
240
+
241
+ # Available transitions
242
+ register(m = ::YARD::CodeObjects::MethodObject.new(namespace, machine.attribute(:transitions)))
243
+ m.docstring = [
244
+ "Gets the list of transitions that can be made for the current #{machine.name}",
245
+ "@param [Hash] requirements The transition requirements to test against",
246
+ "@option requirements [#{state_type}] :from (the current state) One or more initial states",
247
+ "@option requirements [#{state_type}] :to One or more target states",
248
+ "@option requirements [#{event_type}] :on One or more events that fire the transition",
249
+ "@option requirements [Boolean] :guard Whether to guard transitions with conditionals",
250
+ "@return [Array<StateMachine::Transition>] The available transitions"
251
+ ]
252
+ m.parameters = [["requirements", "{}"]]
253
+
254
+ # Available transition paths
255
+ register(m = ::YARD::CodeObjects::MethodObject.new(namespace, machine.attribute(:paths)))
256
+ m.docstring = [
257
+ "Gets the list of sequences of transitions that can be run for the current #{machine.name}",
258
+ "@param [Hash] requirements The transition requirements to test against",
259
+ "@option requirements [#{state_type}] :from (the current state) The initial state",
260
+ "@option requirements [#{state_type}] :to The target state",
261
+ "@option requirements [Boolean] :deep Whether to enable deep searches for the target state",
262
+ "@option requirements [Boolean] :guard Whether to guard transitions with conditionals",
263
+ "@return [StateMachine::PathCollection] The collection of paths"
264
+ ]
265
+ m.parameters = [["requirements", "{}"]]
266
+
267
+ # Generic event fire
268
+ register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "fire_#{machine.attribute(:event)}"))
269
+ m.docstring = [
270
+ "Fires an arbitrary #{machine.name} event with the given argument list",
271
+ "@param [#{event_type}] event The name of the event to fire",
272
+ "@param args Optional arguments to include in the transition",
273
+ "@return [Boolean] +true+ if the event succeeds, otherwise +false+"
274
+ ]
275
+ m.parameters = ["event", "*args"]
276
+ end
277
+
278
+ # Defines auto-generated event methods for the given machine
279
+ def define_event_methods
280
+ machine.events.each do |event|
281
+ next if inherited_machine && inherited_machine.events[event.name]
282
+
283
+ # Event query
284
+ register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "can_#{event.qualified_name}?"))
285
+ m.docstring = [
286
+ "Checks whether #{event.name.inspect} can be fired.",
287
+ "@param [Hash] requirements The transition requirements to test against",
288
+ "@option requirements [#{state_type}] :from (the current state) One or more initial states",
289
+ "@option requirements [#{state_type}] :to One or more target states",
290
+ "@option requirements [Boolean] :guard Whether to guard transitions with conditionals",
291
+ "@return [Boolean] +true+ if #{event.name.inspect} can be fired, otherwise +false+"
292
+ ]
293
+ m.parameters = [["requirements", "{}"]]
294
+
295
+ # Event transition
296
+ register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "#{event.qualified_name}_transition"))
297
+ m.docstring = [
298
+ "Gets the next transition that would be performed if #{event.name.inspect} were to be fired.",
299
+ "@param [Hash] requirements The transition requirements to test against",
300
+ "@option requirements [#{state_type}] :from (the current state) One or more initial states",
301
+ "@option requirements [#{state_type}] :to One or more target states",
302
+ "@option requirements [Boolean] :guard Whether to guard transitions with conditionals",
303
+ "@return [StateMachine::Transition] The transition that would be performed or +nil+"
304
+ ]
305
+ m.parameters = [["requirements", "{}"]]
306
+
307
+ # Fire event
308
+ register(m = ::YARD::CodeObjects::MethodObject.new(namespace, event.qualified_name))
309
+ m.docstring = [
310
+ "Fires the #{event.name.inspect} event.",
311
+ "@param [Array] args Optional arguments to include in transition callbacks",
312
+ "@return [Boolean] +true+ if the transition succeeds, otherwise +false+"
313
+ ]
314
+ m.parameters = ["*args"]
315
+
316
+ # Fire event (raises exception)
317
+ register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "#{event.qualified_name}!"))
318
+ m.docstring = [
319
+ "Fires the #{event.name.inspect} event, raising an exception if it fails.",
320
+ "@param [Array] args Optional arguments to include in transition callbacks",
321
+ "@return [Boolean] +true+ if the transition succeeds",
322
+ "@raise [StateMachine::InvalidTransition] If the transition fails"
323
+ ]
324
+ m.parameters = ["*args"]
325
+ end
326
+ end
327
+
328
+ # Defines auto-generated state methods for the given machine
329
+ def define_state_methods
330
+ machine.states.each do |state|
331
+ next if inherited_machine && inherited_machine.states[state.name] || !state.name
332
+
333
+ # State query
334
+ register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "#{state.qualified_name}?"))
335
+ m.docstring = [
336
+ "Checks whether #{state.name.inspect} is the current state.",
337
+ "@return [Boolean] +true+ if this is the current state, otherwise +false+"
338
+ ]
339
+ end
340
+ end
341
+ end
342
+ end
343
+ end
344
+ end
@@ -0,0 +1,25 @@
1
+ module StateMachine
2
+ module YARD
3
+ module Handlers
4
+ # Handles and processes #state
5
+ class State < Base
6
+ handles method_call(:state)
7
+
8
+ def process
9
+ if owner.is_a?(StateMachine::Machine)
10
+ handler = self
11
+ statement = self.statement
12
+ names = extract_node_names(statement.parameters(false))
13
+
14
+ names.each do |name|
15
+ owner.state(name) do
16
+ # Parse the block
17
+ handler.parse_block(statement.last.last, :owner => self)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,47 @@
1
+ module StateMachine
2
+ module YARD
3
+ module Handlers
4
+ # Handles and processes #transition
5
+ class Transition < Base
6
+ handles method_call(:transition)
7
+
8
+ def process
9
+ if [StateMachine::Machine, StateMachine::Event, StateMachine::State].include?(owner.class)
10
+ options = {}
11
+
12
+ # Extract requirements
13
+ ast = statement.parameters.first
14
+ ast.children.each do |assoc|
15
+ # Skip conditionals
16
+ next if %w(if unless).include?(assoc[0].jump(:ident).source)
17
+
18
+ options[extract_requirement(assoc[0])] = extract_requirement(assoc[1])
19
+ end
20
+
21
+ owner.transition(options)
22
+ end
23
+ end
24
+
25
+ private
26
+ # Extracts the statement requirement from the given node
27
+ def extract_requirement(ast)
28
+ case ast.type
29
+ when :symbol_literal, :string_literal, :array
30
+ extract_node_names(ast, false)
31
+ when :binary
32
+ AllMatcher.instance - extract_node_names(ast.children.last)
33
+ when :var_ref, :vcall
34
+ case ast.source
35
+ when 'nil'
36
+ nil
37
+ when 'same'
38
+ LoopbackMatcher.instance
39
+ else
40
+ AllMatcher.instance
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end