volt 0.3.7 → 0.3.8
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.
- 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
|