liquor 0.1.1 → 0.9.1

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 (176) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +3 -9
  5. data/Gemfile +7 -0
  6. data/Guardfile +11 -0
  7. data/MIT-LICENSE +6 -2
  8. data/README.md +4 -122
  9. data/Rakefile +20 -23
  10. data/doc/language-spec.html +768 -0
  11. data/doc/language-spec.md +698 -0
  12. data/lib/liquor.rb +39 -68
  13. data/lib/liquor/ast_tools.rb +28 -0
  14. data/lib/liquor/compiler.rb +110 -0
  15. data/lib/liquor/context.rb +76 -254
  16. data/lib/liquor/diagnostics.rb +151 -0
  17. data/lib/liquor/drop/drop.rb +168 -0
  18. data/lib/liquor/drop/drop_delegation.rb +24 -0
  19. data/lib/liquor/drop/drop_scope.rb +118 -0
  20. data/lib/liquor/drop/dropable.rb +17 -0
  21. data/lib/liquor/emitter.rb +313 -0
  22. data/lib/liquor/extensions/kaminari.rb +14 -0
  23. data/lib/liquor/extensions/pagination.rb +235 -0
  24. data/lib/liquor/extensions/rails.rb +97 -0
  25. data/lib/liquor/extensions/thinking_sphinx.rb +14 -0
  26. data/lib/liquor/extensions/tire.rb +30 -0
  27. data/lib/liquor/external.rb +79 -0
  28. data/lib/liquor/function.rb +94 -0
  29. data/lib/liquor/grammar/lexer.rb +1223 -0
  30. data/lib/liquor/grammar/lexer.rl +297 -0
  31. data/lib/liquor/grammar/parser.racc +288 -0
  32. data/lib/liquor/grammar/parser.rb +885 -0
  33. data/lib/liquor/library.rb +41 -0
  34. data/lib/liquor/manager.rb +146 -0
  35. data/lib/liquor/runtime.rb +167 -0
  36. data/lib/liquor/stdlib/builtin_functions.rb +315 -0
  37. data/lib/liquor/stdlib/builtin_tags.rb +228 -0
  38. data/lib/liquor/stdlib/html_truncater.rb +162 -0
  39. data/lib/liquor/stdlib/partial_tags.rb +76 -0
  40. data/lib/liquor/tag.rb +83 -14
  41. data/lib/liquor/version.rb +1 -1
  42. data/liquor.gemspec +29 -6
  43. data/spec/builtins_spec.rb +264 -0
  44. data/spec/compiler_spec.rb +136 -0
  45. data/spec/context_spec.rb +49 -0
  46. data/spec/drop_delegation_spec.rb +21 -0
  47. data/spec/drop_spec.rb +222 -0
  48. data/spec/errors_spec.rb +40 -0
  49. data/spec/external_spec.rb +207 -0
  50. data/spec/function_spec.rb +80 -0
  51. data/spec/lexer_spec.rb +173 -0
  52. data/spec/library_spec.rb +18 -0
  53. data/spec/manager_spec.rb +84 -0
  54. data/spec/parser_spec.rb +381 -0
  55. data/spec/partials_spec.rb +74 -0
  56. data/spec/runtime_spec.rb +97 -0
  57. data/spec/spec_helper.rb +94 -0
  58. data/spec/tag_spec.rb +7 -0
  59. metadata +216 -173
  60. data/AUTHORS +0 -2
  61. data/CHANGELOG +0 -48
  62. data/Gemfile.lock +0 -91
  63. data/History.txt +0 -44
  64. data/LICENSE +0 -23
  65. data/example/server/example_servlet.rb +0 -37
  66. data/example/server/liquid_servlet.rb +0 -28
  67. data/example/server/liquor_servlet.rb +0 -28
  68. data/example/server/server.rb +0 -12
  69. data/example/server/templates/index.liquid +0 -6
  70. data/example/server/templates/index.liquor +0 -6
  71. data/example/server/templates/products.liquid +0 -45
  72. data/example/server/templates/products.liquor +0 -45
  73. data/init.rb +0 -8
  74. data/lib/extras/liquid_view.rb +0 -51
  75. data/lib/extras/liquor_view.rb +0 -51
  76. data/lib/liquor/block.rb +0 -101
  77. data/lib/liquor/condition.rb +0 -120
  78. data/lib/liquor/document.rb +0 -17
  79. data/lib/liquor/drop.rb +0 -256
  80. data/lib/liquor/errors.rb +0 -11
  81. data/lib/liquor/extensions.rb +0 -72
  82. data/lib/liquor/file_system.rb +0 -62
  83. data/lib/liquor/htmltags.rb +0 -74
  84. data/lib/liquor/module_ex.rb +0 -60
  85. data/lib/liquor/standardfilters.rb +0 -315
  86. data/lib/liquor/strainer.rb +0 -58
  87. data/lib/liquor/tags/assign.rb +0 -33
  88. data/lib/liquor/tags/capture.rb +0 -35
  89. data/lib/liquor/tags/case.rb +0 -83
  90. data/lib/liquor/tags/comment.rb +0 -9
  91. data/lib/liquor/tags/content_for.rb +0 -54
  92. data/lib/liquor/tags/cycle.rb +0 -59
  93. data/lib/liquor/tags/for.rb +0 -136
  94. data/lib/liquor/tags/if.rb +0 -80
  95. data/lib/liquor/tags/ifchanged.rb +0 -20
  96. data/lib/liquor/tags/include.rb +0 -56
  97. data/lib/liquor/tags/unless.rb +0 -33
  98. data/lib/liquor/tags/yield.rb +0 -49
  99. data/lib/liquor/template.rb +0 -181
  100. data/lib/liquor/variable.rb +0 -52
  101. data/performance/shopify.rb +0 -92
  102. data/performance/shopify/comment_form.rb +0 -33
  103. data/performance/shopify/database.rb +0 -45
  104. data/performance/shopify/json_filter.rb +0 -7
  105. data/performance/shopify/liquid.rb +0 -18
  106. data/performance/shopify/liquor.rb +0 -18
  107. data/performance/shopify/money_filter.rb +0 -18
  108. data/performance/shopify/paginate.rb +0 -93
  109. data/performance/shopify/shop_filter.rb +0 -98
  110. data/performance/shopify/tag_filter.rb +0 -25
  111. data/performance/shopify/vision.database.yml +0 -945
  112. data/performance/shopify/weight_filter.rb +0 -11
  113. data/performance/tests/dropify/article.liquid +0 -74
  114. data/performance/tests/dropify/blog.liquid +0 -33
  115. data/performance/tests/dropify/cart.liquid +0 -66
  116. data/performance/tests/dropify/collection.liquid +0 -22
  117. data/performance/tests/dropify/index.liquid +0 -47
  118. data/performance/tests/dropify/page.liquid +0 -8
  119. data/performance/tests/dropify/product.liquid +0 -68
  120. data/performance/tests/dropify/theme.liquid +0 -105
  121. data/performance/tests/ripen/article.liquid +0 -74
  122. data/performance/tests/ripen/blog.liquid +0 -13
  123. data/performance/tests/ripen/cart.liquid +0 -54
  124. data/performance/tests/ripen/collection.liquid +0 -29
  125. data/performance/tests/ripen/index.liquid +0 -32
  126. data/performance/tests/ripen/page.liquid +0 -4
  127. data/performance/tests/ripen/product.liquid +0 -75
  128. data/performance/tests/ripen/theme.liquid +0 -85
  129. data/performance/tests/tribble/404.liquid +0 -56
  130. data/performance/tests/tribble/article.liquid +0 -98
  131. data/performance/tests/tribble/blog.liquid +0 -41
  132. data/performance/tests/tribble/cart.liquid +0 -134
  133. data/performance/tests/tribble/collection.liquid +0 -70
  134. data/performance/tests/tribble/index.liquid +0 -94
  135. data/performance/tests/tribble/page.liquid +0 -56
  136. data/performance/tests/tribble/product.liquid +0 -116
  137. data/performance/tests/tribble/search.liquid +0 -51
  138. data/performance/tests/tribble/theme.liquid +0 -90
  139. data/performance/tests/vogue/article.liquid +0 -66
  140. data/performance/tests/vogue/blog.liquid +0 -32
  141. data/performance/tests/vogue/cart.liquid +0 -58
  142. data/performance/tests/vogue/collection.liquid +0 -19
  143. data/performance/tests/vogue/index.liquid +0 -22
  144. data/performance/tests/vogue/page.liquid +0 -3
  145. data/performance/tests/vogue/product.liquid +0 -62
  146. data/performance/tests/vogue/theme.liquid +0 -122
  147. data/test/assign_test.rb +0 -11
  148. data/test/block_test.rb +0 -58
  149. data/test/capture_test.rb +0 -41
  150. data/test/condition_test.rb +0 -115
  151. data/test/content_for_test.rb +0 -15
  152. data/test/context_test.rb +0 -479
  153. data/test/drop_test.rb +0 -162
  154. data/test/error_handling_test.rb +0 -89
  155. data/test/extra/breakpoint.rb +0 -547
  156. data/test/extra/caller.rb +0 -80
  157. data/test/file_system_test.rb +0 -30
  158. data/test/filter_test.rb +0 -147
  159. data/test/helper.rb +0 -24
  160. data/test/html_tag_test.rb +0 -31
  161. data/test/if_else_test.rb +0 -139
  162. data/test/include_tag_test.rb +0 -129
  163. data/test/module_ex_test.rb +0 -89
  164. data/test/output_test.rb +0 -121
  165. data/test/parsing_quirks_test.rb +0 -54
  166. data/test/regexp_test.rb +0 -45
  167. data/test/security_test.rb +0 -41
  168. data/test/standard_filter_test.rb +0 -170
  169. data/test/standard_tag_test.rb +0 -405
  170. data/test/statements_test.rb +0 -137
  171. data/test/strainer_test.rb +0 -27
  172. data/test/template_test.rb +0 -82
  173. data/test/test_helper.rb +0 -28
  174. data/test/unless_else_test.rb +0 -27
  175. data/test/variable_test.rb +0 -173
  176. data/test/yield_test.rb +0 -24
@@ -0,0 +1,228 @@
1
+ module Liquor
2
+ module Builtins
3
+ include Library
4
+
5
+ tag "declare" do |emit, context, node|
6
+ arg, kw = check_args node,
7
+ :ident,
8
+ :"=" => :expr
9
+
10
+ var_name, = nvalue(arg)
11
+ expr = kw[:"="]
12
+
13
+ expr_result = emit.expr(expr)
14
+
15
+ context.declare var_name, nloc(arg)
16
+ emit.out! %Q|#{context.access(var_name)} = #{expr_result}\n|
17
+ end
18
+
19
+ tag "assign" do |emit, context, node|
20
+ arg, kw = check_args node,
21
+ :ident,
22
+ :"=" => :expr
23
+
24
+ var_name, = nvalue(arg)
25
+ expr = kw[:"="]
26
+
27
+ expr_result = emit.expr(expr)
28
+
29
+ unless context.variable? var_name
30
+ context.declare var_name, nloc(arg)
31
+ end
32
+
33
+ emit.out! %Q|#{context.access(var_name)} = #{expr_result}\n|
34
+ end
35
+
36
+ class ForLoop
37
+ include Liquor::External
38
+
39
+ def initialize(length)
40
+ @length = length
41
+ @index = 0
42
+ end
43
+
44
+ attr_reader :length
45
+ attr_accessor :index
46
+
47
+ def next!
48
+ @index += 1
49
+ end
50
+
51
+ def rindex
52
+ @length - @index - 1
53
+ end
54
+
55
+ def is_first
56
+ @index == 0
57
+ end
58
+
59
+ def is_last
60
+ @index == @length - 1
61
+ end
62
+
63
+ export :length, :index, :rindex, :is_first, :is_last
64
+ end
65
+
66
+ tag "for" do |emit, context, node|
67
+ name, arg, *kwargs = nvalue(node)
68
+
69
+ if kwargs.empty?
70
+ raise SyntaxError.new("unexpected end of tag, expecting one of: `in:', `from:'.", nloc(node))
71
+ elsif kwname(kwargs[0]) == 'in'
72
+ arg, kw = check_args node,
73
+ :ident,
74
+ :in => :expr,
75
+ :do => :block
76
+ elsif kwname(kwargs[0]) == 'from'
77
+ arg, kw = check_args node,
78
+ :ident,
79
+ :from => :expr,
80
+ :to => :expr,
81
+ :do => :block
82
+ else
83
+ raise SyntaxError.new("unexpected `#{kwname(kwargs[0])}:', expecting one of: `in:', `from:'.", nloc(kwargs[0]))
84
+ end
85
+
86
+ var_name, = nvalue(arg)
87
+ indexer_name = "#{var_name}_loop"
88
+
89
+ context.nest do
90
+ if kw[:in]
91
+ indexer = %{Liquor::Builtins::ForLoop.new(#{emit.check_tuple(kw[:in])}.size)}
92
+ seq = emit.check_tuple(kw[:in])
93
+ elsif kw[:from]
94
+ indexer = %{Liquor::Builtins::ForLoop.new(#{emit.check_integer(kw[:to])} - #{emit.check_integer(kw[:from])})}
95
+ from, to = emit.check_integer(kw[:from]), emit.check_integer(kw[:to])
96
+ end
97
+
98
+ context.declare var_name, nloc(arg)
99
+ context.declare indexer_name
100
+
101
+ if kw[:in]
102
+ emit.out! %Q|#{context.access indexer_name} = #{indexer}\n|
103
+ emit.out! %Q|for #{context.access(var_name)} in #{seq}\n|
104
+ elsif kw[:from]
105
+ emit.out! %Q|#{context.access indexer_name} = #{indexer}\n|
106
+ emit.out! %Q<#{from}.upto(#{to}) do |#{context.access(var_name)}|\n>
107
+ end
108
+
109
+ emit.compile_block kw[:do]
110
+
111
+ emit.out! %Q|#{context.access indexer_name}.next!\n|
112
+ emit.out! %Q|end\n|
113
+ end
114
+ end
115
+
116
+ tag "if", continuations: %w(elsif) do |emit, context, node|
117
+ tag_name, arg, *kwargs = nvalue(node)
118
+
119
+ state = :begin
120
+ while state
121
+ case state
122
+ when :begin
123
+ if arg.nil?
124
+ if kwargs.empty?
125
+ raise SyntaxError.new("unexpected end of tag, expecting `expression'", nloc(node))
126
+ else
127
+ raise SyntaxError.new("unexpected `#{kwname(kwargs[0])}:', expecting `expression'", nloc(kwargs[0]))
128
+ end
129
+ end
130
+
131
+ emit.out! %Q|if #{emit.convert_boolean(arg)}\n|
132
+ state = :then
133
+
134
+ when :elsif
135
+ kwarg = kwargs.shift
136
+
137
+ if kwarg.nil?
138
+ state = nil
139
+ elsif kwname(kwarg) == 'elsif'
140
+ check_arg_type(kwarg, :expr)
141
+
142
+ emit.out! %Q|elsif #{emit.convert_boolean(kwvalue(kwarg))}\n|
143
+ state = :then
144
+ elsif kwname(kwarg) == 'else'
145
+ check_arg_type(kwarg, :block)
146
+
147
+ emit.out! %Q|else\n|
148
+ emit.compile_block kwvalue(kwarg)
149
+ state = :end
150
+ else
151
+ raise SyntaxError.new("unexpected `#{kwname(kwarg)}', expecting one of: `elsif:', `else:'")
152
+ end
153
+
154
+ when :then
155
+ kwarg = kwargs.shift
156
+ if kwarg.nil?
157
+ raise SyntaxError.new("unexpected end of tag, expecting `then:'", nloc(node))
158
+ elsif kwname(kwarg) != 'then'
159
+ raise SyntaxError.new("unexpected `#{kwname(kwarg)}', expecting `then:'", nloc(kwarg))
160
+ end
161
+
162
+ check_arg_type(kwarg, :block)
163
+
164
+ emit.compile_block kwvalue(kwarg)
165
+ state = :elsif
166
+
167
+ when :end
168
+ if kwargs.any?
169
+ raise SyntaxError.new("unexpected `#{kwname(kwargs.first)}', expecting end of tag", nloc(kwarg))
170
+ end
171
+
172
+ state = nil
173
+ end
174
+ end
175
+
176
+ emit.out! %Q|end\n|
177
+ end
178
+
179
+ tag "unless" do |emit, context, node|
180
+ arg, kw = check_args node,
181
+ :expr,
182
+ :then => :block
183
+
184
+ emit.out! %Q|unless #{emit.convert_boolean(arg)}\n|
185
+ emit.compile_block kw[:then]
186
+ emit.out! %Q|end\n|
187
+ end
188
+
189
+ tag "capture" do |emit, context, node|
190
+ tag, arg, *kwargs = nvalue(node)
191
+
192
+ if kwargs.count == 1 && kwname(kwargs.first) == 'yield'
193
+ arg, kw = check_args node,
194
+ :ident,
195
+ :yield => :string
196
+
197
+ var_name, = nvalue(arg)
198
+ fragment = kw[:yield]
199
+
200
+ context.declare var_name, nloc(arg)
201
+ access_var_name = context.access(var_name)
202
+
203
+ context.nest do
204
+ emit.out! %Q|#{access_var_name} = #{emit.storage}[#{emit.expr(fragment)}].to_s\n|
205
+ end
206
+ else
207
+ arg, kw = check_args node,
208
+ :ident,
209
+ :"=" => :block
210
+
211
+ var_name, = nvalue(arg)
212
+ block = kw[:"="]
213
+
214
+ context.declare var_name, nloc(arg)
215
+ access_var_name = context.access(var_name)
216
+
217
+ context.nest do
218
+ capture_var_name = emit.capture do
219
+ emit.compile_block block
220
+ end
221
+
222
+ emit.out! %Q|#{access_var_name} = #{capture_var_name}\n|
223
+ end
224
+ end
225
+ end
226
+
227
+ end
228
+ end
@@ -0,0 +1,162 @@
1
+ # Thanks to https://gist.github.com/101410 and other sources
2
+
3
+ module Liquor::HTMLTruncater
4
+ extend self
5
+
6
+ def truncate(input, number = 300, truncate_string = "...")
7
+ doc = Nokogiri::HTML(input.to_s, nil, "UTF-8")
8
+
9
+ current = doc.children.first
10
+ count = 0
11
+
12
+ while true
13
+ # we found a text node
14
+ if current.is_a?(Nokogiri::XML::Text)
15
+ count += current.text.mb_chars.length
16
+ # we reached our limit, let's get outta here!
17
+ break if count > number
18
+ previous = current
19
+ end
20
+
21
+ if current.children.length > 0
22
+ # this node has children, can't be a text node,
23
+ # lets descend and look for text nodes
24
+ current = current.children.first
25
+ elsif !current.next.nil?
26
+ #this has no children, but has a sibling, let's check it out
27
+ current = current.next
28
+ else
29
+ # we are the last child, we need to ascend until we are
30
+ # either done or find a sibling to continue on to
31
+ n = current
32
+ while !n.is_a?(Nokogiri::HTML::Document) and n.parent.next.nil?
33
+ n = n.parent
34
+ end
35
+
36
+ # we've reached the top and found no more text nodes, break
37
+ if n.is_a?(Nokogiri::HTML::Document)
38
+ break;
39
+ else
40
+ current = n.parent.next
41
+ end
42
+ end
43
+ end
44
+
45
+ if count >= number
46
+ unless count == number
47
+ new_content = current.text.mb_chars
48
+
49
+ index = number-(count-new_content.length)-1
50
+ if index >= 0
51
+ new_content = new_content[0..index]
52
+ current.send(:native_content=, new_content + truncate_string)
53
+ else
54
+ current = previous
55
+ current.send(:native_content=, current.content + truncate_string)
56
+ end
57
+ end
58
+
59
+ # remove everything else
60
+ while !current.is_a?(Nokogiri::HTML::Document)
61
+ while !current.next.nil?
62
+ current.next.remove
63
+ end
64
+ current = current.parent
65
+ end
66
+ end
67
+
68
+ # now we grab the html and not the text.
69
+ # we do first because nokogiri adds html and body tags
70
+ # which we don't want
71
+ if doc.root.present?
72
+ doc.root.children.first.inner_html
73
+ else # no root element present (probably empty?), will return the first element
74
+ doc.children.first.inner_html
75
+ end
76
+ end
77
+
78
+ def truncate_words(input, num_words, truncate_string = "...")
79
+ doc = Nokogiri::HTML(input)
80
+
81
+ current = doc.children.first
82
+ count = 0
83
+
84
+ while true
85
+ # we found a text node
86
+ if current.is_a?(Nokogiri::XML::Text)
87
+ count += current.text.split.length
88
+ # we reached our limit, let's get outta here!
89
+ break if count > num_words
90
+ previous = current
91
+ end
92
+
93
+ if current.children.length > 0
94
+ # this node has children, can't be a text node,
95
+ # lets descend and look for text nodes
96
+ current = current.children.first
97
+ elsif !current.next.nil?
98
+ #this has no children, but has a sibling, let's check it out
99
+ current = current.next
100
+ else
101
+ # we are the last child, we need to ascend until we are
102
+ # either done or find a sibling to continue on to
103
+ n = current
104
+ while !n.is_a?(Nokogiri::HTML::Document) and n.parent.next.nil?
105
+ n = n.parent
106
+ end
107
+
108
+ # we've reached the top and found no more text nodes, break
109
+ if n.is_a?(Nokogiri::HTML::Document)
110
+ break;
111
+ else
112
+ current = n.parent.next
113
+ end
114
+ end
115
+ end
116
+
117
+ if count >= num_words
118
+ unless count == num_words
119
+ new_content = current.text.split
120
+
121
+ # If we're here, the last text node we counted eclipsed the number of words
122
+ # that we want, so we need to cut down on words. The easiest way to think about
123
+ # this is that without this node we'd have fewer words than the limit, so all
124
+ # the previous words plus a limited number of words from this node are needed.
125
+ # We simply need to figure out how many words are needed and grab that many.
126
+ # Then we need to -subtract- an index, because the first word would be index zero.
127
+
128
+ # For example, given:
129
+ # <p>Testing this HTML truncater.</p><p>To see if its working.</p>
130
+ # Let's say I want 6 words. The correct returned string would be:
131
+ # <p>Testing this HTML truncater.</p><p>To see...</p>
132
+ # All the words in both paragraphs = 9
133
+ # The last paragraph is the one that breaks the limit. How many words would we
134
+ # have without it? 4. But we want up to 6, so we might as well get that many.
135
+ # 6 - 4 = 2, so we get 2 words from this node, but words #1-2 are indices #0-1, so
136
+ # we subtract 1. If this gives us -1, we want nothing from this node. So go back to
137
+ # the previous node instead.
138
+ index = num_words-(count-new_content.length)-1
139
+ if index >= 0
140
+ new_content = new_content[0..index]
141
+ current.content = new_content.join(' ') + truncate_string
142
+ else
143
+ current = previous
144
+ current.content = current.content + truncate_string
145
+ end
146
+ end
147
+
148
+ # remove everything else
149
+ while !current.is_a?(Nokogiri::HTML::Document)
150
+ while !current.next.nil?
151
+ current.next.remove
152
+ end
153
+ current = current.parent
154
+ end
155
+ end
156
+
157
+ # now we grab the html and not the text.
158
+ # we do first because nokogiri adds html and body tags
159
+ # which we don't want
160
+ doc.root.children.first.inner_html
161
+ end
162
+ end
@@ -0,0 +1,76 @@
1
+ module Liquor
2
+ module Partials
3
+ include Library
4
+
5
+ tag "yield" do |emit, context, node|
6
+ tag, arg, *kwargs = nvalue(node)
7
+
8
+ if arg.nil? # {% yield %}
9
+ check_args node,
10
+ nil
11
+
12
+ emit.cat! context.access('_inner_template')
13
+ else
14
+ if kwargs.count == 0
15
+ # {% yield "name" %}
16
+ arg, kw = check_args node,
17
+ :string
18
+
19
+ name, = nvalue(arg)
20
+
21
+ emit.cat! %Q|#{emit.storage}[#{name.inspect}].to_s|
22
+ else
23
+ # {% yield "name" if_none: %} block {% end yield %}
24
+ arg, kw = check_args node,
25
+ :string,
26
+ :if_none => :block
27
+
28
+ name, = nvalue(arg)
29
+
30
+ emit.out! %Q|if #{emit.storage}.include?(#{name.inspect})\n|
31
+ emit.cat! %Q| #{emit.storage}[#{name.inspect}].to_s|
32
+ emit.out! %Q|else\n|
33
+ emit.compile_block kw[:if_none]
34
+ emit.out! %Q|end\n|
35
+ end
36
+ end
37
+ end
38
+
39
+ tag "content_for" do |emit, context, node|
40
+ arg, kw = check_args node,
41
+ :string,
42
+ :"capture" => :block
43
+
44
+ name, = nvalue(arg)
45
+ block = kw[:"capture"]
46
+
47
+ context.nest do
48
+ capture_var_name = emit.capture do
49
+ emit.compile_block block
50
+ end
51
+
52
+ emit.out! %Q|#{emit.storage}[#{name.inspect}] = #{capture_var_name}\n|
53
+ end
54
+ end
55
+
56
+ tag "include" do |emit, context, node|
57
+ arg, kw = check_args node,
58
+ :string
59
+
60
+ name, = nvalue(arg)
61
+
62
+ context.nest do
63
+ manager = context.compiler.manager
64
+
65
+ source = manager.fetch_partial "_#{name}"
66
+ if source.nil?
67
+ raise ArgumentError.new("partial `#{name}' does not exist", nloc(arg))
68
+ elsif source == :syntax_error
69
+ # Do nothing. The manager has already reported the error.
70
+ else
71
+ emit.compile_block source
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end