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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/VERSION +1 -1
  3. data/app/volt/assets/css/notices.css.scss +8 -0
  4. data/app/volt/tasks/channel_tasks.rb +33 -0
  5. data/app/volt/tasks/store_tasks.rb +45 -0
  6. data/app/volt/views/notices/index.html +11 -0
  7. data/lib/volt/controllers/model_controller.rb +4 -0
  8. data/lib/volt/models/array_model.rb +11 -1
  9. data/lib/volt/models/model.rb +22 -14
  10. data/lib/volt/models/model_wrapper.rb +13 -6
  11. data/lib/volt/models/params.rb +6 -1
  12. data/lib/volt/models/params_array.rb +9 -0
  13. data/lib/volt/models/store.rb +164 -0
  14. data/lib/volt/models/store_array.rb +15 -0
  15. data/lib/volt/models/url.rb +0 -4
  16. data/lib/volt/models.rb +1 -0
  17. data/lib/volt/{templates → page/bindings}/attribute_binding.rb +8 -7
  18. data/lib/volt/{templates → page/bindings}/base_binding.rb +6 -1
  19. data/lib/volt/{templates → page/bindings}/content_binding.rb +1 -1
  20. data/lib/volt/{templates → page/bindings}/each_binding.rb +15 -6
  21. data/lib/volt/{templates → page/bindings}/event_binding.rb +1 -5
  22. data/lib/volt/{templates → page/bindings}/if_binding.rb +1 -1
  23. data/lib/volt/{templates → page/bindings}/template_binding.rb +16 -7
  24. data/lib/volt/page/channel.rb +105 -0
  25. data/lib/volt/{templates → page}/document_events.rb +0 -0
  26. data/lib/volt/{templates → page}/memory_test.rb +0 -0
  27. data/lib/volt/{templates → page}/page.rb +22 -21
  28. data/lib/volt/{templates → page}/reactive_template.rb +0 -0
  29. data/lib/volt/{templates → page}/render_queue.rb +0 -0
  30. data/lib/volt/{templates → page}/sub_context.rb +0 -0
  31. data/lib/volt/{templates → page}/targets/attribute_section.rb +1 -1
  32. data/lib/volt/{templates → page}/targets/attribute_target.rb +4 -4
  33. data/lib/volt/{templates → page}/targets/base_section.rb +0 -0
  34. data/lib/volt/{templates → page}/targets/binding_document/base_node.rb +0 -0
  35. data/lib/volt/{templates → page}/targets/binding_document/component_node.rb +1 -1
  36. data/lib/volt/{templates → page}/targets/binding_document/html_node.rb +1 -1
  37. data/lib/volt/{templates → page}/targets/dom_section.rb +1 -1
  38. data/lib/volt/{templates → page}/targets/dom_target.rb +2 -2
  39. data/lib/volt/page/tasks.rb +61 -0
  40. data/lib/volt/{templates → page}/template_renderer.rb +1 -1
  41. data/lib/volt/reactive/destructive_methods.rb +19 -0
  42. data/lib/volt/reactive/event_chain.rb +13 -19
  43. data/lib/volt/reactive/events.rb +30 -116
  44. data/lib/volt/reactive/object_tracker.rb +29 -22
  45. data/lib/volt/reactive/reactive_array.rb +2 -3
  46. data/lib/volt/reactive/reactive_tags.rb +7 -0
  47. data/lib/volt/reactive/reactive_value.rb +86 -81
  48. data/lib/volt/reactive/string_extensions.rb +0 -3
  49. data/lib/volt/router/routes.rb +2 -2
  50. data/lib/volt/server/channel_handler.rb +24 -4
  51. data/lib/volt/server/component_handler.rb +2 -1
  52. data/lib/volt/server/rack/component_files.rb +3 -2
  53. data/lib/volt/server/rack/component_paths.rb +11 -1
  54. data/lib/volt/server/rack/index_files.rb +1 -1
  55. data/lib/volt/server.rb +16 -0
  56. data/lib/volt/store/mongo.rb +1 -1
  57. data/lib/volt/tasks/dispatcher.rb +25 -0
  58. data/spec/models/model_spec.rb +90 -15
  59. data/spec/models/params_spec.rb +16 -0
  60. data/spec/models/reactive_array_spec.rb +17 -18
  61. data/spec/models/reactive_value_spec.rb +11 -0
  62. data/spec/models/store_spec.rb +16 -0
  63. data/spec/server/rack/component_files_spec.rb +18 -16
  64. data/spec/server/rack/component_paths_spec.rb +21 -19
  65. data/spec/templates/targets/binding_document/component_node_spec.rb +1 -1
  66. data/spec/templates/template_binding_spec.rb +1 -1
  67. data/templates/project/app/home/views/index/index.html +2 -0
  68. data/volt.gemspec +2 -0
  69. metadata +67 -25
  70. data/lib/volt/templates/channel.rb +0 -47
@@ -1,5 +1,5 @@
1
- require 'volt/templates/base_binding'
2
- require 'volt/templates/template_renderer'
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
- base_object = Object.send(:const_get, base_name.to_sym)
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.send(:const_get, (name + 'Controller').to_sym)
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/templates/attribute_binding'
10
- require 'volt/templates/content_binding'
11
- require 'volt/templates/each_binding'
12
- require 'volt/templates/if_binding'
13
- require 'volt/templates/template_binding'
14
- require 'volt/templates/template_renderer'
15
- require 'volt/templates/reactive_template'
16
- require 'volt/templates/event_binding'
17
- require 'volt/templates/document_events'
18
- require 'volt/templates/sub_context'
19
- require 'volt/templates/targets/dom_target'
20
- require 'volt/templates/channel'
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/templates/render_queue'
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(Model.new)#({}, nil, 'store', @model_classes))
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
- channel.on('message') do |message|
68
- puts "GOT: #{message}"
69
- end
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
- # Benchmark.bm(1) do
78
+ Benchmark.bm(1) do
78
79
  @url.parse("http://localhost:3000" + url)
79
- # end
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
1
  # AttributeSection provides a place to render templates that
2
2
  # will be placed as text into an attribute.
3
3
 
4
- require 'volt/templates/targets/base_section'
4
+ require 'volt/page/targets/base_section'
5
5
 
6
6
  class AttributeSection
7
7
  def initialize(target, binding_name)
@@ -1,7 +1,7 @@
1
- require 'volt/templates/targets/base_section'
2
- require 'volt/templates/targets/attribute_section'
3
- require 'volt/templates/targets/binding_document/component_node'
4
- require 'volt/templates/targets/binding_document/html_node'
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.
@@ -1,4 +1,4 @@
1
- require 'volt/templates/targets/binding_document/html_node'
1
+ require 'volt/page/targets/binding_document/html_node'
2
2
  require 'volt/reactive/events'
3
3
 
4
4
  # Component nodes contain an array of both HtmlNodes and ComponentNodes.
@@ -1,4 +1,4 @@
1
- require 'volt/templates/targets/binding_document/base_node'
1
+ require 'volt/page/targets/binding_document/base_node'
2
2
 
3
3
  class HtmlNode < BaseNode
4
4
  def initialize(html)
@@ -1,4 +1,4 @@
1
- require 'volt/templates/targets/base_section'
1
+ require 'volt/page/targets/base_section'
2
2
 
3
3
  class DomSection < BaseSection
4
4
  def initialize(binding_name)
@@ -1,5 +1,5 @@
1
- require 'volt/templates/targets/base_section'
2
- require 'volt/templates/targets/dom_section'
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
@@ -1,4 +1,4 @@
1
- require 'volt/templates/base_binding'
1
+ require 'volt/page/bindings/base_binding'
2
2
 
3
3
  class TemplateRenderer < BaseBinding
4
4
  attr_reader :context
@@ -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 |*args|
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
- @main_object.trigger!(event, *args)
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
- all_listening_events.uniq.each do |event|
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
@@ -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
- add_event_to_chains(event) if first
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
- remove_event_from_chains(event)
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
- # We need to keep the event chain's updated for any objects we're
182
- # following for events.
183
- event_followings.each {|ef| ef.event_chain.remove_event(event) }
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
- all_listeners_for(event).each do |listener|
262
- # Call the event on each listener
263
- listener.call(*args)
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
- ObjectTracker.process_queue if !reactive? && !respond_to?(:skip_current_queue_flush)
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