apollo 1.0.0 → 1.1.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.
@@ -0,0 +1,57 @@
1
+ Apollo Changelog
2
+ ================
3
+
4
+ 1.1
5
+
6
+ * Added state sets. (7498cf0)
7
+ * Unbranded methods. E.g. `Apollo::apollo` -> `Apollo::state_machine` (f8b42b8)
8
+
9
+ 1.0
10
+ ---
11
+ This is an initial release of the Apollo fork from [Workflow](http://github.com/geekq/workflow). The reason I decided to fork Workflow, and in particular change the name, is because I feel that Workflow is an excellent starting point for my needs; however, my desire for Apollo is somewhat different from the desires of the creators of Workflow. **In no way is my fork (and renaming) intended to be any kind of insult or intellectual rights infringement.** I chose Workflow for the basis of Apollo because I feel it is the **best** state machine gem available. I will most certainly maintain the original MIT-LICENSE as apart of Apollo so long as *any* portion of the code is derived from Workflow (which I suspect will always be the case).
12
+
13
+ * Rebranded modules from "workflow" to "apollo".
14
+ * Using [Jeweler](http://github.com/technicalpickles/jeweler).
15
+ * Removed unnecessary workflow.rb "initializer" file from root of project. (f2ac52a)
16
+ * Extracted Workflow submodules and classes into separated files. (6d4e90f)
17
+ * Changed default "column" name from `workflow_state` to `current_state`. (f562e71)
18
+ * Require reason for `halt` and allow arbitrary exception for `halt!`. (0f903d7)
19
+
20
+ Original Workflow Changelog
21
+ ===========================
22
+
23
+ ### New in the version 0.4.0
24
+
25
+ * completely rewritten the documentation to match my branch. Every
26
+ described feature is backed up by an automated test.
27
+
28
+ ### New in the version 0.3.0
29
+
30
+ Intermixing of transition graph definition (states, transitions)
31
+ on the one side and implementation of the actions on the other side
32
+ for a bigger state machine can introduce clutter.
33
+
34
+ To reduce this clutter it is now possible to use state entry- and
35
+ exit- hooks defined through a naming convention. For example, if there
36
+ is a state :pending, then instead of using a
37
+ block:
38
+
39
+ state :pending do
40
+ on_entry do
41
+ # your implementation here
42
+ end
43
+ end
44
+
45
+ you can hook in by defining method
46
+
47
+ def on_pending_exit(new_state, event, *args)
48
+ # your implementation here
49
+ end
50
+
51
+ anywhere in your class. You can also use a simpler function signature
52
+ like `def on_pending_exit(*args)` if your are not interested in
53
+ arguments. Please note: `def on_pending_exit()` with an empty list
54
+ would not work.
55
+
56
+ If both a function with a name according to naming convention and the
57
+ on_entry/on_exit block are given, then only on_entry/on_exit block is used.
@@ -8,7 +8,38 @@ What is apollo?
8
8
  > poetry, and the arts; and more. Apollo is the son of Zeus and Leto, and has a twin
9
9
  > sister, the chaste huntress Artemis. [Wikipedia: Dionysus (2010/04/23)](http://en.wikipedia.org/wiki/Apollo)
10
10
 
11
- Apollo is an fork of workflow.
11
+ Apollo is an fork of [Workflow](http://github.com/geekq/workflow).
12
+
13
+ Resources
14
+ ---------
15
+
16
+ * [Github Project](http://github.com/tekwiz/apollo)
17
+ * [Rdocs on Rdoc.info](http://rdoc.info/projects/tekwiz/apollo)
18
+ * [Wiki on Github](http://wiki.github.com/tekwiz/apollo/)
19
+ * [Issues Tracker on Github](http://github.com/tekwiz/apollo/issues)
20
+ * [Metrics on Caliper](http://getcaliper.com/caliper/project?repo=http%3A%2F%2Frubygems.org%2Fgems%2Fapollo)
21
+
22
+ Features & Issues
23
+ -----------------
24
+
25
+ This is a brand new project, so if you find bugs or use cases that would helpful to satisfy, post an [Issue](http://github.com/tekwiz/apollo/issues) on Github.
26
+
27
+ This project is intended to be a **very** eclectic mix of helpful tools, so please feel free to send pull requests. That said, make **absolutely sure** your modifications are well tested. The hodge-podge nature of this project may make it prone to issues, so no code will be pulled-in that is not __fully_tested__.
28
+
29
+ Note on Patches/Pull Requests
30
+ -----------------------------
31
+
32
+ * Fork the project.
33
+ * Make your feature addition or bug fix.
34
+ * Add tests for it. This is important so I don't break it in a
35
+ future version unintentionally.
36
+ * Commit, do not mess with rakefile, version, or history.
37
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
38
+ * Send me a pull request. Bonus points for topic branches.
39
+
40
+ ### Documentation Warning
41
+
42
+ Beware that the below documentation has NOT been validated for the changes from Workflow to Apollo.
12
43
 
13
44
  What is workflow?
14
45
  -----------------
@@ -332,106 +363,47 @@ The whole event sequence is as follows:
332
363
  * PERSIST WORKFLOW STATE, i.e. transition
333
364
  * on_entry
334
365
 
335
-
336
366
  Documenting with diagrams
337
367
  -------------------------
338
368
 
339
369
  You can generate a graphical representation of your apollo for
340
370
  documentation purposes. S. Apollo::create_apollo_diagram.
341
371
 
342
-
343
- Earlier versions
344
- ----------------
345
-
346
- The `apollo` library was originally written by Ryan Allen.
347
-
348
- The version 0.3 was almost completely (including ActiveRecord
349
- integration, API for accessing apollo specification,
350
- method_missing free implementation) rewritten by Vladimir Dobriakov
351
- keeping the original apollo DSL spirit.
352
-
353
-
354
- Migration from the original Ryan's library
355
- ------------------------------------------
356
-
357
- Credit: Michael (rockrep)
358
-
359
- Accessing apollo specification
360
-
361
- my_instance.apollo # old
362
- MyClass.apollo_spec # new
363
-
364
- Accessing states, events, meta, e.g.
365
-
366
- my_instance.apollo.states(:some_state).events(:some_event).meta[:some_meta_tag] # old
367
- MyClass.apollo_spec.states[:some_state].events[:some_event].meta[:some_meta_tag] # new
368
-
369
- Causing state transitions
370
-
371
- my_instance.apollo.my_event # old
372
- my_instance.my_event! # new
373
-
374
- when using both a block and a callback method for an event, the block executes prior to the callback
375
-
376
-
377
- Changelog
372
+ Copyright
378
373
  ---------
379
374
 
380
- ### New in the version 0.4.0
381
-
382
- * completely rewritten the documentation to match my branch. Every
383
- described feature is backed up by an automated test.
375
+ Copyright 2010 Travis D. Warlick, Jr.
376
+
377
+ Licensed under the Apache License, Version 2.0 (the "License");
378
+ you may not use this file except in compliance with the License.
379
+ You may obtain a copy of the License at
380
+
381
+ http://www.apache.org/licenses/LICENSE-2.0
382
+
383
+ Unless required by applicable law or agreed to in writing, software
384
+ distributed under the License is distributed on an "AS IS" BASIS,
385
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
386
+ See the License for the specific language governing permissions and
387
+ limitations under the License.
384
388
 
385
- ### New in the version 0.3.0
389
+ The original work from [Workflow](http://github.com/geekq/workflow)
386
390
 
387
- Intermixing of transition graph definition (states, transitions)
388
- on the one side and implementation of the actions on the other side
389
- for a bigger state machine can introduce clutter.
390
-
391
- To reduce this clutter it is now possible to use state entry- and
392
- exit- hooks defined through a naming convention. For example, if there
393
- is a state :pending, then instead of using a
394
- block:
395
-
396
- state :pending do
397
- on_entry do
398
- # your implementation here
399
- end
400
- end
401
-
402
- you can hook in by defining method
403
-
404
- def on_pending_exit(new_state, event, *args)
405
- # your implementation here
406
- end
391
+ Author: Vladimir Dobriakov, http://www.innoq.com/blog/vd, http://blog.geekq.net/
407
392
 
408
- anywhere in your class. You can also use a simpler function signature
409
- like `def on_pending_exit(*args)` if your are not interested in
410
- arguments. Please note: `def on_pending_exit()` with an empty list
411
- would not work.
393
+ Copyright (c) 2008-2009 Vodafone
412
394
 
413
- If both a function with a name according to naming convention and the
414
- on_entry/on_exit block are given, then only on_entry/on_exit block is used.
395
+ Copyright (c) 2007-2008 Ryan Allen, FlashDen Pty Ltd
415
396
 
397
+ Based on the work of Ryan Allen and Scott Barron
416
398
 
417
- Support
418
- -------
399
+ Licensed under MIT license, see the MIT-LICENSE file.
419
400
 
420
- ### Reporting bugs
421
-
422
- http://github.com/geekq/apollo/issues
423
-
424
-
425
- About
426
- -----
427
-
428
- Author: Vladimir Dobriakov, http://www.innoq.com/blog/vd, http://blog.geekq.net/
429
-
430
- Copyright (c) 2008-2009 Vodafone
431
-
432
- Copyright (c) 2007-2008 Ryan Allen, FlashDen Pty Ltd
433
-
434
- Based on the work of Ryan Allen and Scott Barron
401
+ Earlier versions
402
+ ----------------
435
403
 
436
- Licensed under MIT license, see the MIT-LICENSE file.
404
+ The `workflow` library was originally written by Ryan Allen.
437
405
 
406
+ The version 0.3 was almost completely (including ActiveRecord
407
+ integration, API for accessing apollo specification,
408
+ method_missing free implementation) rewritten by Vladimir Dobriakov
409
+ keeping the original apollo DSL spirit.
data/Rakefile CHANGED
@@ -41,8 +41,8 @@ Rake::RDocTask.new do |rdoc|
41
41
 
42
42
  rdoc.rdoc_dir = 'rdoc'
43
43
  rdoc.title = "Apollo #{version}"
44
- rdoc.rdoc_files.include('README*', 'MIT-LICENSE', 'LICENSE', 'VERSION')
44
+ rdoc.rdoc_files.include('README*', 'MIT-LICENSE', 'LICENSE', 'VERSION', 'CHANGELOG.md')
45
45
  rdoc.rdoc_files.include('lib/**/*.rb')
46
46
  end
47
47
 
48
- task :clobber => [:clobber_rcov, :clobber_rdoc]
48
+ # task :clobber => [:clobber_rcov, :clobber_rdoc]
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0
1
+ 1.1.0
@@ -5,21 +5,22 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{apollo}
8
- s.version = "1.0.0"
8
+ s.version = "1.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Travis D. Warlick, Jr."]
12
- s.date = %q{2010-04-23}
12
+ s.date = %q{2010-04-24}
13
13
  s.email = %q{warlickt@operissystems.com}
14
14
  s.extra_rdoc_files = [
15
15
  "LICENSE",
16
- "README.markdown"
16
+ "README.md"
17
17
  ]
18
18
  s.files = [
19
19
  ".gitignore",
20
+ "CHANGELOG.md",
20
21
  "LICENSE",
21
22
  "MIT-LICENSE",
22
- "README.markdown",
23
+ "README.md",
23
24
  "Rakefile",
24
25
  "VERSION",
25
26
  "apollo.gemspec",
@@ -1,3 +1,20 @@
1
+ # Copyright 2010 Travis D. Warlick, Jr.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ # Original work from Workflow:
16
+ # Copyright (c) 2008-2009 Vodafone
17
+ # Copyright (c) 2007-2008 Ryan Allen, FlashDen Pty Ltd
1
18
  module Apollo
2
19
  autoload :Event, 'apollo/event'
3
20
  autoload :State, 'apollo/state'
@@ -23,18 +40,18 @@ module Apollo
23
40
  class ApolloDefinitionError < Exception; end
24
41
 
25
42
  module ClassMethods
26
- attr_reader :apollo_spec
27
-
28
- def apollo_column(column_name=nil)
43
+ def current_state_column(column_name=nil)
29
44
  if column_name
30
- @apollo_state_column_name = column_name.to_sym
45
+ @current_state_column_name = column_name.to_sym
31
46
  else
32
- @apollo_state_column_name ||= :current_state
47
+ @current_state_column_name ||= :current_state
33
48
  end
34
- @apollo_state_column_name
49
+ @current_state_column_name
35
50
  end
36
51
 
37
- def apollo(&specification)
52
+ def state_machine(&specification)
53
+ return @apollo_spec unless block_given?
54
+
38
55
  @apollo_spec = Specification.new(Hash.new, &specification)
39
56
  @apollo_spec.states.values.each do |state|
40
57
  state_name = state.name
@@ -53,12 +70,20 @@ module Apollo
53
70
  end
54
71
  end
55
72
  end
73
+
74
+ @apollo_spec.state_sets.keys.each do |set_name|
75
+ module_eval do
76
+ define_method "#{set_name}?" do
77
+ current_state.sets.include?(set_name)
78
+ end
79
+ end
80
+ end
56
81
  end
57
82
  end
58
83
 
59
84
  module InstanceMethods
60
85
  def current_state
61
- loaded_state = load_apollo_state
86
+ loaded_state = load_current_state
62
87
  res = spec.states[loaded_state.to_sym] if loaded_state
63
88
  res || spec.initial_state
64
89
  end
@@ -76,7 +101,7 @@ module Apollo
76
101
  raise NoTransitionAllowed.new(
77
102
  "There is no event #{name.to_sym} defined for the #{current_state} state") \
78
103
  if event.nil?
79
- # This three member variables are a relict from the old apollo library
104
+ # This three member variables are a relict from the old workflow library
80
105
  # TODO: refactor some day
81
106
  @halted_because = nil
82
107
  @halted = false
@@ -112,10 +137,10 @@ module Apollo
112
137
  c = self.class
113
138
  # using a simple loop instead of class_inheritable_accessor to avoid
114
139
  # dependency on Rails' ActiveSupport
115
- until c.apollo_spec || !(c.include? Apollo)
140
+ until c.state_machine || !(c.include? Apollo)
116
141
  c = c.superclass
117
142
  end
118
- c.apollo_spec
143
+ c.state_machine
119
144
  end
120
145
 
121
146
  def halt(reason)
@@ -137,7 +162,7 @@ module Apollo
137
162
 
138
163
  def transition(from, to, name, *args)
139
164
  run_on_exit(from, to, name, *args)
140
- persist_apollo_state to.to_s
165
+ persist_current_state to.to_s
141
166
  run_on_entry(to, from, name, *args)
142
167
  end
143
168
 
@@ -173,19 +198,19 @@ module Apollo
173
198
  end
174
199
  end
175
200
 
176
- # load_apollo_state and persist_apollo_state
177
- # can be overriden to handle the persistence of the apollo state.
201
+ # load_current_state and persist_current_state
202
+ # can be overriden to handle the persistence of the current state.
178
203
  #
179
204
  # Default (non ActiveRecord) implementation stores the current state
180
205
  # in a variable.
181
206
  #
182
- # Default ActiveRecord implementation uses a 'apollo_state' database column.
183
- def load_apollo_state
184
- @apollo_state if instance_variable_defined? :@apollo_state
207
+ # Default ActiveRecord implementation uses a 'current_state' database column.
208
+ def load_current_state
209
+ @current_state if instance_variable_defined? :@current_state
185
210
  end
186
211
 
187
- def persist_apollo_state(new_value)
188
- @apollo_state = new_value
212
+ def persist_current_state(new_value)
213
+ @current_state = new_value
189
214
  end
190
215
  end
191
216
 
@@ -200,21 +225,21 @@ module Apollo
200
225
  end
201
226
  end
202
227
 
203
- # Generates a `dot` graph of the apollo.
228
+ # Generates a `dot` graph of the state machine.
204
229
  # Prerequisite: the `dot` binary.
205
230
  # You can use it in your own Rakefile like this:
206
231
  #
207
232
  # namespace :doc do
208
- # desc "Generate a graph of the apollo."
209
- # task :apollo do
210
- # Apollo::create_apollo_diagram(Order.new)
233
+ # desc "Generate a graph of the state machine."
234
+ # task :state_machine do
235
+ # Apollo::create_state_diagram(Order.new)
211
236
  # end
212
237
  # end
213
238
  #
214
239
  # You can influence the placement of nodes by specifying
215
240
  # additional meta information in your states and transition descriptions.
216
241
  # You can assign higher `doc_weight` value to the typical transitions
217
- # in your apollo. All other states and transitions will be arranged
242
+ # in your state machine. All other states and transitions will be arranged
218
243
  # around that main line. See also `weight` in the graphviz documentation.
219
244
  # Example:
220
245
  #
@@ -223,21 +248,21 @@ module Apollo
223
248
  # end
224
249
  #
225
250
  #
226
- # @param klass A class with the Apollo mixin, for which you wish the graphical apollo representation
251
+ # @param klass A class with the Apollo mixin, for which you wish the graphical state machine representation
227
252
  # @param [String] target_dir Directory, where to save the dot and the pdf files
228
253
  # @param [String] graph_options You can change graph orientation, size etc. See graphviz documentation
229
- def self.create_apollo_diagram(klass, target_dir, graph_options='rankdir="LR", size="7,11.6", ratio="fill"')
230
- apollo_name = "#{klass.name.tableize}_apollo"
231
- fname = File.join(target_dir, "generated_#{apollo_name}")
254
+ def self.create_state_diagram(klass, target_dir, graph_options='rankdir="LR", size="7,11.6", ratio="fill"')
255
+ state_machine_name = "#{klass.name.tableize}_state_machine"
256
+ fname = File.join(target_dir, "generated_#{state_machine_name}")
232
257
  File.open("#{fname}.dot", 'w') do |file|
233
258
  file.puts %Q|
234
- digraph #{apollo_name} {
259
+ digraph #{state_machine_name} {
235
260
  graph [#{graph_options}];
236
261
  node [shape=box];
237
262
  edge [len=1];
238
263
  |
239
264
 
240
- klass.apollo_spec.states.each do |state_name, state|
265
+ klass.state_machine.states.each do |state_name, state|
241
266
  file.puts %Q{ #{state.name} [label="#{state.name}"];}
242
267
  state.events.each do |event_name, event|
243
268
  meta_info = event.meta
@@ -1,24 +1,24 @@
1
1
  module Apollo
2
2
  module ActiveRecordInstanceMethods
3
- def load_apollo_state
4
- read_attribute(self.class.apollo_column)
3
+ def load_current_state
4
+ read_attribute(self.class.current_state_column)
5
5
  end
6
6
 
7
- # On transition the new apollo state is immediately saved in the
7
+ # On transition the new current state is immediately saved in the
8
8
  # database.
9
- def persist_apollo_state(new_value)
10
- update_attribute self.class.apollo_column, new_value
9
+ def persist_current_state(new_value)
10
+ update_attribute self.class.current_state_column, new_value
11
11
  end
12
12
 
13
13
  private
14
14
 
15
- # Motivation: even if NULL is stored in the apollo_state database column,
15
+ # Motivation: even if NULL is stored in the current_state database column,
16
16
  # the current_state is correctly recognized in the Ruby code. The problem
17
17
  # arises when you want to SELECT records filtering by the value of initial
18
18
  # state. That's why it is important to save the string with the name of the
19
19
  # initial state in all the new records.
20
20
  def write_initial_state
21
- write_attribute self.class.apollo_column, current_state.to_s
21
+ write_attribute self.class.current_state_column, current_state.to_s
22
22
  end
23
23
  end
24
24
  end
@@ -1,9 +1,10 @@
1
1
  module Apollo
2
2
  class Specification
3
- attr_accessor :states, :initial_state, :meta, :on_transition_proc
3
+ attr_accessor :states, :initial_state, :meta, :on_transition_proc, :state_sets
4
4
 
5
5
  def initialize(meta = {}, &specification)
6
6
  @states = Hash.new
7
+ @state_sets = Hash.new
7
8
  @meta = meta
8
9
  instance_eval(&specification)
9
10
  end
@@ -11,6 +12,8 @@ module Apollo
11
12
  private
12
13
 
13
14
  def state(name, meta = {:meta => {}}, &events_and_etc)
15
+ validate_state_name(name)
16
+
14
17
  # meta[:meta] to keep the API consistent..., gah
15
18
  new_state = State.new(name, meta[:meta])
16
19
  @initial_state = new_state if @states.empty?
@@ -18,6 +21,21 @@ module Apollo
18
21
  @scoped_state = new_state
19
22
  instance_eval(&events_and_etc) if events_and_etc
20
23
  end
24
+
25
+ def state_set(name, *state_names)
26
+ validate_state_set_name(name)
27
+
28
+ set = Set.new
29
+ state_names.each do |state_name|
30
+ if state = @states[state_name]
31
+ set << state
32
+ state.sets << name
33
+ else
34
+ raise ApolloDefinitionError, "Unknown state: #{state}"
35
+ end
36
+ end
37
+ @state_sets[name] = set
38
+ end
21
39
 
22
40
  def event(name, args = {}, &action)
23
41
  target = args[:to] || args[:to]
@@ -39,5 +57,17 @@ module Apollo
39
57
  def on_transition(&proc)
40
58
  @on_transition_proc = proc
41
59
  end
60
+
61
+ def validate_state_name(name)
62
+ if @state_sets[name]
63
+ raise ApolloDefinitionError, "State name conflicts with state set name: #{name}"
64
+ end
65
+ end
66
+
67
+ def validate_state_set_name(name)
68
+ if @states[name]
69
+ raise ApolloDefinitionError, "State set name conflicts with state name: #{name}"
70
+ end
71
+ end
42
72
  end
43
73
  end
@@ -1,9 +1,9 @@
1
1
  module Apollo
2
2
  class State
3
- attr_accessor :name, :events, :meta, :on_entry, :on_exit
3
+ attr_accessor :name, :events, :meta, :on_entry, :on_exit, :sets
4
4
 
5
5
  def initialize(name, meta = {})
6
- @name, @events, @meta = name, Hash.new, meta
6
+ @name, @events, @meta, @sets = name, Hash.new, meta, Set.new
7
7
  end
8
8
 
9
9
  def to_s
@@ -14,4 +14,4 @@ module Apollo
14
14
  name.to_sym
15
15
  end
16
16
  end
17
- end
17
+ end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 1
7
+ - 1
7
8
  - 0
8
- - 0
9
- version: 1.0.0
9
+ version: 1.1.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Travis D. Warlick, Jr.
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-04-23 00:00:00 -05:00
17
+ date: 2010-04-24 00:00:00 -05:00
18
18
  default_executable:
19
19
  dependencies: []
20
20
 
@@ -26,12 +26,13 @@ extensions: []
26
26
 
27
27
  extra_rdoc_files:
28
28
  - LICENSE
29
- - README.markdown
29
+ - README.md
30
30
  files:
31
31
  - .gitignore
32
+ - CHANGELOG.md
32
33
  - LICENSE
33
34
  - MIT-LICENSE
34
- - README.markdown
35
+ - README.md
35
36
  - Rakefile
36
37
  - VERSION
37
38
  - apollo.gemspec