webflow 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.
@@ -0,0 +1,324 @@
1
+ #--
2
+ # Copyright (c) 2007 The World in General
3
+ #
4
+ # Released under the Creative Commons Attribution-Share Alike 3.0 License.
5
+ # Licence details available at : http://creativecommons.org/licenses/by-sa/3.0/
6
+ #
7
+ # Created by Luc Boudreau ( lucboudreau at gmail )
8
+ #
9
+ # The above copyright notice and this permission notice shall be
10
+ # included in all copies or substantial portions of the Software.
11
+ #
12
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+ #++
20
+
21
+ #require 'event'
22
+ #require 'base'
23
+
24
+ module WebFlow
25
+
26
+ # The FlowStep is a superclass from which every step type inherits.
27
+ # It's methods are therefore available to any step type present in the
28
+ # WebFlow framework.
29
+ #
30
+ # == Mapping instructions
31
+ #
32
+ # The mapping instructions common to every step type are the following.
33
+ #
34
+ # === on
35
+ #
36
+ # The <tt>on</tt> instruction is used to map a returned event name to a
37
+ # subsequent step. Here's a simple example of it's usage.
38
+ #
39
+ # action_step :example_step do
40
+ #
41
+ # (...)
42
+ #
43
+ # on :success => :next_step_name
44
+ #
45
+ # on :back => :previous_step_name
46
+ #
47
+ # end
48
+ #
49
+ # In the previous example, the example step will route the flow to the
50
+ # step named <tt>next_step_name</tt> if the step definition returns an
51
+ # event which is named <tt>success</tt>. If the step definition
52
+ # returns a <tt>back</tt> event, the flow will then call the
53
+ # <tt>previous_step_name</tt> step.
54
+ #
55
+ # === upon
56
+ #
57
+ # The <tt>upon</tt> instruction is used to map a raised error class to a
58
+ # subsequent step. Here's a simple example of it's usage.
59
+ #
60
+ # action_step :example_step do
61
+ #
62
+ # (...)
63
+ #
64
+ # upon :StandardError => :next_step_name
65
+ #
66
+ # end
67
+ #
68
+ # In the previous example, the example step will route the flow to the
69
+ # step named <tt>next_step_name</tt> if the step definition raises an
70
+ # error which is a kind of <tt>StandardError</tt>. If the upon instruction
71
+ # is used more than once, the first declaration has priority. Also note
72
+ # that the <tt>kind_of?</tt> method is used to validate the correspondence,
73
+ # so subclasses of mapped error classes will be included as well.
74
+ #
75
+ # === method
76
+ #
77
+ # The <tt>method</tt> instruction is meant to override the default step
78
+ # definition name. By default, the definition of a given step has to be declared
79
+ # with the same name as the step name in the mapping. If a step is named
80
+ # <tt>my_jolly_step</tt>, the controller has to implement the step logic
81
+ # with a <tt>def</tt> block which is named <tt>my_jolly_step</tt>.
82
+ #
83
+ # The <tt>method</tt> instruction will tell the WebFlow framework to look for
84
+ # a definition of the given value instead of looking for the same name.
85
+ # This allows to reuse business logic and decouple the mapping from the
86
+ # step implementation. One could then do something like :
87
+ #
88
+ # class MyController < WebFlow::Base
89
+ #
90
+ # def initialize
91
+ #
92
+ # (...)
93
+ #
94
+ # action_step :step_1 do
95
+ # method :shared_implementation
96
+ # (...)
97
+ # end
98
+ #
99
+ # action_step :step_2 do
100
+ # method :shared_implementation
101
+ # (...)
102
+ # end
103
+ # end
104
+ #
105
+ # def shared_implementation
106
+ # (...)
107
+ # end
108
+ #
109
+ # end
110
+ #
111
+ #
112
+ # == Developer infos
113
+ #
114
+ # This class defines a basic skeleton for different step types
115
+ # used by the WebFlow framework. All new step types must inherit
116
+ # of this superclass to be usable in the WebFlow controller.
117
+ #
118
+ # It is also the responsibility of any subclass to add it's declaration
119
+ # method in the WebFlow::Base class. See view_step.rb for an example.
120
+ #
121
+ # Also, subclasses can change the @definition_required instance variable value
122
+ # to tell the WebFlow framework that it can handle the execution
123
+ # without the user controller defining explicitly a step implementation.
124
+ # See view_step.rb(initialize) for an example.
125
+ #
126
+ # Event outcomes are defined in the @outcomes instance variable while
127
+ # the error handlers are defined in the @handlers instance variable.
128
+ # Those variables are protected, so any subclasses of FlowStep can access
129
+ # them and hack the mechanism if required.
130
+ #
131
+ class FlowStep
132
+
133
+
134
+ # Method signature to override in implementing classes.
135
+ # Does the 'dirty job' once it is required.
136
+ #
137
+ # Each implementing step HAS TO VERIFY THE VALUE OF THE lookup_method_to_call
138
+ # method. Or else, the 'method' instruction won't be respected
139
+ # if the user has used the 'method' instruction.
140
+ #
141
+ def execute(*args)
142
+
143
+ raise(WebFlowError.new, "You can't directly use FlowStep as a step. As a matter of fact, I don't even know how you were able in the first place anyways...")
144
+
145
+ end
146
+
147
+
148
+ # Used to know if the step needs an explicitly declared step definition or if it can
149
+ # manage the task with a default behavior.
150
+ def definition_required?
151
+ # We have to distinguish the false value from the non
152
+ # existence of the variable. Therefore the ||= operator
153
+ # can't help us.
154
+ if @definition_required.class.kind_of?(NilClass)
155
+ @definition_required = true
156
+ else
157
+ @definition_required
158
+ end
159
+ end
160
+
161
+
162
+
163
+ # Maps an event to a method call. Use in a mapping like this :
164
+ #
165
+ # implemented_step_type :id_of_step do
166
+ # on :event => :method
167
+ # end
168
+ #
169
+ # This is also possible :
170
+ #
171
+ # implemented_step_type :id_of_step do
172
+ # on { :event => :method,
173
+ # :event2 => :method2 }
174
+ # end
175
+ #
176
+ def on(options)
177
+
178
+ # Make sure we received a hash
179
+ raise(WebFlowError.new, "The 'on' method takes a Hash object as a parameter.") unless options.kind_of? Hash
180
+
181
+ # Make sure the key is not used twice in a definition
182
+ # to enforce coherence of the mapping.
183
+ options.each_key do |key|
184
+ raise(WebFlowError.new, "An event in this step scope already uses the name '#{key}'.") if outcomes.has_key?(key.to_s)
185
+ raise(WebFlowError.new, "#{key} is a reserved event keyword.") if WebFlow::Base.reserved_event?(key.to_s)
186
+ end
187
+
188
+ # Store the values in the possible outcomes hash
189
+ options.each { |key,value| outcomes[key.to_s] = value.to_s }
190
+
191
+ end
192
+
193
+
194
+ #def on(*args, &block)
195
+ #
196
+ # if block_given?
197
+ # event = args.first
198
+ # outcomes[event.to_s] = block
199
+ # else
200
+ # options = args.first || {}
201
+ #
202
+ # options.each_key do |key|
203
+ # raise(WebFlowError.new, "An event already uses the name '#{key}'. Names must be unique within a step scope.") if outcomes.has_key?(key.to_s)
204
+ # raise(WebFlowError.new, "#{key} is a reserved event keyword.") if WebFlow::Base.reserved_event?(key.to_s)
205
+ # end
206
+ #
207
+ # # Store the values in the possible outcomes hash
208
+ # options.each { |key, value| outcomes[key.to_s] = value.to_s }
209
+ #
210
+ # end
211
+ #
212
+ #end
213
+
214
+
215
+ # Maps an error class to a step name to execute.
216
+ # Use in a mapping like this :
217
+ #
218
+ # implemented_step_type :id_of_step do
219
+ # upon :WebFlowError => :step_name
220
+ # end
221
+ #
222
+ # This is also possible :
223
+ #
224
+ # implemented_step_type :id_of_step do
225
+ # upon { :WebFlowError => :step_name,
226
+ # :WhateverError => :step_name }
227
+ # end
228
+ #
229
+ # Only subclasses of StandardError will be rescued. This means that RuntimeError
230
+ # cannot be handled.
231
+ #
232
+ def upon(hash)
233
+
234
+ # Make sure we received a hash
235
+ raise(WebFlowError.new, "The 'upon' method takes a Hash object as a parameter. Go back to the API documents since you've obviously didn't read them well enough...") unless hash.kind_of? Hash
236
+
237
+ # Make sure the key is not used twice in a definition
238
+ # to enforce coherence of the mapping.
239
+ hash.each_key do |key|
240
+ raise(WebFlowError.new, "An error of the class '#{key}' is already mapped. They must be unique within a step scope.") if handlers.has_key?(key.to_s)
241
+ end
242
+
243
+ # Store the values in the handlers hash
244
+ hash.each { |key,value| handlers.store key.to_s, value.to_s }
245
+
246
+ end
247
+
248
+
249
+ # Maps a method name to execute instead of searching
250
+ # the controller for a method name who is the same as
251
+ # the step name.
252
+ #
253
+ # Use it as :
254
+ #
255
+ # action_step :my_step do
256
+ # method :call_this_instead
257
+ # end
258
+ #
259
+ def method(method_name)
260
+
261
+ @_method = method_name.to_s
262
+
263
+ end
264
+
265
+
266
+ # Tells if the error class passed as an argument is handled
267
+ def handles?(error_class)
268
+ handlers.has_key?(error_class)
269
+ end
270
+
271
+
272
+
273
+ # Returns the appropriate step name to execute upon
274
+ # the given error class
275
+ def handler(error_class)
276
+ error_class.kind_of?(String) ? key = error_class : key = error_class.to_s
277
+ handlers.has_key?(key) ? handlers.fetch(key) : raise(WebFlowError.new, "There's no handler defined for the error class '#{key}'.")
278
+ end
279
+
280
+
281
+ # Tells if the given event name is a possibe outcome of this step
282
+ def has_an_outcome_for?(event_name)
283
+ outcomes.has_key? event_name.to_s
284
+ end
285
+
286
+
287
+ # Returns the step name associated to the given event.
288
+ def outcome(event_name)
289
+ outcomes.has_key?(event_name.to_s) ? outcomes.fetch(event_name.to_s) : raise(WebFlowError.new, "There's no outcome defined for the event name '#{event_name}'.")
290
+ end
291
+
292
+
293
+ protected
294
+
295
+ # Tells the framework if the step needs a definition in the declaring controller. For example, the
296
+ # view step doesn't need it. It can act without any step definition.
297
+ @definition_required = true
298
+
299
+
300
+ # Holds the different outcomes possible, which are
301
+ # defined via the 'on' method
302
+ def outcomes
303
+ @outcomes ||= {}
304
+ end
305
+
306
+
307
+ # Holds the methods to call upon errors
308
+ def handlers
309
+ @handlers ||= {}
310
+ end
311
+
312
+
313
+ # Call this method from within a subclass to get the correct
314
+ # method name to call. Pass a default value to return if nothing
315
+ # is defined for this step. Don't pass a value and it will return
316
+ # either nil or a method name.
317
+ def lookup_method_to_call( default_value=nil )
318
+ @_method ||= nil
319
+ @_method.nil? ? default_value.to_s : @_method
320
+ end
321
+
322
+
323
+ end
324
+ end
@@ -0,0 +1,77 @@
1
+ #--
2
+ # Copyright (c) 2007 The World in General
3
+ #
4
+ # Released under the Creative Commons Attribution-Share Alike 3.0 License.
5
+ # Licence details available at : http://creativecommons.org/licenses/by-sa/3.0/
6
+ #
7
+ # Created by Luc Boudreau ( lucboudreau at gmail )
8
+ #
9
+ # The above copyright notice and this permission notice shall be
10
+ # included in all copies or substantial portions of the Software.
11
+ #
12
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+ #++
20
+
21
+
22
+
23
+ module WebFlow
24
+
25
+ # This is a skeleton class to create plugins to the WebFlow framework.
26
+ # Any plugin MUST me a subclass of this superclass.
27
+ #
28
+ # Plugins are still under development so they are not activated yet.
29
+ class Plugin
30
+
31
+ # Method called by a controller when a system event for which this
32
+ # plugin is registered is reached.
33
+ def self::call(system_event)
34
+
35
+ # Execute the plugin
36
+ return_value = self.notify
37
+
38
+ # Make sure we got a system event back
39
+ raise(WebFlowError.new, "One of your plugin didn't return a system event, as required. The culprit is : " + self.inspect ) unless return_value.kind_of? WebFlow::SystemEvent
40
+
41
+ # Act accordingly
42
+ return return_value
43
+
44
+
45
+ end
46
+
47
+
48
+ protected
49
+
50
+ # Method to override in a plugin subclass. This method will be called
51
+ # when a controller encounters one of it's internal events
52
+ def self::notify( internal_event_name, controller, flow_data, request, response, session)
53
+
54
+ raise(WebFlowError.new, "One of your plugins didn't override the notify method. The culprit is : " + self.inspect)
55
+
56
+ end
57
+
58
+
59
+
60
+ # Allows a plugin to register itself as a listener of the WebFlow::Base class
61
+ # internal events.
62
+ def self::listen(*event_names)
63
+
64
+ event_names.each do |name|
65
+ WebFlow::Base.listen name.to_s, self
66
+ end
67
+
68
+ end
69
+
70
+ # Tells the notifier that this filter has done it's job and other filters
71
+ # may do whatever the see fit.
72
+ def self::resume
73
+ WebFlow::SystemEvent
74
+
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,48 @@
1
+ #--
2
+ # Copyright (c) 2007 The World in General
3
+ #
4
+ # Released under the Creative Commons Attribution-Share Alike 3.0 License.
5
+ # Licence details available at : http://creativecommons.org/licenses/by-sa/3.0/
6
+ #
7
+ # Created by Luc Boudreau ( lucboudreau at gmail )
8
+ #
9
+ # The above copyright notice and this permission notice shall be
10
+ # included in all copies or substantial portions of the Software.
11
+ #
12
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
13
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
14
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
15
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
16
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
17
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
18
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
19
+ #++
20
+
21
+ module WebFlow
22
+
23
+ # This class is used by the WebFlow framework to generate the
24
+ # FlowExecutionKey which uniquely identifies a user flow execution.
25
+ #
26
+ # It creates random alphanumeric strings with upper-case characters.
27
+ class RandomGenerator
28
+
29
+ # Defines which characters are used to generate random strings
30
+ Chars = ("A".."Z").to_a + ("0".."9").to_a
31
+
32
+ # Generates a random string of the given length. By default, it's
33
+ # 64 characters long.
34
+ def self::random_string(len = 64)
35
+
36
+ # Initial string
37
+ string = ""
38
+
39
+ # Pick characters at random
40
+ 1.upto(len) { |i| string << Chars[rand(Chars.size-1)] }
41
+
42
+ # Return the resulting string
43
+ return string.to_s
44
+
45
+ end
46
+
47
+ end
48
+ end