volt 0.9.3.pre1 → 0.9.3.pre2

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 (123) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/CHANGELOG.md +15 -1
  4. data/Gemfile +30 -3
  5. data/README.md +7 -2
  6. data/Rakefile +17 -0
  7. data/app/volt/models/user.rb +1 -1
  8. data/app/volt/tasks/live_query/live_query.rb +0 -1
  9. data/app/volt/tasks/live_query/live_query_pool.rb +8 -2
  10. data/app/volt/tasks/live_query/query_tracker.rb +2 -2
  11. data/app/volt/tasks/query_tasks.rb +10 -27
  12. data/app/volt/tasks/store_tasks.rb +6 -5
  13. data/app/volt/tasks/user_tasks.rb +2 -2
  14. data/docs/UPGRADE_GUIDE.md +14 -0
  15. data/lib/volt/boot.rb +1 -0
  16. data/lib/volt/cli/asset_compile.rb +25 -7
  17. data/lib/volt/cli/console.rb +6 -5
  18. data/lib/volt/cli/generate.rb +2 -2
  19. data/lib/volt/config.rb +2 -1
  20. data/lib/volt/controllers/http_controller.rb +4 -3
  21. data/lib/volt/controllers/model_controller.rb +41 -19
  22. data/lib/volt/controllers/template_helpers.rb +19 -0
  23. data/lib/volt/extra_core/array.rb +6 -0
  24. data/lib/volt/extra_core/hash.rb +8 -26
  25. data/lib/volt/extra_core/string.rb +1 -1
  26. data/lib/volt/models/array_model.rb +12 -4
  27. data/lib/volt/models/associations.rb +11 -13
  28. data/lib/volt/models/buffer.rb +1 -1
  29. data/lib/volt/models/model.rb +22 -13
  30. data/lib/volt/models/model_helpers/model_change_helpers.rb +0 -1
  31. data/lib/volt/models/model_helpers/model_helpers.rb +11 -0
  32. data/lib/volt/models/permissions.rb +9 -12
  33. data/lib/volt/models/persistors/array_store.rb +7 -7
  34. data/lib/volt/models/persistors/base.rb +9 -0
  35. data/lib/volt/models/persistors/cookies.rb +0 -4
  36. data/lib/volt/models/persistors/flash.rb +0 -4
  37. data/lib/volt/models/persistors/local_store.rb +0 -4
  38. data/lib/volt/models/persistors/model_store.rb +13 -21
  39. data/lib/volt/models/persistors/page.rb +22 -0
  40. data/lib/volt/models/persistors/params.rb +0 -4
  41. data/lib/volt/models/persistors/query/query_listener.rb +3 -2
  42. data/lib/volt/models/url.rb +2 -2
  43. data/lib/volt/models/validators/unique_validator.rb +1 -1
  44. data/lib/volt/models.rb +1 -0
  45. data/lib/volt/page/bindings/attribute_binding.rb +2 -2
  46. data/lib/volt/page/bindings/base_binding.rb +7 -3
  47. data/lib/volt/page/bindings/bindings.rb +9 -0
  48. data/lib/volt/page/bindings/content_binding.rb +2 -2
  49. data/lib/volt/page/bindings/each_binding.rb +16 -12
  50. data/lib/volt/page/bindings/event_binding.rb +4 -4
  51. data/lib/volt/page/bindings/if_binding.rb +3 -3
  52. data/lib/volt/page/bindings/view_binding.rb +4 -4
  53. data/lib/volt/page/bindings/yield_binding.rb +3 -3
  54. data/lib/volt/page/channel.rb +6 -0
  55. data/lib/volt/page/channel_stub.rb +1 -1
  56. data/lib/volt/page/page.rb +20 -54
  57. data/lib/volt/page/path_string_renderer.rb +5 -6
  58. data/lib/volt/page/string_template_renderer.rb +2 -2
  59. data/lib/volt/page/targets/attribute_section.rb +47 -0
  60. data/lib/volt/page/targets/base_section.rb +5 -5
  61. data/lib/volt/page/targets/binding_document/component_node.rb +6 -1
  62. data/lib/volt/page/template_renderer.rb +4 -4
  63. data/lib/volt/reactive/computation.rb +32 -3
  64. data/lib/volt/router/routes.rb +5 -5
  65. data/lib/volt/server/component_templates.rb +30 -2
  66. data/lib/volt/server/forking_server.rb +2 -2
  67. data/lib/volt/server/message_bus/base_message_bus.rb +52 -0
  68. data/lib/volt/server/message_bus/message_encoder.rb +64 -0
  69. data/lib/volt/server/message_bus/peer_to_peer/peer_connection.rb +186 -0
  70. data/lib/volt/server/message_bus/peer_to_peer/peer_server.rb +78 -0
  71. data/lib/volt/server/message_bus/peer_to_peer/server_tracker.rb +57 -0
  72. data/lib/volt/server/message_bus/peer_to_peer/socket_with_timeout.rb +27 -0
  73. data/lib/volt/server/message_bus/peer_to_peer.rb +198 -0
  74. data/lib/volt/server/message_bus/redis.rb +1 -0
  75. data/lib/volt/server/rack/asset_files.rb +2 -2
  76. data/lib/volt/server/rack/component_paths.rb +1 -1
  77. data/lib/volt/server/rack/http_resource.rb +3 -2
  78. data/lib/volt/server/rack/opal_files.rb +6 -9
  79. data/lib/volt/server/websocket/websocket_handler.rb +0 -3
  80. data/lib/volt/server.rb +5 -3
  81. data/lib/volt/spec/setup.rb +11 -12
  82. data/lib/volt/tasks/dispatcher.rb +8 -12
  83. data/lib/volt/tasks/task_handler.rb +3 -2
  84. data/lib/volt/utils/csso_patch.rb +24 -0
  85. data/lib/volt/utils/promise_patch.rb +2 -0
  86. data/lib/volt/version.rb +1 -1
  87. data/lib/volt/volt/app.rb +73 -36
  88. data/lib/volt/volt/server_setup/app.rb +81 -0
  89. data/lib/volt.rb +22 -1
  90. data/spec/apps/kitchen_sink/Gemfile +1 -1
  91. data/spec/controllers/http_controller_spec.rb +5 -3
  92. data/spec/controllers/model_controller_spec.rb +2 -2
  93. data/spec/extra_core/hash_spec.rb +9 -0
  94. data/spec/integration/list_spec.rb +3 -3
  95. data/spec/models/associations_spec.rb +10 -2
  96. data/spec/models/dirty_spec.rb +7 -7
  97. data/spec/models/model_spec.rb +10 -2
  98. data/spec/models/permissions_spec.rb +9 -0
  99. data/spec/models/persistors/store_spec.rb +8 -0
  100. data/spec/page/bindings/content_binding_spec.rb +6 -2
  101. data/spec/page/bindings/each_binding_spec.rb +59 -0
  102. data/spec/page/bindings/if_binding_spec.rb +57 -0
  103. data/spec/page/path_string_renderer_spec.rb +5 -5
  104. data/spec/reactive/computation_spec.rb +65 -1
  105. data/spec/router/routes_spec.rb +1 -1
  106. data/spec/server/html_parser/sandlebars_parser_spec.rb +12 -22
  107. data/spec/server/message_bus/message_encoder_spec.rb +49 -0
  108. data/spec/server/message_bus/peer_to_peer/peer_connection_spec.rb +108 -0
  109. data/spec/server/message_bus/peer_to_peer/peer_server_spec.rb +66 -0
  110. data/spec/server/message_bus/peer_to_peer/socket_with_timeout_spec.rb +11 -0
  111. data/spec/server/message_bus/peer_to_peer_spec.rb +11 -0
  112. data/spec/server/rack/asset_files_spec.rb +1 -1
  113. data/spec/server/rack/http_resource_spec.rb +4 -4
  114. data/spec/spec_helper.rb +16 -3
  115. data/spec/tasks/dispatcher_spec.rb +17 -5
  116. data/spec/tasks/live_query_spec.rb +1 -1
  117. data/spec/tasks/query_tracker_spec.rb +34 -34
  118. data/spec/tasks/user_tasks_spec.rb +4 -2
  119. data/templates/project/Gemfile.tt +14 -3
  120. data/templates/project/config/app.rb.tt +27 -2
  121. data/volt.gemspec +3 -8
  122. metadata +32 -101
  123. data/docs/FAQ.md +0 -7
@@ -0,0 +1,9 @@
1
+ # A file to require all client side bindings
2
+ require 'volt/page/bindings/attribute_binding'
3
+ require 'volt/page/bindings/content_binding'
4
+ require 'volt/page/bindings/each_binding'
5
+ require 'volt/page/bindings/if_binding'
6
+ require 'volt/page/bindings/view_binding'
7
+ require 'volt/page/bindings/yield_binding'
8
+ require 'volt/page/bindings/component_binding'
9
+ require 'volt/page/bindings/event_binding'
@@ -6,8 +6,8 @@ module Volt
6
6
  HTML_ESCAPE_REGEXP = /[&"'><\n]/
7
7
  HTML_ESCAPE = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;', "'" => '&#39;', "\n" => "<br />\n" }
8
8
 
9
- def initialize(page, target, context, binding_name, getter)
10
- super(page, target, context, binding_name)
9
+ def initialize(volt_app, target, context, binding_name, getter)
10
+ super(volt_app, target, context, binding_name)
11
11
 
12
12
  # Listen for changes
13
13
  @computation = lambda do
@@ -2,8 +2,8 @@ require 'volt/page/bindings/base_binding'
2
2
 
3
3
  module Volt
4
4
  class EachBinding < BaseBinding
5
- def initialize(page, target, context, binding_name, getter, variable_name, index_name, template_name)
6
- super(page, target, context, binding_name)
5
+ def initialize(volt_app, target, context, binding_name, getter, variable_name, index_name, template_name)
6
+ super(volt_app, target, context, binding_name)
7
7
 
8
8
  @item_name = variable_name
9
9
  @index_name = index_name
@@ -14,18 +14,22 @@ module Volt
14
14
  @getter = getter
15
15
 
16
16
  # Listen for changes
17
- @computation = -> { reload }.watch!
18
- end
17
+ @computation = lambda do
18
+ begin
19
+ value = @context.instance_eval(&@getter)
20
+ rescue => e
21
+ Volt.logger.error("EachBinding Error: #{e.inspect}")
22
+ value = []
23
+ end
19
24
 
20
- # When a changed event happens, we update to the new size.
21
- def reload
22
- begin
23
- value = @context.instance_eval(&@getter)
24
- rescue => e
25
- Volt.logger.error("EachBinding Error: #{e.inspect}")
26
- value = []
25
+ value
26
+ end.watch_and_resolve! do |value|
27
+ update(value)
27
28
  end
29
+ end
28
30
 
31
+ # When a changed event happens, we update to the new size.
32
+ def update(value)
29
33
  # Since we're checking things like size, we don't want this to be re-triggered on a
30
34
  # size change, so we run without tracking.
31
35
  Computation.run_without_tracking do
@@ -110,7 +114,7 @@ module Volt
110
114
  end
111
115
  end
112
116
 
113
- item_template = TemplateRenderer.new(@page, @target, item_context, binding_name, @template_name)
117
+ item_template = TemplateRenderer.new(@volt_app, @target, item_context, binding_name, @template_name)
114
118
  @templates.insert(position, item_template)
115
119
 
116
120
  update_indexes_after(position)
@@ -29,8 +29,8 @@ module Volt
29
29
  class EventBinding < BaseBinding
30
30
  attr_accessor :context, :binding_name
31
31
 
32
- def initialize(page, target, context, binding_name, event_name, call_proc)
33
- super(page, target, context, binding_name)
32
+ def initialize(volt_app, target, context, binding_name, event_name, call_proc)
33
+ super(volt_app, target, context, binding_name)
34
34
  @event_name = event_name
35
35
 
36
36
  handler = proc do |js_event|
@@ -42,12 +42,12 @@ module Volt
42
42
  result = @context.instance_exec(event, &call_proc)
43
43
  end
44
44
 
45
- @listener = @page.events.add(event_name, self, handler)
45
+ @listener = page.events.add(event_name, self, handler)
46
46
  end
47
47
 
48
48
  # Remove the event binding
49
49
  def remove
50
- @page.events.remove(@event_name, self)
50
+ page.events.remove(@event_name, self)
51
51
  end
52
52
  end
53
53
  end
@@ -2,8 +2,8 @@ require 'volt/page/bindings/base_binding'
2
2
 
3
3
  module Volt
4
4
  class IfBinding < BaseBinding
5
- def initialize(page, target, context, binding_name, branches)
6
- super(page, target, context, binding_name)
5
+ def initialize(volt_app, target, context, binding_name, branches)
6
+ super(volt_app, target, context, binding_name)
7
7
 
8
8
  getter, template_name = branches[0]
9
9
 
@@ -88,7 +88,7 @@ module Volt
88
88
  end
89
89
 
90
90
  if true_template
91
- @template = TemplateRenderer.new(@page, @target, @context, binding_name, true_template)
91
+ @template = TemplateRenderer.new(@volt_app, @target, @context, binding_name, true_template)
92
92
  end
93
93
  end
94
94
  end
@@ -10,8 +10,8 @@ module Volt
10
10
  # lookup paths in ViewLookupForPath
11
11
  # @param [String|nil] content_template_path is the path to the template for the content
12
12
  # provided in the tag.
13
- def initialize(page, target, context, binding_name, binding_in_path, getter, content_template_path = nil)
14
- super(page, target, context, binding_name)
13
+ def initialize(volt_app, target, context, binding_name, binding_in_path, getter, content_template_path = nil)
14
+ super(volt_app, target, context, binding_name)
15
15
 
16
16
  @content_template_path = content_template_path
17
17
 
@@ -189,7 +189,7 @@ module Volt
189
189
  # from the group)
190
190
  generated_new = true
191
191
  # Setup the controller
192
- controller_class.new(*args)
192
+ controller_class.new(@volt_app, *args)
193
193
  end
194
194
 
195
195
  # Fetch grouped controllers if we're grouping
@@ -217,7 +217,7 @@ module Volt
217
217
 
218
218
  # The context for templates can be either a controller, or the original context.
219
219
  def render_template(full_path, path)
220
- @current_template = TemplateRenderer.new(@page, @target, @controller, @binding_name, full_path, path)
220
+ @current_template = TemplateRenderer.new(@volt_app, @target, @controller, @binding_name, full_path, path)
221
221
 
222
222
  call_ready
223
223
  end
@@ -5,8 +5,8 @@ require 'volt/page/template_renderer'
5
5
 
6
6
  module Volt
7
7
  class YieldBinding < BaseBinding
8
- def initialize(page, target, context, binding_name)
9
- super(page, target, context, binding_name)
8
+ def initialize(volt_app, target, context, binding_name)
9
+ super(volt_app, target, context, binding_name)
10
10
 
11
11
  # Get the path to the template to yield
12
12
  full_path = @context.attrs.content_template_path
@@ -14,7 +14,7 @@ module Volt
14
14
  # Grab the controller for the content
15
15
  controller = @context.attrs.content_controller
16
16
 
17
- @current_template = TemplateRenderer.new(@page, @target, controller, @binding_name, full_path)
17
+ @current_template = TemplateRenderer.new(volt_app, @target, controller, @binding_name, full_path)
18
18
  end
19
19
 
20
20
  def remove
@@ -64,6 +64,9 @@ module Volt
64
64
  @queue.each do |message|
65
65
  send_message(message)
66
66
  end
67
+
68
+ # Trigger a connect event
69
+ trigger!('connect')
67
70
  end
68
71
 
69
72
  def closed(error)
@@ -71,6 +74,9 @@ module Volt
71
74
  self.connected = false
72
75
  self.error = `error.reason`
73
76
 
77
+ # Trigger a disconnect event
78
+ trigger!('disconnect')
79
+
74
80
  reconnect!
75
81
  end
76
82
 
@@ -13,7 +13,7 @@ module Volt
13
13
 
14
14
  attr_reader :state, :error, :reconnect_interval
15
15
 
16
- def initiailze
16
+ def initialize
17
17
  @state = :connected
18
18
  end
19
19
 
@@ -1,38 +1,10 @@
1
- require 'opal'
2
- require 'volt/models'
3
- require 'volt/controllers/model_controller'
4
- require 'volt/tasks/task_handler'
5
- require 'volt/page/bindings/attribute_binding'
6
- require 'volt/page/bindings/content_binding'
7
- require 'volt/page/bindings/each_binding'
8
- require 'volt/page/bindings/if_binding'
9
- require 'volt/page/bindings/view_binding'
10
- require 'volt/page/bindings/yield_binding'
11
- require 'volt/page/bindings/component_binding'
12
- require 'volt/page/bindings/event_binding'
13
- require 'volt/page/template_renderer'
14
- require 'volt/page/string_template_renderer'
15
- require 'volt/page/document_events'
16
- require 'volt/page/sub_context'
17
- require 'volt/page/targets/dom_target'
18
- require 'volt/data_stores/base_adaptor_client'
19
-
20
- if RUBY_PLATFORM == 'opal'
21
- require 'volt/page/channel'
22
- else
23
- require 'volt/page/channel_stub'
24
- end
25
- require 'volt/router/routes'
26
- require 'volt/models/url'
27
- require 'volt/page/url_tracker'
28
- require 'volt/benchmark/benchmark'
29
- require 'volt/page/tasks'
30
1
 
31
2
  module Volt
32
3
  class Page
33
4
  attr_reader :url, :params, :page, :routes, :events
34
5
 
35
- def initialize
6
+ def initialize(volt_app)
7
+ @volt_app = volt_app
36
8
  # Run the code to setup the page
37
9
  @page = Model.new
38
10
 
@@ -44,24 +16,26 @@ module Volt
44
16
  @events = DocumentEvents.new
45
17
 
46
18
  if RUBY_PLATFORM == 'opal'
47
- # Setup escape binding for console
48
- `
49
- $(document).keyup(function(e) {
50
- if (e.keyCode == 27) {
51
- Opal.gvars.page.$launch_console();
52
- }
53
- });
54
-
55
- $(document).on('click', 'a', function(event) {
56
- return Opal.gvars.page.$link_clicked($(this).attr('href'), event);
57
- });
58
- `
19
+ if Volt.in_browser?
20
+ # Setup escape binding for console
21
+ `
22
+ $(document).keyup(function(e) {
23
+ if (e.keyCode == 27) {
24
+ Opal.gvars.page.$launch_console();
25
+ }
26
+ });
27
+
28
+ $(document).on('click', 'a', function(event) {
29
+ return Opal.gvars.page.$link_clicked($(this).attr('href'), event);
30
+ });
31
+ `
32
+ end
59
33
  end
60
34
 
61
35
  # Initialize tasks so we can get the reload message
62
36
  tasks if Volt.env.development?
63
37
 
64
- if Volt.client?
38
+ if Volt.in_browser?
65
39
  channel.on('reconnected') do
66
40
  @page._reconnected = true
67
41
 
@@ -178,13 +152,13 @@ module Volt
178
152
  # Do the initial url params parse
179
153
  @url_tracker.url_updated(true)
180
154
 
181
- main_controller = Main::MainController.new
155
+ main_controller = Main::MainController.new(@volt_app)
182
156
 
183
157
  # Setup main page template
184
- TemplateRenderer.new(self, DomTarget.new, main_controller, 'CONTENT', 'main/main/main/body')
158
+ TemplateRenderer.new(@volt_app, DomTarget.new, main_controller, 'CONTENT', 'main/main/main/body')
185
159
 
186
160
  # Setup title reactive template
187
- @title_template = StringTemplateRenderer.new(self, main_controller, 'main/main/main/title')
161
+ @title_template = StringTemplateRenderer.new(@volt_app, main_controller, 'main/main/main/title')
188
162
 
189
163
  # Watch for changes to the title template
190
164
  proc do
@@ -214,12 +188,4 @@ module Volt
214
188
  Volt.logger.error("Unable to restore: #{e.inspect}")
215
189
  end
216
190
  end
217
-
218
- if Volt.client?
219
- $page = Page.new
220
-
221
- `$(document).ready(function() {`
222
- $page.start
223
- `});`
224
- end
225
191
  end
@@ -9,13 +9,12 @@ module Volt
9
9
  class ViewLookupException < Exception; end
10
10
  class PathStringRenderer
11
11
  attr_reader :html
12
- def initialize(path, attrs = nil, page = nil, render_from_path = nil)
13
- # use the global page if one is not passed in
14
- page ||= $page
15
-
12
+ def initialize(volt_app, path, attrs = nil, page = nil, render_from_path = nil)
16
13
  # where to do the path lookup from
17
14
  render_from_path ||= 'main/main/main/body'
18
15
 
16
+ page ||= volt_app.page
17
+
19
18
  # Make path into a full path
20
19
  @view_lookup = Volt::ViewLookupForPath.new(page, render_from_path)
21
20
  full_path, controller_path = @view_lookup.path_for_template(path, nil)
@@ -26,10 +25,10 @@ module Volt
26
25
 
27
26
  controller_class, action = ControllerHandler.get_controller_and_action(controller_path)
28
27
 
29
- controller = controller_class.new # (SubContext.new(attrs, nil, true))
28
+ controller = controller_class.new(volt_app) # (SubContext.new(attrs, nil, true))
30
29
  controller.model = SubContext.new(attrs, nil, true)
31
30
 
32
- renderer = StringTemplateRenderer.new(page, controller, full_path)
31
+ renderer = StringTemplateRenderer.new(volt_app, controller, full_path)
33
32
 
34
33
  @html = renderer.html
35
34
 
@@ -5,12 +5,12 @@ module Volt
5
5
  # StringTemplateRenderer will intellegently update the string in the same way
6
6
  # a normal bindings will update the dom.
7
7
  class StringTemplateRenderer
8
- def initialize(page, context, template_path)
8
+ def initialize(volt_app, context, template_path)
9
9
  @dependency = Dependency.new
10
10
 
11
11
  @template_path = template_path
12
12
  @target = AttributeTarget.new(nil, nil, self)
13
- @template = TemplateRenderer.new(page, @target, context, 'main', template_path)
13
+ @template = TemplateRenderer.new(volt_app, @target, context, 'main', template_path)
14
14
  end
15
15
 
16
16
  # Render the template and get the current value
@@ -18,9 +18,56 @@ module Volt
18
18
  set_content_and_rezero_bindings(value, {})
19
19
  end
20
20
 
21
+ def insert_anchor_before_end(binding_name)
22
+ end_node = @target.find_by_binding_id(@binding_name)
23
+ if end_node.is_a?(ComponentNode)
24
+ component_node = ComponentNode.new(binding_name, end_node, end_node.root || end_node)
25
+ end_node.insert(-1, component_node)
26
+ else
27
+ raise "can not insert on HtmlNode"
28
+ end
29
+ end
30
+
31
+ # When using bindings, we have to change the binding id so we don't reuse
32
+ # the same id when rendering a binding multiple times.
33
+ def rezero_bindings(html, bindings)
34
+ @@base_binding_id ||= 20_000
35
+ # rezero
36
+ parts = html.split(/(\<\!\-\- \$\/?[0-9]+ \-\-\>)/).reject { |v| v == '' }
37
+
38
+ new_html = []
39
+ new_bindings = {}
40
+ id_map = {}
41
+
42
+ parts.each do |part|
43
+ case part
44
+ when /\<\!\-\- \$[0-9]+ \-\-\>/
45
+ # Open
46
+ binding_id = part.match(/\<\!\-\- \$([0-9]+) \-\-\>/)[1].to_i
47
+ binding = bindings[binding_id]
48
+ new_bindings[@@base_binding_id] = binding if binding
49
+
50
+ new_html << "<!-- $#{@@base_binding_id} -->"
51
+ id_map[binding_id] = @@base_binding_id
52
+ @@base_binding_id += 1
53
+ when /\<\!\-\- \$\/[0-9]+ \-\-\>/
54
+ # Close
55
+ binding_id = part.match(/\<\!\-\- \$\/([0-9]+) \-\-\>/)[1].to_i
56
+ new_html << "<!-- $/#{id_map[binding_id]} -->"
57
+ else
58
+ # html string
59
+ new_html << part
60
+ end
61
+ end
62
+
63
+ return new_html.join(''), new_bindings
64
+ end
65
+
21
66
  # Takes in our html and bindings, and rezero's the comment names, and the
22
67
  # bindings. Returns an updated bindings hash
23
68
  def set_content_and_rezero_bindings(html, bindings)
69
+ html, bindings = rezero_bindings(html, bindings)
70
+
24
71
  if @binding_name == 'main'
25
72
  @target.html = html
26
73
  else
@@ -6,19 +6,19 @@ module Volt
6
6
  @@template_cache = {}
7
7
 
8
8
  def remove
9
- fail 'not implemented'
9
+ fail 'remove is not implemented'
10
10
  end
11
11
 
12
12
  def remove_anchors
13
- fail 'not implemented'
13
+ fail 'remove_anchors is not implemented'
14
14
  end
15
15
 
16
- def insert_anchor_before_end
17
- fail 'not implemented'
16
+ def insert_anchor_before_end(binding_name)
17
+ fail 'insert_anchor_before_end is not implemented'
18
18
  end
19
19
 
20
20
  def set_template
21
- fail 'not implemented'
21
+ fail 'set_template is not implemented'
22
22
  end
23
23
 
24
24
  def set_content_to_template(page, template_name)
@@ -9,7 +9,7 @@ module Volt
9
9
  class ComponentNode < BaseNode
10
10
  include Eventable
11
11
 
12
- attr_accessor :parent, :binding_id, :nodes
12
+ attr_accessor :parent, :binding_id, :nodes, :root
13
13
 
14
14
  def initialize(binding_id = nil, parent = nil, root = nil)
15
15
  @nodes = []
@@ -65,6 +65,11 @@ module Volt
65
65
  @nodes << node
66
66
  end
67
67
 
68
+ def insert(index, node)
69
+ @nodes.insert(index, node)
70
+ changed!
71
+ end
72
+
68
73
  def to_html
69
74
  str = []
70
75
  @nodes.each do |node|
@@ -4,16 +4,16 @@ module Volt
4
4
  class TemplateRenderer < BaseBinding
5
5
  attr_reader :context
6
6
 
7
- def initialize(page, target, context, binding_name, template_name)
8
- super(page, target, context, binding_name)
7
+ def initialize(volt_app, target, context, binding_name, template_name)
8
+ super(volt_app, target, context, binding_name)
9
9
 
10
10
  @sub_bindings = []
11
11
 
12
- bindings = dom_section.set_content_to_template(page, template_name)
12
+ bindings = dom_section.set_content_to_template(volt_app.page, template_name)
13
13
 
14
14
  bindings.each_pair do |id, bindings_for_id|
15
15
  bindings_for_id.each do |binding|
16
- @sub_bindings << binding.call(page, target, context, id)
16
+ @sub_bindings << binding.call(volt_app, target, context, id)
17
17
  end
18
18
  end
19
19
  end
@@ -74,6 +74,10 @@ module Volt
74
74
  end
75
75
  end
76
76
 
77
+ def stopped?
78
+ @stopped
79
+ end
80
+
77
81
  # Runs in this computation as the current computation, returns the computation
78
82
  def run_in
79
83
  previous = Computation.current
@@ -172,20 +176,45 @@ class Proc
172
176
  #
173
177
  # Example:
174
178
  # -> { }
175
- def watch_and_resolve!
179
+ def watch_and_resolve!(yield_nil_for_unresolved_promise=false)
176
180
  unless block_given?
177
181
  fail 'watch_and_resolve! requires a block to call when the value is resolved or another value other than a promise is returned in the watch.'
178
182
  end
179
183
 
180
- computation = proc do
184
+ # Keep results between runs
185
+ result = nil
186
+
187
+ computation = proc do |comp|
181
188
  result = call
189
+ last_promise = nil
182
190
 
183
191
  if result.is_a?(Promise)
192
+ last_promise = result
193
+
194
+ # Often you want a to be alerted that an unresolved promise is waiting
195
+ # to be resolved.
196
+ if yield_nil_for_unresolved_promise && !result.resolved?
197
+ yield(nil)
198
+ end
199
+
184
200
  result.then do |final|
185
- yield(final)
201
+ # Check to make sure that a new value didn't get reactively pushed
202
+ # before the promise resolved.
203
+ if last_promise.is_a?(Promise) && last_promise == result
204
+ # Don't resolve if the computation was stopped
205
+ unless comp.stopped?
206
+ yield(final)
207
+ end
208
+
209
+ # Clear result for GC
210
+ result = nil
211
+ end
186
212
  end
187
213
  else
188
214
  yield(result)
215
+
216
+ # Clear result for GC
217
+ result = nil
189
218
  end
190
219
  end.watch!
191
220
 
@@ -5,8 +5,8 @@ module Volt
5
5
  # a url to params, and params to url.
6
6
  # routes do
7
7
  # client "/about", _view: 'about'
8
- # client "/blog/{_id}/edit", _view: 'blog/edit', _action: 'edit'
9
- # client "/blog/{_id}", _view: 'blog/show', _action: 'show'
8
+ # client "/blog/{id}/edit", _view: 'blog/edit', _action: 'edit'
9
+ # client "/blog/{id}", _view: 'blog/show', _action: 'show'
10
10
  # client "/blog", _view: 'blog'
11
11
  # client "/blog/new", _view: 'blog/new', _action: 'new'
12
12
  # client "/cool/{_name}", _view: 'cool'
@@ -27,16 +27,16 @@ module Volt
27
27
  # @indirect_routes = {
28
28
  # '*' => {
29
29
  # 'edit' => {
30
- # nil => {_id: 1, _view: 'blog/edit', _action: 'edit'}
30
+ # nil => {id: 1, _view: 'blog/edit', _action: 'edit'}
31
31
  # }
32
- # nil => {_id: 1, _view: 'blog/show', _action: 'show'}
32
+ # nil => {id: 1, _view: 'blog/show', _action: 'show'}
33
33
  # }
34
34
  # }
35
35
  # }
36
36
  #
37
37
  # Match for params
38
38
  # @param_matches = [
39
- # {_id: nil, _view: 'blog/edit', _action: 'edit'} => Proc.new {|params| "/blog/#{params.id}/edit", params.reject {|k,v| k == :id }}
39
+ # {id: nil, _view: 'blog/edit', _action: 'edit'} => Proc.new {|params| "/blog/#{params.id}/edit", params.reject {|k,v| k == :id }}
40
40
  # ]
41
41
  class Routes
42
42
  def initialize
@@ -79,6 +79,9 @@ module Volt
79
79
 
80
80
  # Load all templates in the folder
81
81
  Dir["#{views_path}*/*.{#{exts.join(',')}}"].sort.each do |view_path|
82
+ path_parts = view_path.scan(/([^\/]+)\/([^\/]+)\/[^\/]+\/([^\/]+)[.](html|email)$/)
83
+ component_name, controller_name, view, _ = path_parts[0]
84
+
82
85
  # file extension
83
86
  format = File.extname(view_path).downcase.delete('.').to_sym
84
87
 
@@ -88,6 +91,8 @@ module Volt
88
91
 
89
92
  file_contents = File.read(view_path)
90
93
 
94
+ template_calls = []
95
+
91
96
  # Process template if we have a handler for this file type
92
97
  if handler = ComponentTemplates.handler_for_extension(format)
93
98
  file_contents = handler.call(file_contents)
@@ -107,8 +112,11 @@ module Volt
107
112
  binding_code = "{#{binding_code.join(', ')}}"
108
113
 
109
114
  code << "#{page_reference}.add_template(#{name.inspect}, #{template['html'].inspect}, #{binding_code})\n"
115
+ template_calls << "template(#{name.inspect}, #{template['html'].inspect}, #{binding_code})"
110
116
  end
111
117
  end
118
+
119
+ # puts "module #{component_name.camelize}\n class #{controller_name.camelize}\n class VoltTemplates < VoltTemplates\n #{template_calls.join("\n")}\n end\n end\nend"
112
120
  end
113
121
 
114
122
  code
@@ -118,9 +126,29 @@ module Volt
118
126
  def generate_controller_code
119
127
  code = ''
120
128
  controllers_path = "#{@component_path}/controllers/"
129
+ views_path = "#{@component_path}/views/"
121
130
 
122
- Dir["#{controllers_path}*_controller.rb"].sort.each do |controller_path|
123
- code << File.read(controller_path) + "\n\n"
131
+ # Controllers are optional, specifying a view folder is enough to auto
132
+ # generate the controller.
133
+
134
+ implicit_controllers = Dir["#{views_path}*"].sort.map do |path|
135
+ # remove the /views/ folder and add _controller.rb
136
+ path.split('/').tap {|v| v[-2] = 'controllers' }.join('/') + '_controller.rb'
137
+ end
138
+ explicit_controllers = Dir["#{controllers_path}*_controller.rb"].sort
139
+
140
+ controllers = (implicit_controllers + explicit_controllers).uniq
141
+ controllers.each do |path|
142
+ if File.exists?(path)
143
+ code << File.read(path) + "\n\n"
144
+ else
145
+ # parts = path.scan(/([^\/]+)\/controllers\/([^\/]+)_controller[.]rb$/)
146
+ # component, controller = parts[0]
147
+
148
+ # # Generate a blank controller. (We need to actually generate one so
149
+ # # the Template can be attached to it for template inheritance)
150
+ # code << "\nmodule #{component.camelize}\n class #{controller.camelize} < Volt::ModelController\n end\nend\n"
151
+ end
124
152
  end
125
153
 
126
154
  code
@@ -58,11 +58,11 @@ module Volt
58
58
  # Running as child
59
59
  @reader.close
60
60
 
61
- @server.boot_volt
61
+ volt_app = @server.boot_volt
62
62
  @rack_app = @server.new_server
63
63
 
64
64
  # Set the drb object locally
65
- @dispatcher = Dispatcher.new
65
+ @dispatcher = Dispatcher.new(volt_app)
66
66
  drb_object = DRb.start_service('drbunix:', [self, @dispatcher])
67
67
 
68
68
  @writer.puts(drb_object.uri)