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,4 +1,3 @@
|
|
1
|
-
OBJECT_TRACKER_DEBUG = false
|
2
1
|
|
3
2
|
class ObjectTracker
|
4
3
|
@@queue = {}
|
@@ -9,6 +8,15 @@ class ObjectTracker
|
|
9
8
|
@@cache_enabled
|
10
9
|
end
|
11
10
|
|
11
|
+
def self.enable_cache
|
12
|
+
clear_cache
|
13
|
+
@@cache_enabled = true
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.disable_cache
|
17
|
+
@@cache_enabled = false
|
18
|
+
end
|
19
|
+
|
12
20
|
def self.cache_version
|
13
21
|
@@cache_version
|
14
22
|
end
|
@@ -18,7 +26,6 @@ class ObjectTracker
|
|
18
26
|
end
|
19
27
|
|
20
28
|
def initialize(main_object)
|
21
|
-
# puts "NEW OBJECT TRACKER FOR: #{main_object.inspect}"
|
22
29
|
@main_object = main_object
|
23
30
|
@enabled = false
|
24
31
|
end
|
@@ -28,41 +35,38 @@ class ObjectTracker
|
|
28
35
|
end
|
29
36
|
|
30
37
|
def queue_update
|
31
|
-
puts "QUEUE UPDATE" if OBJECT_TRACKER_DEBUG
|
32
38
|
@@queue[self] = true
|
33
39
|
end
|
34
40
|
|
35
41
|
# Run through the queue and update the followers for each
|
36
42
|
def self.process_queue
|
43
|
+
return if @@queue.size == 0
|
37
44
|
# puts "PROCESS QUEUE: #{@@queue.size}"
|
38
|
-
|
39
|
-
|
40
|
-
|
45
|
+
queue = @@queue
|
46
|
+
|
47
|
+
# puts "Update Followers #{@@queue.size}"
|
41
48
|
|
42
49
|
# Clear before running incase someone adds during
|
43
50
|
@@queue = {}
|
44
|
-
|
45
|
-
@@cache_enabled = true
|
46
|
-
self.clear_cache
|
51
|
+
# self.enable_cache
|
47
52
|
|
48
53
|
queue.each_pair do |object_tracker,val|
|
49
54
|
object_tracker.update_followers
|
50
55
|
end
|
51
56
|
|
52
|
-
|
57
|
+
# self.disable_cache
|
53
58
|
|
59
|
+
# puts "UPDATED FOLLOWERS"
|
54
60
|
end
|
55
61
|
|
56
62
|
def enable!
|
57
63
|
unless @enabled
|
58
|
-
puts "Enable OBJ Tracker" if OBJECT_TRACKER_DEBUG
|
59
64
|
@enabled = true
|
60
65
|
queue_update
|
61
66
|
end
|
62
67
|
end
|
63
68
|
|
64
69
|
def disable!
|
65
|
-
puts "Disable OBJ Tracker" if OBJECT_TRACKER_DEBUG
|
66
70
|
remove_followers
|
67
71
|
@@queue.delete(self)
|
68
72
|
@enabled = false
|
@@ -70,21 +74,22 @@ class ObjectTracker
|
|
70
74
|
|
71
75
|
def update_followers
|
72
76
|
if @enabled
|
73
|
-
puts "UPDATE" if OBJECT_TRACKER_DEBUG
|
74
77
|
current_obj = @main_object.cur#(true)
|
75
78
|
|
76
79
|
# puts "UPDATE ON #{current_obj.inspect}"
|
77
|
-
|
78
|
-
|
80
|
+
|
81
|
+
if !@cached_current_obj || current_obj.object_id != @cached_current_obj.object_id
|
82
|
+
remove_followers
|
79
83
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
84
|
+
# Add to current
|
85
|
+
should_attach = current_obj.respond_to?(:on)
|
86
|
+
if should_attach
|
87
|
+
@cached_current_obj = current_obj
|
88
|
+
# puts "ATTACH: #{@cached_current_obj}"
|
89
|
+
@current_obj_chain_listener = @main_object.event_chain.add_object(@cached_current_obj)
|
90
|
+
end
|
85
91
|
end
|
86
92
|
else
|
87
|
-
puts "DISABLED, no update" if OBJECT_TRACKER_DEBUG
|
88
93
|
end
|
89
94
|
end
|
90
95
|
|
@@ -92,7 +97,9 @@ class ObjectTracker
|
|
92
97
|
def remove_followers
|
93
98
|
# Remove from previous
|
94
99
|
if @cached_current_obj
|
95
|
-
@
|
100
|
+
@current_obj_chain_listener.remove
|
101
|
+
@current_obj_chain_listener = nil
|
102
|
+
|
96
103
|
@cached_current_obj = nil
|
97
104
|
end
|
98
105
|
end
|
@@ -18,7 +18,6 @@ class ReactiveArray# < Array
|
|
18
18
|
end
|
19
19
|
|
20
20
|
tag_method(:[]=) do
|
21
|
-
destructive!
|
22
21
|
pass_reactive!
|
23
22
|
end
|
24
23
|
|
@@ -69,7 +68,6 @@ class ReactiveArray# < Array
|
|
69
68
|
|
70
69
|
|
71
70
|
tag_method(:<<) do
|
72
|
-
destructive!
|
73
71
|
pass_reactive!
|
74
72
|
end
|
75
73
|
# alias :__old_append :<<
|
@@ -83,7 +81,7 @@ class ReactiveArray# < Array
|
|
83
81
|
trigger_on_direct_listeners!('added', self.size-1)
|
84
82
|
|
85
83
|
trigger_size_change!
|
86
|
-
|
84
|
+
|
87
85
|
return result
|
88
86
|
end
|
89
87
|
|
@@ -172,6 +170,7 @@ class ReactiveArray# < Array
|
|
172
170
|
self.trigger_by_scope!(event_name, *passed_args) do |scope|
|
173
171
|
# method_name, *args, block = scope
|
174
172
|
method_name, args, block = split_scope(scope)
|
173
|
+
# puts "SCOPE CHECK: TFI: #{method_name.inspect} - #{args.inspect} on #{self.inspect}"
|
175
174
|
|
176
175
|
result = case method_name
|
177
176
|
when nil
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'volt/reactive/destructive_methods'
|
2
|
+
|
1
3
|
# ReactiveTags provide an easy way to specify how a class deals with
|
2
4
|
# reactive events and method calls.als
|
3
5
|
module ReactiveTags
|
@@ -29,6 +31,11 @@ module ReactiveTags
|
|
29
31
|
|
30
32
|
@reactive_method_tags ||= {}
|
31
33
|
@reactive_method_tags[method_name.to_sym] = tagger.method_tags
|
34
|
+
|
35
|
+
# Track a destructive method
|
36
|
+
if tagger.method_tags.destructive
|
37
|
+
DestructiveMethods.add_method(method_name)
|
38
|
+
end
|
32
39
|
end
|
33
40
|
|
34
41
|
def tag_all_methods(&block)
|
@@ -4,6 +4,7 @@ require 'volt/reactive/string_extensions'
|
|
4
4
|
require 'volt/reactive/array_extensions'
|
5
5
|
require 'volt/reactive/reactive_array'
|
6
6
|
require 'volt/reactive/object_tracker'
|
7
|
+
require 'volt/reactive/destructive_methods'
|
7
8
|
|
8
9
|
class Object
|
9
10
|
def cur
|
@@ -41,7 +42,7 @@ class ReactiveValue < BasicObject
|
|
41
42
|
# Proxy methods to the ReactiveManager. We want to have as few
|
42
43
|
# as possible methods on reactive values, so all other methods
|
43
44
|
# are forwarded to the object the reactive value points to.
|
44
|
-
[:cur, :cur=, :on, :trigger!].each do |method_name|
|
45
|
+
[:cur, :cur=, :on, :trigger!, :trigger_by_scope!].each do |method_name|
|
45
46
|
define_method(method_name) do |*args, &block|
|
46
47
|
@reactive_manager.send(method_name, *args, &block)
|
47
48
|
end
|
@@ -52,75 +53,53 @@ class ReactiveValue < BasicObject
|
|
52
53
|
end
|
53
54
|
alias_method :rm, :reactive_manager
|
54
55
|
|
55
|
-
def check_tag(method_name, tag_name)
|
56
|
-
current_obj = cur # TODO: should be cached somehow
|
57
|
-
|
58
|
-
if current_obj.respond_to?(:reactive_method_tag)
|
59
|
-
tag = current_obj.reactive_method_tag(method_name, tag_name)
|
60
|
-
|
61
|
-
unless tag
|
62
|
-
# Get the tag from the all methods if its not directly specified
|
63
|
-
tag = current_obj.reactive_method_tag(:__all_methods, tag_name)
|
64
|
-
end
|
65
|
-
|
66
|
-
# Evaluate now if its a proc
|
67
|
-
tag = tag.call(method_name) if tag.class == ::Proc
|
68
|
-
|
69
|
-
return tag
|
70
|
-
end
|
71
|
-
|
72
|
-
return nil
|
73
|
-
end
|
74
|
-
|
75
56
|
def puts(*args)
|
76
57
|
::Object.send(:puts, *args)
|
77
58
|
end
|
78
59
|
|
60
|
+
def __is_destructive?(method_name)
|
61
|
+
last_char = method_name[-1]
|
62
|
+
if last_char == '=' && method_name[-2] != '='
|
63
|
+
# Method is an assignment (and not a comparator ==)
|
64
|
+
return true
|
65
|
+
elsif last_char == '!' || last_char == '<'
|
66
|
+
# Method is tagged as destructive, or is a push ( << )
|
67
|
+
return true
|
68
|
+
elsif ::DestructiveMethods.might_be_destructive?(method_name)
|
69
|
+
# Method may be destructive, check if it actually is on the current value
|
70
|
+
# TODO: involves a call to cur
|
71
|
+
return reactive_manager.check_tag(method_name, :destructive, self.cur)
|
72
|
+
else
|
73
|
+
return false
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
79
77
|
def method_missing(method_name, *args, &block)
|
80
78
|
# Unroll send into a direct call
|
81
79
|
if method_name == :send
|
82
80
|
method_name, *args = args
|
83
81
|
end
|
84
|
-
|
85
|
-
# Check to see if the method we're calling wants to receive reactive values.
|
86
|
-
pass_reactive = check_tag(method_name, :pass_reactive)
|
87
82
|
|
88
83
|
# For some methods, we pass directly to the current object. This
|
89
84
|
# helps ReactiveValue's be well behaved ruby citizens.
|
90
85
|
# Also skip if this is a destructive method
|
91
|
-
if SKIP_METHODS.include?(method_name) ||
|
92
|
-
|
93
|
-
|
86
|
+
if SKIP_METHODS.include?(method_name) || __is_destructive?(method_name)
|
87
|
+
current_obj = self.cur
|
88
|
+
|
89
|
+
# Unwrap arguments if the method doesn't want reactive values
|
90
|
+
pass_args = reactive_manager.unwrap_if_pass_reactive(args, method_name, current_obj)
|
91
|
+
|
92
|
+
return current_obj.__send__(method_name, *pass_args, &block)
|
94
93
|
end
|
95
94
|
|
96
95
|
@block_reactives = []
|
97
|
-
result = @reactive_manager.with_and_options(args
|
98
|
-
#
|
99
|
-
#
|
100
|
-
|
101
|
-
|
102
|
-
#
|
103
|
-
|
104
|
-
#
|
105
|
-
# if false && new_block
|
106
|
-
# new_block = ::Proc.new do |*block_args|
|
107
|
-
# res = block.call(*block_args.map {|v| ::ReactiveValue.new(v) })
|
108
|
-
#
|
109
|
-
# result.rm.remove_parent!(index_cache[index]) if index_cache[index]
|
110
|
-
# puts "index: #{index}"
|
111
|
-
# index_cache[index] = res
|
112
|
-
#
|
113
|
-
# # @block_reactives << res
|
114
|
-
# result.rm.add_parent!(res)
|
115
|
-
# # puts "Parent Size: #{result.rm.parents.size}"
|
116
|
-
#
|
117
|
-
# index += 1
|
118
|
-
#
|
119
|
-
# res.cur
|
120
|
-
# end
|
121
|
-
# end
|
122
|
-
|
123
|
-
val.__send__(method_name, *in_args, &new_block)
|
96
|
+
result = @reactive_manager.with_and_options(args) do |val, in_args|
|
97
|
+
# Unwrap arguments if the method doesn't want reactive values
|
98
|
+
# TODO: Should cache the lookup on pass_reactive
|
99
|
+
pass_args = reactive_manager.unwrap_if_pass_reactive(in_args, method_name, val)
|
100
|
+
|
101
|
+
# puts "GET #{method_name.inspect}"
|
102
|
+
val.__send__(method_name, *pass_args, &block)
|
124
103
|
end
|
125
104
|
|
126
105
|
manager = result.reactive_manager
|
@@ -209,8 +188,6 @@ class ReactiveManager
|
|
209
188
|
@scope = scope
|
210
189
|
|
211
190
|
@parents = []
|
212
|
-
|
213
|
-
object_tracker.enable!
|
214
191
|
end
|
215
192
|
|
216
193
|
def reactive?
|
@@ -226,20 +203,17 @@ class ReactiveManager
|
|
226
203
|
end
|
227
204
|
|
228
205
|
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
# # update_current_object(true) if last && !has_non_tracking_listeners?
|
241
|
-
# # object_tracker.disable! if @listeners.size == 0
|
242
|
-
# end
|
206
|
+
def event_added(event, scope, first)
|
207
|
+
# When the first event is registered, we need to start listening on our current object
|
208
|
+
# for it to publish events.
|
209
|
+
object_tracker.enable! if first
|
210
|
+
end
|
211
|
+
|
212
|
+
def event_removed(event, last)
|
213
|
+
# If no one is listening on the reactive value, then we don't need to listen on our
|
214
|
+
# current object for events, because no one cares.
|
215
|
+
object_tracker.disable! if @listeners.size == 0
|
216
|
+
end
|
243
217
|
|
244
218
|
def object_tracker
|
245
219
|
@object_tracker ||= ::ObjectTracker.new(self)
|
@@ -248,7 +222,10 @@ class ReactiveManager
|
|
248
222
|
|
249
223
|
# Fetch the current value
|
250
224
|
def cur
|
251
|
-
#
|
225
|
+
# @@cur_count ||= 0
|
226
|
+
# @@cur_count += 1
|
227
|
+
# puts "Cur: #{@@cur_count}"# if @@cur_count % 100 == 0
|
228
|
+
# if ObjectTracker.cache_version == @cached_version
|
252
229
|
# return @cached_obj
|
253
230
|
# end
|
254
231
|
|
@@ -270,8 +247,8 @@ class ReactiveManager
|
|
270
247
|
end
|
271
248
|
|
272
249
|
# if ObjectTracker.cache_enabled
|
273
|
-
|
274
|
-
|
250
|
+
# @cached_obj = result
|
251
|
+
# @cached_version = ObjectTracker.cache_version
|
275
252
|
# end
|
276
253
|
|
277
254
|
return result
|
@@ -290,14 +267,44 @@ class ReactiveManager
|
|
290
267
|
end
|
291
268
|
end
|
292
269
|
|
270
|
+
|
271
|
+
# Method calls can be tagged so the reactive value knows
|
272
|
+
# how to handle them. This lets you check the state of
|
273
|
+
# the tags.
|
274
|
+
def check_tag(method_name, tag_name, current_obj)
|
275
|
+
if current_obj.respond_to?(:reactive_method_tag)
|
276
|
+
tag = current_obj.reactive_method_tag(method_name, tag_name)
|
277
|
+
|
278
|
+
unless tag
|
279
|
+
# Get the tag from the all methods if its not directly specified
|
280
|
+
tag = current_obj.reactive_method_tag(:__all_methods, tag_name)
|
281
|
+
end
|
282
|
+
|
283
|
+
# Evaluate now if its a proc
|
284
|
+
tag = tag.call(method_name) if tag.class == ::Proc
|
285
|
+
|
286
|
+
return tag
|
287
|
+
end
|
288
|
+
|
289
|
+
return nil
|
290
|
+
end
|
291
|
+
|
292
|
+
def unwrap_if_pass_reactive(args, method_name, current_obj)
|
293
|
+
# Check to see if the method we're calling wants to receive reactive values.
|
294
|
+
pass_reactive = check_tag(method_name, :pass_reactive, current_obj)
|
295
|
+
|
296
|
+
# Unwrap arguments if the method doesn't want reactive values
|
297
|
+
return pass_reactive ? args : args.map{|v| v.cur }
|
298
|
+
end
|
299
|
+
|
293
300
|
# With returns a new reactive value dependent on any arguments passed in.
|
294
301
|
# If a block is passed in, the getter is the block its self, which will
|
295
302
|
# be passed the .cur and the .cur of any reactive arguments.
|
296
303
|
def with(*args, &block)
|
297
|
-
return with_and_options(args,
|
304
|
+
return with_and_options(args, &block)
|
298
305
|
end
|
299
306
|
|
300
|
-
def with_and_options(args,
|
307
|
+
def with_and_options(args, &block)
|
301
308
|
getter = @getter
|
302
309
|
setter = @setter
|
303
310
|
scope = @scope
|
@@ -305,17 +312,14 @@ class ReactiveManager
|
|
305
312
|
if block
|
306
313
|
# If a block was passed in, the getter now becomes a proc that calls
|
307
314
|
# the passed in block with the right arguments.
|
308
|
-
getter = ::Proc.new do
|
309
|
-
# Unwrap arguments if the method doesn't want reactive values
|
310
|
-
pass_args = pass_reactive ? args : args.map{|v| v.cur }
|
311
|
-
|
315
|
+
getter = ::Proc.new do
|
312
316
|
# TODO: Calling cur every time
|
313
317
|
current_val = self.cur
|
314
318
|
|
315
319
|
if current_val.is_a?(Exception)
|
316
320
|
current_val
|
317
321
|
else
|
318
|
-
block.call(current_val,
|
322
|
+
block.call(current_val, args)
|
319
323
|
end
|
320
324
|
end
|
321
325
|
|
@@ -331,7 +335,7 @@ class ReactiveManager
|
|
331
335
|
|
332
336
|
# Add the ReactiveValue we're building from
|
333
337
|
new_val.reactive_manager.add_parent!(self)
|
334
|
-
|
338
|
+
|
335
339
|
# Add any reactive arguments as parents
|
336
340
|
args.select(&:reactive?).each do |arg|
|
337
341
|
new_val.reactive_manager.add_parent!(arg.reactive_manager)
|
@@ -349,6 +353,7 @@ class ReactiveManager
|
|
349
353
|
@parents.delete(parent)
|
350
354
|
event_chain.remove_object(parent)
|
351
355
|
end
|
356
|
+
|
352
357
|
|
353
358
|
def set_scope!(new_scope)
|
354
359
|
@scope = new_scope
|
data/lib/volt/router/routes.rb
CHANGED
@@ -63,9 +63,9 @@ class Routes
|
|
63
63
|
match_path = ''
|
64
64
|
sections.each do |section|
|
65
65
|
if section[0] == '{' && section[-1] == '}'
|
66
|
-
match_path
|
66
|
+
match_path = match_path + "[^\/]+"
|
67
67
|
else
|
68
|
-
match_path
|
68
|
+
match_path = match_path + section
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
@@ -2,9 +2,19 @@ require 'json'
|
|
2
2
|
require 'sockjs/session'
|
3
3
|
|
4
4
|
class ChannelHandler < SockJS::Session
|
5
|
-
|
5
|
+
# Create one instance of the dispatcher
|
6
|
+
|
7
|
+
def self.dispatcher=(val)
|
8
|
+
@@dispatcher = val
|
9
|
+
end
|
10
|
+
|
11
|
+
# Sends a message to all, optionally skipping a users channel
|
12
|
+
def self.send_message_all(skip_channel=nil, *args)
|
6
13
|
@@channels.each do |channel|
|
7
|
-
channel
|
14
|
+
if skip_channel && channel == skip_channel
|
15
|
+
next
|
16
|
+
end
|
17
|
+
channel.send_message(*args)
|
8
18
|
end
|
9
19
|
|
10
20
|
end
|
@@ -19,8 +29,18 @@ class ChannelHandler < SockJS::Session
|
|
19
29
|
end
|
20
30
|
|
21
31
|
def process_message(message)
|
22
|
-
|
23
|
-
|
32
|
+
# self.class.message_all(message)
|
33
|
+
# Messages are json and wrapped in an array
|
34
|
+
message = JSON.parse(message).first
|
35
|
+
|
36
|
+
puts "GOT: #{message.inspect}"
|
37
|
+
@@dispatcher.dispatch(self, message)
|
38
|
+
end
|
39
|
+
|
40
|
+
def send_message(*args)
|
41
|
+
str = JSON.dump([*args])
|
42
|
+
|
43
|
+
send(str)
|
24
44
|
end
|
25
45
|
|
26
46
|
def closed
|
@@ -16,8 +16,9 @@ class ComponentHandler
|
|
16
16
|
component_name = req.path.strip.gsub(/^\/components\//, '').gsub(/[.]js$/, '')
|
17
17
|
|
18
18
|
code = ''
|
19
|
+
|
19
20
|
|
20
|
-
component_files = ComponentFiles.new(component_name, @component_paths)
|
21
|
+
component_files = ComponentFiles.new(component_name, @component_paths, true)
|
21
22
|
component_files.component_paths.each do |component_path, component_name|
|
22
23
|
code << ComponentTemplates.new(component_path, component_name).code
|
23
24
|
code << "\n\n"
|
@@ -12,6 +12,7 @@ class ComponentFiles
|
|
12
12
|
@main_component = main_component
|
13
13
|
|
14
14
|
if @main_component
|
15
|
+
# puts "ADD VOLT"
|
15
16
|
# Add in volt's JS files first
|
16
17
|
component('volt')
|
17
18
|
end
|
@@ -89,9 +90,9 @@ class ComponentFiles
|
|
89
90
|
|
90
91
|
opal_js_files = []
|
91
92
|
if Volt.source_maps?
|
92
|
-
opal_js_files += opal_files.environment['volt/
|
93
|
+
opal_js_files += opal_files.environment['volt/page/page'].to_a.map {|v| '/assets/' + v.logical_path + '?body=1' }
|
93
94
|
else
|
94
|
-
opal_js_files << '/assets/volt/
|
95
|
+
opal_js_files << '/assets/volt/page/page.js'
|
95
96
|
end
|
96
97
|
opal_js_files << '/components/home.js'
|
97
98
|
|
@@ -37,7 +37,7 @@ class ComponentPaths
|
|
37
37
|
folder_name = folder[/[^\/]+$/]
|
38
38
|
|
39
39
|
@components[folder_name] ||= []
|
40
|
-
@components[folder_name] << folder
|
40
|
+
@components[folder_name] << folder
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
@@ -45,6 +45,16 @@ class ComponentPaths
|
|
45
45
|
return @components
|
46
46
|
end
|
47
47
|
|
48
|
+
def add_tasks_to_load_path
|
49
|
+
components.each do |name,component_folders|
|
50
|
+
component_folders.each do |component_folder|
|
51
|
+
Dir["#{component_folder}/tasks"].each do |tasks_folder|
|
52
|
+
$LOAD_PATH.unshift(tasks_folder)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
48
58
|
def component_path(name)
|
49
59
|
folders = components[name]
|
50
60
|
|
data/lib/volt/server.rb
CHANGED
@@ -6,6 +6,7 @@ if RUBY_PLATFORM != 'java'
|
|
6
6
|
end
|
7
7
|
require "sprockets-sass"
|
8
8
|
require "sass"
|
9
|
+
require 'listen'
|
9
10
|
|
10
11
|
require 'volt/extra_core/extra_core'
|
11
12
|
require 'volt/server/component_handler'
|
@@ -15,12 +16,24 @@ end
|
|
15
16
|
require 'volt/server/rack/component_paths'
|
16
17
|
require 'volt/server/rack/index_files'
|
17
18
|
require 'volt/server/rack/opal_files'
|
19
|
+
require 'volt/tasks/dispatcher'
|
18
20
|
|
19
21
|
|
20
22
|
class Server
|
21
23
|
def initialize
|
22
24
|
@app_path = File.expand_path(File.join(Dir.pwd, "app"))
|
23
25
|
@component_paths = ComponentPaths.new
|
26
|
+
|
27
|
+
setup_change_listener
|
28
|
+
end
|
29
|
+
|
30
|
+
def setup_change_listener
|
31
|
+
# Setup the listeners for file changes
|
32
|
+
puts "Listen for changes at #{@app_path}"
|
33
|
+
listener = Listen.to("#{@app_path}/") do |modified, added, removed|
|
34
|
+
ChannelHandler.send_message_all(nil, 'reload')
|
35
|
+
end
|
36
|
+
listener.start
|
24
37
|
end
|
25
38
|
|
26
39
|
def app
|
@@ -42,6 +55,9 @@ class Server
|
|
42
55
|
|
43
56
|
# Handle socks js connection
|
44
57
|
if RUBY_PLATFORM != 'java'
|
58
|
+
component_paths.add_tasks_to_load_path
|
59
|
+
ChannelHandler.dispatcher = Dispatcher.new
|
60
|
+
|
45
61
|
@app.map "/channel" do
|
46
62
|
run Rack::SockJS.new(ChannelHandler)#, :websocket => false
|
47
63
|
end
|
data/lib/volt/store/mongo.rb
CHANGED
@@ -0,0 +1,25 @@
|
|
1
|
+
# The task dispatcher is responsible for taking incoming messages
|
2
|
+
# from the socket channel and dispatching them to the proper handler.
|
3
|
+
class Dispatcher
|
4
|
+
|
5
|
+
def dispatch(channel, message)
|
6
|
+
callback_id, class_name, method_name, *args = message
|
7
|
+
|
8
|
+
# TODO: Think about security?
|
9
|
+
|
10
|
+
if class_name[/Tasks$/] && !class_name['::']
|
11
|
+
require(class_name.underscore)
|
12
|
+
|
13
|
+
# Get the class
|
14
|
+
klass = Object.send(:const_get, class_name)
|
15
|
+
|
16
|
+
# Init and send the method
|
17
|
+
result = klass.new(channel, self).send(method_name, *args)
|
18
|
+
|
19
|
+
if callback_id
|
20
|
+
# Callback with result
|
21
|
+
channel.send_message('response', callback_id, result)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|