Skip to content

Commit ff6752f

Browse files
committedAug 26, 2013
New workflow definition cleanly undefines the old convinience methods, s. also #92, #69
* works on both subclassing and dynamic workflow spec assignment * solving problems caused by metaprogramming with even more metaprogramming ;-)
1 parent 68951c9 commit ff6752f

File tree

2 files changed

+88
-4
lines changed

2 files changed

+88
-4
lines changed
 

Diff for: ‎lib/workflow.rb

+34-4
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,28 @@ def workflow(&specification)
2525

2626
private
2727

28+
# Creates the convinience methods like `my_transition!`
2829
def assign_workflow(specification_object)
29-
# TODO Ensure assign_workflow can be only called once
30-
#
31-
# creates the convinence methods, note: there is currently no way
32-
# to reassign different workflow, undefining created methods
30+
31+
# Merging two workflow specifications can **not** be done automically, so
32+
# just make the latest specification win. Same for inheritance -
33+
# definition in the subclass wins.
34+
if respond_to? :inherited_workflow_spec # undefine methods defined by the old workflow_spec
35+
inherited_workflow_spec.states.values.each do |state|
36+
state_name = state.name
37+
module_eval do
38+
undef_method "#{state_name}?"
39+
end
40+
41+
state.events.values.each do |event|
42+
event_name = event.name
43+
module_eval do
44+
undef_method "#{event_name}!".to_sym
45+
undef_method "can_#{event_name}?"
46+
end
47+
end
48+
end
49+
end
3350

3451
@workflow_spec = specification_object
3552
@workflow_spec.states.values.each do |state|
@@ -230,6 +247,19 @@ def persist_workflow_state(new_value)
230247

231248
def self.included(klass)
232249
klass.send :include, InstanceMethods
250+
251+
# backup the parent workflow spec, making accessible through #inherited_workflow_spec
252+
if klass.superclass.respond_to?(:workflow_spec, true)
253+
klass.module_eval do
254+
# see http://stackoverflow.com/a/2495650/111995 for implementation explanation
255+
pro = Proc.new { klass.superclass.workflow_spec }
256+
singleton_class = class << self; self; end
257+
singleton_class.send(:define_method, :inherited_workflow_spec) do
258+
pro.call
259+
end
260+
end
261+
end
262+
233263
klass.extend ClassMethods
234264

235265
if Object.const_defined?(:ActiveRecord)

Diff for: ‎test/inheritance_test.rb

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
require File.join(File.dirname(__FILE__), 'test_helper')
2+
require 'workflow'
3+
class InheritanceTest < ActiveRecordTestCase
4+
5+
test '#69 inheritance' do
6+
class Animal
7+
include Workflow
8+
9+
workflow do
10+
11+
state :conceived do
12+
event :birth, :transition_to => :born
13+
end
14+
15+
state :born do
16+
17+
end
18+
end
19+
end
20+
21+
class Cat < Animal
22+
include Workflow
23+
workflow do
24+
25+
state :upset do
26+
event :scratch, :transition_to => :hiding
27+
end
28+
29+
state :hiding do
30+
31+
end
32+
end
33+
end
34+
35+
assert_equal [:born, :conceived] , Animal.workflow_spec.states.keys.sort
36+
assert_equal [:hiding, :upset], Cat.workflow_spec.states.keys.sort, "Workflow definitions are not inherited"
37+
38+
animal = Animal.new
39+
cat = Cat.new
40+
41+
animal.birth!
42+
43+
assert_raise NoMethodError, 'Methods defined by the old workflow spec should have be gone away' do
44+
cat.birth!
45+
end
46+
47+
assert_equal [:birth!, :halt!, :process_event!], bang_methods(animal)
48+
assert_equal [:halt!, :process_event!, :scratch!], bang_methods(cat)
49+
end
50+
51+
def bang_methods(obj)
52+
(obj.public_methods-Object.public_methods).select {|m| m =~ /!$/}.sort
53+
end
54+
end

0 commit comments

Comments
 (0)
Please sign in to comment.