webflow 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|