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.
- data/.gitignore +6 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/README +0 -0
- data/Rakefile +1 -0
- data/lib/plugins/controller_level_error_handler.rb +79 -0
- data/lib/plugins/flow_resume_validations.rb +48 -0
- data/lib/plugins/new_flow_validations.rb +52 -0
- data/lib/webflow.rb +18 -0
- data/lib/webflow/action_step.rb +114 -0
- data/lib/webflow/base.rb +723 -0
- data/lib/webflow/base_helper.rb +312 -0
- data/lib/webflow/event.rb +82 -0
- data/lib/webflow/exceptions.rb +80 -0
- data/lib/webflow/flow_step.rb +324 -0
- data/lib/webflow/plugin.rb +77 -0
- data/lib/webflow/random_generator.rb +48 -0
- data/lib/webflow/session_handler.rb +315 -0
- data/lib/webflow/state.rb +70 -0
- data/lib/webflow/version.rb +3 -0
- data/lib/webflow/view_step.rb +238 -0
- data/webflow.gemspec +24 -0
- metadata +69 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm --create 1.9.2@webflow
|
data/Gemfile
ADDED
data/README
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,79 @@
|
|
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
|
+
|
26
|
+
# This class adds a couple validations before creating a new flow.
|
27
|
+
class Controller < WebFlow::Plugin
|
28
|
+
|
29
|
+
# Listen to the 'before_new_flow' internal event
|
30
|
+
self.listen :before_new_flow
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
# Method to override in a plugin subclass. This method will be called
|
36
|
+
# when a controller encounters one of it's internal events
|
37
|
+
def self::notify( system_event)
|
38
|
+
|
39
|
+
# Make sure there's a start_step defined
|
40
|
+
raise (WebFlowError.new, "Your controller must declare a start step name. Use 'start_step :step_name' and define this step in the mapping.") if controller.instance_variable_get("@_start_step").nil?
|
41
|
+
|
42
|
+
# Make sure there's an end_step defined
|
43
|
+
raise (WebFlowError.new, "Your controller must declare an end step name. Use 'end_step :step_name' and define this step in the mapping. I suggest using a view step which could redirect if you don't want to create a 'thank you' screen.") if controller.instance_variable_get("@_end_step").nil?
|
44
|
+
|
45
|
+
# Resume filter chain
|
46
|
+
resume
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
WebFlow::Base.class_eval do
|
57
|
+
|
58
|
+
protected
|
59
|
+
|
60
|
+
# Allows the controller to handle errors which were not handled by
|
61
|
+
# the steps themselves.
|
62
|
+
def upon
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
# Holds the methods to call upon errors
|
71
|
+
def handlers
|
72
|
+
@handlers ||= {}
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
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
|
+
|
22
|
+
|
23
|
+
|
24
|
+
module WebFlow
|
25
|
+
|
26
|
+
# This class adds a couple validations before creating a new flow.
|
27
|
+
class FlowResumeValidations < WebFlow::Plugin
|
28
|
+
|
29
|
+
# Listen to the 'before_new_flow' internal event
|
30
|
+
#self.listen :before_flow_resume
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
# Method to override in a plugin subclass. This method will be called
|
36
|
+
# when a controller encounters one of it's internal events
|
37
|
+
def self::notify( internal_event_name, controller, flow_data)
|
38
|
+
|
39
|
+
# Nothing... deactivated.
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
|
@@ -0,0 +1,52 @@
|
|
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
|
+
|
24
|
+
module WebFlow
|
25
|
+
|
26
|
+
# This class adds a couple validations before creating a new flow.
|
27
|
+
class NewFlowValidations < WebFlow::Plugin
|
28
|
+
|
29
|
+
# Listen to the 'before_new_flow' internal event
|
30
|
+
self.listen :before_new_flow
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
|
35
|
+
# Method to override in a plugin subclass. This method will be called
|
36
|
+
# when a controller encounters one of it's internal events
|
37
|
+
def self::notify( internal_event_name, controller, flow_data)
|
38
|
+
|
39
|
+
# Make sure there's a start_step defined
|
40
|
+
raise (WebFlowError.new, "Your controller must declare a start step name. Use 'start_step :step_name' and define this step in the mapping.") if controller.instance_variable_get("@_start_step").nil?
|
41
|
+
|
42
|
+
# Make sure there's an end_step defined
|
43
|
+
raise (WebFlowError.new, "Your controller must declare an end step name. Use 'end_step :step_name' and define this step in the mapping. I suggest using a view step which could redirect if you don't want to create a 'thank you' screen.") if controller.instance_variable_get("@_end_step").nil?
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
|
data/lib/webflow.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require "webflow/version"
|
2
|
+
|
3
|
+
# Load the basic stuff
|
4
|
+
require 'webflow/exceptions'
|
5
|
+
require 'webflow/base'
|
6
|
+
require 'webflow/session_handler'
|
7
|
+
require 'webflow/state'
|
8
|
+
require 'webflow/event'
|
9
|
+
require 'webflow/random_generator'
|
10
|
+
require 'webflow/flow_step'
|
11
|
+
require 'webflow/action_step'
|
12
|
+
require 'webflow/view_step'
|
13
|
+
#require 'webflow/plugin'
|
14
|
+
|
15
|
+
module WebFlow
|
16
|
+
# Reserve the render user event for the Base class.
|
17
|
+
WebFlow::Base.reserve_event :render
|
18
|
+
end
|
@@ -0,0 +1,114 @@
|
|
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
|
+
module WebFlow
|
23
|
+
|
24
|
+
# This class allows the controllers to create action steps. Action steps
|
25
|
+
# do nothing but call a method defined in the controller and then route
|
26
|
+
# the flow according to it's events mapping.
|
27
|
+
#
|
28
|
+
# == Simple usage
|
29
|
+
#
|
30
|
+
# action_step :action_name do
|
31
|
+
# on :success => :another_step
|
32
|
+
# on :back => :yet_another_step
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# The method identified by :action_name will simply be called and then the
|
36
|
+
# flow will be routed to either another_step or yet_another_step steps.
|
37
|
+
#
|
38
|
+
# One could also use the action_step instruction to batch define action
|
39
|
+
# steps. Simply call the action_step instruction and give it a comma separated
|
40
|
+
# list of step names.
|
41
|
+
#
|
42
|
+
# action_step :first_step, :second_step do
|
43
|
+
# (...)
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# == Inherited instructions
|
47
|
+
#
|
48
|
+
# The ViewStep class inherits all the standard step methods defined in FlowStep class.
|
49
|
+
#
|
50
|
+
#
|
51
|
+
class ActionStep < WebFlow::FlowStep
|
52
|
+
|
53
|
+
# Holds the view name to render once the execution is complete
|
54
|
+
@action_name
|
55
|
+
|
56
|
+
|
57
|
+
# Initializes the instance
|
58
|
+
def initialize( m_action_name )
|
59
|
+
|
60
|
+
@action_name = m_action_name
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
# This method is required by the WebFlow::Base and will be called
|
66
|
+
# once it relays the execution to this step instance
|
67
|
+
def execute(*args)
|
68
|
+
|
69
|
+
# Verify if the controller answers to the @action_name value
|
70
|
+
raise WebFlowError, "The action step #{@action_name} is not defined in your controller." unless args[0].respond_to? lookup_method_to_call(@action_name)
|
71
|
+
|
72
|
+
# The controller defines a method with the same name as @action_name.
|
73
|
+
# Kewl! A 'someone else problem' !!!
|
74
|
+
args[0].send( lookup_method_to_call( @action_name ) )
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
# Let's create the view_step definition inside the WebFlow::Base
|
83
|
+
# controller class
|
84
|
+
WebFlow::Base.class_eval do
|
85
|
+
|
86
|
+
protected
|
87
|
+
|
88
|
+
# Called to define a step into a flow. Example of usage :
|
89
|
+
#
|
90
|
+
# view_step :view_name do
|
91
|
+
# on :success => :another_step
|
92
|
+
# on :back => :yet_another_step
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# See the ActionStep class documentation for more options.
|
96
|
+
#
|
97
|
+
def action_step(*action_name, &block)
|
98
|
+
|
99
|
+
# Instantiate a ViewStep object if symbol or string received
|
100
|
+
action_name.each do |name|
|
101
|
+
|
102
|
+
step = WebFlow::ActionStep.new(name.to_s)
|
103
|
+
|
104
|
+
# Register this step in the flow repository
|
105
|
+
register name.to_s, step
|
106
|
+
|
107
|
+
# Execute the config block
|
108
|
+
step.instance_eval(&block) if block_given?
|
109
|
+
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
data/lib/webflow/base.rb
ADDED
@@ -0,0 +1,723 @@
|
|
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
|
+
module WebFlow
|
23
|
+
|
24
|
+
|
25
|
+
# The WebFlow framework turns an ActionController into a flow capable
|
26
|
+
# controller. It can handle event triggering, the back button and any
|
27
|
+
# user defined step. Here are the main concepts.
|
28
|
+
#
|
29
|
+
# == Concepts
|
30
|
+
#
|
31
|
+
# === Flow
|
32
|
+
#
|
33
|
+
# A flow is a logical procedure which defines how to handle different
|
34
|
+
# steps and guide the user through them.
|
35
|
+
#
|
36
|
+
# === Step
|
37
|
+
#
|
38
|
+
# A step is a single unit of processing included inside a flow. All steps
|
39
|
+
# have to inherit from WebFlow::FlowStep. They can be used to define one
|
40
|
+
# of the following :
|
41
|
+
#
|
42
|
+
# - ViewStep : displays a view and then relays the execution to methods or steps according to the triggered event.
|
43
|
+
#
|
44
|
+
# - ActionStep : calls a method of an WebFlow controller and then relays the execution to methods or steps according to the triggered event.
|
45
|
+
#
|
46
|
+
# More step types can be created to extend the WebFlow framework.
|
47
|
+
#
|
48
|
+
# === Event
|
49
|
+
#
|
50
|
+
# An event is nothing more than a symbol which triggers the execution of methods
|
51
|
+
# or steps, depending on the mapping. There are reserved events which cannot be
|
52
|
+
# mapped by a user inside of a subclass of WebFlow::Base controllers. To ask
|
53
|
+
# the controller if an event name has been reserved for internal purposes, use :
|
54
|
+
#
|
55
|
+
# WebFlow::Base.reserved_event? :event_name_to_verify
|
56
|
+
#
|
57
|
+
# == Creating a mapping
|
58
|
+
#
|
59
|
+
# The mapping is where you tell your controller how to connect the different steps.
|
60
|
+
#
|
61
|
+
# To declare a controller mapping, you must do it in the object's <tt>initialize</tt>
|
62
|
+
# method. This method will be automatically called upon the instantiation of your
|
63
|
+
# controller.
|
64
|
+
#
|
65
|
+
# === Simple mapping example
|
66
|
+
#
|
67
|
+
# class MyController < WebFlow::Base
|
68
|
+
#
|
69
|
+
# def initialize
|
70
|
+
#
|
71
|
+
# start_with :step_1
|
72
|
+
# end_with :end_my_flow
|
73
|
+
# redirect_invalid_flows :step_1
|
74
|
+
#
|
75
|
+
# upon :StandardError => :end_my_flow
|
76
|
+
#
|
77
|
+
# action_step :step_1 do
|
78
|
+
# method :start_my_flow
|
79
|
+
# on :success => :step_2
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# view_step :step_2 do
|
83
|
+
# on :finish => end_my_flow
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# view_step :end_my_flow
|
87
|
+
#
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# def start_my_flow
|
91
|
+
# # Nothing to be done
|
92
|
+
# event :success
|
93
|
+
# end
|
94
|
+
#
|
95
|
+
# end
|
96
|
+
#
|
97
|
+
# Let's decompose what we just did in this very minimalist controller.
|
98
|
+
#
|
99
|
+
# * The flow starts with the step named <tt>step_1</tt>.
|
100
|
+
# * The flow data will be destroyed once we reach the <tt>end_my_flow</tt> step.
|
101
|
+
# * If the user sends an invalid key or his flow data is expired, we redirect the flow to the <tt>step_1</tt> step.
|
102
|
+
# * If an error of class StandardError or any subclass of it is raised and not handled by the steps, the controller will route the flow to the <tt>end_my_flow</tt> step.
|
103
|
+
# * We declared a step named <tt>step_1</tt>. The step is implemented via the method named <tt>start_my_flow</tt>. If the <tt>success</tt> event is returned, we route the flow to the step named <tt>step_2</tt>.
|
104
|
+
# * We declared a step named <tt>step_2</tt>. If the <tt>finish</tt> event is returned, the step named <tt>end_my_flow</tt> will be called.
|
105
|
+
#
|
106
|
+
# There are many other config options which can be used. I strongly suggest reading
|
107
|
+
# the whole API documentation.
|
108
|
+
#
|
109
|
+
# == Reserved events
|
110
|
+
#
|
111
|
+
# The WebFlow::Base controller reserves the following event names for
|
112
|
+
# itself.
|
113
|
+
#
|
114
|
+
# - render : Tells the framework that we must return the control to the view
|
115
|
+
# parser, since one of the 'render' method has been called and we
|
116
|
+
# are now ready to display something.
|
117
|
+
#
|
118
|
+
# == About Plugins
|
119
|
+
#
|
120
|
+
# The WebFlow framework has a plugin system, but is unstable and
|
121
|
+
# incomplete. Don't bother using it for now, unless you want to contribute
|
122
|
+
# of course. This class is stable and ready for plugin registration though.
|
123
|
+
# The Plugin class is not. To activate the plugins system, go to the
|
124
|
+
# #{ACTIONFLOW_ROOT}/webflow/webflow.rb file and uncomment the
|
125
|
+
# plugins initialisation code.
|
126
|
+
#
|
127
|
+
class Base < ApplicationController
|
128
|
+
|
129
|
+
|
130
|
+
# Defines the symbols name prefix which are used to identify the next events.
|
131
|
+
# As an example, in a view_state, the button used to trigger the save event
|
132
|
+
# would be named '_event_save'. If we were in an action_state, to trigger the
|
133
|
+
# success event, we would 'return :_event_success'
|
134
|
+
@@event_prefix = '_event_'
|
135
|
+
cattr_accessor :event_prefix
|
136
|
+
|
137
|
+
|
138
|
+
# Defines the name of the parameter submitted which contains the next event name.
|
139
|
+
@@event_input_name_prefix = '_submit'
|
140
|
+
cattr_accessor :event_input_name_prefix
|
141
|
+
|
142
|
+
|
143
|
+
# Defines the regex used to validate the proper format of a submitted
|
144
|
+
# event name.
|
145
|
+
Event_input_name_regex = /^#{event_input_name_prefix}#{event_prefix}([a-zA-Z0-9_]*)$/
|
146
|
+
|
147
|
+
|
148
|
+
# Defines the name of the variable which holds the flow unique state id.
|
149
|
+
# As an example, a view_state would add a hidden field named 'flow_exec_key',
|
150
|
+
# which would contain the key used to store a state in the session.
|
151
|
+
#
|
152
|
+
# We could then restore the state by accessing it with :
|
153
|
+
# session[:flow_data-1234abcd].fetch( params[:flow_exec_key] )
|
154
|
+
#
|
155
|
+
# See the SessionHandler for more details
|
156
|
+
@@flow_execution_key_id = 'flow_exec_key'
|
157
|
+
cattr_accessor :flow_execution_key_id
|
158
|
+
|
159
|
+
|
160
|
+
# Aliases the 'render' method to intercept it and cache it's execution results.
|
161
|
+
# Doing this allows to delay the calls to the render method until the end of the
|
162
|
+
# current step chain so that if a plugin wishes to interrupt the chain and render
|
163
|
+
# some other content, there won't be any conflict between previous calls to the
|
164
|
+
# render method.
|
165
|
+
alias_method :old_render, :render
|
166
|
+
|
167
|
+
|
168
|
+
# Aliases the 'redirect_to' method to intercept it and cache it's execution results.
|
169
|
+
# Doing this allows to delay the calls to the redirect_to method until the end of the
|
170
|
+
# current step chain so that if a plugin wishes to interrupt the chain and redirect
|
171
|
+
# the request, there won't be any conflict between previous calls to the
|
172
|
+
# redirect_to method.
|
173
|
+
alias_method :old_redirect_to, :redirect_to
|
174
|
+
|
175
|
+
|
176
|
+
# Class method to tell the WebFlow framework to prevent users from mapping certain
|
177
|
+
# event names. Use it as :
|
178
|
+
#
|
179
|
+
# WebFlow::Base.reserve_event :some_event_name
|
180
|
+
#
|
181
|
+
def self::reserve_event(event_name)
|
182
|
+
|
183
|
+
# Reserve the event passed as a parameter
|
184
|
+
reserved_events.push( event_name.to_s )
|
185
|
+
|
186
|
+
# Remove duplicates
|
187
|
+
reserved_events.uniq!
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
|
192
|
+
# Class method to tell the WebFlow framework to notify a plugin upon
|
193
|
+
# a certain internal event happening. Use it in a plugin class as follows :
|
194
|
+
#
|
195
|
+
# WebFlow::Base.listen :some_event, self
|
196
|
+
#
|
197
|
+
def self::listen(internal_event_name, plugin)
|
198
|
+
|
199
|
+
# Reserve the event passed as a parameter but first validate
|
200
|
+
# that it's a subclass of WebFlow::Plugin
|
201
|
+
#plugin.kind_of?(WebFlow::Plugin) ? filters.fetch(internal_event_name.to_s).push(plugin) : raise(WebFlowError.new, "One of your plugins tried to register itself as a listener and is not a subclass of WebFlow::Plugin. The culprit is : " + plugin.inspect )
|
202
|
+
|
203
|
+
filters.fetch(internal_event_name.to_s).push(plugin)
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
# Class method to ask the WebFlow framework if a given event name has been reserved
|
209
|
+
# and can't be mapped by users.
|
210
|
+
#
|
211
|
+
# WebFlow::Base.reserved_event? :some_event_name
|
212
|
+
#
|
213
|
+
def self::reserved_event?(event_name)
|
214
|
+
|
215
|
+
reserved_events.include? event_name.to_s
|
216
|
+
|
217
|
+
end
|
218
|
+
|
219
|
+
|
220
|
+
# Default method to handle the requests. All the dispatching is done here.
|
221
|
+
def index
|
222
|
+
|
223
|
+
# Holds the calls to the render method to delay their execution
|
224
|
+
@renders = Array.new
|
225
|
+
|
226
|
+
# Holds the calls to the redirect method to delay it's execution
|
227
|
+
@redirects = Array.new
|
228
|
+
|
229
|
+
begin
|
230
|
+
|
231
|
+
# Notify the plugins we got here
|
232
|
+
#notify :request_entry
|
233
|
+
|
234
|
+
# Tells which was the last state
|
235
|
+
@flow_id = params[flow_execution_key_id]
|
236
|
+
|
237
|
+
# Flag used to know if we reached the end_step
|
238
|
+
@end_step_reached = false
|
239
|
+
|
240
|
+
# Check if the flow has already started
|
241
|
+
if @flow_id && !@flow_id.empty?
|
242
|
+
|
243
|
+
# Notify plugins we've just re-started a flow
|
244
|
+
#notify :before_flow_resume
|
245
|
+
|
246
|
+
# Get the event name
|
247
|
+
raise(WebFlowError.new, "Your view did not submit properly to an event name.") unless event_name = url_event_value(params)
|
248
|
+
|
249
|
+
# Make sure there's data associated to this flow
|
250
|
+
return nil if redirect_invalid_flow!
|
251
|
+
|
252
|
+
# We need to know where we came from
|
253
|
+
last_step = fetch_last_step_name
|
254
|
+
|
255
|
+
# Make sure that the step has an outcome defined for this event
|
256
|
+
raise(WebFlowError.new,
|
257
|
+
"No outcome has been defined for the event '#{event_name}' on the step named '#{last_step}' as specified in the submitted data. Use the 'on' method on your mapped step or make sure that you are submitting valid data.") unless step_registry.fetch(last_step).has_an_outcome_for?(event_name)
|
258
|
+
|
259
|
+
# Before doing anything, we create a new state so we can restore the previous one with
|
260
|
+
# the back button.
|
261
|
+
#serialize
|
262
|
+
|
263
|
+
# Call the resulting step associated to this event
|
264
|
+
execute_step step_registry.fetch(last_step).outcome(event_name)
|
265
|
+
|
266
|
+
else
|
267
|
+
|
268
|
+
# Notify plugins we've just started a new flow
|
269
|
+
#notify :before_new_flow
|
270
|
+
|
271
|
+
# Make sure there's a start_step defined
|
272
|
+
raise(WebFlowError.new,
|
273
|
+
"Your controller must declare a start step name. Use 'start_with :step_name' and define this step in the mapping.") if start_step.nil?
|
274
|
+
|
275
|
+
# Make sure there's an end_step defined
|
276
|
+
raise(WebFlowError.new,
|
277
|
+
"Your controller must declare an end step name. Use 'end_with :step_name' and define this step in the mapping. I suggest using a view step which could redirect if you don't want to create a 'thank you' screen.") if end_step.nil?
|
278
|
+
|
279
|
+
# Start a new flow session storage
|
280
|
+
start_new_flow_session_storage
|
281
|
+
|
282
|
+
# Notify plugins
|
283
|
+
#notify :before_step_execution_chain
|
284
|
+
|
285
|
+
# Execute the start_step
|
286
|
+
execute_step start_step
|
287
|
+
|
288
|
+
end
|
289
|
+
|
290
|
+
# Notify plugins
|
291
|
+
#notify :after_step_execution_chain
|
292
|
+
|
293
|
+
# Cleanup the flows which have been hanging too long in the session placeholder
|
294
|
+
cleanup
|
295
|
+
|
296
|
+
# Check if we continue
|
297
|
+
terminate if @end_step_reached
|
298
|
+
|
299
|
+
# Execute all the cached render calls
|
300
|
+
render!
|
301
|
+
|
302
|
+
# Execute all the cached redirect calls
|
303
|
+
redirect!
|
304
|
+
|
305
|
+
# Rescue interruptions thrown by the plugins
|
306
|
+
#rescue PluginInterruptionError => error
|
307
|
+
|
308
|
+
# We execute the interruption block
|
309
|
+
#instance_eval &error.block
|
310
|
+
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
|
315
|
+
protected
|
316
|
+
|
317
|
+
|
318
|
+
# Overrides the calls to the 'render' method so that renders are delayed
|
319
|
+
# until the end of the execution chain.
|
320
|
+
#
|
321
|
+
# Prevents conflicts between regular rendering and subsequent plugin interruptions.
|
322
|
+
#
|
323
|
+
def render(options = nil, deprecated_status = nil, &block)
|
324
|
+
|
325
|
+
# Cache the parameters
|
326
|
+
@renders.push( :options => options, :deprecated_status => deprecated_status, :block => block )
|
327
|
+
|
328
|
+
end
|
329
|
+
|
330
|
+
|
331
|
+
# Executes the 'render' method calls intercepted while executing the
|
332
|
+
# step execution chain.
|
333
|
+
def render!
|
334
|
+
|
335
|
+
@renders.each do |parameters|
|
336
|
+
|
337
|
+
old_render parameters[:options], parameters[:deprecated_status] do parameters[:block].call end
|
338
|
+
|
339
|
+
end
|
340
|
+
|
341
|
+
end
|
342
|
+
|
343
|
+
|
344
|
+
# Overrides the calls to the 'redirect_to' method so that redirects are delayed
|
345
|
+
# until the end of the execution chain.
|
346
|
+
#
|
347
|
+
# Prevents conflicts between regular redirection and subsequent plugin interruptions.
|
348
|
+
#
|
349
|
+
def redirect_to(options = {}, response_status = {})
|
350
|
+
|
351
|
+
# Cache the parameters
|
352
|
+
@redirects.push( :options => options, :response_status => response_status )
|
353
|
+
|
354
|
+
end
|
355
|
+
|
356
|
+
|
357
|
+
# Executes the 'render' method calls intercepted while executing the
|
358
|
+
# step execution chain.
|
359
|
+
def redirect!
|
360
|
+
|
361
|
+
@redirects.each do |parameters|
|
362
|
+
|
363
|
+
old_redirect_to parameters[:options], parameters[:response_status]
|
364
|
+
|
365
|
+
end
|
366
|
+
|
367
|
+
end
|
368
|
+
|
369
|
+
|
370
|
+
# Adds a step name to the registry and associates it to an object
|
371
|
+
# which will be used to handle a given step when necessary.
|
372
|
+
def register(step_name, step)
|
373
|
+
step_registry.store(step_name.to_s, step)
|
374
|
+
end
|
375
|
+
|
376
|
+
|
377
|
+
# Called in a mapping to define the end point of the flow. Once this
|
378
|
+
# step finishes it's execution, the flow data is deleted.
|
379
|
+
# Pass it a symbol which bears the same name as a defined step.
|
380
|
+
# ie.
|
381
|
+
#
|
382
|
+
# end_with :defined_step_name
|
383
|
+
#
|
384
|
+
# would point to the definition :
|
385
|
+
#
|
386
|
+
# view_state :defined_step_name do
|
387
|
+
# (...)
|
388
|
+
# end
|
389
|
+
#
|
390
|
+
def end_with(step_name)
|
391
|
+
@_end_step = step_name.to_s
|
392
|
+
end
|
393
|
+
|
394
|
+
|
395
|
+
# Called in a mapping to define the starting point of the flow.
|
396
|
+
# Pass it a symbol which bears the same name as a defined step.
|
397
|
+
# ie.
|
398
|
+
#
|
399
|
+
# start_with :defined_step_name
|
400
|
+
#
|
401
|
+
# would point to the definition :
|
402
|
+
#
|
403
|
+
# view_state :defined_step_name do
|
404
|
+
# (...)
|
405
|
+
# end
|
406
|
+
#
|
407
|
+
def start_with(step_name)
|
408
|
+
@_start_step = step_name.to_s
|
409
|
+
end
|
410
|
+
|
411
|
+
|
412
|
+
# Called in a mapping to define which method should be called
|
413
|
+
# if the user submits an invalid flow id. Use it as :
|
414
|
+
#
|
415
|
+
# redirect_invalid_flows :url => { :controller => :my_controller, :action => :index }
|
416
|
+
#
|
417
|
+
def redirect_invalid_flows( url={:action=>:index} )
|
418
|
+
@_no_flow_url = url
|
419
|
+
end
|
420
|
+
|
421
|
+
|
422
|
+
# Allows the controller to handle errors which were not handled by
|
423
|
+
# the steps themselves.
|
424
|
+
#
|
425
|
+
# class MyController < WebFlow::Base
|
426
|
+
# def initialize
|
427
|
+
# (...)
|
428
|
+
# upon :WebFlowError => :step_name
|
429
|
+
# end
|
430
|
+
# (...)
|
431
|
+
# end
|
432
|
+
#
|
433
|
+
# This is also possible :
|
434
|
+
#
|
435
|
+
# class MyController < WebFlow::Base
|
436
|
+
# def initialize
|
437
|
+
# (...)
|
438
|
+
# upon { :WebFlowError => :step_name,
|
439
|
+
# :WhateverError => :step_name }
|
440
|
+
# end
|
441
|
+
# (...)
|
442
|
+
# end
|
443
|
+
#
|
444
|
+
# Only subclasses of StandardError will be rescued. This means that RuntimeError
|
445
|
+
# cannot be handled.
|
446
|
+
#
|
447
|
+
def upon(hash)
|
448
|
+
|
449
|
+
# Make sure we received a hash
|
450
|
+
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)
|
451
|
+
|
452
|
+
# Make sure the key is not used twice in a definition
|
453
|
+
# to enforce coherence of the mapping.
|
454
|
+
hash.each_key do |key|
|
455
|
+
raise(WebFlowError.new, "An error of the class '#{key}' is already mapped. They must be unique within a controller scope.") if handlers.has_key?(key.to_s)
|
456
|
+
end
|
457
|
+
|
458
|
+
# Store the values in the handlers hash
|
459
|
+
hash.each { |key,value| handlers.store key.to_s, value.to_s }
|
460
|
+
|
461
|
+
end
|
462
|
+
|
463
|
+
|
464
|
+
# Helper method called from subclasses to return events.
|
465
|
+
#
|
466
|
+
# Use it as :
|
467
|
+
#
|
468
|
+
# def my_action
|
469
|
+
#
|
470
|
+
# # Do something
|
471
|
+
#
|
472
|
+
# # Now return an event
|
473
|
+
# event :success
|
474
|
+
#
|
475
|
+
# end
|
476
|
+
#
|
477
|
+
def event(name)
|
478
|
+
WebFlow::Event.new(name.to_s)
|
479
|
+
end
|
480
|
+
|
481
|
+
|
482
|
+
private
|
483
|
+
|
484
|
+
# Launches the execution of a given step name. Encountered errors and events
|
485
|
+
# will be managed as defined in the mapping.
|
486
|
+
#
|
487
|
+
# If a step returns one of the reserved event names defined in the class variable,
|
488
|
+
# the execution chain will be stopped and the event will be returned to the
|
489
|
+
# caller of the execute_step method.
|
490
|
+
#
|
491
|
+
def execute_step(step_name)
|
492
|
+
|
493
|
+
# Notify plugins we've just started a new flow
|
494
|
+
#notify :before_any_step_execution
|
495
|
+
|
496
|
+
@step_name = step_name = step_name.to_s
|
497
|
+
|
498
|
+
# Make sure that this step is registered
|
499
|
+
raise(WebFlowError.new, "The desired step ( #{step_name} ) is not present in the mapping.") unless step_registry.has_key? step_name
|
500
|
+
|
501
|
+
# Validate that the controller will answer to the message
|
502
|
+
raise(WebFlowError.new, "Your controller doesn't have a method called '#{step_name}' as defined in your mapping.") if !respond_to?(step_name) && step_registry.fetch(step_name).definition_required?
|
503
|
+
|
504
|
+
# Everything is ready for execution, we just have to memorize that this step
|
505
|
+
# was called.
|
506
|
+
persist_last_step_name step_name
|
507
|
+
|
508
|
+
# Encapsulate the execution in a begin block to manage
|
509
|
+
# the errors. Runtime errors are not managed. Only subclasses
|
510
|
+
# of StandardError will be.
|
511
|
+
begin
|
512
|
+
|
513
|
+
# Execute the step and keep the returned value
|
514
|
+
return_value = step_registry.fetch(step_name).send :execute, self
|
515
|
+
|
516
|
+
# Notify plugins we've just started a new flow
|
517
|
+
#notify :after_any_step_execution
|
518
|
+
|
519
|
+
# Check if this was the end step, if so, cut the execution short
|
520
|
+
if end_step.to_s == fetch_last_step_name
|
521
|
+
|
522
|
+
# Raise that flag
|
523
|
+
@end_step_reached = true
|
524
|
+
|
525
|
+
#Notify plugins
|
526
|
+
#notify :after_end_step
|
527
|
+
|
528
|
+
# Terminate execution chain
|
529
|
+
return
|
530
|
+
end
|
531
|
+
|
532
|
+
# Make sure that the step returned an event object
|
533
|
+
raise(WebFlowError.new, "Your step definition named '#{step_name}' didn't return an WebFlow::Event object. All steps must return either events or errors.") unless return_value.kind_of? WebFlow::Event
|
534
|
+
|
535
|
+
# If the event name was reserved in the class registry by someone, ignore the
|
536
|
+
# event and return the event to the caller
|
537
|
+
if self.class.reserved_event? return_value.name
|
538
|
+
|
539
|
+
return return_value
|
540
|
+
|
541
|
+
else
|
542
|
+
|
543
|
+
# This event name wasn't reserved, so let's call the outcome.
|
544
|
+
|
545
|
+
# Make sure that the step has an outcome defined for this event
|
546
|
+
raise(WebFlowError.new, "No outcome has been defined for the event '#{return_value.name}'. Use the 'on' method on your mapped step.") unless step_registry.fetch(step_name).has_an_outcome_for?(return_value.name.to_s)
|
547
|
+
|
548
|
+
# Call recursively the step mapped to the returned event
|
549
|
+
value = execute_step step_registry.fetch(step_name).outcome(return_value.name)
|
550
|
+
|
551
|
+
return value
|
552
|
+
|
553
|
+
end
|
554
|
+
|
555
|
+
# Rescue any subclass of StandardError
|
556
|
+
rescue StandardError => error
|
557
|
+
|
558
|
+
# Ask the step if he handles this error
|
559
|
+
if step_registry.fetch(step_name).handles? error.class.to_s
|
560
|
+
|
561
|
+
# Notify plugins
|
562
|
+
#notify :upon_handled_error
|
563
|
+
|
564
|
+
# Get the handler step name
|
565
|
+
handler_name = step_registry.fetch(step_name).handler(error.class.to_s)
|
566
|
+
|
567
|
+
# Make sure this handler is in the registry
|
568
|
+
raise(WebFlowError.new, "The error handling step ( #{handler_name} ) is not present in the mapping.") unless step_registry.has_key? handler_name
|
569
|
+
|
570
|
+
# Execute the error handling step recursively
|
571
|
+
execute_step handler_name
|
572
|
+
|
573
|
+
# Check if the controller has something to say about it.
|
574
|
+
elsif handlers.has_key? error.class.to_s
|
575
|
+
|
576
|
+
# Notify plugins
|
577
|
+
#notify :upon_handled_error
|
578
|
+
|
579
|
+
# Get the handler step name
|
580
|
+
handler_name = handlers.fetch error.class.to_s
|
581
|
+
|
582
|
+
# Make sure this handler is in the registry
|
583
|
+
# Execute the error handling step recursively if present
|
584
|
+
step_registry.has_key?(handler_name) ? execute_step(handler_name) : raise(WebFlowError.new, "The error handling step ( #{handler_name} ) is not present in the mapping.")
|
585
|
+
|
586
|
+
|
587
|
+
# Nobody to route to
|
588
|
+
else
|
589
|
+
|
590
|
+
# Notify plugins
|
591
|
+
#notify :upon_unhandled_error
|
592
|
+
|
593
|
+
# Well, we've tried. It's now an official 'Someone Else Problem'
|
594
|
+
raise error
|
595
|
+
|
596
|
+
end
|
597
|
+
|
598
|
+
end
|
599
|
+
|
600
|
+
# Notify plugins
|
601
|
+
notify :after_any_step_execution
|
602
|
+
|
603
|
+
end
|
604
|
+
|
605
|
+
|
606
|
+
# Called in a mapping to define which method should be called
|
607
|
+
# if the user submits an invalid flow id
|
608
|
+
def redirect_invalid_flow!
|
609
|
+
|
610
|
+
unless validate_flow_existence
|
611
|
+
|
612
|
+
unless @_no_flow_url ||= nil
|
613
|
+
|
614
|
+
raise(WebFlowError.new, "No flow data could be found for the given flow key. Your session doesn't exist.")
|
615
|
+
|
616
|
+
else
|
617
|
+
|
618
|
+
redirect_to @_no_flow_url
|
619
|
+
|
620
|
+
return true
|
621
|
+
|
622
|
+
end
|
623
|
+
|
624
|
+
end
|
625
|
+
|
626
|
+
false
|
627
|
+
|
628
|
+
end
|
629
|
+
|
630
|
+
|
631
|
+
# Makes the step_registry available and instantiates it,
|
632
|
+
# if necessary.
|
633
|
+
def step_registry
|
634
|
+
@_step_registry ||= {}
|
635
|
+
end
|
636
|
+
|
637
|
+
# Makes the handlers available and instantiates it
|
638
|
+
# if necessary.
|
639
|
+
def handlers
|
640
|
+
@_handlers ||= {}
|
641
|
+
end
|
642
|
+
|
643
|
+
# Makes the start_step available and instantiates it
|
644
|
+
# if necessary.
|
645
|
+
def start_step
|
646
|
+
@_start_step ||= nil
|
647
|
+
end
|
648
|
+
|
649
|
+
# Makes the end_step available and instantiates it
|
650
|
+
# if necessary.
|
651
|
+
def end_step
|
652
|
+
@_end_step ||= nil
|
653
|
+
end
|
654
|
+
|
655
|
+
# Returns the array of event names which cannot be mapped by users.
|
656
|
+
# These names are class level, so they are shared by all the instances
|
657
|
+
# of WebFlow::Base.
|
658
|
+
def self::reserved_events
|
659
|
+
|
660
|
+
# Holds the reserved event names that cannot be mapped by users.
|
661
|
+
@@_reserved_events ||= Array.new
|
662
|
+
|
663
|
+
end
|
664
|
+
|
665
|
+
|
666
|
+
# This method returns a basic set of internal filter handles on which the plugin
|
667
|
+
# system can tap into. When developing a plugin, we can call the WebFlow::Base.listen
|
668
|
+
# class method to get called upon the encounter of a given internal event.
|
669
|
+
def self::filters
|
670
|
+
|
671
|
+
@@_filters ||= { 'request_entry' => Array.new,
|
672
|
+
'before_new_flow' => Array.new,
|
673
|
+
'before_flow_resume' => Array.new,
|
674
|
+
'before_any_step_execution' => Array.new,
|
675
|
+
'after_any_step_execution' => Array.new,
|
676
|
+
'before_step_execution_chain' => Array.new,
|
677
|
+
'after_step_execution_chain' => Array.new,
|
678
|
+
'upon_handled_error' => Array.new,
|
679
|
+
'upon_unhandled_error' => Array.new,
|
680
|
+
'after_end_step' => Array.new
|
681
|
+
}
|
682
|
+
|
683
|
+
end
|
684
|
+
|
685
|
+
|
686
|
+
# Internal method used to notify all the listeners of a given event that
|
687
|
+
# we just reached one of the internal events
|
688
|
+
def notify(internal_event_name)
|
689
|
+
|
690
|
+
# Get the array of listeners for a given event
|
691
|
+
WebFlow::Base.filters.has_key?(internal_event_name.to_s) ? listeners = WebFlow::Base.filters.fetch(internal_event_name.to_s) : raise(WebFlowError.new, "Internal programming error...")
|
692
|
+
|
693
|
+
# Iterate over the listeners
|
694
|
+
listeners.each do |plugin|
|
695
|
+
|
696
|
+
# Notify each one of the event
|
697
|
+
plugin.send :notify, WebFlow::SystemEvent.new(internal_event_name.to_s, self, current_flow_data, request, response, session)
|
698
|
+
|
699
|
+
end
|
700
|
+
|
701
|
+
end
|
702
|
+
|
703
|
+
|
704
|
+
# Searches the parameters for the event name passed from the view.
|
705
|
+
# Returns nil if it can't be found.
|
706
|
+
def url_event_value(param_hash)
|
707
|
+
|
708
|
+
# Iterate over the parameters received
|
709
|
+
param_hash.each do |key,value|
|
710
|
+
|
711
|
+
# Return regex group 1 if we have a match
|
712
|
+
return $1 if key.to_s =~ Event_input_name_regex
|
713
|
+
|
714
|
+
end
|
715
|
+
|
716
|
+
# We didn't find any event name, return nil as the contract says.
|
717
|
+
nil
|
718
|
+
|
719
|
+
end
|
720
|
+
|
721
|
+
end
|
722
|
+
|
723
|
+
end
|