volt 0.3.7 → 0.3.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/VERSION +1 -1
- data/app/volt/assets/css/notices.css.scss +8 -0
- data/app/volt/tasks/channel_tasks.rb +33 -0
- data/app/volt/tasks/store_tasks.rb +45 -0
- data/app/volt/views/notices/index.html +11 -0
- data/lib/volt/controllers/model_controller.rb +4 -0
- data/lib/volt/models/array_model.rb +11 -1
- data/lib/volt/models/model.rb +22 -14
- data/lib/volt/models/model_wrapper.rb +13 -6
- data/lib/volt/models/params.rb +6 -1
- data/lib/volt/models/params_array.rb +9 -0
- data/lib/volt/models/store.rb +164 -0
- data/lib/volt/models/store_array.rb +15 -0
- data/lib/volt/models/url.rb +0 -4
- data/lib/volt/models.rb +1 -0
- data/lib/volt/{templates → page/bindings}/attribute_binding.rb +8 -7
- data/lib/volt/{templates → page/bindings}/base_binding.rb +6 -1
- data/lib/volt/{templates → page/bindings}/content_binding.rb +1 -1
- data/lib/volt/{templates → page/bindings}/each_binding.rb +15 -6
- data/lib/volt/{templates → page/bindings}/event_binding.rb +1 -5
- data/lib/volt/{templates → page/bindings}/if_binding.rb +1 -1
- data/lib/volt/{templates → page/bindings}/template_binding.rb +16 -7
- data/lib/volt/page/channel.rb +105 -0
- data/lib/volt/{templates → page}/document_events.rb +0 -0
- data/lib/volt/{templates → page}/memory_test.rb +0 -0
- data/lib/volt/{templates → page}/page.rb +22 -21
- data/lib/volt/{templates → page}/reactive_template.rb +0 -0
- data/lib/volt/{templates → page}/render_queue.rb +0 -0
- data/lib/volt/{templates → page}/sub_context.rb +0 -0
- data/lib/volt/{templates → page}/targets/attribute_section.rb +1 -1
- data/lib/volt/{templates → page}/targets/attribute_target.rb +4 -4
- data/lib/volt/{templates → page}/targets/base_section.rb +0 -0
- data/lib/volt/{templates → page}/targets/binding_document/base_node.rb +0 -0
- data/lib/volt/{templates → page}/targets/binding_document/component_node.rb +1 -1
- data/lib/volt/{templates → page}/targets/binding_document/html_node.rb +1 -1
- data/lib/volt/{templates → page}/targets/dom_section.rb +1 -1
- data/lib/volt/{templates → page}/targets/dom_target.rb +2 -2
- data/lib/volt/page/tasks.rb +61 -0
- data/lib/volt/{templates → page}/template_renderer.rb +1 -1
- data/lib/volt/reactive/destructive_methods.rb +19 -0
- data/lib/volt/reactive/event_chain.rb +13 -19
- data/lib/volt/reactive/events.rb +30 -116
- data/lib/volt/reactive/object_tracker.rb +29 -22
- data/lib/volt/reactive/reactive_array.rb +2 -3
- data/lib/volt/reactive/reactive_tags.rb +7 -0
- data/lib/volt/reactive/reactive_value.rb +86 -81
- data/lib/volt/reactive/string_extensions.rb +0 -3
- data/lib/volt/router/routes.rb +2 -2
- data/lib/volt/server/channel_handler.rb +24 -4
- data/lib/volt/server/component_handler.rb +2 -1
- data/lib/volt/server/rack/component_files.rb +3 -2
- data/lib/volt/server/rack/component_paths.rb +11 -1
- data/lib/volt/server/rack/index_files.rb +1 -1
- data/lib/volt/server.rb +16 -0
- data/lib/volt/store/mongo.rb +1 -1
- data/lib/volt/tasks/dispatcher.rb +25 -0
- data/spec/models/model_spec.rb +90 -15
- data/spec/models/params_spec.rb +16 -0
- data/spec/models/reactive_array_spec.rb +17 -18
- data/spec/models/reactive_value_spec.rb +11 -0
- data/spec/models/store_spec.rb +16 -0
- data/spec/server/rack/component_files_spec.rb +18 -16
- data/spec/server/rack/component_paths_spec.rb +21 -19
- data/spec/templates/targets/binding_document/component_node_spec.rb +1 -1
- data/spec/templates/template_binding_spec.rb +1 -1
- data/templates/project/app/home/views/index/index.html +2 -0
- data/volt.gemspec +2 -0
- metadata +67 -25
- data/lib/volt/templates/channel.rb +0 -47
@@ -1,5 +1,5 @@
|
|
1
|
-
require 'volt/
|
2
|
-
require 'volt/
|
1
|
+
require 'volt/page/bindings/base_binding'
|
2
|
+
require 'volt/page/template_renderer'
|
3
3
|
|
4
4
|
class TemplateBinding < BaseBinding
|
5
5
|
def initialize(target, context, binding_name, binding_in_path, getter)
|
@@ -131,7 +131,7 @@ class TemplateBinding < BaseBinding
|
|
131
131
|
controller = get_controller(controller_name)
|
132
132
|
|
133
133
|
# Initialize the new controller
|
134
|
-
current_context = controller.new(*args)
|
134
|
+
current_context = (ModelController || controller).new(*args)
|
135
135
|
elsif @model
|
136
136
|
# Passed in attributes, but there is no controller
|
137
137
|
current_context = SubContext.new(@model, current_context)
|
@@ -169,14 +169,23 @@ class TemplateBinding < BaseBinding
|
|
169
169
|
# For the home object, we do not need to namespace our controller
|
170
170
|
if controller_name[0] != 'home'
|
171
171
|
# Controller is namespaced, lookup outer module first
|
172
|
-
base_name = controller_name[0].camelize
|
173
|
-
|
172
|
+
base_name = controller_name[0].camelize.to_sym
|
173
|
+
if Object.send(:const_defined?, base_name)
|
174
|
+
base_object = Object.send(:const_get, base_name)
|
175
|
+
end
|
174
176
|
else
|
175
177
|
# Get controller directlry
|
176
178
|
base_object = Object
|
177
179
|
end
|
178
|
-
|
179
|
-
base_object
|
180
|
+
|
181
|
+
if base_object
|
182
|
+
name = (name + 'Controller').to_sym
|
183
|
+
if base_object.send(:const_defined?, name)
|
184
|
+
return base_object.send(:const_get, name)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
return nil
|
180
189
|
end
|
181
190
|
|
182
191
|
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# The channel is the connection between the front end and the backend.
|
2
|
+
|
3
|
+
require 'volt/reactive/events'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
class Channel
|
7
|
+
include ReactiveTags
|
8
|
+
|
9
|
+
attr_reader :state, :error, :reconnect_interval
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@socket = nil
|
13
|
+
@state = :opening
|
14
|
+
@error = nil
|
15
|
+
@queue = []
|
16
|
+
|
17
|
+
connect!
|
18
|
+
end
|
19
|
+
|
20
|
+
def connect!
|
21
|
+
%x{
|
22
|
+
this.socket = new SockJS('http://localhost:3000/channel');
|
23
|
+
|
24
|
+
this.socket.onopen = function() {
|
25
|
+
self.$opened();
|
26
|
+
};
|
27
|
+
|
28
|
+
this.socket.onmessage = function(message) {
|
29
|
+
self['$message_received'](message.data);
|
30
|
+
};
|
31
|
+
|
32
|
+
this.socket.onclose = function(error) {
|
33
|
+
self.$closed(error);
|
34
|
+
};
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def opened
|
39
|
+
@state = :open
|
40
|
+
@reconnect_interval = nil
|
41
|
+
@queue.each do |message|
|
42
|
+
send_message(message)
|
43
|
+
end
|
44
|
+
|
45
|
+
trigger!('open')
|
46
|
+
trigger!('changed')
|
47
|
+
end
|
48
|
+
|
49
|
+
def closed(error)
|
50
|
+
@state = :closed
|
51
|
+
@error = `error.reason`
|
52
|
+
|
53
|
+
trigger!('closed')
|
54
|
+
trigger!('changed')
|
55
|
+
|
56
|
+
reconnect!
|
57
|
+
end
|
58
|
+
|
59
|
+
def reconnect!
|
60
|
+
@reconnect_interval ||= 0
|
61
|
+
@reconnect_interval += (2000 + rand(5000))
|
62
|
+
|
63
|
+
# Trigger changed for reconnect interval
|
64
|
+
trigger!('changed')
|
65
|
+
|
66
|
+
interval = @reconnect_interval
|
67
|
+
|
68
|
+
%x{
|
69
|
+
setTimeout(function() {
|
70
|
+
self['$connect!']();
|
71
|
+
}, interval);
|
72
|
+
}
|
73
|
+
end
|
74
|
+
|
75
|
+
def message_received(message)
|
76
|
+
message = JSON.parse(message)
|
77
|
+
# puts "GOT: #{message.inspect}"
|
78
|
+
trigger!('message', nil, *message)
|
79
|
+
end
|
80
|
+
|
81
|
+
tag_method(:send_message) do
|
82
|
+
destructive!
|
83
|
+
end
|
84
|
+
def send_message(message)
|
85
|
+
`console.log('do send message');`
|
86
|
+
puts "Send #{message.inspect}"
|
87
|
+
if @state != :open
|
88
|
+
@queue << message
|
89
|
+
puts "Queue"
|
90
|
+
else
|
91
|
+
# TODO: Temp: wrap message in an array, so we're sure its valid JSON
|
92
|
+
message = JSON.dump([message])
|
93
|
+
%x{
|
94
|
+
this.socket.send(message);
|
95
|
+
}
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def close!
|
100
|
+
@state = :closed
|
101
|
+
%x{
|
102
|
+
this.socket.close();
|
103
|
+
}
|
104
|
+
end
|
105
|
+
end
|
File without changes
|
File without changes
|
@@ -6,24 +6,25 @@ require 'opal-jquery'
|
|
6
6
|
require 'volt/models'
|
7
7
|
require 'volt/models/params'
|
8
8
|
require 'volt/controllers/model_controller'
|
9
|
-
require 'volt/
|
10
|
-
require 'volt/
|
11
|
-
require 'volt/
|
12
|
-
require 'volt/
|
13
|
-
require 'volt/
|
14
|
-
require 'volt/
|
15
|
-
require 'volt/
|
16
|
-
require 'volt/
|
17
|
-
require 'volt/
|
18
|
-
require 'volt/
|
19
|
-
require 'volt/
|
20
|
-
require 'volt/
|
9
|
+
require 'volt/page/bindings/attribute_binding'
|
10
|
+
require 'volt/page/bindings/content_binding'
|
11
|
+
require 'volt/page/bindings/each_binding'
|
12
|
+
require 'volt/page/bindings/if_binding'
|
13
|
+
require 'volt/page/bindings/template_binding'
|
14
|
+
require 'volt/page/bindings/event_binding'
|
15
|
+
require 'volt/page/template_renderer'
|
16
|
+
require 'volt/page/reactive_template'
|
17
|
+
require 'volt/page/document_events'
|
18
|
+
require 'volt/page/sub_context'
|
19
|
+
require 'volt/page/targets/dom_target'
|
20
|
+
require 'volt/page/channel'
|
21
21
|
require 'volt/router/routes'
|
22
22
|
require 'volt/models/url'
|
23
23
|
require 'volt/page/url_tracker'
|
24
24
|
require 'volt'
|
25
25
|
require 'volt/benchmark/benchmark'
|
26
|
-
require 'volt/
|
26
|
+
require 'volt/page/render_queue'
|
27
|
+
require 'volt/page/tasks'
|
27
28
|
|
28
29
|
class Page
|
29
30
|
attr_reader :url, :params, :page, :store, :templates, :routes, :render_queue
|
@@ -36,7 +37,7 @@ class Page
|
|
36
37
|
|
37
38
|
# Run the code to setup the page
|
38
39
|
@page = ReactiveValue.new(Model.new)#({}, nil, 'page', @model_classes))
|
39
|
-
@store = ReactiveValue.new(
|
40
|
+
@store = ReactiveValue.new(Store.new(tasks))#({}, nil, 'store', @model_classes))
|
40
41
|
|
41
42
|
@url = ReactiveValue.new(URL.new)
|
42
43
|
@params = @url.params
|
@@ -63,10 +64,10 @@ class Page
|
|
63
64
|
return false;
|
64
65
|
});
|
65
66
|
}
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
67
|
+
end
|
68
|
+
|
69
|
+
def tasks
|
70
|
+
@tasks ||= Tasks.new(self)
|
70
71
|
end
|
71
72
|
|
72
73
|
def link_clicked(url)
|
@@ -74,9 +75,9 @@ class Page
|
|
74
75
|
return if url.blank?
|
75
76
|
|
76
77
|
# Normalize url
|
77
|
-
|
78
|
+
Benchmark.bm(1) do
|
78
79
|
@url.parse("http://localhost:3000" + url)
|
79
|
-
|
80
|
+
end
|
80
81
|
end
|
81
82
|
|
82
83
|
# We provide a binding_name, so we can bind events on the document
|
@@ -89,7 +90,7 @@ class Page
|
|
89
90
|
end
|
90
91
|
|
91
92
|
def channel
|
92
|
-
@channel ||= Channel.new
|
93
|
+
@channel ||= ReactiveValue.new(Channel.new)
|
93
94
|
end
|
94
95
|
|
95
96
|
def events
|
File without changes
|
File without changes
|
File without changes
|
@@ -1,7 +1,7 @@
|
|
1
|
-
require 'volt/
|
2
|
-
require 'volt/
|
3
|
-
require 'volt/
|
4
|
-
require 'volt/
|
1
|
+
require 'volt/page/targets/base_section'
|
2
|
+
require 'volt/page/targets/attribute_section'
|
3
|
+
require 'volt/page/targets/binding_document/component_node'
|
4
|
+
require 'volt/page/targets/binding_document/html_node'
|
5
5
|
|
6
6
|
# AttributeTarget's provide an interface that can render bindings into
|
7
7
|
# a string that can then be used to update a attribute binding.
|
File without changes
|
File without changes
|
@@ -1,5 +1,5 @@
|
|
1
|
-
require 'volt/
|
2
|
-
require 'volt/
|
1
|
+
require 'volt/page/targets/base_section'
|
2
|
+
require 'volt/page/targets/dom_section'
|
3
3
|
|
4
4
|
# DomTarget's provide an interface that can render bindings into
|
5
5
|
# the dom. Currently only one "dom" is supported, but multiple
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# The tasks class provides an interface to call tasks on
|
2
|
+
# the backend server.
|
3
|
+
class Tasks
|
4
|
+
def initialize(page)
|
5
|
+
@page = page
|
6
|
+
@callback_id = 0
|
7
|
+
@callbacks = {}
|
8
|
+
|
9
|
+
page.channel.on('message') do |_, *args|
|
10
|
+
received_message(*args)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(class_name, method_name, *args, &callback)
|
15
|
+
if callback
|
16
|
+
callback_id = @callback_id
|
17
|
+
@callback_id += 1
|
18
|
+
|
19
|
+
# Track the callback
|
20
|
+
# TODO: Timeout on these callbacks
|
21
|
+
@callbacks[callback_id] = callback
|
22
|
+
else
|
23
|
+
callback_id = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
@page.channel.send_message([callback_id, class_name, method_name, *args])
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def received_message(name, callback_id, *args)
|
31
|
+
case name
|
32
|
+
when 'response'
|
33
|
+
response(callback_id, *args)
|
34
|
+
when 'update'
|
35
|
+
update(*args)
|
36
|
+
when 'reload'
|
37
|
+
reload
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def response(callback_id, *args)
|
42
|
+
callback = @callbacks.delete(callback_id)
|
43
|
+
|
44
|
+
if callback
|
45
|
+
callback.call(*args)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def update(model_id, data)
|
50
|
+
$loading_models = true
|
51
|
+
puts "UPDATE: #{model_id} with #{data.inspect}"
|
52
|
+
Store.update(model_id, data)
|
53
|
+
$loading_models = false
|
54
|
+
end
|
55
|
+
|
56
|
+
def reload
|
57
|
+
puts "RELOAD"
|
58
|
+
$page.page._reloading = true
|
59
|
+
`window.location.reload(false);`
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# DestructiveMethods tracks the names of all methods that are marked as
|
2
|
+
# destructive. This lets us do an optimization where we don't need to
|
3
|
+
# check any methods with names that aren't here, we can be sure that they
|
4
|
+
# are not destructive. If the method is tracked here, we need to check
|
5
|
+
# it on its current class.
|
6
|
+
class DestructiveMethods
|
7
|
+
@@method_names = {}
|
8
|
+
|
9
|
+
def self.add_method(method_name)
|
10
|
+
@@method_names[method_name] = true
|
11
|
+
end
|
12
|
+
|
13
|
+
# Check to see if a method might be destructive. If this returns
|
14
|
+
# false, then we can guarentee that it won't be destructive and
|
15
|
+
# we can skip a destructive check.
|
16
|
+
def self.might_be_destructive?(method_name)
|
17
|
+
return @@method_names[method_name]
|
18
|
+
end
|
19
|
+
end
|
@@ -40,40 +40,34 @@ class EventChain
|
|
40
40
|
|
41
41
|
# Register an event listener that chains from object to self
|
42
42
|
def setup_listener(event, chain_listener)
|
43
|
-
return chain_listener.object.on(event, @main_object) do
|
43
|
+
return chain_listener.object.on(event, @main_object) do |filter, *args|
|
44
44
|
if callback = chain_listener.callback
|
45
|
-
callback.call(event, *args)
|
45
|
+
callback.call(event, filter, *args)
|
46
46
|
else
|
47
47
|
# Trigger on this value, when it happens on the parent
|
48
|
-
|
48
|
+
|
49
|
+
# Only pass the filter from non-reactive to reactive? This
|
50
|
+
# lets us scope the calls on a proxied object.
|
51
|
+
# Filters limit which listeners are triggered, passing them to
|
52
|
+
# the ReactiveValue's from non-reactive lets us filter at the
|
53
|
+
# reactive level as well.
|
54
|
+
filter = nil unless !chain_listener.object.reactive? && @main_object.reactive?
|
55
|
+
|
56
|
+
@main_object.trigger!(event, filter, *args)
|
49
57
|
end
|
50
58
|
end
|
51
59
|
end
|
52
|
-
|
53
|
-
def all_listening_events
|
54
|
-
all_listeners = []
|
55
|
-
all_listeners += @main_object.listeners.keys
|
56
|
-
|
57
|
-
if @main_object
|
58
|
-
@main_object.event_followers.each do |event_follower|
|
59
|
-
all_listeners += event_follower.listeners.keys
|
60
|
-
end
|
61
|
-
end
|
62
60
|
|
63
|
-
return all_listeners.uniq
|
64
|
-
end
|
65
|
-
|
66
|
-
|
67
61
|
# We can chain our events to any other object that includes
|
68
62
|
# Events
|
69
63
|
def add_object(object, &block)
|
70
64
|
# puts "ADD OBJECT: #{object.inspect} to #{self.inspect}"
|
71
|
-
|
65
|
+
|
72
66
|
chain_listener = ChainListener.new(self, object, block)
|
73
67
|
|
74
68
|
listeners = {}
|
75
69
|
|
76
|
-
|
70
|
+
@main_object.listeners.keys.each do |event|
|
77
71
|
# Create a listener for each event
|
78
72
|
listeners[event] = setup_listener(event, chain_listener)
|
79
73
|
end
|
data/lib/volt/reactive/events.rb
CHANGED
@@ -6,7 +6,7 @@ DEBUG = false
|
|
6
6
|
# A listener gets returned when adding an 'on' event listener. It can be
|
7
7
|
# used to clear the event listener.
|
8
8
|
class Listener
|
9
|
-
attr_reader :scope_provider
|
9
|
+
attr_reader :scope_provider, :klass
|
10
10
|
|
11
11
|
def initialize(klass, event, scope_provider, callback)
|
12
12
|
@klass = klass
|
@@ -43,13 +43,13 @@ class Listener
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def scope
|
46
|
-
@scope_provider && @scope_provider.scope
|
46
|
+
@scope_provider && scope_provider.respond_to?(:scope) && @scope_provider.scope
|
47
47
|
end
|
48
48
|
|
49
49
|
def call(*args)
|
50
50
|
# raise "Triggered on removed: #{@event} on #{@klass2.inspect}" if @removed
|
51
51
|
if @removed
|
52
|
-
puts "Triggered on removed: #{@event}"
|
52
|
+
puts "Triggered on a removed event: #{@event}"
|
53
53
|
return
|
54
54
|
end
|
55
55
|
|
@@ -98,7 +98,6 @@ class Listener
|
|
98
98
|
end
|
99
99
|
|
100
100
|
module Events
|
101
|
-
attr_accessor :scope
|
102
101
|
# Add a listener for an event
|
103
102
|
def on(event, scope_provider=nil, &block)
|
104
103
|
# puts "Register: #{event} on #{self.inspect}"
|
@@ -111,7 +110,10 @@ module Events
|
|
111
110
|
@listeners[event] << new_listener
|
112
111
|
|
113
112
|
first = @listeners[event].size == 1
|
114
|
-
|
113
|
+
|
114
|
+
# When events get added, we need to notify event chains so they
|
115
|
+
# can update and chain any new events.
|
116
|
+
event_chain.add_event(event) if first
|
115
117
|
|
116
118
|
# Let the included class know that an event was registered. (if it cares)
|
117
119
|
if self.respond_to?(:event_added)
|
@@ -144,7 +146,9 @@ module Events
|
|
144
146
|
|
145
147
|
no_more_events = @listeners[event].size == 0
|
146
148
|
if no_more_events
|
147
|
-
|
149
|
+
# When events are removed, we need to notify any relevent chains so they
|
150
|
+
# can remove any chained events.
|
151
|
+
event_chain.remove_event(event)
|
148
152
|
|
149
153
|
# No registered listeners now on this event
|
150
154
|
@listeners.delete(event)
|
@@ -157,110 +161,29 @@ module Events
|
|
157
161
|
end
|
158
162
|
# end
|
159
163
|
end
|
160
|
-
|
161
|
-
# When events get added, we need to notify event chains so they
|
162
|
-
# can update and chain any new events.
|
163
|
-
def add_event_to_chains(event)
|
164
|
-
# First time this event is added, update any chains
|
165
|
-
event_chain.add_event(event)
|
166
|
-
|
167
|
-
# We need to keep the event chain's updated for any objects we're
|
168
|
-
# following for events.
|
169
|
-
event_followings.each {|ef| ef.event_chain.add_event(event) }
|
170
|
-
|
171
|
-
if event != :changed && !@other_event_listener
|
172
|
-
@other_event_listener = on('changed') { }
|
173
|
-
end
|
174
|
-
end
|
175
|
-
|
176
|
-
# When events are removed, we need to notify any relevent chains so they
|
177
|
-
# can remove any chained events.
|
178
|
-
def remove_event_from_chains(event)
|
179
|
-
event_chain.remove_event(event)
|
180
164
|
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
if event != :changed
|
186
|
-
# See if there are any remaining events that aren't changed
|
187
|
-
if listeners.keys.reject {|k| k == :changed }.size == 0
|
188
|
-
@other_event_listener.remove
|
189
|
-
@other_event_listener = nil
|
190
|
-
end
|
191
|
-
end
|
192
|
-
end
|
193
|
-
|
194
|
-
|
195
|
-
# Track the current object that we're following.
|
196
|
-
def event_followings
|
197
|
-
@event_followings || []
|
198
|
-
end
|
199
|
-
|
200
|
-
def add_following(object)
|
201
|
-
@event_followings ||= []
|
202
|
-
@event_followings << object
|
203
|
-
|
204
|
-
# Take all of our listeners and add them to the
|
205
|
-
listeners.keys.each do |event|
|
206
|
-
object.event_chain.add_event(event)
|
207
|
-
end
|
208
|
-
end
|
209
|
-
|
210
|
-
def remove_following(object)
|
211
|
-
@event_followings.delete(object)
|
212
|
-
|
213
|
-
listeners.keys.each do |event|
|
214
|
-
object.event_chain.remove_event(event)
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
# Track who's following us
|
219
|
-
def event_followers
|
220
|
-
@event_followers || []
|
221
|
-
end
|
222
|
-
|
223
|
-
def add_event_follower(follower)
|
224
|
-
@event_followers ||= []
|
225
|
-
@event_followers << follower
|
226
|
-
|
227
|
-
follower.add_following(self)
|
228
|
-
end
|
229
|
-
|
230
|
-
def remove_event_follower(follower)
|
231
|
-
if @event_followers
|
232
|
-
@event_followers.delete(follower)
|
233
|
-
|
234
|
-
follower.remove_following(self)
|
235
|
-
end
|
236
|
-
end
|
237
|
-
|
238
|
-
# Return all listeners for an event on the current object and any event
|
239
|
-
# following objects.
|
240
|
-
def all_listeners_for(event)
|
241
|
-
# TODO: We dup at the moment because some events unregister events, is there
|
242
|
-
# a better solution than this?
|
243
|
-
all_listeners = []
|
244
|
-
all_listeners += @listeners[event].dup if @listeners && @listeners[event]
|
245
|
-
|
246
|
-
if @event_followers
|
247
|
-
@event_followers.each do |event_follower|
|
248
|
-
ef_listeners = event_follower.listeners
|
249
|
-
all_listeners += ef_listeners[event].dup if ef_listeners[event]
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
|
-
return all_listeners
|
254
|
-
end
|
255
|
-
|
256
|
-
def trigger!(event, *args)
|
257
|
-
ObjectTracker.process_queue if !reactive? && !respond_to?(:skip_current_queue_flush)
|
165
|
+
def trigger!(event, filter=nil, *args)
|
166
|
+
are_reactive = reactive?
|
167
|
+
# puts "TRIGGER FOR: #{event} on #{self.inspect}" if !reactive?
|
168
|
+
ObjectTracker.process_queue if !are_reactive# && !respond_to?(:skip_current_queue_flush)
|
258
169
|
|
259
170
|
event = event.to_sym
|
260
171
|
|
261
|
-
|
262
|
-
#
|
263
|
-
|
172
|
+
if @listeners && @listeners[event]
|
173
|
+
# puts "LISTENERS FOR #{event} on #{self.inspect} - #{@listeners[event].inspect}"
|
174
|
+
# TODO: We have to dup here because one trigger might remove another
|
175
|
+
@listeners[event].dup.each do |listener|
|
176
|
+
# Call the event on each listener
|
177
|
+
# If there is no listener, that means another event trigger removed it.
|
178
|
+
# If there is no filter, call
|
179
|
+
# if we aren't reactive, we should pass to all of our reactive listeners, since they
|
180
|
+
# just proxy us.
|
181
|
+
# If the filter exists, check it
|
182
|
+
# puts "CHECK #{listener.inspect} : #{self.inspect} -- #{listener.klass.inspect}"
|
183
|
+
if (!filter || (!are_reactive && listener.scope_provider.reactive?) || filter.call(listener.scope))
|
184
|
+
listener.call(filter, *args)
|
185
|
+
end
|
186
|
+
end
|
264
187
|
end
|
265
188
|
|
266
189
|
nil
|
@@ -268,16 +191,7 @@ module Events
|
|
268
191
|
|
269
192
|
# Takes a block, which passes in
|
270
193
|
def trigger_by_scope!(event, *args, &block)
|
271
|
-
|
272
|
-
|
273
|
-
event = event.to_sym
|
274
|
-
|
275
|
-
all_listeners_for(event).each do |listener|
|
276
|
-
# Call the block, pass in the scope
|
277
|
-
if block.call(listener.scope)
|
278
|
-
listener.call(*args)
|
279
|
-
end
|
280
|
-
end
|
194
|
+
trigger!(event, block, *args)
|
281
195
|
end
|
282
196
|
|
283
197
|
end
|