volt 0.7.23 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +8 -1
- data/CHANGELOG.md +22 -0
- data/Gemfile +8 -0
- data/Guardfile +2 -2
- data/Readme.md +139 -136
- data/VERSION +1 -1
- data/app/volt/assets/js/setImmediate.js +175 -0
- data/app/volt/tasks/live_query/data_store.rb +0 -2
- data/app/volt/tasks/live_query/live_query.rb +4 -4
- data/docs/GETTING_STARTED.md +24 -3
- data/docs/WHY.md +1 -22
- data/lib/volt.rb +20 -1
- data/lib/volt/console.rb +20 -0
- data/lib/volt/controllers/model_controller.rb +25 -11
- data/lib/volt/extra_core/object.rb +2 -14
- data/lib/volt/extra_core/string.rb +4 -0
- data/lib/volt/models.rb +0 -1
- data/lib/volt/models/array_model.rb +8 -16
- data/lib/volt/models/cursor.rb +1 -1
- data/lib/volt/models/model.rb +40 -60
- data/lib/volt/models/model_hash_behaviour.rb +10 -24
- data/lib/volt/models/model_helpers.rb +2 -2
- data/lib/volt/models/model_state.rb +1 -1
- data/lib/volt/models/model_wrapper.rb +4 -4
- data/lib/volt/models/persistors/array_store.rb +44 -28
- data/lib/volt/models/persistors/base.rb +1 -1
- data/lib/volt/models/persistors/model_store.rb +1 -1
- data/lib/volt/models/persistors/params.rb +5 -1
- data/lib/volt/models/persistors/query/query_listener.rb +2 -0
- data/lib/volt/models/persistors/store.rb +3 -2
- data/lib/volt/models/persistors/store_state.rb +7 -2
- data/lib/volt/models/url.rb +35 -29
- data/lib/volt/models/validations.rb +7 -17
- data/lib/volt/page/bindings/attribute_binding.rb +57 -39
- data/lib/volt/page/bindings/base_binding.rb +0 -14
- data/lib/volt/page/bindings/content_binding.rb +15 -18
- data/lib/volt/page/bindings/each_binding.rb +67 -34
- data/lib/volt/page/bindings/if_binding.rb +15 -12
- data/lib/volt/page/bindings/template_binding.rb +77 -59
- data/lib/volt/page/bindings/template_binding/grouped_controllers.rb +19 -4
- data/lib/volt/page/channel.rb +22 -38
- data/lib/volt/page/channel_stub.rb +3 -6
- data/lib/volt/page/page.rb +24 -26
- data/lib/volt/page/string_template_renderer.rb +46 -0
- data/lib/volt/page/sub_context.rb +7 -1
- data/lib/volt/page/targets/binding_document/component_node.rb +11 -9
- data/lib/volt/page/tasks.rb +3 -2
- data/lib/volt/page/url_tracker.rb +4 -3
- data/lib/volt/reactive/computation.rb +131 -0
- data/lib/volt/reactive/dependency.rb +71 -0
- data/lib/volt/reactive/eventable.rb +82 -0
- data/lib/volt/reactive/hash_dependency.rb +36 -0
- data/lib/volt/{controllers → reactive}/reactive_accessors.rb +8 -11
- data/lib/volt/reactive/reactive_array.rb +100 -193
- data/lib/volt/reactive/reactive_hash.rb +49 -0
- data/lib/volt/server/html_parser/attribute_scope.rb +24 -4
- data/lib/volt/server/html_parser/if_view_scope.rb +15 -15
- data/lib/volt/server/html_parser/view_scope.rb +31 -1
- data/spec/apps/kitchen_sink/Gemfile +4 -8
- data/spec/apps/kitchen_sink/app/main/config/dependencies.rb +8 -0
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +8 -1
- data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +8 -0
- data/spec/apps/kitchen_sink/app/main/views/main/bindings.html +73 -0
- data/spec/apps/kitchen_sink/app/main/views/main/index.html +6 -1
- data/spec/apps/kitchen_sink/app/main/views/main/main.html +26 -6
- data/spec/apps/kitchen_sink/app/main/views/main/store.html +6 -0
- data/spec/controllers/reactive_accessors_spec.rb +13 -15
- data/spec/integration/bindings_spec.rb +159 -0
- data/spec/integration/templates_spec.rb +15 -0
- data/spec/models/model_spec.rb +130 -228
- data/spec/reactive/computation_spec.rb +63 -0
- data/spec/reactive/dependency_spec.rb +5 -0
- data/spec/reactive/eventable_spec.rb +48 -0
- data/spec/reactive/reactive_array_spec.rb +97 -0
- data/spec/router/routes_spec.rb +26 -27
- data/spec/server/html_parser/view_parser_spec.rb +3 -21
- data/spec/server/rack/asset_files_spec.rb +1 -1
- data/templates/project/app/main/views/main/main.html +2 -2
- metadata +29 -41
- data/lib/volt/extra_core/time.rb +0 -16
- data/lib/volt/page/draw_cycle.rb +0 -31
- data/lib/volt/page/memory_test.rb +0 -26
- data/lib/volt/page/reactive_template.rb +0 -32
- data/lib/volt/reactive/array_extensions.rb +0 -12
- data/lib/volt/reactive/destructive_methods.rb +0 -19
- data/lib/volt/reactive/event_chain.rb +0 -125
- data/lib/volt/reactive/events.rb +0 -216
- data/lib/volt/reactive/object_tracking.rb +0 -14
- data/lib/volt/reactive/reactive_block.rb +0 -88
- data/lib/volt/reactive/reactive_generator.rb +0 -44
- data/lib/volt/reactive/reactive_tags.rb +0 -71
- data/lib/volt/reactive/reactive_value.rb +0 -427
- data/lib/volt/reactive/string_extensions.rb +0 -31
- data/spec/integration/test_integration_spec.rb +0 -14
- data/spec/models/event_chain_spec.rb +0 -150
- data/spec/models/model_buffers_spec.rb +0 -9
- data/spec/models/old_model_spec.rb +0 -67
- data/spec/models/reactive_array_spec.rb +0 -364
- data/spec/models/reactive_block_spec.rb +0 -13
- data/spec/models/reactive_call_times_spec.rb +0 -28
- data/spec/models/reactive_generator_spec.rb +0 -58
- data/spec/models/reactive_tags_spec.rb +0 -35
- data/spec/models/reactive_value_spec.rb +0 -370
- data/spec/models/store_spec.rb +0 -16
- data/spec/models/string_extensions_spec.rb +0 -57
@@ -2,24 +2,23 @@ require 'volt/page/bindings/base_binding'
|
|
2
2
|
|
3
3
|
class ContentBinding < BaseBinding
|
4
4
|
def initialize(page, target, context, binding_name, getter)
|
5
|
+
# puts "New Content Binding: #{self.inspect}"
|
5
6
|
super(page, target, context, binding_name)
|
6
7
|
|
7
|
-
#
|
8
|
-
@
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
end
|
8
|
+
# Listen for changes
|
9
|
+
@computation = -> do
|
10
|
+
begin
|
11
|
+
update(@context.instance_eval(&getter))
|
12
|
+
rescue => e
|
13
|
+
Volt.logger.error("ContentBinding Error: #{e.inspect}")
|
14
|
+
update('')
|
15
|
+
end
|
16
|
+
end.watch!
|
16
17
|
end
|
17
18
|
|
18
|
-
def update
|
19
|
-
|
20
|
-
|
21
|
-
puts "GOT CUR: #{value.inspect}"
|
22
|
-
end
|
19
|
+
def update(value)
|
20
|
+
# TODORW:
|
21
|
+
value = value.nil? ? '' : value
|
23
22
|
|
24
23
|
# Exception values display the exception as a string
|
25
24
|
value = value.to_s
|
@@ -30,10 +29,8 @@ class ContentBinding < BaseBinding
|
|
30
29
|
end
|
31
30
|
|
32
31
|
def remove
|
33
|
-
if @
|
34
|
-
|
35
|
-
@changed_listener = nil
|
36
|
-
end
|
32
|
+
@computation.stop if @computation
|
33
|
+
@computation = nil
|
37
34
|
|
38
35
|
super
|
39
36
|
end
|
@@ -7,40 +7,57 @@ class EachBinding < BaseBinding
|
|
7
7
|
@item_name = variable_name
|
8
8
|
@template_name = template_name
|
9
9
|
|
10
|
-
# Find the source for the content binding
|
11
|
-
@value = value_from_getter(getter)
|
12
|
-
|
13
10
|
@templates = []
|
14
11
|
|
15
|
-
|
16
|
-
# update
|
17
|
-
reload
|
12
|
+
@getter = getter
|
18
13
|
|
19
|
-
|
20
|
-
@
|
21
|
-
@removed_listener = @value.on('removed') { |_, position| item_removed(position) }
|
14
|
+
# Listen for changes
|
15
|
+
@computation = -> { reload }.watch!
|
22
16
|
end
|
23
17
|
|
24
18
|
# When a changed event happens, we update to the new size.
|
25
19
|
def reload
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
20
|
+
begin
|
21
|
+
value = @context.instance_eval(&@getter)
|
22
|
+
rescue => e
|
23
|
+
Volt.logger.error("EachBinding Error: #{e.inspect}")
|
24
|
+
value = []
|
25
|
+
end
|
30
26
|
|
31
|
-
|
32
|
-
|
33
|
-
|
27
|
+
# Since we're checking things like size, we don't want this to be re-triggered on a
|
28
|
+
# size change, so we run without tracking.
|
29
|
+
Computation.run_without_tracking do
|
30
|
+
# puts "RELOAD:-------------- #{value.inspect}"
|
31
|
+
# Adjust to the new size
|
32
|
+
values = current_values(value)
|
33
|
+
@value = values
|
34
|
+
|
35
|
+
@added_listener.remove if @added_listener
|
36
|
+
@removed_listener.remove if @removed_listener
|
37
|
+
|
38
|
+
if @value.respond_to?(:on)
|
39
|
+
@added_listener = @value.on('added') { |position| item_added(position) }
|
40
|
+
@removed_listener = @value.on('removed') { |position| item_removed(position) }
|
34
41
|
end
|
35
|
-
|
36
|
-
|
42
|
+
|
43
|
+
templates_size = @templates.size
|
44
|
+
values_size = values.size
|
45
|
+
|
46
|
+
# Start over, re-create all nodes
|
47
|
+
(templates_size-1).downto(0) do |index|
|
37
48
|
item_removed(index)
|
38
49
|
end
|
50
|
+
0.upto(values_size-1) do |index|
|
51
|
+
item_added(index)
|
52
|
+
end
|
39
53
|
end
|
40
54
|
end
|
41
55
|
|
42
56
|
def item_removed(position)
|
43
|
-
|
57
|
+
# Remove dependency
|
58
|
+
@templates[position].context.locals[:index_dependency].remove
|
59
|
+
|
60
|
+
# puts "REMOVE AT: #{position.inspect} - #{@templates[position].inspect} - #{@templates.inspect}"
|
44
61
|
@templates[position].remove_anchors
|
45
62
|
@templates[position].remove
|
46
63
|
@templates.delete_at(position)
|
@@ -50,7 +67,6 @@ class EachBinding < BaseBinding
|
|
50
67
|
end
|
51
68
|
|
52
69
|
def item_added(position)
|
53
|
-
# ObjectTracker.enable_cache
|
54
70
|
binding_name = @@binding_number
|
55
71
|
@@binding_number += 1
|
56
72
|
|
@@ -62,15 +78,28 @@ class EachBinding < BaseBinding
|
|
62
78
|
dom_section.insert_anchor_before(binding_name, @templates[position].binding_name)
|
63
79
|
end
|
64
80
|
|
65
|
-
|
66
|
-
|
81
|
+
# TODORW: :parent => @value may change
|
82
|
+
item_context = SubContext.new({:_index_value => position, :parent => @value}, @context)
|
83
|
+
item_context.locals[@item_name.to_sym] = Proc.new { @value[item_context.locals[:_index_value]] }
|
67
84
|
|
68
|
-
|
85
|
+
position_dependency = Dependency.new
|
86
|
+
item_context.locals[:index_dependency] = position_dependency
|
87
|
+
|
88
|
+
# Get and set index
|
89
|
+
item_context.locals[:index=] = Proc.new do |val|
|
90
|
+
position_dependency.changed!
|
91
|
+
item_context.locals[:_index_value] = val
|
92
|
+
end
|
93
|
+
|
94
|
+
item_context.locals[:index] = Proc.new do
|
95
|
+
position_dependency.depend
|
96
|
+
item_context.locals[:_index_value]
|
97
|
+
end
|
69
98
|
|
70
99
|
item_template = TemplateRenderer.new(@page, @target, item_context, binding_name, @template_name)
|
71
100
|
@templates.insert(position, item_template)
|
72
101
|
|
73
|
-
|
102
|
+
update_indexes_after(position)
|
74
103
|
end
|
75
104
|
|
76
105
|
# When items are added or removed in the middle of the list, we need
|
@@ -78,18 +107,15 @@ class EachBinding < BaseBinding
|
|
78
107
|
def update_indexes_after(start_index)
|
79
108
|
size = @templates.size
|
80
109
|
if size > 0
|
81
|
-
puts @templates.inspect
|
82
110
|
start_index.upto(size-1) do |index|
|
83
|
-
@templates[index].context.locals[:index].
|
111
|
+
@templates[index].context.locals[:index=].call(index)
|
84
112
|
end
|
85
113
|
end
|
86
114
|
end
|
87
115
|
|
88
|
-
def current_values
|
89
|
-
values = @value.cur
|
90
|
-
|
116
|
+
def current_values(values)
|
91
117
|
return [] if values.is_a?(Model) || values.is_a?(Exception)
|
92
|
-
values = values.attributes
|
118
|
+
values = values.attributes if values.respond_to?(:attributes)
|
93
119
|
|
94
120
|
return values
|
95
121
|
end
|
@@ -97,17 +123,24 @@ class EachBinding < BaseBinding
|
|
97
123
|
|
98
124
|
# When this each_binding is removed, cleanup.
|
99
125
|
def remove
|
126
|
+
@computation.stop
|
127
|
+
@computation = nil
|
128
|
+
|
129
|
+
# Clear value
|
130
|
+
@value = nil
|
131
|
+
|
100
132
|
@added_listener.remove
|
101
133
|
@added_listener = nil
|
102
134
|
|
103
|
-
@changed_listener.remove
|
104
|
-
@changed_listener = nil
|
105
|
-
|
106
135
|
@removed_listener.remove
|
107
136
|
@removed_listener = nil
|
108
137
|
|
109
138
|
if @templates
|
110
|
-
@templates.
|
139
|
+
template_count = @templates.size
|
140
|
+
template_count.times do |index|
|
141
|
+
item_removed(template_count - index - 1)
|
142
|
+
end
|
143
|
+
# @templates.compact.each(&:remove)
|
111
144
|
@templates = nil
|
112
145
|
end
|
113
146
|
|
@@ -13,13 +13,7 @@ class IfBinding < BaseBinding
|
|
13
13
|
getter, template_name = branch
|
14
14
|
|
15
15
|
if getter.present?
|
16
|
-
|
17
|
-
value = value_from_getter(getter)
|
18
|
-
|
19
|
-
if value.reactive?
|
20
|
-
# Trigger change when value changes
|
21
|
-
@listeners << value.on('changed') { update }
|
22
|
-
end
|
16
|
+
value = getter
|
23
17
|
else
|
24
18
|
# A nil value means this is an unconditional else branch, it
|
25
19
|
# should always be true
|
@@ -29,7 +23,7 @@ class IfBinding < BaseBinding
|
|
29
23
|
@branches << [value, template_name]
|
30
24
|
end
|
31
25
|
|
32
|
-
update
|
26
|
+
@computation = -> { update }.watch!
|
33
27
|
end
|
34
28
|
|
35
29
|
def update
|
@@ -38,10 +32,19 @@ class IfBinding < BaseBinding
|
|
38
32
|
@branches.each do |branch|
|
39
33
|
value, template_name = branch
|
40
34
|
|
41
|
-
|
35
|
+
if value.is_a?(Proc)
|
36
|
+
begin
|
37
|
+
current_value = @context.instance_eval(&value)
|
38
|
+
rescue => e
|
39
|
+
Volt.logger.error("IfBinding:#{object_id} error: #{e.inspect}\n" + `value.toString()`)
|
40
|
+
current_value = false
|
41
|
+
end
|
42
|
+
else
|
43
|
+
current_value = value
|
44
|
+
end
|
42
45
|
|
43
46
|
# TODO: A bug in opal requires us to check == true
|
44
|
-
if current_value.
|
47
|
+
if current_value && !current_value.nil? && !current_value.is_a?(Exception)
|
45
48
|
# This branch is currently true
|
46
49
|
true_template = template_name
|
47
50
|
break
|
@@ -64,8 +67,8 @@ class IfBinding < BaseBinding
|
|
64
67
|
end
|
65
68
|
|
66
69
|
def remove
|
67
|
-
|
68
|
-
@
|
70
|
+
@computation.stop if @computation
|
71
|
+
@computation = nil
|
69
72
|
|
70
73
|
@template.remove if @template
|
71
74
|
|
@@ -11,28 +11,10 @@ class TemplateBinding < BaseBinding
|
|
11
11
|
|
12
12
|
@current_template = nil
|
13
13
|
|
14
|
-
|
15
|
-
@path, section, @options = value_from_getter(getter)
|
16
|
-
|
17
|
-
if section.is_a?(String)
|
18
|
-
# Render this as a section
|
19
|
-
@section = section
|
20
|
-
else
|
21
|
-
# Use the value passed in as the default arguments
|
22
|
-
@arguments = section
|
23
|
-
end
|
24
|
-
|
25
|
-
# Sometimes we want multiple template bindings to share the same controller (usually
|
26
|
-
# when displaying a :Title and a :Body), this instance tracks those.
|
27
|
-
if @options && (controller_group = @options[:controller_group])
|
28
|
-
@grouped_controller = GroupedControllers.new(controller_group)
|
29
|
-
end
|
14
|
+
@getter = getter
|
30
15
|
|
31
16
|
# Run the initial render
|
32
|
-
update
|
33
|
-
|
34
|
-
@path_changed_listener = @path.on('changed') { queue_update } if @path.reactive?
|
35
|
-
@section_changed_listener = @section.on('changed') { queue_update } if @section && @section.reactive?
|
17
|
+
@computation = -> { update(*@context.instance_eval(&getter)) }.watch!
|
36
18
|
end
|
37
19
|
|
38
20
|
def setup_path(binding_in_path)
|
@@ -112,43 +94,88 @@ class TemplateBinding < BaseBinding
|
|
112
94
|
return nil, nil
|
113
95
|
end
|
114
96
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
97
|
+
def update(path, section_or_arguments=nil, options={})
|
98
|
+
Computation.run_without_tracking do
|
99
|
+
# Remove existing template and call _removed
|
100
|
+
controller_send(:"#{@action}_removed") if @action && @controller
|
101
|
+
@current_template.remove if @current_template
|
119
102
|
|
120
|
-
|
121
|
-
end
|
103
|
+
@options = options
|
122
104
|
|
123
|
-
|
124
|
-
|
105
|
+
# A blank path needs to load a missing template, otherwise it tries to load
|
106
|
+
# the same template.
|
107
|
+
path = path.blank? ? '---missing---' : path
|
125
108
|
|
126
|
-
|
109
|
+
section = nil
|
110
|
+
@arguments = nil
|
127
111
|
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
112
|
+
if section_or_arguments.is_a?(String)
|
113
|
+
# Render this as a section
|
114
|
+
section = section_or_arguments
|
115
|
+
else
|
116
|
+
# Use the value passed in as the default arguments
|
117
|
+
@arguments = section_or_arguments
|
118
|
+
end
|
119
|
+
|
120
|
+
# Sometimes we want multiple template bindings to share the same controller (usually
|
121
|
+
# when displaying a :Title and a :Body), this instance tracks those.
|
122
|
+
if @options && (controller_group = @options[:controller_group])
|
123
|
+
@grouped_controller = GroupedControllers.new(controller_group)
|
124
|
+
else
|
125
|
+
clear_grouped_controller
|
134
126
|
end
|
127
|
+
|
128
|
+
full_path, controller_path = path_for_template(path, section)
|
129
|
+
render_template(full_path, controller_path)
|
130
|
+
|
131
|
+
queue_clear_grouped_controller
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# On the next tick, we clear the grouped controller so that any changes to template paths
|
136
|
+
# will create a new controller and trigger the action.
|
137
|
+
def queue_clear_grouped_controller
|
138
|
+
if Volt.in_browser?
|
139
|
+
# In the browser, we want to keep a grouped controller around during a single run
|
140
|
+
# of the event loop. To make that happen, we clear it on the next tick.
|
141
|
+
`setImmediate(function() {`
|
142
|
+
clear_grouped_controller
|
143
|
+
`})`
|
144
|
+
else
|
145
|
+
# For the backend, clear it immediately
|
146
|
+
clear_grouped_controller
|
135
147
|
end
|
148
|
+
end
|
136
149
|
|
137
|
-
|
150
|
+
def clear_grouped_controller
|
151
|
+
if @grouped_controller
|
152
|
+
@grouped_controller.clear
|
153
|
+
@grouped_controller = nil
|
154
|
+
end
|
138
155
|
end
|
139
156
|
|
140
157
|
# The context for templates can be either a controller, or the original context.
|
141
158
|
def render_template(full_path, controller_path)
|
142
|
-
|
159
|
+
if @arguments
|
160
|
+
args = [SubContext.new(@arguments)]
|
161
|
+
else
|
162
|
+
args = []
|
163
|
+
end
|
143
164
|
|
144
165
|
@controller = nil
|
145
166
|
|
146
167
|
# Fetch grouped controllers if we're grouping
|
147
168
|
@controller = @grouped_controller.get if @grouped_controller
|
148
169
|
|
149
|
-
#
|
150
|
-
|
151
|
-
|
170
|
+
# The action to be called and rendered
|
171
|
+
@action = nil
|
172
|
+
|
173
|
+
if @controller
|
174
|
+
# Track that we're using the group controller
|
175
|
+
@grouped_controller.inc if @grouped_controller
|
176
|
+
else
|
177
|
+
# Otherwise, make a new controller
|
178
|
+
controller_class, @action = get_controller(controller_path)
|
152
179
|
|
153
180
|
if controller_class
|
154
181
|
# Setup the controller
|
@@ -158,7 +185,7 @@ class TemplateBinding < BaseBinding
|
|
158
185
|
end
|
159
186
|
|
160
187
|
# Trigger the action
|
161
|
-
@
|
188
|
+
controller_send(@action) if @action
|
162
189
|
|
163
190
|
# Track the grouped controller
|
164
191
|
@grouped_controller.set(@controller) if @grouped_controller
|
@@ -177,24 +204,12 @@ class TemplateBinding < BaseBinding
|
|
177
204
|
@controller.section = @current_template.dom_section
|
178
205
|
end
|
179
206
|
|
180
|
-
if @
|
181
|
-
@controller.dom_ready
|
182
|
-
end
|
207
|
+
controller_send(:"#{@action}_ready") if @action
|
183
208
|
end
|
184
209
|
end
|
185
210
|
|
186
211
|
def remove
|
187
|
-
|
188
|
-
|
189
|
-
if @path_changed_listener
|
190
|
-
@path_changed_listener.remove
|
191
|
-
@path_changed_listener = nil
|
192
|
-
end
|
193
|
-
|
194
|
-
if @section_changed_listener
|
195
|
-
@section_changed_listener.remove
|
196
|
-
@section_changed_listener = nil
|
197
|
-
end
|
212
|
+
clear_grouped_controller
|
198
213
|
|
199
214
|
if @current_template
|
200
215
|
# Remove the template if one has been rendered, when the template binding is
|
@@ -205,16 +220,19 @@ class TemplateBinding < BaseBinding
|
|
205
220
|
super
|
206
221
|
|
207
222
|
if @controller
|
208
|
-
#
|
209
|
-
if @controller.respond_to?(:dom_removed)
|
210
|
-
@controller.dom_removed
|
211
|
-
end
|
223
|
+
controller_send(:"#{@action}_removed") if @action
|
212
224
|
|
213
225
|
@controller = nil
|
214
226
|
end
|
215
227
|
end
|
216
228
|
|
217
229
|
private
|
230
|
+
# Sends the action to the controller if it exists
|
231
|
+
def controller_send(action_name)
|
232
|
+
if @controller.respond_to?(action_name)
|
233
|
+
@controller.send(action_name)
|
234
|
+
end
|
235
|
+
end
|
218
236
|
|
219
237
|
# Fetch the controller class
|
220
238
|
def get_controller(controller_path)
|