volt 0.8.14 → 0.8.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/Readme.md +8 -2
  4. data/VERSION +1 -1
  5. data/app/volt/controllers/notices_controller.rb +1 -1
  6. data/app/volt/models/user.rb +2 -2
  7. data/app/volt/tasks/live_query/live_query_pool.rb +1 -1
  8. data/app/volt/tasks/query_tasks.rb +1 -1
  9. data/app/volt/tasks/store_tasks.rb +1 -1
  10. data/app/volt/tasks/user_tasks.rb +2 -2
  11. data/lib/volt/boot.rb +2 -2
  12. data/lib/volt/cli/asset_compile.rb +31 -27
  13. data/lib/volt/cli.rb +64 -65
  14. data/lib/volt/config.rb +25 -23
  15. data/lib/volt/console.rb +17 -16
  16. data/lib/volt/controllers/model_controller.rb +82 -80
  17. data/lib/volt/data_stores/data_store.rb +2 -2
  18. data/lib/volt/data_stores/mongo_driver.rb +2 -2
  19. data/lib/volt/extra_core/inflections.rb +2 -2
  20. data/lib/volt/extra_core/inflector/inflections.rb +185 -183
  21. data/lib/volt/extra_core/inflector/methods.rb +50 -48
  22. data/lib/volt/extra_core/string.rb +2 -2
  23. data/lib/volt/models/array_model.rb +93 -92
  24. data/lib/volt/models/cursor.rb +3 -2
  25. data/lib/volt/models/model.rb +248 -251
  26. data/lib/volt/models/model_hash_behaviour.rb +44 -44
  27. data/lib/volt/models/model_helpers.rb +38 -36
  28. data/lib/volt/models/model_state.rb +16 -17
  29. data/lib/volt/models/model_wrapper.rb +25 -24
  30. data/lib/volt/models/persistors/array_store.rb +145 -143
  31. data/lib/volt/models/persistors/base.rb +18 -16
  32. data/lib/volt/models/persistors/flash.rb +24 -22
  33. data/lib/volt/models/persistors/local_store.rb +46 -44
  34. data/lib/volt/models/persistors/model_identity_map.rb +10 -8
  35. data/lib/volt/models/persistors/model_store.rb +76 -76
  36. data/lib/volt/models/persistors/params.rb +19 -17
  37. data/lib/volt/models/persistors/query/query_listener.rb +65 -63
  38. data/lib/volt/models/persistors/query/query_listener_pool.rb +12 -10
  39. data/lib/volt/models/persistors/store.rb +28 -28
  40. data/lib/volt/models/persistors/store_factory.rb +12 -10
  41. data/lib/volt/models/persistors/store_state.rb +33 -31
  42. data/lib/volt/models/url.rb +96 -104
  43. data/lib/volt/models/validations.rb +56 -54
  44. data/lib/volt/models/validators/length_validator.rb +24 -22
  45. data/lib/volt/models/validators/presence_validator.rb +14 -12
  46. data/lib/volt/page/bindings/attribute_binding.rb +106 -106
  47. data/lib/volt/page/bindings/base_binding.rb +23 -21
  48. data/lib/volt/page/bindings/component_binding.rb +3 -1
  49. data/lib/volt/page/bindings/content_binding.rb +34 -34
  50. data/lib/volt/page/bindings/each_binding.rb +113 -113
  51. data/lib/volt/page/bindings/event_binding.rb +38 -34
  52. data/lib/volt/page/bindings/if_binding.rb +56 -54
  53. data/lib/volt/page/bindings/template_binding/grouped_controllers.rb +24 -22
  54. data/lib/volt/page/bindings/template_binding.rb +182 -185
  55. data/lib/volt/page/channel.rb +79 -77
  56. data/lib/volt/page/channel_stub.rb +29 -27
  57. data/lib/volt/page/document.rb +6 -5
  58. data/lib/volt/page/document_events.rb +54 -52
  59. data/lib/volt/page/page.rb +139 -138
  60. data/lib/volt/page/string_template_renderer.rb +36 -36
  61. data/lib/volt/page/sub_context.rb +26 -25
  62. data/lib/volt/page/targets/attribute_section.rb +27 -25
  63. data/lib/volt/page/targets/attribute_target.rb +7 -6
  64. data/lib/volt/page/targets/base_section.rb +27 -26
  65. data/lib/volt/page/targets/binding_document/base_node.rb +3 -1
  66. data/lib/volt/page/targets/binding_document/component_node.rb +85 -82
  67. data/lib/volt/page/targets/binding_document/html_node.rb +11 -9
  68. data/lib/volt/page/targets/dom_section.rb +78 -77
  69. data/lib/volt/page/targets/dom_target.rb +8 -6
  70. data/lib/volt/page/targets/dom_template.rb +90 -88
  71. data/lib/volt/page/targets/helpers/comment_searchers.rb +51 -49
  72. data/lib/volt/page/tasks.rb +59 -57
  73. data/lib/volt/page/template_renderer.rb +17 -14
  74. data/lib/volt/page/url_tracker.rb +26 -24
  75. data/lib/volt/reactive/computation.rb +87 -88
  76. data/lib/volt/reactive/dependency.rb +30 -28
  77. data/lib/volt/reactive/eventable.rb +64 -62
  78. data/lib/volt/reactive/hash_dependency.rb +25 -23
  79. data/lib/volt/reactive/reactive_accessors.rb +34 -32
  80. data/lib/volt/reactive/reactive_array.rb +162 -162
  81. data/lib/volt/reactive/reactive_hash.rb +37 -35
  82. data/lib/volt/router/routes.rb +99 -101
  83. data/lib/volt/server/component_handler.rb +20 -21
  84. data/lib/volt/server/component_templates.rb +72 -70
  85. data/lib/volt/server/html_parser/attribute_scope.rb +109 -99
  86. data/lib/volt/server/html_parser/each_scope.rb +17 -16
  87. data/lib/volt/server/html_parser/if_view_scope.rb +51 -49
  88. data/lib/volt/server/html_parser/sandlebars_parser.rb +184 -177
  89. data/lib/volt/server/html_parser/textarea_scope.rb +24 -22
  90. data/lib/volt/server/html_parser/view_handler.rb +66 -65
  91. data/lib/volt/server/html_parser/view_parser.rb +23 -21
  92. data/lib/volt/server/html_parser/view_scope.rb +142 -141
  93. data/lib/volt/server/rack/asset_files.rb +81 -79
  94. data/lib/volt/server/rack/component_code.rb +17 -15
  95. data/lib/volt/server/rack/component_html_renderer.rb +14 -12
  96. data/lib/volt/server/rack/component_paths.rb +72 -71
  97. data/lib/volt/server/rack/index_files.rb +36 -39
  98. data/lib/volt/server/rack/opal_files.rb +43 -41
  99. data/lib/volt/server/rack/source_map_server.rb +23 -21
  100. data/lib/volt/server/socket_connection_handler.rb +46 -45
  101. data/lib/volt/server/socket_connection_handler_stub.rb +21 -19
  102. data/lib/volt/server.rb +60 -58
  103. data/lib/volt/spec/setup.rb +3 -3
  104. data/lib/volt/tasks/dispatcher.rb +24 -23
  105. data/lib/volt/tasks/task_handler.rb +35 -33
  106. data/lib/volt/utils/ejson.rb +8 -6
  107. data/lib/volt/utils/generic_counting_pool.rb +33 -31
  108. data/lib/volt/utils/generic_pool.rb +73 -70
  109. data/lib/volt/utils/local_storage.rb +42 -38
  110. data/lib/volt/volt/environment.rb +1 -1
  111. data/lib/volt.rb +44 -42
  112. data/spec/apps/kitchen_sink/app/main/assets/css/todos.css +28 -0
  113. data/spec/apps/kitchen_sink/app/main/config/routes.rb +1 -0
  114. data/spec/apps/kitchen_sink/app/main/controllers/main_controller.rb +2 -2
  115. data/spec/apps/kitchen_sink/app/main/controllers/todos_controller.rb +17 -0
  116. data/spec/apps/kitchen_sink/app/main/views/main/main.html +1 -0
  117. data/spec/apps/kitchen_sink/app/main/views/todos/index.html +24 -0
  118. data/spec/apps/kitchen_sink/config.ru +1 -1
  119. data/spec/controllers/reactive_accessors_spec.rb +5 -5
  120. data/spec/extra_core/inflector_spec.rb +2 -2
  121. data/spec/integration/list_spec.rb +68 -0
  122. data/spec/models/model_spec.rb +57 -57
  123. data/spec/models/persistors/params_spec.rb +6 -6
  124. data/spec/models/persistors/store_spec.rb +7 -7
  125. data/spec/models/validations_spec.rb +3 -3
  126. data/spec/page/bindings/content_binding_spec.rb +7 -7
  127. data/spec/page/bindings/template_binding_spec.rb +4 -5
  128. data/spec/page/sub_context_spec.rb +2 -2
  129. data/spec/reactive/computation_spec.rb +10 -10
  130. data/spec/reactive/dependency_spec.rb +2 -2
  131. data/spec/reactive/eventable_spec.rb +4 -4
  132. data/spec/reactive/reactive_array_spec.rb +13 -13
  133. data/spec/router/routes_spec.rb +5 -5
  134. data/spec/server/html_parser/sandlebars_parser_spec.rb +9 -9
  135. data/spec/server/html_parser/view_parser_spec.rb +27 -27
  136. data/spec/server/rack/asset_files_spec.rb +5 -5
  137. data/spec/server/rack/component_paths_spec.rb +2 -2
  138. data/spec/tasks/live_query_spec.rb +2 -2
  139. data/spec/tasks/query_tasks.rb +1 -1
  140. data/spec/tasks/query_tracker_spec.rb +1 -1
  141. data/spec/templates/targets/binding_document/component_node_spec.rb +2 -2
  142. data/spec/utils/generic_counting_pool_spec.rb +2 -2
  143. data/spec/utils/generic_pool_spec.rb +2 -2
  144. data/templates/component/controllers/main_controller.rb +1 -1
  145. data/templates/model/model.rb.tt +2 -2
  146. data/templates/newgem/app/newgem/controllers/main_controller.rb.tt +2 -2
  147. data/templates/project/app/main/controllers/main_controller.rb +1 -1
  148. data/templates/project/config.ru +1 -1
  149. metadata +10 -3
  150. data/app/volt/assets/js/vertxbus.js +0 -216
@@ -1,222 +1,229 @@
1
1
  require 'strscan'
2
2
 
3
- class HTMLParseError < RuntimeError
4
- end
5
- # Parses html and bindings
6
- # based on http://ejohn.org/files/htmlparser.js
7
- #
8
- # takes the html and a handler object that will have the following methods
9
- # called as each is seen: comment, text, binding, start_tag, end_tag
10
- #
11
- # This is not a full html parser, but should cover most common cases.
12
- class SandlebarsParser
13
- def self.truth_hash(array)
14
- hash = {}
15
- array.each {|v| hash[v] = true }
16
-
17
- return hash
3
+ module Volt
4
+ class HTMLParseError < RuntimeError
18
5
  end
19
6
 
20
- # regex matchers
21
- START_TAG = /^<([-!\:A-Za-z0-9_]+)((?:\s+[\w\-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/
22
- END_TAG = /^<\/([-!\:A-Za-z0-9_]+)[^>]*>/
23
- ATTRIBUTES = /([-\:A-Za-z0-9_]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/
7
+ # Parses html and bindings
8
+ # based on http://ejohn.org/files/htmlparser.js
9
+ #
10
+ # takes the html and a handler object that will have the following methods
11
+ # called as each is seen: comment, text, binding, start_tag, end_tag
12
+ #
13
+ # This is not a full html parser, but should cover most common cases.
14
+ class SandlebarsParser
15
+ def self.truth_hash(array)
16
+ hash = {}
17
+ array.each { |v| hash[v] = true }
18
+
19
+ return hash
20
+ end
24
21
 
25
- # Types of elements
26
- BLOCK = truth_hash(%w{a address applet blockquote button center dd del dir div dl dt fieldset form frameset hr iframe ins isindex li map menu noframes noscript object ol p pre script table tbody td tfoot th thead tr ul})
27
- EMPTY = truth_hash(%w{area base basefont br col frame hr img input isindex link meta param embed})
28
- INLINE = truth_hash(%w{abbr acronym applet b basefont bdo big br button cite code del dfn em font i iframe img input ins kbd label map object q s samp script select small span strike strong sub sup textarea tt u var})
29
- CLOSE_SELF = truth_hash(%w{colgroup dd dt li options p td tfoot th thead tr})
30
- SPECIAL = truth_hash(%w{script style})
22
+ # regex matchers
23
+ START_TAG = /^<([-!\:A-Za-z0-9_]+)((?:\s+[\w\-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/
24
+ END_TAG = /^<\/([-!\:A-Za-z0-9_]+)[^>]*>/
25
+ ATTRIBUTES = /([-\:A-Za-z0-9_]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/
31
26
 
32
- FILL_IN_ATTRIBUTES = truth_hash(%w{checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected})
27
+ # Types of elements
28
+ BLOCK = truth_hash(%w{a address applet blockquote button center dd del dir div dl dt fieldset form frameset hr iframe ins isindex li map menu noframes noscript object ol p pre script table tbody td tfoot th thead tr ul})
29
+ EMPTY = truth_hash(%w{area base basefont br col frame hr img input isindex link meta param embed})
30
+ INLINE = truth_hash(%w{abbr acronym applet b basefont bdo big br button cite code del dfn em font i iframe img input ins kbd label map object q s samp script select small span strike strong sub sup textarea tt u var})
31
+ CLOSE_SELF = truth_hash(%w{colgroup dd dt li options p td tfoot th thead tr})
32
+ SPECIAL = truth_hash(%w{script style})
33
33
 
34
- def initialize(html, handler, file_path=nil)
35
- @html = StringScanner.new(html)
36
- @handler = handler
37
- @file_path = file_path
34
+ FILL_IN_ATTRIBUTES = truth_hash(%w{checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected})
38
35
 
39
- @stack = []
36
+ def initialize(html, handler, file_path=nil)
37
+ @html = StringScanner.new(html)
38
+ @handler = handler
39
+ @file_path = file_path
40
40
 
41
- parse
42
- end
41
+ @stack = []
43
42
 
44
- def last
45
- @stack.last
46
- end
43
+ parse
44
+ end
47
45
 
48
- def parse
49
- loop do
50
- if last && SPECIAL[last]
51
- # In a script or style tag, just look for the first end
52
- close_tag = "</#{last}>"
53
- body = @html.scan_until(/#{close_tag}/)
54
- body = body[0..((-1 * close_tag.size)-1)]
55
-
56
- body = body.gsub(/\<\!--(.*?)--\>/, "\\1").gsub(/\<\!\[CDATA\[(.*?)\]\]\>/, "\\1")
57
-
58
- text(body)
59
-
60
- end_tag(last, last)
61
- elsif @html.scan(/\<\!--/)
62
- # start comment
63
- comment = @html.scan_until(/--\>/)
64
- comment = comment[0..-4]
65
-
66
- @handler.comment(comment) if @handler.respond_to?(:comment)
67
- elsif (tag = @html.scan(START_TAG))
68
- tag_name = @html[1]
69
- rest = @html[2]
70
- unary = @html[3]
71
-
72
- start_tag(tag, tag_name, rest, unary)
73
- elsif @html.scan(END_TAG)
74
- tag_name = @html[1]
75
-
76
- end_tag(tag_name, tag_name)
77
- elsif (escaped = @html.scan(/\{\{\{(.*?)\}\}\}([^\}]|$)/))
78
- # Anything between {{{ and }}} is escaped and not processed (treaded as text)
79
- if escaped[-1] != '}'
80
- # Move back if we matched a new non } for close, skip if we hit the end
81
- @html.pos = @html.pos - 1
82
- end
46
+ def last
47
+ @stack.last
48
+ end
83
49
 
84
- text(@html[1])
85
- elsif (binding = @html.scan(/\{\{/))
86
- # We are in text mode and matched the start of a binding
87
- start_binding
88
- elsif (text = @html.scan(/\{/))
89
- # A single { outside of a binding
90
- text(text)
91
- elsif (text = @html.scan(/(?:[^\<\{]+)/))
92
- # matched text up until the next html tag
93
- text(text)
94
- else
95
- # Nothing left
96
- break
50
+ def parse
51
+ loop do
52
+ if last && SPECIAL[last]
53
+ # In a script or style tag, just look for the first end
54
+ close_tag = "</#{last}>"
55
+ body = @html.scan_until(/#{close_tag}/)
56
+ special_tag(close_tag, body)
57
+ elsif @html.scan(/\<\!--/)
58
+ # start comment
59
+ comment = @html.scan_until(/--\>/)
60
+ comment = comment[0..-4]
61
+
62
+ @handler.comment(comment) if @handler.respond_to?(:comment)
63
+ elsif (tag = @html.scan(START_TAG))
64
+ tag_name = @html[1]
65
+ rest = @html[2]
66
+ unary = @html[3]
67
+
68
+ start_tag(tag, tag_name, rest, unary)
69
+ elsif @html.scan(END_TAG)
70
+ tag_name = @html[1]
71
+
72
+ end_tag(tag_name, tag_name)
73
+ elsif (escaped = @html.scan(/\{\{\{(.*?)\}\}\}([^\}]|$)/))
74
+ # Anything between {{{ and }}} is escaped and not processed (treaded as text)
75
+ if escaped[-1] != '}'
76
+ # Move back if we matched a new non } for close, skip if we hit the end
77
+ @html.pos = @html.pos - 1
78
+ end
79
+
80
+ text(@html[1])
81
+ elsif (binding = @html.scan(/\{\{/))
82
+ # We are in text mode and matched the start of a binding
83
+ start_binding
84
+ elsif (text = @html.scan(/\{/))
85
+ # A single { outside of a binding
86
+ text(text)
87
+ elsif (text = @html.scan(/(?:[^\<\{]+)/))
88
+ # matched text up until the next html tag
89
+ text(text)
90
+ else
91
+ # Nothing left
92
+ break
93
+ end
97
94
  end
98
- end
99
95
 
100
- end_tag(nil, nil)
101
- end
96
+ end_tag(nil, nil)
97
+ end
102
98
 
103
- def text(text)
104
- @handler.text(text) if @handler.respond_to?(:text)
105
- end
99
+ def text(text)
100
+ @handler.text(text) if @handler.respond_to?(:text)
101
+ end
106
102
 
107
- # Findings the end of a binding
108
- def start_binding
109
- binding = ''
110
- open_count = 1
111
-
112
- # scan until we reach a {{ or }}
113
- loop do
114
- binding << @html.scan_until(/(\{\{|\}\}|\n|\Z)/)
115
-
116
- match = @html[1]
117
- if match == '}}'
118
- # close
119
- open_count -= 1
120
- break if open_count == 0
121
- elsif match == '{{'
122
- # open more
123
- open_count += 1
124
- elsif match == "\n" || @html.eos?
125
- # Starting new tag, should be closed before this
126
- # or end of doc before closed binding
127
- raise_parse_error("unclosed binding: {#{binding.strip}")
128
- else
129
- raise "should not reach here"
103
+ # Findings the end of a binding
104
+ def start_binding
105
+ binding = ''
106
+ open_count = 1
107
+
108
+ # scan until we reach a {{ or }}
109
+ loop do
110
+ binding << @html.scan_until(/(\{\{|\}\}|\n|\Z)/)
111
+
112
+ match = @html[1]
113
+ if match == '}}'
114
+ # close
115
+ open_count -= 1
116
+ break if open_count == 0
117
+ elsif match == '{{'
118
+ # open more
119
+ open_count += 1
120
+ elsif match == "\n" || @html.eos?
121
+ # Starting new tag, should be closed before this
122
+ # or end of doc before closed binding
123
+ raise_parse_error("unclosed binding: {#{binding.strip}")
124
+ else
125
+ raise "should not reach here"
126
+ end
130
127
  end
131
- end
132
128
 
133
- binding = binding[0..-3]
134
- @handler.binding(binding) if @handler.respond_to?(:binding)
135
- end
129
+ binding = binding[0..-3]
130
+ @handler.binding(binding) if @handler.respond_to?(:binding)
131
+ end
136
132
 
137
- def raise_parse_error(error)
138
- line_number = @html.pre_match.count("\n") + 1
133
+ def raise_parse_error(error)
134
+ line_number = @html.pre_match.count("\n") + 1
139
135
 
140
- error_str = error + " on line: #{line_number}"
141
- error_str += " of #{@file_path}" if @file_path
136
+ error_str = error + " on line: #{line_number}"
137
+ error_str += " of #{@file_path}" if @file_path
142
138
 
143
- raise HTMLParseError, error_str
144
- end
139
+ raise HTMLParseError, error_str
140
+ end
145
141
 
146
- def start_tag(tag, tag_name, rest, unary)
147
- section_tag = tag_name[0] == ':' && tag_name[1] =~ /[A-Z]/
142
+ def start_tag(tag, tag_name, rest, unary)
143
+ section_tag = tag_name[0] == ':' && tag_name[1] =~ /[A-Z]/
148
144
 
149
- tag_name = tag_name.downcase
145
+ tag_name = tag_name.downcase
150
146
 
151
- # handle doctype so we get it output exactly the same way
152
- if tag_name == '!doctype'
153
- @handler.text(tag) if @handler.respond_to?(:start_tag)
154
- return
155
- end
147
+ # handle doctype so we get it output exactly the same way
148
+ if tag_name == '!doctype'
149
+ @handler.text(tag) if @handler.respond_to?(:start_tag)
150
+ return
151
+ end
156
152
 
157
- # Auto-close the last inline tag if we started a new block
158
- if BLOCK[tag_name]
159
- if last && INLINE[last]
160
- end_tag(nil, last)
153
+ # Auto-close the last inline tag if we started a new block
154
+ if BLOCK[tag_name]
155
+ if last && INLINE[last]
156
+ end_tag(nil, last)
157
+ end
161
158
  end
162
- end
163
159
 
164
- # Some tags close themselves when a new one of themselves is reached.
165
- # ex, a tr will close the previous tr
166
- if CLOSE_SELF[tag_name] && last == tag_name
167
- end_tag(nil, tag_name)
168
- end
160
+ # Some tags close themselves when a new one of themselves is reached.
161
+ # ex, a tr will close the previous tr
162
+ if CLOSE_SELF[tag_name] && last == tag_name
163
+ end_tag(nil, tag_name)
164
+ end
169
165
 
170
- unary = EMPTY[tag_name] || !unary.blank?
166
+ unary = EMPTY[tag_name] || !unary.blank?
171
167
 
172
- # Section tag's are also unary
173
- unless unary || section_tag
174
- @stack.push(tag_name)
175
- end
168
+ # Section tag's are also unary
169
+ unless unary || section_tag
170
+ @stack.push(tag_name)
171
+ end
176
172
 
177
- if @handler.respond_to?(:start_tag)
178
- attributes = {}
173
+ if @handler.respond_to?(:start_tag)
174
+ attributes = {}
179
175
 
180
- # Take the rest string and extract the attributes, filling in any
181
- # "fill in" attribute values if not provided.
182
- rest.scan(ATTRIBUTES).each do |match|
183
- name = match[0]
176
+ # Take the rest string and extract the attributes, filling in any
177
+ # "fill in" attribute values if not provided.
178
+ rest.scan(ATTRIBUTES).each do |match|
179
+ name = match[0]
184
180
 
185
- value = match[1] || match[2] || match[3] || FILL_IN_ATTRIBUTES[name] || ''
181
+ value = match[1] || match[2] || match[3] || FILL_IN_ATTRIBUTES[name] || ''
186
182
 
187
- attributes[name] = value
188
- end
183
+ attributes[name] = value
184
+ end
189
185
 
190
- if section_tag
191
- @handler.start_section(tag_name, attributes, unary)
192
- else
193
- @handler.start_tag(tag_name, attributes, unary)
186
+ if section_tag
187
+ @handler.start_section(tag_name, attributes, unary)
188
+ else
189
+ @handler.start_tag(tag_name, attributes, unary)
190
+ end
194
191
  end
195
192
  end
196
- end
197
-
198
- def end_tag(tag, tag_name)
199
- # If no tag name is provided, close all the way up
200
- new_size = 0
201
193
 
202
- if tag
203
- # Find the closest tag that closes.
204
- (@stack.size-1).downto(0) do |index|
205
- if @stack[index] == tag_name
206
- new_size = index
207
- break
194
+ def end_tag(tag, tag_name)
195
+ # If no tag name is provided, close all the way up
196
+ new_size = 0
197
+
198
+ if tag
199
+ # Find the closest tag that closes.
200
+ (@stack.size-1).downto(0) do |index|
201
+ if @stack[index] == tag_name
202
+ new_size = index
203
+ break
204
+ end
208
205
  end
209
206
  end
210
- end
211
207
 
212
- if new_size >= 0
213
- if @handler.respond_to?(:end_tag)
214
- (@stack.size-1).downto(new_size) do |index|
215
- @handler.end_tag(@stack[index])
208
+ if new_size >= 0
209
+ if @handler.respond_to?(:end_tag)
210
+ (@stack.size-1).downto(new_size) do |index|
211
+ @handler.end_tag(@stack[index])
212
+ end
216
213
  end
214
+
215
+ @stack = @stack[0...new_size]
217
216
  end
217
+ end
218
+
219
+ def special_tag(close_tag, body)
220
+ body = body[0..((-1 * close_tag.size)-1)]
221
+
222
+ body = body.gsub(/\<\!--(.*?)--\>/, "\\1").gsub(/\<\!\[CDATA\[(.*?)\]\]\>/, "\\1")
218
223
 
219
- @stack = @stack[0...new_size]
224
+ text(body)
225
+
226
+ end_tag(last, last)
220
227
  end
221
228
  end
222
- end
229
+ end
@@ -1,31 +1,33 @@
1
- class TextareaScope < ViewScope
2
- def initialize(handler, path, attributes)
3
- super(handler, path)
1
+ module Volt
2
+ class TextareaScope < ViewScope
3
+ def initialize(handler, path, attributes)
4
+ super(handler, path)
4
5
 
5
- @attributes = attributes
6
- end
6
+ @attributes = attributes
7
+ end
7
8
 
8
- def add_binding(content)
9
- @html << "{{#{content}}}"
10
- end
9
+ def add_binding(content)
10
+ @html << "{{#{content}}}"
11
+ end
11
12
 
12
- def close_scope(pop=true)
13
- # Remove from the scope
14
- @handler.scope.pop
13
+ def close_scope(pop=true)
14
+ # Remove from the scope
15
+ @handler.scope.pop
15
16
 
16
- attributes = @attributes
17
+ attributes = @attributes
17
18
 
18
- if @html[/\{\{[^\}]+\}\}/]
19
- # If the html inside the textarea has a binding, process it as
20
- # a value attribute.
21
- attributes['value'] = @html
22
- @html = ''
23
- end
19
+ if @html[/\{\{[^\}]+\}\}/]
20
+ # If the html inside the textarea has a binding, process it as
21
+ # a value attribute.
22
+ attributes['value'] = @html
23
+ @html = ''
24
+ end
24
25
 
25
- # Normal tag
26
- attributes = @handler.last.process_attributes('textarea', attributes)
26
+ # Normal tag
27
+ attributes = @handler.last.process_attributes('textarea', attributes)
27
28
 
28
- @handler.last.html << "<textarea#{attribute_string(attributes)}>#{@html}</textarea>"
29
+ @handler.last.html << "<textarea#{attribute_string(attributes)}>#{@html}</textarea>"
29
30
 
31
+ end
30
32
  end
31
- end
33
+ end
@@ -1,82 +1,83 @@
1
- class ViewHandler
2
- attr_reader :templates, :scope
1
+ module Volt
2
+ class ViewHandler
3
+ attr_reader :templates, :scope
3
4
 
4
- def html
5
- last.html
6
- end
7
-
8
- def last
9
- @scope.last
10
- end
5
+ def html
6
+ last.html
7
+ end
11
8
 
12
- def initialize(initial_path, allow_sections=true)
13
- @original_path = initial_path
9
+ def last
10
+ @scope.last
11
+ end
14
12
 
15
- # Default to the body section
16
- initial_path += '/body' if allow_sections
13
+ def initialize(initial_path, allow_sections=true)
14
+ @original_path = initial_path
17
15
 
18
- @scope = [ViewScope.new(self, initial_path)]
19
- @templates = {}
20
- end
16
+ # Default to the body section
17
+ initial_path += '/body' if allow_sections
21
18
 
22
- def comment(comment)
23
- last << "<!--#{comment}-->"
24
- end
25
-
26
- def text(text)
27
- last << text
28
- end
19
+ @scope = [ViewScope.new(self, initial_path)]
20
+ @templates = {}
21
+ end
29
22
 
30
- def binding(binding)
31
- @scope.last.add_binding(binding)
32
- end
23
+ def comment(comment)
24
+ last << "<!--#{comment}-->"
25
+ end
33
26
 
34
- def start_tag(tag_name, attributes, unary)
35
- case tag_name[0]
36
- when ':'
37
- # Component
38
- last.add_component(tag_name, attributes, unary)
39
- else
40
- if tag_name == 'textarea'
41
- @in_textarea = true
42
- last.add_textarea(tag_name, attributes, unary)
43
- else
27
+ def text(text)
28
+ last << text
29
+ end
44
30
 
45
- # Normal tag
46
- attributes = last.process_attributes(tag_name, attributes)
47
- attr_str = last.attribute_string(attributes)
31
+ def binding(binding)
32
+ @scope.last.add_binding(binding)
33
+ end
48
34
 
49
- last << "<#{tag_name}#{attr_str}#{unary ? ' /' : ''}>"
35
+ def start_tag(tag_name, attributes, unary)
36
+ case tag_name[0]
37
+ when ':'
38
+ # Component
39
+ last.add_component(tag_name, attributes, unary)
40
+ else
41
+ if tag_name == 'textarea'
42
+ @in_textarea = true
43
+ last.add_textarea(tag_name, attributes, unary)
44
+ else
45
+
46
+ # Normal tag
47
+ attributes = last.process_attributes(tag_name, attributes)
48
+ attr_str = last.attribute_string(attributes)
49
+
50
+ last << "<#{tag_name}#{attr_str}#{unary ? ' /' : ''}>"
51
+ end
50
52
  end
51
53
  end
52
- end
53
54
 
54
- def end_tag(tag_name)
55
- if @in_textarea && tag_name == 'textarea'
56
- last.close_scope
57
- @in_textarea = nil
58
- else
59
- last << "</#{tag_name}>"
55
+ def end_tag(tag_name)
56
+ if @in_textarea && tag_name == 'textarea'
57
+ last.close_scope
58
+ @in_textarea = nil
59
+ else
60
+ last << "</#{tag_name}>"
61
+ end
60
62
  end
61
- end
62
63
 
63
- def start_section(tag_name, attributes, unary)
64
- path = last.path
65
- # Start of section
66
- if @in_section
67
- # Close any previous sections
68
- last.close_scope
69
- else
70
- # This is the first time we've hit a section header, everything
71
- # outside of the headers should be removed
72
- @templates = {}
73
- end
64
+ def start_section(tag_name, attributes, unary)
65
+ path = last.path
66
+ # Start of section
67
+ if @in_section
68
+ # Close any previous sections
69
+ last.close_scope
70
+ else
71
+ # This is the first time we've hit a section header, everything
72
+ # outside of the headers should be removed
73
+ @templates = {}
74
+ end
74
75
 
75
- @in_section = tag_name[1..-1]
76
+ @in_section = tag_name[1..-1]
76
77
 
77
- # Set the new path to include the section
78
- new_path = @original_path + '/' + @in_section
79
- @scope = [ViewScope.new(self, new_path)]
78
+ # Set the new path to include the section
79
+ new_path = @original_path + '/' + @in_section
80
+ @scope = [ViewScope.new(self, new_path)]
81
+ end
80
82
  end
81
-
82
- end
83
+ end
@@ -5,35 +5,37 @@ require 'volt/server/html_parser/view_handler'
5
5
  require 'volt/server/html_parser/each_scope'
6
6
  require 'volt/server/html_parser/textarea_scope'
7
7
 
8
- class ViewParser
9
- attr_reader :templates
8
+ module Volt
9
+ class ViewParser
10
+ attr_reader :templates
10
11
 
11
- def initialize(html, template_path)
12
- @template_path = template_path
12
+ def initialize(html, template_path)
13
+ @template_path = template_path
13
14
 
14
- handler = ViewHandler.new(template_path)
15
+ handler = ViewHandler.new(template_path)
15
16
 
16
- SandlebarsParser.new(html, handler)
17
+ SandlebarsParser.new(html, handler)
17
18
 
18
- # Close out the last scope
19
- handler.scope.last.close_scope
19
+ # Close out the last scope
20
+ handler.scope.last.close_scope
20
21
 
21
- @templates = handler.templates
22
- end
22
+ @templates = handler.templates
23
+ end
23
24
 
24
- # Returns a parsed version of the data (useful for backend rendering
25
- # and testing)
26
- def data
27
- templates = @templates.deep_clone
25
+ # Returns a parsed version of the data (useful for backend rendering
26
+ # and testing)
27
+ def data
28
+ templates = @templates.deep_clone
28
29
 
29
- templates.each_pair do |name, value|
30
- if value['bindings']
31
- value['bindings'].each_pair do |number, binding|
32
- value['bindings'][number] = binding.map {|code| eval(code) }
30
+ templates.each_pair do |name, value|
31
+ if value['bindings']
32
+ value['bindings'].each_pair do |number, binding|
33
+ value['bindings'][number] = binding.map { |code| eval(code) }
34
+ end
33
35
  end
34
36
  end
35
- end
36
37
 
37
- return templates
38
+ return templates
39
+ end
38
40
  end
39
- end
41
+ end