state_machines-yard 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.
- checksums.yaml +7 -0
- data/.gitignore +21 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +23 -0
- data/README.md +26 -0
- data/Rakefile +1 -0
- data/lib/state_machines/yard.rb +10 -0
- data/lib/state_machines/yard/handlers.rb +12 -0
- data/lib/state_machines/yard/handlers/base.rb +34 -0
- data/lib/state_machines/yard/handlers/event.rb +25 -0
- data/lib/state_machines/yard/handlers/machine.rb +342 -0
- data/lib/state_machines/yard/handlers/state.rb +25 -0
- data/lib/state_machines/yard/handlers/transition.rb +47 -0
- data/lib/state_machines/yard/templates.rb +1 -0
- data/lib/state_machines/yard/templates/default/class/html/setup.rb +28 -0
- data/lib/state_machines/yard/templates/default/class/html/state_machines.erb +12 -0
- data/lib/state_machines/yard/version.rb +5 -0
- data/state_machines-yard.gemspec +25 -0
- metadata +119 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ae4aeccc3211632b5b25f19bfa30b576adfcc4fc
|
4
|
+
data.tar.gz: 797a207f6c1a883619c089148ab7d76fa4c5640c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3bd4c0b651acab5ea3469ec81e33a78209ebbec1f81c84adc89159fa42cba8b97178570d662223388855c7cbe8493c65a84c9cc0d9ffcdedbcd4d063939065d2
|
7
|
+
data.tar.gz: f027b5799f58e0a5a43d1910013ee1d55d8d0eb0af5ae75ea57c39b7e90e5ba19139c6f486bb5b092841ebabae7b811d31344dc32fdd2e4cba840288b5fba72b
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (c) 2006-2012 Aaron Pfeifer
|
2
|
+
Copyright (c) 2014 Abdelkader Boudih
|
3
|
+
|
4
|
+
MIT License
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
7
|
+
a copy of this software and associated documentation files (the
|
8
|
+
"Software"), to deal in the Software without restriction, including
|
9
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
10
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
11
|
+
permit persons to whom the Software is furnished to do so, subject to
|
12
|
+
the following conditions:
|
13
|
+
|
14
|
+
The above copyright notice and this permission notice shall be
|
15
|
+
included in all copies or substantial portions of the Software.
|
16
|
+
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
18
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
19
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
20
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
21
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
22
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
23
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# State Machines YARD plugin
|
2
|
+
|
3
|
+
State machines YARD plugin for automated documentation
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'state_machines-yard', group: :development
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install state_machines-yard
|
18
|
+
|
19
|
+
|
20
|
+
## Contributing
|
21
|
+
|
22
|
+
1. Fork it ( https://github.com/seuros/state_machines-yard/fork )
|
23
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
24
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
25
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
26
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
@@ -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_machines/yard/handlers/#{File.basename(path)}"
|
12
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module StateMachine
|
2
|
+
module YARD
|
3
|
+
module Handlers
|
4
|
+
# Handles and processes nodes
|
5
|
+
class Base < ::YARD::Handlers::Ruby::Base
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
# Extracts the value from the node as either a string or symbol
|
10
|
+
def extract_node_name(ast)
|
11
|
+
case ast.type
|
12
|
+
when :symbol_literal
|
13
|
+
ast.jump(:ident).source.to_sym
|
14
|
+
when :string_literal
|
15
|
+
ast.jump(:tstring_content).source
|
16
|
+
else
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Extracts the values from the node as either strings or symbols.
|
22
|
+
# If the node isn't an array, it'll be converted to an array.
|
23
|
+
def extract_node_names(ast, convert_to_array = true)
|
24
|
+
if [nil, :array].include?(ast.type)
|
25
|
+
ast.children.map { |child| extract_node_name(child) }
|
26
|
+
else
|
27
|
+
node_name = extract_node_name(ast)
|
28
|
+
convert_to_array ? [node_name] : node_name
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
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?(StateMachines::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,342 @@
|
|
1
|
+
module StateMachine
|
2
|
+
module YARD
|
3
|
+
module Handlers
|
4
|
+
# Handles and processes #state_machine
|
5
|
+
class Machine < Base
|
6
|
+
handles method_call(:state_machine)
|
7
|
+
namespace_only
|
8
|
+
|
9
|
+
# The generated state machine
|
10
|
+
attr_reader :machine
|
11
|
+
|
12
|
+
def process
|
13
|
+
# Cross-file storage for state machines
|
14
|
+
globals.state_machines ||= Hash.new { |h, k| h[k] = {} }
|
15
|
+
namespace['state_machines'] ||= {}
|
16
|
+
|
17
|
+
# Create new machine
|
18
|
+
klass = inherited_machine ? Class.new(inherited_machine.owner_class) : Class.new { extend StateMachines::MacroMethods }
|
19
|
+
@machine = klass.state_machine(name, options) {}
|
20
|
+
|
21
|
+
# Track the state machine
|
22
|
+
globals.state_machines[namespace.name][name] = machine
|
23
|
+
namespace['state_machines'][name] = {name: name, description: statement.docstring}
|
24
|
+
|
25
|
+
# Parse the block
|
26
|
+
parse_block(statement.last.last, owner: machine)
|
27
|
+
|
28
|
+
# Draw the machine for reference in the template
|
29
|
+
file = Tempfile.new(['state_machine', '.png'])
|
30
|
+
begin
|
31
|
+
if machine.draw(name: File.basename(file.path, '.png'), path: File.dirname(file.path), orientation: 'landscape')
|
32
|
+
namespace['state_machines'][name][:image] = file.read
|
33
|
+
end
|
34
|
+
ensure
|
35
|
+
# Clean up tempfile
|
36
|
+
file.close
|
37
|
+
file.unlink
|
38
|
+
end
|
39
|
+
|
40
|
+
# Define auto-generated methods
|
41
|
+
define_macro_methods
|
42
|
+
define_state_methods
|
43
|
+
define_event_methods
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
# Extracts the machine name's
|
48
|
+
def name
|
49
|
+
@name ||= begin
|
50
|
+
ast = statement.parameters.first
|
51
|
+
if ast && [:symbol_literal, :string_literal].include?(ast.type)
|
52
|
+
extract_node_name(ast)
|
53
|
+
else
|
54
|
+
:state
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Extracts the machine options. Note that this will only extract a
|
60
|
+
# subset of the options supported.
|
61
|
+
def options
|
62
|
+
@options ||= begin
|
63
|
+
options = {}
|
64
|
+
ast = statement.parameters(false).last
|
65
|
+
|
66
|
+
if !inherited_machine && ast && ![:symbol_literal, :string_literal].include?(ast.type)
|
67
|
+
ast.children.each do |assoc|
|
68
|
+
# Only extract important options
|
69
|
+
key = extract_node_name(assoc[0])
|
70
|
+
next unless [:initial, :attribute, :namespace, :action].include?(key)
|
71
|
+
|
72
|
+
value = extract_node_name(assoc[1])
|
73
|
+
options[key] = value
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
options
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# Gets the machine that was inherited from a superclass. This also
|
82
|
+
# ensures each ancestor has been loaded prior to looking up their definitions.
|
83
|
+
def inherited_machine
|
84
|
+
@inherited_machine ||= begin
|
85
|
+
namespace.inheritance_tree.each do |ancestor|
|
86
|
+
begin
|
87
|
+
ensure_loaded!(ancestor)
|
88
|
+
rescue ::YARD::Handlers::NamespaceMissingError
|
89
|
+
# Ignore: just means that we can't access an ancestor
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Find the first ancestor that has the machine
|
94
|
+
loaded_superclasses.find do |superclass|
|
95
|
+
if superclass != namespace
|
96
|
+
machine = globals.state_machines[superclass.name][name]
|
97
|
+
break machine if machine
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Gets members of this class's superclasses have already been loaded
|
104
|
+
# by YARD
|
105
|
+
def loaded_superclasses
|
106
|
+
namespace.inheritance_tree.select { |ancestor| ancestor.is_a?(::YARD::CodeObjects::ClassObject) }
|
107
|
+
end
|
108
|
+
|
109
|
+
# Gets a list of all attributes for the current class, including those
|
110
|
+
# that are inherited
|
111
|
+
def instance_attributes
|
112
|
+
attributes = {}
|
113
|
+
loaded_superclasses.each { |superclass| attributes.merge!(superclass.instance_attributes) }
|
114
|
+
attributes
|
115
|
+
end
|
116
|
+
|
117
|
+
# Gets the type of ORM integration being used based on the list of
|
118
|
+
# ancestors (including mixins)
|
119
|
+
def integration
|
120
|
+
@integration ||= Integrations.match_ancestors(namespace.inheritance_tree(true).map { |ancestor| ancestor.path })
|
121
|
+
end
|
122
|
+
|
123
|
+
# Gets the class type being used to define states. Default is "Symbol".
|
124
|
+
def state_type
|
125
|
+
@state_type ||= machine.states.any? ? machine.states.map { |state| state.name }.compact.first.class.to_s : 'Symbol'
|
126
|
+
end
|
127
|
+
|
128
|
+
# Gets the class type being used to define events. Default is "Symbol".
|
129
|
+
def event_type
|
130
|
+
@event_type ||= machine.events.any? ? machine.events.first.name.class.to_s : 'Symbol'
|
131
|
+
end
|
132
|
+
|
133
|
+
# Defines auto-generated macro methods for the given machine
|
134
|
+
def define_macro_methods
|
135
|
+
return if inherited_machine
|
136
|
+
|
137
|
+
# Human state name lookup
|
138
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "human_#{machine.attribute(:name)}", :class))
|
139
|
+
m.docstring = [
|
140
|
+
'Gets the humanized name for the given state.',
|
141
|
+
"@param [#{state_type}] state The state to look up",
|
142
|
+
'@return [String] The human state name'
|
143
|
+
]
|
144
|
+
m.parameters = ['state']
|
145
|
+
|
146
|
+
# Human event name lookup
|
147
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "human_#{machine.attribute(:event_name)}", :class))
|
148
|
+
m.docstring = [
|
149
|
+
'Gets the humanized name for the given event.',
|
150
|
+
"@param [#{event_type}] event The event to look up",
|
151
|
+
'@return [String] The human event name'
|
152
|
+
]
|
153
|
+
m.parameters = ['event']
|
154
|
+
|
155
|
+
# Only register attributes when the accessor isn't explicitly defined
|
156
|
+
# by the class / superclass *and* isn't defined by inference from the
|
157
|
+
# ORM being used
|
158
|
+
unless integration || instance_attributes.include?(machine.attribute.to_sym)
|
159
|
+
attribute = machine.attribute
|
160
|
+
namespace.attributes[:instance][attribute] = {}
|
161
|
+
|
162
|
+
# Machine attribute getter
|
163
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, attribute))
|
164
|
+
namespace.attributes[:instance][attribute][:read] = m
|
165
|
+
m.docstring = [
|
166
|
+
'Gets the current attribute value for the machine',
|
167
|
+
'@return The attribute value'
|
168
|
+
]
|
169
|
+
|
170
|
+
# Machine attribute setter
|
171
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "#{attribute}="))
|
172
|
+
namespace.attributes[:instance][attribute][:write] = m
|
173
|
+
m.docstring = [
|
174
|
+
'Sets the current value for the machine',
|
175
|
+
"@param new_#{attribute} The new value to set"
|
176
|
+
]
|
177
|
+
m.parameters = ["new_#{attribute}"]
|
178
|
+
end
|
179
|
+
|
180
|
+
if integration && integration.defaults[:action] && !options.include?(:action) || options[:action]
|
181
|
+
attribute = "#{machine.name}_event"
|
182
|
+
namespace.attributes[:instance][attribute] = {}
|
183
|
+
|
184
|
+
# Machine event attribute getter
|
185
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, attribute))
|
186
|
+
namespace.attributes[:instance][attribute][:read] = m
|
187
|
+
m.docstring = [
|
188
|
+
'Gets the current event attribute value for the machine',
|
189
|
+
'@return The event attribute value'
|
190
|
+
]
|
191
|
+
|
192
|
+
# Machine event attribute setter
|
193
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "#{attribute}="))
|
194
|
+
namespace.attributes[:instance][attribute][:write] = m
|
195
|
+
m.docstring = [
|
196
|
+
'Sets the current value for the machine',
|
197
|
+
"@param new_#{attribute} The new value to set"
|
198
|
+
]
|
199
|
+
m.parameters = ["new_#{attribute}"]
|
200
|
+
end
|
201
|
+
|
202
|
+
# Presence query
|
203
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "#{machine.name}?"))
|
204
|
+
m.docstring = [
|
205
|
+
'Checks the given state name against the current state.',
|
206
|
+
"@param [#{state_type}] state_name The name of the state to check",
|
207
|
+
'@return [Boolean] True if they are the same state, otherwise false',
|
208
|
+
'@raise [IndexError] If the state name is invalid'
|
209
|
+
]
|
210
|
+
m.parameters = ['state_name']
|
211
|
+
|
212
|
+
# Internal state name
|
213
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, machine.attribute(:name)))
|
214
|
+
m.docstring = [
|
215
|
+
'Gets the internal name of the state for the current value.',
|
216
|
+
"@return [#{state_type}] The internal name of the state"
|
217
|
+
]
|
218
|
+
|
219
|
+
# Human state name
|
220
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "human_#{machine.attribute(:name)}"))
|
221
|
+
m.docstring = [
|
222
|
+
'Gets the human-readable name of the state for the current value.',
|
223
|
+
'@return [String] The human-readable state name'
|
224
|
+
]
|
225
|
+
|
226
|
+
# Available events
|
227
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, machine.attribute(:events)))
|
228
|
+
m.docstring = [
|
229
|
+
"Gets the list of events that can be fired on the current #{machine.name} (uses the *unqualified* event names)",
|
230
|
+
'@param [Hash] requirements The transition requirements to test against',
|
231
|
+
"@option requirements [#{state_type}] :from (the current state) One or more initial states",
|
232
|
+
"@option requirements [#{state_type}] :to One or more target states",
|
233
|
+
"@option requirements [#{event_type}] :on One or more events that fire the transition",
|
234
|
+
'@option requirements [Boolean] :guard Whether to guard transitions with conditionals',
|
235
|
+
"@return [Array<#{event_type}>] The list of event names"
|
236
|
+
]
|
237
|
+
m.parameters = [['requirements', '{}']]
|
238
|
+
|
239
|
+
# Available transitions
|
240
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, machine.attribute(:transitions)))
|
241
|
+
m.docstring = [
|
242
|
+
"Gets the list of transitions that can be made for the current #{machine.name}",
|
243
|
+
'@param [Hash] requirements The transition requirements to test against',
|
244
|
+
"@option requirements [#{state_type}] :from (the current state) One or more initial states",
|
245
|
+
"@option requirements [#{state_type}] :to One or more target states",
|
246
|
+
"@option requirements [#{event_type}] :on One or more events that fire the transition",
|
247
|
+
'@option requirements [Boolean] :guard Whether to guard transitions with conditionals',
|
248
|
+
'@return [Array<StateMachines::Transition>] The available transitions'
|
249
|
+
]
|
250
|
+
m.parameters = [['requirements', '{}']]
|
251
|
+
|
252
|
+
# Available transition paths
|
253
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, machine.attribute(:paths)))
|
254
|
+
m.docstring = [
|
255
|
+
"Gets the list of sequences of transitions that can be run for the current #{machine.name}",
|
256
|
+
'@param [Hash] requirements The transition requirements to test against',
|
257
|
+
"@option requirements [#{state_type}] :from (the current state) The initial state",
|
258
|
+
"@option requirements [#{state_type}] :to The target state",
|
259
|
+
'@option requirements [Boolean] :deep Whether to enable deep searches for the target state',
|
260
|
+
'@option requirements [Boolean] :guard Whether to guard transitions with conditionals',
|
261
|
+
'@return [StateMachines::PathCollection] The collection of paths'
|
262
|
+
]
|
263
|
+
m.parameters = [['requirements', '{}']]
|
264
|
+
|
265
|
+
# Generic event fire
|
266
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "fire_#{machine.attribute(:event)}"))
|
267
|
+
m.docstring = [
|
268
|
+
"Fires an arbitrary #{machine.name} event with the given argument list",
|
269
|
+
"@param [#{event_type}] event The name of the event to fire",
|
270
|
+
'@param args Optional arguments to include in the transition',
|
271
|
+
'@return [Boolean] +true+ if the event succeeds, otherwise +false+'
|
272
|
+
]
|
273
|
+
m.parameters = ['event', '*args']
|
274
|
+
end
|
275
|
+
|
276
|
+
# Defines auto-generated event methods for the given machine
|
277
|
+
def define_event_methods
|
278
|
+
machine.events.each do |event|
|
279
|
+
next if inherited_machine && inherited_machine.events[event.name]
|
280
|
+
|
281
|
+
# Event query
|
282
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "can_#{event.qualified_name}?"))
|
283
|
+
m.docstring = [
|
284
|
+
"Checks whether #{event.name.inspect} can be fired.",
|
285
|
+
'@param [Hash] requirements The transition requirements to test against',
|
286
|
+
"@option requirements [#{state_type}] :from (the current state) One or more initial states",
|
287
|
+
"@option requirements [#{state_type}] :to One or more target states",
|
288
|
+
'@option requirements [Boolean] :guard Whether to guard transitions with conditionals',
|
289
|
+
"@return [Boolean] +true+ if #{event.name.inspect} can be fired, otherwise +false+"
|
290
|
+
]
|
291
|
+
m.parameters = [['requirements', '{}']]
|
292
|
+
|
293
|
+
# Event transition
|
294
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "#{event.qualified_name}_transition"))
|
295
|
+
m.docstring = [
|
296
|
+
"Gets the next transition that would be performed if #{event.name.inspect} were to be fired.",
|
297
|
+
'@param [Hash] requirements The transition requirements to test against',
|
298
|
+
"@option requirements [#{state_type}] :from (the current state) One or more initial states",
|
299
|
+
"@option requirements [#{state_type}] :to One or more target states",
|
300
|
+
'@option requirements [Boolean] :guard Whether to guard transitions with conditionals',
|
301
|
+
'@return [StateMachines::Transition] The transition that would be performed or +nil+'
|
302
|
+
]
|
303
|
+
m.parameters = [['requirements', '{}']]
|
304
|
+
|
305
|
+
# Fire event
|
306
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, event.qualified_name))
|
307
|
+
m.docstring = [
|
308
|
+
"Fires the #{event.name.inspect} event.",
|
309
|
+
'@param [Array] args Optional arguments to include in transition callbacks',
|
310
|
+
'@return [Boolean] +true+ if the transition succeeds, otherwise +false+'
|
311
|
+
]
|
312
|
+
m.parameters = ['*args']
|
313
|
+
|
314
|
+
# Fire event (raises exception)
|
315
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "#{event.qualified_name}!"))
|
316
|
+
m.docstring = [
|
317
|
+
"Fires the #{event.name.inspect} event, raising an exception if it fails.",
|
318
|
+
'@param [Array] args Optional arguments to include in transition callbacks',
|
319
|
+
'@return [Boolean] +true+ if the transition succeeds',
|
320
|
+
'@raise [StateMachines::InvalidTransition] If the transition fails'
|
321
|
+
]
|
322
|
+
m.parameters = ['*args']
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
# Defines auto-generated state methods for the given machine
|
327
|
+
def define_state_methods
|
328
|
+
machine.states.each do |state|
|
329
|
+
next if inherited_machine && inherited_machine.states[state.name] || !state.name
|
330
|
+
|
331
|
+
# State query
|
332
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "#{state.qualified_name}?"))
|
333
|
+
m.docstring = [
|
334
|
+
"Checks whether #{state.name.inspect} is the current state.",
|
335
|
+
'@return [Boolean] +true+ if this is the current state, otherwise +false+'
|
336
|
+
]
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
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?(StateMachines::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 [StateMachines::Machine, StateMachines::Event, StateMachines::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
|
@@ -0,0 +1 @@
|
|
1
|
+
YARD::Templates::Engine.register_template_path File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Define where state machine descriptions will be rendered
|
2
|
+
def init
|
3
|
+
super
|
4
|
+
sections.place(:state_machine_details).before(:children)
|
5
|
+
end
|
6
|
+
|
7
|
+
# Renders state machine details in the main content of the class's documentation
|
8
|
+
def state_machine_details
|
9
|
+
erb(:state_machines) if state_machines
|
10
|
+
end
|
11
|
+
|
12
|
+
# Gets a list of state machines for this class
|
13
|
+
def state_machines
|
14
|
+
@state_machines ||= begin
|
15
|
+
if state_machines = object['state_machines']
|
16
|
+
state_machines.each do |_name, machine|
|
17
|
+
serializer.serialize(state_machine_image_path(machine), machine[:image]) if machine[:image]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Generates the image path for the given machine's visualization
|
24
|
+
def state_machine_image_path(machine)
|
25
|
+
base_path = File.dirname(serializer.serialized_path(object))
|
26
|
+
image_name = "#{object.name}_#{machine[:name]}"
|
27
|
+
"#{File.join(base_path, image_name)}.png"
|
28
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
<h2>State Machines</h2>
|
2
|
+
|
3
|
+
This class contains <%= state_machines.count %> state machine(s).
|
4
|
+
|
5
|
+
<% state_machines.each do |name, machine| %>
|
6
|
+
<h3><%= h(machine[:name]) %></h3>
|
7
|
+
<p><%= h(machine[:description]) %></p>
|
8
|
+
|
9
|
+
<% if machine[:image] %>
|
10
|
+
<img alt="State machine diagram for <%= h(machine[:name]) %>" src="<%= url_for(state_machine_image_path(machine)) %>"/>
|
11
|
+
<% end %>
|
12
|
+
<% end %>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'state_machines/yard/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'state_machines-yard'
|
8
|
+
spec.version = StateMachines::Yard::VERSION
|
9
|
+
spec.authors = ['Abdelkader Boudih', 'Aaron Pfeifer']
|
10
|
+
spec.email = ['terminale@gmail.com']
|
11
|
+
spec.summary = %q(State machines YARD plugin)
|
12
|
+
spec.description = %q(State machines YARD plugin for automated documentation)
|
13
|
+
spec.homepage = 'https://github.com/seuros/state_machines-yard'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.required_ruby_version = '>= 1.9.3'
|
17
|
+
|
18
|
+
spec.files = `git ls-files -z`.split("\x0")
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_dependency 'yard'
|
22
|
+
spec.add_dependency 'state_machines-graphviz'
|
23
|
+
spec.add_development_dependency 'bundler'
|
24
|
+
spec.add_development_dependency 'rake'
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: state_machines-yard
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Abdelkader Boudih
|
8
|
+
- Aaron Pfeifer
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-05-03 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: yard
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: state_machines-graphviz
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ">="
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: bundler
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '0'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rake
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
description: State machines YARD plugin for automated documentation
|
71
|
+
email:
|
72
|
+
- terminale@gmail.com
|
73
|
+
executables: []
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- ".gitignore"
|
78
|
+
- Gemfile
|
79
|
+
- LICENSE.txt
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- lib/state_machines/yard.rb
|
83
|
+
- lib/state_machines/yard/handlers.rb
|
84
|
+
- lib/state_machines/yard/handlers/base.rb
|
85
|
+
- lib/state_machines/yard/handlers/event.rb
|
86
|
+
- lib/state_machines/yard/handlers/machine.rb
|
87
|
+
- lib/state_machines/yard/handlers/state.rb
|
88
|
+
- lib/state_machines/yard/handlers/transition.rb
|
89
|
+
- lib/state_machines/yard/templates.rb
|
90
|
+
- lib/state_machines/yard/templates/default/class/html/setup.rb
|
91
|
+
- lib/state_machines/yard/templates/default/class/html/state_machines.erb
|
92
|
+
- lib/state_machines/yard/version.rb
|
93
|
+
- state_machines-yard.gemspec
|
94
|
+
homepage: https://github.com/seuros/state_machines-yard
|
95
|
+
licenses:
|
96
|
+
- MIT
|
97
|
+
metadata: {}
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: 1.9.3
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 2.2.2
|
115
|
+
signing_key:
|
116
|
+
specification_version: 4
|
117
|
+
summary: State machines YARD plugin
|
118
|
+
test_files: []
|
119
|
+
has_rdoc:
|