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,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
- puts "Process #{@@queue.size} items" if OBJECT_TRACKER_DEBUG
39
- # TODO: Doing a full dup here is expensive?
40
- queue = @@queue.dup
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
- @@cache_enabled = false
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
- remove_followers
80
+
81
+ if !@cached_current_obj || current_obj.object_id != @cached_current_obj.object_id
82
+ remove_followers
79
83
 
80
- # Add to current
81
- should_attach = current_obj.respond_to?(:on)
82
- if should_attach
83
- current_obj.add_event_follower(@main_object)
84
- @cached_current_obj = current_obj
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
- @cached_current_obj.remove_event_follower(@main_object)
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) || check_tag(method_name, :destructive)# || (method_name[0] =~ /[a-zA-Z]/ && !cur.is_a?(::Exception))
92
- pass_args = pass_reactive ? args : args.map{|v| v.cur }
93
- return cur.__send__(method_name, *pass_args, &block)
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, pass_reactive) do |val, in_args|
98
- # When a method is called with a block, we pass in our own block that wraps the
99
- # block passed in. This way we can pass in any arguments as reactive and track
100
- # the return values.
101
- new_block = block
102
- # index_cache = []
103
- # index = 0
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
- # def event_added(event, scope, first)
230
- # # When the first event is registered, we need to start listening on our current object
231
- # # for it to publish events.
232
- # # object_tracker.enable! if first
233
- # end
234
- #
235
- # def event_removed(event, last)
236
- # # If no one is listening on the reactive value, then we don't need to listen on our
237
- # # current object for events, because no one cares.
238
- # # Note: when we're tracking the current object, it will have one registered changed
239
- # # event.
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
- # if @cached_obj && ObjectTracker.cache_version == @cached_version
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
- # @cached_obj = result
274
- # @cached_version = ObjectTracker.cache_version
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, false, &block)
304
+ return with_and_options(args, &block)
298
305
  end
299
306
 
300
- def with_and_options(args, pass_reactive, &block)
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, pass_args)
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
@@ -19,9 +19,6 @@ class String
19
19
  end
20
20
 
21
21
  if RUBY_PLATFORM != 'opal'
22
- tag_method(:<<) do
23
- destructive!
24
- end
25
22
  def <<(val)
26
23
  if val.reactive?
27
24
  raise "Cannot append a reactive string to non-reactive string. Use + instead"
@@ -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 << section
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
- def self.message_all(message)
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.send(message)
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
- puts "Process: #{message}"
23
- self.class.message_all(message)
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/templates/page'].to_a.map {|v| '/assets/' + v.logical_path + '?body=1' }
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/templates/page.js'
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
 
@@ -44,7 +44,7 @@ class IndexFiles
44
44
  end
45
45
 
46
46
  def css_files
47
- ComponentFiles.new('home', @component_paths).css_files
47
+ ComponentFiles.new('home', @component_paths, true).css_files
48
48
  end
49
49
 
50
50
 
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
@@ -2,4 +2,4 @@ require 'mongo'
2
2
 
3
3
  mongo_client = Mongo::MongoClient.new("localhost", 27017)
4
4
 
5
- db = mongo_client.db("test1")
5
+ db = mongo_client.db("test1")
@@ -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