volt 0.6.5 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Readme.md +47 -40
- data/VERSION +1 -1
- data/app/volt/controllers/notices_controller.rb +3 -3
- data/app/volt/tasks/live_query/data_store.rb +2 -2
- data/app/volt/tasks/live_query/live_query.rb +20 -20
- data/app/volt/tasks/live_query/live_query_pool.rb +6 -6
- data/app/volt/tasks/live_query/query_tracker.rb +15 -15
- data/app/volt/tasks/query_tasks.rb +13 -13
- data/app/volt/tasks/store_tasks.rb +7 -7
- data/app/volt/views/notices/index.html +17 -18
- data/lib/volt/assets/test.rb +2 -2
- data/lib/volt/benchmark/benchmark.rb +25 -23
- data/lib/volt/cli/asset_compile.rb +11 -0
- data/lib/volt/cli/new_gem.rb +16 -16
- data/lib/volt/cli.rb +14 -12
- data/lib/volt/console.rb +5 -6
- data/lib/volt/controllers/model_controller.rb +18 -18
- data/lib/volt/extra_core/array.rb +4 -4
- data/lib/volt/extra_core/hash.rb +3 -3
- data/lib/volt/extra_core/object.rb +6 -6
- data/lib/volt/extra_core/string.rb +6 -6
- data/lib/volt/extra_core/symbol.rb +5 -5
- data/lib/volt/extra_core/time.rb +4 -4
- data/lib/volt/extra_core/true_false.rb +6 -6
- data/lib/volt/extra_core/try.rb +9 -9
- data/lib/volt/models/array_model.rb +26 -26
- data/lib/volt/models/model.rb +35 -35
- data/lib/volt/models/model_hash_behaviour.rb +15 -15
- data/lib/volt/models/model_helpers.rb +8 -8
- data/lib/volt/models/model_wrapper.rb +6 -6
- data/lib/volt/models/persistors/array_store.rb +36 -36
- data/lib/volt/models/persistors/base.rb +6 -6
- data/lib/volt/models/persistors/flash.rb +5 -5
- data/lib/volt/models/persistors/model_identity_map.rb +2 -2
- data/lib/volt/models/persistors/model_store.rb +22 -22
- data/lib/volt/models/persistors/params.rb +3 -3
- data/lib/volt/models/persistors/query/query_listener.rb +14 -14
- data/lib/volt/models/persistors/query/query_listener_pool.rb +2 -2
- data/lib/volt/models/persistors/store.rb +8 -8
- data/lib/volt/models/persistors/store_factory.rb +2 -2
- data/lib/volt/models/url.rb +37 -37
- data/lib/volt/page/bindings/attribute_binding.rb +14 -14
- data/lib/volt/page/bindings/base_binding.rb +9 -9
- data/lib/volt/page/bindings/component_binding.rb +7 -7
- data/lib/volt/page/bindings/content_binding.rb +3 -3
- data/lib/volt/page/bindings/each_binding.rb +13 -13
- data/lib/volt/page/bindings/event_binding.rb +4 -4
- data/lib/volt/page/bindings/if_binding.rb +12 -12
- data/lib/volt/page/bindings/template_binding.rb +30 -30
- data/lib/volt/page/channel.rb +19 -19
- data/lib/volt/page/channel_stub.rb +6 -6
- data/lib/volt/page/document.rb +2 -2
- data/lib/volt/page/document_events.rb +4 -4
- data/lib/volt/page/draw_cycle.rb +3 -3
- data/lib/volt/page/memory_test.rb +6 -6
- data/lib/volt/page/page.rb +19 -19
- data/lib/volt/page/reactive_template.rb +9 -9
- data/lib/volt/page/sub_context.rb +5 -5
- data/lib/volt/page/targets/attribute_section.rb +9 -9
- data/lib/volt/page/targets/attribute_target.rb +3 -3
- data/lib/volt/page/targets/base_section.rb +2 -2
- data/lib/volt/page/targets/binding_document/component_node.rb +23 -23
- data/lib/volt/page/targets/binding_document/html_node.rb +2 -2
- data/lib/volt/page/targets/dom_section.rb +40 -38
- data/lib/volt/page/targets/dom_target.rb +2 -2
- data/lib/volt/page/tasks.rb +12 -12
- data/lib/volt/page/template_renderer.rb +4 -4
- data/lib/volt/page/url_tracker.rb +6 -6
- data/lib/volt/reactive/array_extensions.rb +2 -2
- data/lib/volt/reactive/destructive_methods.rb +5 -5
- data/lib/volt/reactive/event_chain.rb +25 -25
- data/lib/volt/reactive/events.rb +33 -33
- data/lib/volt/reactive/object_tracker.rb +21 -21
- data/lib/volt/reactive/object_tracking.rb +2 -2
- data/lib/volt/reactive/reactive_array.rb +57 -57
- data/lib/volt/reactive/reactive_tags.rb +16 -16
- data/lib/volt/reactive/reactive_value.rb +72 -72
- data/lib/volt/reactive/string_extensions.rb +3 -3
- data/lib/volt/router/routes.rb +22 -23
- data/lib/volt/server/component_handler.rb +5 -5
- data/lib/volt/server/component_templates.rb +14 -11
- data/lib/volt/server/html_parser/attribute_scope.rb +116 -0
- data/lib/volt/server/html_parser/each_scope.rb +18 -0
- data/lib/volt/server/html_parser/if_view_scope.rb +71 -0
- data/lib/volt/server/html_parser/sandlebars_parser.rb +219 -0
- data/lib/volt/server/html_parser/textarea_scope.rb +31 -0
- data/lib/volt/server/html_parser/view_handler.rb +82 -0
- data/lib/volt/server/html_parser/view_parser.rb +23 -0
- data/lib/volt/server/html_parser/view_scope.rb +145 -0
- data/lib/volt/server/rack/asset_files.rb +17 -17
- data/lib/volt/server/rack/component_paths.rb +18 -18
- data/lib/volt/server/rack/index_files.rb +8 -8
- data/lib/volt/server/rack/opal_files.rb +11 -11
- data/lib/volt/server/socket_connection_handler.rb +13 -13
- data/lib/volt/server/socket_connection_handler_stub.rb +2 -2
- data/lib/volt/server.rb +18 -18
- data/lib/volt/tasks/dispatcher.rb +5 -5
- data/lib/volt/utils/ejson.rb +2 -2
- data/lib/volt/utils/generic_counting_pool.rb +8 -8
- data/lib/volt/utils/generic_pool.rb +16 -16
- data/lib/volt/volt/environment.rb +4 -4
- data/lib/volt.rb +6 -6
- data/spec/integration/test_integration_spec.rb +2 -2
- data/spec/models/event_chain_spec.rb +38 -38
- data/spec/models/model_spec.rb +128 -128
- data/spec/models/old_model_spec.rb +17 -17
- data/spec/models/persistors/params_spec.rb +3 -3
- data/spec/models/persistors/store_spec.rb +7 -7
- data/spec/models/reactive_array_spec.rb +82 -82
- data/spec/models/reactive_generator_spec.rb +11 -11
- data/spec/models/reactive_tags_spec.rb +6 -6
- data/spec/models/reactive_value_spec.rb +70 -70
- data/spec/models/store_spec.rb +4 -4
- data/spec/models/string_extensions_spec.rb +13 -13
- data/spec/page/bindings/content_binding_spec.rb +6 -6
- data/spec/page/sub_context_spec.rb +1 -1
- data/spec/router/routes_spec.rb +3 -3
- data/spec/server/html_parser/sample_page.html +595 -0
- data/spec/server/html_parser/sandlebars_parser_spec.rb +192 -0
- data/spec/server/html_parser/view_parser_spec.rb +286 -0
- data/spec/server/rack/asset_files_spec.rb +6 -6
- data/spec/server/rack/component_paths_spec.rb +5 -5
- data/spec/spec_helper.rb +4 -5
- data/spec/store/mongo_spec.rb +3 -3
- data/spec/tasks/live_query_spec.rb +6 -6
- data/spec/tasks/query_tasks.rb +4 -4
- data/spec/tasks/query_tracker_spec.rb +20 -20
- data/spec/templates/targets/binding_document/component_node_spec.rb +4 -4
- data/spec/templates/template_binding_spec.rb +28 -28
- data/spec/utils/generic_counting_pool_spec.rb +5 -5
- data/spec/utils/generic_pool_spec.rb +14 -14
- data/templates/newgem/app/newgem/views/index/index.html +1 -2
- data/templates/project/app/home/config/dependencies.rb +1 -1
- data/templates/project/app/home/controllers/index_controller.rb +1 -1
- data/templates/project/app/home/views/index/about.html +4 -6
- data/templates/project/app/home/views/index/home.html +4 -5
- data/templates/project/app/home/views/index/index.html +8 -9
- data/templates/project/spec/spec_helper.rb +1 -1
- metadata +17 -8
- data/lib/volt/server/binding_setup.rb +0 -2
- data/lib/volt/server/if_binding_setup.rb +0 -31
- data/lib/volt/server/scope.rb +0 -43
- data/lib/volt/server/template_parser.rb +0 -453
- data/spec/server/template_parser_spec.rb +0 -50
@@ -1,453 +0,0 @@
|
|
1
|
-
require 'volt/server/scope'
|
2
|
-
require 'volt/server/if_binding_setup'
|
3
|
-
require 'nokogiri'
|
4
|
-
|
5
|
-
# TODO: The section_name that we're passing in should probably be
|
6
|
-
# abstracted out. Possibly this whole thing needs a rewrite.
|
7
|
-
|
8
|
-
class Template
|
9
|
-
attr_accessor :current_scope, :section_name
|
10
|
-
|
11
|
-
def initialize(template_parser, section_name, template, scope=Scope.new)
|
12
|
-
@binding_number = 0
|
13
|
-
|
14
|
-
@template_parser = template_parser
|
15
|
-
@section_name = section_name
|
16
|
-
@template = template
|
17
|
-
@scopes = [scope]
|
18
|
-
@current_scope = @scopes.first
|
19
|
-
end
|
20
|
-
|
21
|
-
def html
|
22
|
-
if @template.respond_to?(:name) && @template.name[0] == ':'
|
23
|
-
# Don't return the <:section> tags
|
24
|
-
return @template.children.to_html
|
25
|
-
else
|
26
|
-
# if @template.class == Nokogiri::XML::NodeSet
|
27
|
-
# result = ''
|
28
|
-
# @template.each do |node|
|
29
|
-
# result << node.to_html
|
30
|
-
# end
|
31
|
-
# else
|
32
|
-
result = @template.to_html
|
33
|
-
# end
|
34
|
-
result
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
|
39
|
-
def add_binding(node, content)
|
40
|
-
if content[0] == '/'
|
41
|
-
add_close_mustache(node)
|
42
|
-
elsif content[0] == '#'
|
43
|
-
command, *content = content.split(/ /)
|
44
|
-
content = content.join(' ')
|
45
|
-
|
46
|
-
case command
|
47
|
-
when '#template'
|
48
|
-
return add_template(node, content)
|
49
|
-
when '#each'
|
50
|
-
return add_each_binding(node, content)
|
51
|
-
when '#if'
|
52
|
-
return add_if_binding(node, content)
|
53
|
-
when '#elsif'
|
54
|
-
return add_else_binding(node, content)
|
55
|
-
when '#else'
|
56
|
-
if content.present?
|
57
|
-
# TODO: improve error, include line/file
|
58
|
-
raise "#else should not include a condition, use #elsif instead. #{content} was passed as a condition."
|
59
|
-
end
|
60
|
-
|
61
|
-
return add_else_binding(node, nil)
|
62
|
-
else
|
63
|
-
# TODO: Handle invalid command
|
64
|
-
raise "Invalid Command"
|
65
|
-
end
|
66
|
-
else
|
67
|
-
# text binding
|
68
|
-
return add_text_binding(content)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def add_template(node, content, name='Template')
|
73
|
-
html = "<!-- $#{@binding_number} --><!-- $/#{@binding_number} -->"
|
74
|
-
|
75
|
-
@current_scope.add_binding(@binding_number, "lambda { |__p, __t, __c, __id| #{name}Binding.new(__p, __t, __c, __id, #{@template_parser.template_path.inspect}, Proc.new { [#{content}] }) }")
|
76
|
-
|
77
|
-
@binding_number += 1
|
78
|
-
return html
|
79
|
-
end
|
80
|
-
|
81
|
-
def add_each_binding(node, content)
|
82
|
-
html = "<!-- $#{@binding_number} -->"
|
83
|
-
|
84
|
-
content, variable_name = content.strip.split(/ as /)
|
85
|
-
|
86
|
-
template_name = "#{@template_parser.template_path}/#{section_name}/__template/#{@binding_number}"
|
87
|
-
@current_scope.add_binding(@binding_number, "lambda { |__p, __t, __c, __id| EachBinding.new(__p, __t, __c, __id, Proc.new { #{content} }, #{variable_name.inspect}, #{template_name.inspect}) }")
|
88
|
-
|
89
|
-
# Add the node, the binding number, then store the location where the
|
90
|
-
# bindings for this block starts.
|
91
|
-
@current_scope = Scope.new(@binding_number)
|
92
|
-
@scopes << @current_scope
|
93
|
-
|
94
|
-
@binding_number += 1
|
95
|
-
return html
|
96
|
-
end
|
97
|
-
|
98
|
-
def add_if_binding(node, content)
|
99
|
-
html = "<!-- $#{@binding_number} -->"
|
100
|
-
|
101
|
-
template_name = "#{@template_parser.template_path}/#{section_name}/__template/#{@binding_number}"
|
102
|
-
if_binding_setup = IfBindingSetup.new
|
103
|
-
if_binding_setup.add_branch(content, template_name)
|
104
|
-
|
105
|
-
@current_scope.start_if_binding(@binding_number, if_binding_setup)
|
106
|
-
|
107
|
-
# Add the node, the binding number, then store the location where the
|
108
|
-
# bindings for this block starts.
|
109
|
-
@current_scope = Scope.new(@binding_number)
|
110
|
-
@scopes << @current_scope
|
111
|
-
|
112
|
-
@binding_number += 1
|
113
|
-
return html
|
114
|
-
end
|
115
|
-
|
116
|
-
def add_else_binding(node, content)
|
117
|
-
html = add_close_mustache(node, false)
|
118
|
-
|
119
|
-
html += "<!-- $#{@binding_number} -->"
|
120
|
-
template_name = "#{@template_parser.template_path}/#{section_name}/__template/#{@binding_number}"
|
121
|
-
|
122
|
-
@current_scope.current_if_binding[1].add_branch(content, template_name)
|
123
|
-
|
124
|
-
# Add the node, the binding number, then store the location where the
|
125
|
-
# bindings for this block starts.
|
126
|
-
@current_scope = Scope.new(@binding_number)
|
127
|
-
@scopes << @current_scope
|
128
|
-
|
129
|
-
@binding_number += 1
|
130
|
-
|
131
|
-
return html
|
132
|
-
end
|
133
|
-
|
134
|
-
def add_close_mustache(node, close_if=true)
|
135
|
-
scope = @scopes.pop
|
136
|
-
@current_scope = @scopes.last
|
137
|
-
|
138
|
-
# Close an outstanding if binding (if it exists)
|
139
|
-
@current_scope.close_if_binding! if close_if
|
140
|
-
|
141
|
-
# Track that this scope was closed out
|
142
|
-
@current_scope.add_closed_child_scope(scope)
|
143
|
-
|
144
|
-
html = "<!-- $/#{scope.outer_binding_number} -->"
|
145
|
-
|
146
|
-
return html
|
147
|
-
end
|
148
|
-
|
149
|
-
# When we find a binding, we pass it's content in here and replace it with
|
150
|
-
# the return value
|
151
|
-
def add_text_binding(content)
|
152
|
-
html = "<!-- $#{@binding_number} --><!-- $/#{@binding_number} -->"
|
153
|
-
|
154
|
-
@current_scope.add_binding(@binding_number, "lambda { |__p, __t, __c, __id| ContentBinding.new(__p, __t, __c, __id, Proc.new { #{content} }) }")
|
155
|
-
|
156
|
-
@binding_number += 1
|
157
|
-
return html
|
158
|
-
end
|
159
|
-
|
160
|
-
def setup_node_id(node)
|
161
|
-
id = node['id']
|
162
|
-
# First assign this node an id if it doesn't have one
|
163
|
-
unless id
|
164
|
-
id = node['id'] = "id#{@binding_number}"
|
165
|
-
@binding_number += 1
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
# Attribute bindings support multiple handlebar listeners
|
170
|
-
# Exvoltle:
|
171
|
-
# <button click="{_primary} {_important}">...
|
172
|
-
#
|
173
|
-
# To accomplish this, we create a new listener from the existing ones in the Proc
|
174
|
-
# that we pass to the binding when it is created.
|
175
|
-
def add_attribute_binding(node, attribute, content)
|
176
|
-
setup_node_id(node)
|
177
|
-
|
178
|
-
if content =~ /^\{[^\{]+\}$/
|
179
|
-
# Getter is the content inside of { ... }
|
180
|
-
add_single_getter(node, attribute, content)
|
181
|
-
else
|
182
|
-
add_multiple_getters(node, attribute, content)
|
183
|
-
end
|
184
|
-
|
185
|
-
end
|
186
|
-
|
187
|
-
def add_single_getter(node, attribute, content)
|
188
|
-
if attribute == 'checked' || true
|
189
|
-
# For a checkbox, we don't want to add
|
190
|
-
getter = content[1..-2]
|
191
|
-
else
|
192
|
-
# Otherwise we should combine them
|
193
|
-
# TODO: We should make .or handle assignment
|
194
|
-
getter = "_tmp = #{content[1..-2]}.or('') ; _tmp.reactive_manager.setter! { |val| self.#{content[1..-2]} = val } ; _tmp"
|
195
|
-
end
|
196
|
-
|
197
|
-
@current_scope.add_binding(node['id'], "lambda { |__p, __t, __c, __id| AttributeBinding.new(__p, __t, __c, __id, #{attribute.inspect}, Proc.new { #{getter} }) }")
|
198
|
-
end
|
199
|
-
|
200
|
-
def add_multiple_getters(node, attribute, content)
|
201
|
-
case attribute
|
202
|
-
when 'checked', 'value'
|
203
|
-
if parts.size > 1
|
204
|
-
# Multiple ReactiveValue's can not be passed to value or checked attributes.
|
205
|
-
raise "Multiple bindings can not be passed to a #{attribute} binding."
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
reactive_template_path = add_reactive_template(content)
|
210
|
-
|
211
|
-
@current_scope.add_binding(node['id'], "lambda { |__p, __t, __c, __id| AttributeBinding.new(__p, __t, __c, __id, #{attribute.inspect}, Proc.new { ReactiveTemplate.new(__p, __c, #{reactive_template_path.inspect}) }) }")
|
212
|
-
end
|
213
|
-
|
214
|
-
# Returns a path to a template for the content. This can be passed
|
215
|
-
# into ReactiveTemplate.new, along with the current context.
|
216
|
-
def add_reactive_template(content)
|
217
|
-
# Return a template path instead
|
218
|
-
template_name = "__attribute/#{@binding_number}"
|
219
|
-
full_template_path = "#{@template_parser.template_path}/#{section_name}/#{template_name}"
|
220
|
-
@binding_number += 1
|
221
|
-
|
222
|
-
attribute_template = Template.new(@template_parser, "#{section_name}/#{template_name}", Nokogiri::HTML::DocumentFragment.parse(content))
|
223
|
-
@template_parser.add_template("#{section_name}/#{template_name}", attribute_template)
|
224
|
-
attribute_template.start_walk
|
225
|
-
attribute_template.pull_closed_block_scopes
|
226
|
-
|
227
|
-
return full_template_path
|
228
|
-
end
|
229
|
-
|
230
|
-
def add_event_binding(node, attribute_name, content)
|
231
|
-
setup_node_id(node)
|
232
|
-
|
233
|
-
event = attribute_name[2..-1]
|
234
|
-
|
235
|
-
if node.name == 'a'
|
236
|
-
# For links, we need to add blank href to make it clickable.
|
237
|
-
node['href'] ||= ''
|
238
|
-
end
|
239
|
-
|
240
|
-
@current_scope.add_binding(node['id'], "lambda { |__p, __t, __c, __id| EventBinding.new(__p, __t, __c, __id, #{event.inspect}, Proc.new {|event| #{content} })}")
|
241
|
-
end
|
242
|
-
|
243
|
-
def pull_closed_block_scopes(scope=@current_scope)
|
244
|
-
if scope.closed_block_scopes
|
245
|
-
scope.closed_block_scopes.each do |sub_scope|
|
246
|
-
# Loop through any subscopes first, pull them in.
|
247
|
-
pull_closed_block_scopes(sub_scope)
|
248
|
-
|
249
|
-
# Grab everything between the start/end html comments
|
250
|
-
start_node = find_by_comment("$#{sub_scope.outer_binding_number}")
|
251
|
-
end_node = find_by_comment("$/#{sub_scope.outer_binding_number}")
|
252
|
-
|
253
|
-
move_nodes_to_new_template(start_node, end_node, sub_scope)
|
254
|
-
end
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
|
-
|
259
|
-
def move_nodes_to_new_template(start_node, end_node, scope)
|
260
|
-
# TODO: currently this doesn't handle spanning nodes within seperate containers.
|
261
|
-
# so doing tr's doesn't work for some reason.
|
262
|
-
|
263
|
-
start_parent = start_node.parent
|
264
|
-
start_parent = start_parent.children if start_parent.is_a?(Nokogiri::HTML::DocumentFragment) || start_parent.is_a?(Nokogiri::XML::Element)
|
265
|
-
start_index = start_parent.index(start_node) + 1
|
266
|
-
|
267
|
-
end_parent = end_node.parent
|
268
|
-
end_parent = end_parent.children if end_parent.is_a?(Nokogiri::HTML::DocumentFragment) || end_parent.is_a?(Nokogiri::XML::Element)
|
269
|
-
end_index = end_parent.index(end_node) - 1
|
270
|
-
|
271
|
-
move_nodes = start_parent[start_index..end_index]
|
272
|
-
move_nodes.remove
|
273
|
-
|
274
|
-
new_template = Template.new(@template_parser, section_name, move_nodes, scope)
|
275
|
-
|
276
|
-
@template_parser.add_template("#{section_name}/__template/#{scope.outer_binding_number}", new_template)
|
277
|
-
end
|
278
|
-
|
279
|
-
|
280
|
-
def find_by_comment(name)
|
281
|
-
return @template.xpath("descendant::comment()[. = ' #{name} ']").first
|
282
|
-
end
|
283
|
-
|
284
|
-
def start_walk
|
285
|
-
walk(@template)
|
286
|
-
end
|
287
|
-
|
288
|
-
# We implement a dom walker that can walk down the dom and spit out output
|
289
|
-
# html as we go
|
290
|
-
def walk(node)
|
291
|
-
case node.type
|
292
|
-
when 1
|
293
|
-
# html node
|
294
|
-
walk_html_node(node)
|
295
|
-
when 3
|
296
|
-
# text node
|
297
|
-
walk_text_node(node)
|
298
|
-
end
|
299
|
-
|
300
|
-
node.children.each do |child|
|
301
|
-
walk(child)
|
302
|
-
end
|
303
|
-
end
|
304
|
-
|
305
|
-
def walk_html_node(node)
|
306
|
-
if node.name[0] == ':' && node.path.count('/') > 1
|
307
|
-
parse_component(node)
|
308
|
-
elsif node.name == 'textarea'
|
309
|
-
parse_textarea(node)
|
310
|
-
else
|
311
|
-
parse_html_node(node)
|
312
|
-
end
|
313
|
-
end
|
314
|
-
|
315
|
-
# We provide a quick way to render components with tags starting
|
316
|
-
# with a :
|
317
|
-
# Count the number of /'s in the path, if we are at the root node
|
318
|
-
# we can ignore it, since this is the template its self.
|
319
|
-
# TODO: Root node might not be the template if we parsed directly
|
320
|
-
# without a subtemplate specifier. We need to find a good way to
|
321
|
-
# parse only within the subtemplate.
|
322
|
-
def parse_component(node)
|
323
|
-
template_path = node.name[1..-1].gsub(':', '/')
|
324
|
-
|
325
|
-
# Take the attributes and turn them into a hash
|
326
|
-
attribute_hash = {}
|
327
|
-
node.attribute_nodes.each do |attribute_node|
|
328
|
-
content = attribute_node.value
|
329
|
-
|
330
|
-
if !content.index('{')
|
331
|
-
# passing in a string
|
332
|
-
value = content.inspect
|
333
|
-
elsif content =~ /^\{[^\}]+\}$/
|
334
|
-
# Has one binding, just get it
|
335
|
-
value = "Proc.new { #{content[1..-2]} }"
|
336
|
-
else
|
337
|
-
# Has multiple bindings, we need to render a template here
|
338
|
-
attr_template_path = add_reactive_template(content)
|
339
|
-
|
340
|
-
value = "Proc.new { ReactiveTemplate.new(__p, __c, #{attr_template_path.inspect}) }"
|
341
|
-
end
|
342
|
-
|
343
|
-
attribute_hash[attribute_node.name] = value
|
344
|
-
end
|
345
|
-
|
346
|
-
attributes_string = attribute_hash.to_a.map do |key, value|
|
347
|
-
"#{key.inspect} => #{value}"
|
348
|
-
end.join(', ')
|
349
|
-
|
350
|
-
# Setup the arguments string, which goes to the TemplateBinding
|
351
|
-
args_str = "#{template_path.inspect}"
|
352
|
-
args_str << ", {#{attributes_string}}" if attribute_hash.size > 0
|
353
|
-
|
354
|
-
new_html = add_template(node, args_str, 'Component')
|
355
|
-
|
356
|
-
node.swap(new_html)#Nokogiri::HTML::DocumentFragment.parse(new_html))
|
357
|
-
end
|
358
|
-
|
359
|
-
def parse_textarea(node)
|
360
|
-
# The contents of textareas should really be treated like a
|
361
|
-
# value= attribute. So here we pull the content into a value attribute
|
362
|
-
# if the textarea has bindings in the content.
|
363
|
-
if node.inner_html =~ /\{[^\}]+\}/
|
364
|
-
node[:value] = node.inner_html
|
365
|
-
node.children.remove
|
366
|
-
end
|
367
|
-
|
368
|
-
parse_html_node(node)
|
369
|
-
end
|
370
|
-
|
371
|
-
def parse_html_node(node)
|
372
|
-
|
373
|
-
node.attribute_nodes.each do |attribute_node|
|
374
|
-
if attribute_node.name =~ /^e\-/
|
375
|
-
# We have an e- binding
|
376
|
-
add_event_binding(node, attribute_node.name, attribute_node.value)
|
377
|
-
|
378
|
-
# remove the attribute
|
379
|
-
attribute_node.remove
|
380
|
-
elsif attribute_node.value.match(/\{[^\}]+\}/)
|
381
|
-
# Has bindings
|
382
|
-
add_attribute_binding(node, attribute_node.name, attribute_node.value)
|
383
|
-
|
384
|
-
# remove the attribute
|
385
|
-
attribute_node.remove
|
386
|
-
end
|
387
|
-
end
|
388
|
-
end
|
389
|
-
|
390
|
-
def walk_text_node(node)
|
391
|
-
new_html = node.to_html.gsub(/\{([^\}]+)\}/) do |template_binding|
|
392
|
-
add_binding(node, $1)
|
393
|
-
end
|
394
|
-
|
395
|
-
# puts "------! #{new_html.inspect} - #{node.class.inspect} - #{node.inspect}"
|
396
|
-
|
397
|
-
# TODO: Broke here in jruby
|
398
|
-
node.swap(new_html)# if new_html.blank?
|
399
|
-
|
400
|
-
#Nokogiri::HTML::DocumentFragment.parse(new_html))
|
401
|
-
end
|
402
|
-
end
|
403
|
-
|
404
|
-
class TemplateParser
|
405
|
-
attr_accessor :dom, :bindings, :template_path
|
406
|
-
|
407
|
-
def initialize(template, template_path)
|
408
|
-
@templates = {}
|
409
|
-
@template_path = template_path
|
410
|
-
|
411
|
-
template_fragment = Nokogiri::HTML::DocumentFragment.parse(template)
|
412
|
-
# Add templates for each section
|
413
|
-
|
414
|
-
# Check for sections
|
415
|
-
sections = []
|
416
|
-
if (first_child = template_fragment.children[0]) && first_child.name[0] == ':'
|
417
|
-
template_fragment.children.each do |child|
|
418
|
-
if child.is_a?(Nokogiri::XML::Element)
|
419
|
-
sections << [child, child.name[1..-1]]
|
420
|
-
end
|
421
|
-
end
|
422
|
-
else
|
423
|
-
sections << [template_fragment, 'body']
|
424
|
-
end
|
425
|
-
|
426
|
-
sections.each do |section, name|
|
427
|
-
template = Template.new(self, name, section)
|
428
|
-
add_template(name, template)
|
429
|
-
template.start_walk
|
430
|
-
template.pull_closed_block_scopes
|
431
|
-
end
|
432
|
-
|
433
|
-
end
|
434
|
-
|
435
|
-
def add_template(name, template)
|
436
|
-
raise "Already defined at #{@template_path + '/' + name}" if @templates[@template_path + '/' + name]
|
437
|
-
@templates[@template_path + '/' + name] = template
|
438
|
-
end
|
439
|
-
|
440
|
-
# Return the templates, but map the html from nokogiri to html
|
441
|
-
def templates
|
442
|
-
mapped = {}
|
443
|
-
@templates.each_pair do |name, template|
|
444
|
-
mapped[name] = {
|
445
|
-
'html' => template.html,
|
446
|
-
'bindings' => template.current_scope.bindings
|
447
|
-
}
|
448
|
-
end
|
449
|
-
|
450
|
-
return mapped
|
451
|
-
end
|
452
|
-
|
453
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
# require 'volt/server/template_parser'
|
2
|
-
#
|
3
|
-
# describe TemplateParser do
|
4
|
-
# it 'should parse a doc' do
|
5
|
-
# template = <<-END
|
6
|
-
# <h1>Header {name}</h1>
|
7
|
-
#
|
8
|
-
# <p class="{paragraph_class}">{content}</p>
|
9
|
-
#
|
10
|
-
# <!-- Original Comment -->
|
11
|
-
# {#each some_array}
|
12
|
-
# <p>Line {some_name}</p>
|
13
|
-
# {/}
|
14
|
-
#
|
15
|
-
# END
|
16
|
-
# parser = TemplateParser.new(template, 'main')
|
17
|
-
# end
|
18
|
-
#
|
19
|
-
# it 'should parse nested' do
|
20
|
-
# template = <<-END
|
21
|
-
# <div class="test">
|
22
|
-
# <div class="test2">
|
23
|
-
# <h1>Header</h1>
|
24
|
-
# </div>
|
25
|
-
# </div>
|
26
|
-
# END
|
27
|
-
# parser = TemplateParser.new(template, 'main')
|
28
|
-
# end
|
29
|
-
#
|
30
|
-
# it "should parse nested bindings" do
|
31
|
-
# template = <<-END
|
32
|
-
# 1{#if _a}2
|
33
|
-
# 3{#if _b}4
|
34
|
-
# _a and _b
|
35
|
-
# {/}
|
36
|
-
# {/}
|
37
|
-
# END
|
38
|
-
# parser = TemplateParser.new(template, 'main')
|
39
|
-
#
|
40
|
-
# expect(parser.templates.keys).to eq(["main/body", "main/body/__template/1", "main/body/__template/0"])
|
41
|
-
# expect(parser.templates['main/body/__template/1']).to eq({"html"=>"4\n _a and _b\n ", "bindings"=>{}})
|
42
|
-
# end
|
43
|
-
#
|
44
|
-
# it "should parse templates in attributes" do
|
45
|
-
# template = <<-END
|
46
|
-
# <div class="{#if _model._is_cool}cool{/}">yes</div>
|
47
|
-
# END
|
48
|
-
# parser = TemplateParser.new(template, 'main')
|
49
|
-
# end
|
50
|
-
# end
|