public
Description: Shade is a state machine (or workflow) engine for ColdFusion business objects.
Homepage:
Clone URL: git://github.com/ryanwood/shade.git
Prevented manually state change.
ryanwood (author)
Fri Aug 08 12:16:52 -0700 2008
commit  a0424f7ec70c1d1f8a0d8c1d83097ef9b5b92d1c
tree    05209adc66da6a01cd3990e24855d512a8bc8a78
parent  4245d4bde90bb8ef808702716d99aedae1409387
...
15
16
17
18
 
 
 
19
20
21
...
65
66
67
68
 
69
70
71
72
73
74
 
75
76
77
...
85
86
87
88
 
89
90
91
...
137
138
139
140
141
 
142
143
144
145
146
147
148
149
 
 
 
 
 
 
 
 
 
 
150
151
152
...
15
16
17
 
18
19
20
21
22
23
...
67
68
69
 
70
71
72
73
74
75
 
76
77
78
79
...
87
88
89
 
90
91
92
93
...
139
140
141
 
 
142
143
144
145
146
147
 
 
 
148
149
150
151
152
153
154
155
156
157
158
159
160
0
@@ -15,7 +15,9 @@
0
       instance.initialState = arguments.initialState;
0
             
0
       configureState();
0
-      setInitialState(instance.initialState);
0
+      if(getState() eq '') {
0
+        setInitialState(instance.initialState);
0
+      }
0
             
0
       return this;
0
     </cfscript>
0
@@ -65,13 +67,13 @@
0
     <cfreturn invokeMethod("get#instance.stateMethod#") />
0
   </cffunction>
0
   
0
-  <cffunction name="setState" access="public" output="false">
0
+  <cffunction name="setInternalState" access="public" output="false">
0
     <cfargument name="state" type="string" required="true" />  
0
     <cfset var local = structNew() />
0
     <cfset local.state = arguments.state />
0
     <cfset invokeMethod("set#instance.stateMethod#", local) />
0
   </cffunction>
0
-
0
+  
0
   <!--- Accessors --->
0
   
0
   <cffunction name="getInitialState" access="public" returntype="string" output="false">
0
@@ -85,7 +87,7 @@
0
     <cfscript>
0
       var state = instance.states[arguments.stateName];
0
       state.before(this);
0
-      setState(arguments.stateName);
0
+      setInternalState(arguments.stateName);
0
       state.after(this);
0
     </cfscript>
0
   </cffunction>  
0
@@ -137,16 +139,22 @@
0
   
0
     1. To defined the state query handlers, i.e. isClosed()
0
     2. To define the event firing shortcuts, i.e. close()
0
-    3. To pass any unknown method on to the decorated object to handle
0
-        
0
+    3. To pass any unknown method on to the decorated object to handle        
0
   --->  
0
   <cffunction name="onMissingMethod" output="false" access="public">
0
     <cfargument name="missingMethodName" type="string" />
0
     <cfargument name="missingMethodArguments" type="struct" />
0
     
0
-    <!--- 
0
-    Shortcut to wire up event methods, so basically if you have a 
0
-    'trash' event, you can call obj.trash() rather than the longer obj.fireEvent('trash') --->
0
+    <!--- This is to highly discourage the manual setting of state. 
0
+          It can still be done by getting the original object and 
0
+          setting it directly. BAD. --->
0
+    <cfif arguments.missingMethodName is "set#getStateMethod()#" >
0
+      <cfthrow type="shade.InvalidStateChange" message="You cannot set the state directly. Please call an event instead to change the state." />
0
+    </cfif>
0
+    
0
+    <!--- Shortcut to wire up event methods, so basically if you have a 
0
+          'trash' event, you can call obj.trash() rather than the longer 
0
+          obj.fireEvent('trash') --->
0
     <cfif structKeyExists(instance.eventTable, arguments.missingMethodName)>
0
       <cfreturn fireEvent(arguments.missingMethodName) />    
0
     </cfif>
...
50
51
52
53
 
54
55
56
...
50
51
52
 
53
54
55
56
0
@@ -50,7 +50,7 @@
0
       
0
       if(not local.isLoopback) {
0
         if(local.nextState.before(obj)) {  
0
-          obj.setState(getToState());
0
+          obj.setInternalState(getToState());
0
           local.nextState.after(obj);  
0
           local.oldState.exit(obj);  
0
         } else {
...
2
3
4
 
5
6
7
...
2
3
4
5
6
7
8
0
@@ -2,6 +2,7 @@
0
   <cfset instance = structNew() />
0
 
0
   <cffunction name="init" access="public" returntype="any" output="false" hint="">
0
+    <cfset instance.state = '' />
0
     <cfreturn this />
0
   </cffunction>
0
     
...
210
211
212
213
 
 
 
 
 
 
 
 
 
 
214
215
216
...
210
211
212
 
213
214
215
216
217
218
219
220
221
222
223
224
225
0
@@ -210,6 +210,15 @@
0
     </cfscript>
0
   </cffunction>
0
 
0
-
0
+  <cffunction name="testCannotSetStateDirectly" returntype="void" access="public" output="false">
0
+    <cfscript>
0
+      try {
0
+        conversation.setMyState('new');
0
+        fail('Should not be able to set state directly.');
0
+      } catch( any e ) {
0
+        assertEquals( 'shade.InvalidStateChange', e.type );
0
+      }
0
+    </cfscript>
0
+  </cffunction>
0
   
0
 </cfcomponent>
0
\ No newline at end of file

Comments