public
Description: Shade is a state machine (or workflow) engine for ColdFusion business objects.
Homepage:
Clone URL: git://github.com/ryanwood/shade.git
Added License. Changed 'entering' callback to 'before'. Events now return a 
boolean value as to their success. Before callback will halt the transition if 
false. [#1 state:resolved]
ryanwood (author)
Fri Jul 18 12:11:38 -0700 2008
commit  94ee712cb48348cfe764d06a9181405bfb125a95
tree    f8fcfd4417fe161a7a9f9867cee834091c073a3b
parent  b336ed32deeb1f7a7410b1ad692e363d88624a07
...
37
38
39
40
 
41
42
 
 
43
44
45
...
37
38
39
 
40
41
 
42
43
44
45
46
0
@@ -37,9 +37,10 @@
0
     <cfloop condition="transitions.hasNext()">
0
       <cfset transition = transitions.next() />
0
       <cfif transition.perform(obj)>
0
-        <cfbreak />
0
+        <cfreturn true />
0
       </cfif>
0
-    </cfloop>    
0
+    </cfloop>  
0
+    <cfreturn false />  
0
   </cffunction>  
0
   
0
   <cffunction name="transitions" access="public" output="false">
...
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
 
38
39
 
40
41
42
 
43
44
 
45
46
47
 
48
49
 
50
51
52
 
53
54
 
55
56
 
 
 
 
57
 
58
59
60
61
...
10
11
12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
14
15
16
 
17
18
 
19
20
21
 
22
23
 
24
25
26
 
27
28
 
29
30
31
 
32
33
 
34
35
 
36
37
38
39
40
41
42
43
44
45
0
@@ -10,51 +10,35 @@
0
     <cfreturn this />
0
   </cffunction>
0
   
0
-  <!--- Set up chainable callback handlers --->
0
-  
0
-  <cffunction name="setEnterCallback" access="public" output="false">
0
-    <cfargument name="method" type="string" required="true" />      
0
-    <cfset instance.callbacks.enter = arguments.method />
0
-    <cfreturn this />
0
-  </cffunction>
0
-  
0
-  <cffunction name="setAfterCallback" access="public" output="false">
0
-    <cfargument name="method" type="string" required="true" />      
0
-    <cfset instance.callbacks.after = arguments.method />
0
-    <cfreturn this />
0
-  </cffunction>
0
-  
0
-  <cffunction name="setExitCallback" access="public" output="false">
0
-    <cfargument name="method" type="string" required="true" />      
0
-    <cfset instance.callbacks.exit = arguments.method />
0
-    <cfreturn this />
0
-  </cffunction>
0
-  
0
   <cffunction name="getName" returntype="string" access="public" output="false">
0
     <cfreturn instance.name />
0
   </cffunction>
0
   
0
-  <cffunction name="entering" returntype="void" access="public" output="false">
0
+  <cffunction name="before" returntype="boolean" access="public" output="false">
0
     <cfargument name="obj" required="true" />
0
-    <cfset invokeCallback('#getName()#EnteringAction', arguments.obj) />
0
+    <cfreturn invokeCallback('before#getName()#Action', arguments.obj) />
0
   </cffunction>
0
   
0
-  <cffunction name="entered" returntype="void" access="public" output="false">
0
+  <cffunction name="after" returntype="void" access="public" output="false">
0
     <cfargument name="obj" required="true" />
0
-    <cfset invokeCallback('#getName()#AfterAction', arguments.obj) />
0
+    <cfset invokeCallback('after#getName()#Action', arguments.obj) />
0
   </cffunction>  
0
   
0
-  <cffunction name="exited" returntype="void" access="public" output="false">
0
+  <cffunction name="exit" returntype="void" access="public" output="false">
0
     <cfargument name="obj" required="true" />
0
-    <cfset invokeCallback('#getName()#ExitAction', arguments.obj) />
0
+    <cfset invokeCallback('exit#getName()#Action', arguments.obj) />
0
   </cffunction>
0
   
0
-  <cffunction name="invokeCallback" returntype="void" access="public" output="false">
0
+  <cffunction name="invokeCallback" returntype="boolean" access="public" output="false">
0
     <cfargument name="callback" type="string" required="true" />
0
-    <cfargument name="obj" required="true" />    
0
+    <cfargument name="obj" required="true" />
0
     <cfif structKeyExists(arguments.obj, arguments.callback)>
0
-      <cfinvoke component="#arguments.obj#" method="#arguments.callback#" />
0
+      <cfinvoke component="#arguments.obj#" method="#arguments.callback#" returnvariable="result" />
0
+      <cfif isDefined('result') and isBoolean(result)>
0
+        <cfreturn result />
0
+      </cfif> 
0
     </cfif>
0
+    <cfreturn true />
0
   </cffunction>
0
 
0
 </cfcomponent>
0
\ No newline at end of file
...
83
84
85
86
 
87
88
 
89
90
91
...
83
84
85
 
86
87
 
88
89
90
91
0
@@ -83,9 +83,9 @@
0
     <cfargument name="stateName" required="true" />
0
     <cfscript>
0
       var state = instance.states[arguments.stateName];
0
-      state.entering(this);
0
+      state.before(this);
0
       setState(arguments.stateName);
0
-      state.entered(this);
0
+      state.after(this);
0
     </cfscript>
0
   </cffunction>
0
   
...
39
40
41
42
 
 
 
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
 
 
 
 
 
 
 
 
58
59
60
...
39
40
41
 
42
43
44
45
46
47
48
49
50
 
 
 
 
 
 
 
 
 
51
52
53
54
55
56
57
58
59
60
61
0
@@ -39,22 +39,23 @@
0
     <cfscript>
0
       var local = structNew();
0
     
0
-      if(not guard(obj)) return false;
0
+      if(not guard(obj)) {
0
+        return false;
0
+      }
0
     
0
       local.isLoopback = obj.getCurrentState() eq getToState();
0
       local.states = arguments.obj.getStates();
0
       local.nextState = local.states[getToState()];
0
       local.oldState = local.states[obj.getCurrentState()];
0
       
0
-      if(not local.isLoopback) { 
0
-        local.nextState.entering(obj);  
0
-      }
0
-      
0
-      obj.setState(getToState());
0
-      
0
-      if(not local.isLoopback) { 
0
-        local.nextState.entered(obj);  
0
-        local.oldState.exited(obj);  
0
+      if(not local.isLoopback) {
0
+        if(local.nextState.before(obj)) {  
0
+          obj.setState(getToState());
0
+          local.nextState.after(obj);  
0
+          local.oldState.exit(obj);  
0
+        } else {
0
+         return false;
0
+        }
0
       }
0
       
0
       return true;
...
13
14
15
 
16
17
18
...
21
22
23
 
24
25
26
27
28
29
 
30
31
32
33
 
 
 
 
 
 
34
35
36
37
 
38
39
40
41
42
 
43
44
45
46
 
47
48
49
50
 
51
52
53
...
13
14
15
16
17
18
19
...
22
23
24
25
26
27
28
29
30
 
31
32
33
34
 
35
36
37
38
39
40
41
42
43
 
44
45
46
47
48
 
49
50
51
52
 
53
54
55
56
 
57
58
59
60
0
@@ -13,6 +13,7 @@
0
       addState('read');
0
       addState('closed');
0
       addState('awaitingResponse');
0
+      addState('filed');
0
       addState('junk');
0
 
0
       addEvent('newMessage').addTransitions('read,closed,awaitingResponse', 'needingAttention');
0
@@ -21,33 +22,39 @@
0
       addEvent('close').addTransitions('read,awaitingResponse', 'closed', 'getCanClose').addTransitions('read,awaitingResponse', 'read', 'alwaysTrue');
0
       addEvent('junk').addTransitions('read,closed,awaitingResponse', 'junk');
0
       addEvent('unjunk').addTransitions('junk', 'closed');
0
+      addEvent('file').addTransitions('read,closed,awaitingResponse', 'filed');
0
     </cfscript>
0
   </cffunction>
0
   
0
   <!--- State Observer Events --->
0
   
0
-  <cffunction name="readEnteringAction" access="public" returntype="void" output="false">
0
+  <cffunction name="beforeReadAction" access="public" returntype="void" output="false">
0
     <cfset getOriginalObject().setReadEnter(true) />
0
   </cffunction>
0
 
0
-  <cffunction name="readExitAction" access="public" returntype="void" output="false">
0
+  <cffunction name="beforeFiledAction" access="public" returntype="boolean" output="false">
0
+    <!--- Let's assume this event checked for the existence of a folder and failed  --->
0
+    <cfreturn false />
0
+  </cffunction>
0
+  
0
+  <cffunction name="exitReadAction" access="public" returntype="void" output="false">
0
     <cfset getOriginalObject().setReadExit(true) />
0
   </cffunction>
0
   
0
-  <cffunction name="readAfterAction" access="public" returntype="void" output="false">
0
+  <cffunction name="afterReadAction" access="public" returntype="void" output="false">
0
     <cfset getOriginalObject().setReadAfterFirstAction(true) />
0
     <cfset getOriginalObject().setReadAfterSecondAction(true) />
0
   </cffunction>
0
 
0
-  <cffunction name="needingAttentionEnteringAction" access="public" returntype="void" output="false">
0
+  <cffunction name="beforeNeedingAttentionAction" access="public" returntype="void" output="false">
0
     <cfset getOriginalObject().setNeedingAttentionEnter(true) />
0
   </cffunction>
0
   
0
-  <cffunction name="needingAttentionAfterAction" access="public" returntype="void" output="false">
0
+  <cffunction name="afterNeedingAttentionAction" access="public" returntype="void" output="false">
0
     <cfset getOriginalObject().setNeedingAttentionAfter(true) />
0
   </cffunction>
0
 
0
-  <cffunction name="closedAfterAction" access="public" returntype="void" output="false">
0
+  <cffunction name="afterClosedAction" access="public" returntype="void" output="false">
0
     <cfset getOriginalObject().setClosedAfter(true) />
0
   </cffunction>
0
 
...
112
113
114
115
 
116
117
118
...
193
194
195
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
197
198
...
112
113
114
 
115
116
117
118
...
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
0
@@ -112,7 +112,7 @@
0
       assertTrue(conversation.getReadEnter());
0
     </cfscript>
0
   </cffunction>  
0
-  
0
+
0
   <cffunction name="testAfterActionsExecuted" returntype="void" access="public" output="false">
0
     <cfscript>
0
       conversation.setReadAfterFirstAction(false);
0
@@ -193,5 +193,23 @@
0
       assertTrue(conversation.isJunk());
0
     </cfscript>
0
   </cffunction>
0
+
0
+  <cffunction name="testTransitionAbortedIfBeforeActionReturnsFalse" returntype="void" access="public" output="false">
0
+    <cfscript>
0
+      assertTrue(conversation.view());
0
+      assertTrue(conversation.isRead());
0
+      assertFalse(conversation.file());
0
+      assertTrue(conversation.isRead());
0
+    </cfscript>
0
+  </cffunction>  
0
+  
0
+  <cffunction name="testEventReturnsTrueWhenTransitionIsSuccessful" returntype="void" access="public" output="false">
0
+    <cfscript>
0
+      assertFalse(conversation.junk());
0
+      assertTrue(conversation.view());
0
+    </cfscript>
0
+  </cffunction>
0
+
0
+
0
   
0
 </cfcomponent>
0
\ No newline at end of file

Comments