curly-templates 2.0.1 → 2.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile +2 -0
- data/README.md +85 -5
- data/curly-templates.gemspec +37 -8
- data/lib/curly.rb +1 -1
- data/lib/curly/{attribute_parser.rb → attribute_scanner.rb} +6 -4
- data/lib/curly/compiler.rb +81 -72
- data/lib/curly/component_compiler.rb +37 -31
- data/lib/curly/component_scanner.rb +19 -0
- data/lib/curly/incomplete_block_error.rb +0 -7
- data/lib/curly/incorrect_ending_error.rb +0 -21
- data/lib/curly/parser.rb +171 -0
- data/lib/curly/presenter.rb +1 -1
- data/lib/curly/scanner.rb +23 -9
- data/spec/attribute_scanner_spec.rb +46 -0
- data/spec/collection_blocks_spec.rb +88 -0
- data/spec/compiler/context_blocks_spec.rb +42 -0
- data/spec/component_compiler_spec.rb +26 -77
- data/spec/component_scanner_spec.rb +19 -0
- data/spec/{integration/components_spec.rb → components_spec.rb} +0 -0
- data/spec/{integration/conditional_blocks_spec.rb → conditional_blocks_spec.rb} +0 -0
- data/spec/dummy/.gitignore +1 -0
- data/spec/dummy/app/controllers/application_controller.rb +2 -0
- data/spec/dummy/app/controllers/dashboards_controller.rb +13 -0
- data/spec/dummy/app/helpers/application_helper.rb +5 -0
- data/spec/dummy/app/presenters/dashboards/collection_presenter.rb +7 -0
- data/spec/dummy/app/presenters/dashboards/item_presenter.rb +7 -0
- data/spec/dummy/app/presenters/dashboards/new_presenter.rb +19 -0
- data/spec/dummy/app/presenters/dashboards/partials_presenter.rb +5 -0
- data/spec/dummy/app/presenters/dashboards/show_presenter.rb +12 -0
- data/spec/dummy/app/presenters/layouts/application_presenter.rb +9 -0
- data/spec/dummy/app/views/dashboards/_item.html.curly +1 -0
- data/spec/dummy/app/views/dashboards/collection.html.curly +5 -0
- data/spec/dummy/app/views/dashboards/new.html.curly +3 -0
- data/spec/dummy/app/views/dashboards/partials.html.curly +3 -0
- data/spec/dummy/app/views/dashboards/show.html.curly +3 -0
- data/spec/dummy/app/views/layouts/application.html.curly +8 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +12 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/test.rb +36 -0
- data/spec/dummy/config/routes.rb +6 -0
- data/spec/integration/application_layout_spec.rb +21 -0
- data/spec/integration/collection_blocks_spec.rb +17 -78
- data/spec/integration/context_blocks_spec.rb +21 -0
- data/spec/integration/partials_spec.rb +23 -0
- data/spec/parser_spec.rb +95 -0
- data/spec/scanner_spec.rb +24 -14
- data/spec/spec_helper.rb +4 -3
- metadata +49 -14
- data/lib/curly/component_parser.rb +0 -13
- data/spec/attribute_parser_spec.rb +0 -46
- data/spec/incorrect_ending_error_spec.rb +0 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6cfdffc4b7505394d372817977a7ac58c3eae42d
|
4
|
+
data.tar.gz: 55c4eb68a070f354e165c26895fa8168b9d2e28b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 755ce7ead9b1a2330e7416fb770c343bca17b877fcf582d5d38634ff2a83f7c87f6acfef8ad7bfada134d29a4eb49c4411d058d502c0ef2530325670ff9982b0
|
7
|
+
data.tar.gz: d947e0a11699021badcd38e870a9bb2b59ae0364011e61b87623db3f747c606f1e898052bc51eec6a5fe1129e3b6196db370abf26330dd689ac06516f456b343
|
data/CHANGELOG.md
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -28,6 +28,7 @@ or [Handlebars](http://handlebarsjs.com/), Curly is different in some key ways:
|
|
28
28
|
1. [Attributes](#attributes)
|
29
29
|
1. [Conditional blocks](#conditional-blocks)
|
30
30
|
1. [Collection blocks](#collection-blocks)
|
31
|
+
1. [Context blocks](#context-blocks)
|
31
32
|
1. [Setting up state](#setting-up-state)
|
32
33
|
2. [Escaping Curly syntax](#escaping-curly-syntax)
|
33
34
|
2. [Comments](#comments)
|
@@ -263,6 +264,84 @@ Collection blocks are an alternative to splitting out a separate template and re
|
|
263
264
|
that from the presenter – which solution is best depends on your use case.
|
264
265
|
|
265
266
|
|
267
|
+
### Context blocks
|
268
|
+
|
269
|
+
While collection blocks allow you to define the template that should be used to render
|
270
|
+
items in a collection right within the parent template, **context blocks** allow you
|
271
|
+
to define the template for an arbitrary context. This is very powerful, and can be used
|
272
|
+
to define widget-style components and helpers, and provide an easy way to work with
|
273
|
+
structured data. Let's say you have a comment form on your page, and you'd rather keep
|
274
|
+
the template inline. A simple template could look like:
|
275
|
+
|
276
|
+
```html
|
277
|
+
<!-- post.html.curly -->
|
278
|
+
<h1>{{title}}</h1>
|
279
|
+
{{body}}
|
280
|
+
|
281
|
+
{{@comment_form}}
|
282
|
+
<b>Name: </b> {{name_field}}<br>
|
283
|
+
<b>E-mail: </b> {{email_field}}<br>
|
284
|
+
{{comment_field}}
|
285
|
+
|
286
|
+
{{submit_button}}
|
287
|
+
{{/comment_form}}
|
288
|
+
```
|
289
|
+
|
290
|
+
Note that an `@` character is used to denote a context block. Like with
|
291
|
+
[collection blocks](#collection-blocks), a separate presenter class is used within the
|
292
|
+
block, and a simple convention is used to find it. The name of the context component
|
293
|
+
(in this case, `comment_form`) will be camel cased, and the current presenter's namespace
|
294
|
+
will be searched:
|
295
|
+
|
296
|
+
```ruby
|
297
|
+
class PostPresenter < Curly::Presenter
|
298
|
+
presents :post
|
299
|
+
def title; @post.title; end
|
300
|
+
def body; markdown(@post.body); end
|
301
|
+
|
302
|
+
# A context block method *must* take a block argument. The return value
|
303
|
+
# of the method will be used when rendering. Calling the block argument will
|
304
|
+
# render the nested template. If you pass a value when calling the block
|
305
|
+
# argument it will be passed to the presenter.
|
306
|
+
def comment_form(&block)
|
307
|
+
form_for(Comment.new, &block)
|
308
|
+
end
|
309
|
+
|
310
|
+
# The presenter name is automatically deduced.
|
311
|
+
class CommentFormPresenter < Curly::Presenter
|
312
|
+
# The value passed to the block argument will be passed in a parameter named
|
313
|
+
# after the component.
|
314
|
+
presents :comment_form
|
315
|
+
|
316
|
+
# Any parameters passed to the parent presenter will be forwarded to this
|
317
|
+
# presenter as well.
|
318
|
+
presents :post
|
319
|
+
|
320
|
+
def name_field
|
321
|
+
@comment_form.text_field :name
|
322
|
+
end
|
323
|
+
|
324
|
+
# ...
|
325
|
+
end
|
326
|
+
end
|
327
|
+
```
|
328
|
+
|
329
|
+
Context blocks were designed to work well with Rails' helper methods such as `form_for`
|
330
|
+
and `content_tag`, but you can also work directly with the block. For instance, if you
|
331
|
+
want to directly control the value that is passed to the nested presenter, you can call
|
332
|
+
the `call` method on the block yourself:
|
333
|
+
|
334
|
+
```ruby
|
335
|
+
def author(&block)
|
336
|
+
content_tag :div, class: "author" do
|
337
|
+
# The return value of `call` will be the result of rendering the nested template
|
338
|
+
# with the argument. You can post-process the string if you want.
|
339
|
+
block.call(@post.author)
|
340
|
+
end
|
341
|
+
end
|
342
|
+
```
|
343
|
+
|
344
|
+
|
266
345
|
### Setting up state
|
267
346
|
|
268
347
|
Although most code in Curly presenters should be free of side effects, sometimes side
|
@@ -525,7 +604,7 @@ end
|
|
525
604
|
Static caching will only be enabled for presenters that define a non-nil `#cache_key`
|
526
605
|
method (see [Dynamic Caching.](#dynamic-caching))
|
527
606
|
|
528
|
-
In order to make a deploy expire the cache for a specific view, set the version of the
|
607
|
+
In order to make a deploy expire the cache for a specific view, set the `version` of the
|
529
608
|
view to something new, usually by incrementing by one:
|
530
609
|
|
531
610
|
```ruby
|
@@ -559,7 +638,6 @@ end
|
|
559
638
|
|
560
639
|
class Posts::CommentPresenter < Curly::Presenter
|
561
640
|
version 4
|
562
|
-
depends_on 'posts/comment'
|
563
641
|
|
564
642
|
def cache_key
|
565
643
|
# Some objects
|
@@ -567,11 +645,13 @@ class Posts::CommentPresenter < Curly::Presenter
|
|
567
645
|
end
|
568
646
|
```
|
569
647
|
|
570
|
-
Now, if the version of `Posts::CommentPresenter` is bumped, the cache keys for both
|
648
|
+
Now, if the `version` of `Posts::CommentPresenter` is bumped, the cache keys for both
|
571
649
|
presenters would change. You can register any number of view paths with `depends_on`.
|
572
650
|
|
573
|
-
|
574
|
-
|
651
|
+
Curly integrates well with the
|
652
|
+
[caching mechanism](http://guides.rubyonrails.org/caching_with_rails.html) in Rails 4 (or
|
653
|
+
[Cache Digests](https://github.com/rails/cache_digests) in Rails 3), so the dependencies
|
654
|
+
defined with `depends_on` will be tracked by Rails. This will allow you to deploy changes
|
575
655
|
to your templates and have the relevant caches automatically expire.
|
576
656
|
|
577
657
|
|
data/curly-templates.gemspec
CHANGED
@@ -4,8 +4,8 @@ Gem::Specification.new do |s|
|
|
4
4
|
s.rubygems_version = '1.3.5'
|
5
5
|
|
6
6
|
s.name = 'curly-templates'
|
7
|
-
s.version = '2.0.
|
8
|
-
s.date = '2014-
|
7
|
+
s.version = '2.1.0.beta1'
|
8
|
+
s.date = '2014-11-03'
|
9
9
|
|
10
10
|
s.summary = "Free your views!"
|
11
11
|
s.description = "A view layer for your Rails apps that separates structure and logic."
|
@@ -36,15 +36,16 @@ Gem::Specification.new do |s|
|
|
36
36
|
curly-templates.gemspec
|
37
37
|
lib/curly-templates.rb
|
38
38
|
lib/curly.rb
|
39
|
-
lib/curly/
|
39
|
+
lib/curly/attribute_scanner.rb
|
40
40
|
lib/curly/compiler.rb
|
41
41
|
lib/curly/component_compiler.rb
|
42
|
-
lib/curly/
|
42
|
+
lib/curly/component_scanner.rb
|
43
43
|
lib/curly/dependency_tracker.rb
|
44
44
|
lib/curly/error.rb
|
45
45
|
lib/curly/incomplete_block_error.rb
|
46
46
|
lib/curly/incorrect_ending_error.rb
|
47
47
|
lib/curly/invalid_component.rb
|
48
|
+
lib/curly/parser.rb
|
48
49
|
lib/curly/presenter.rb
|
49
50
|
lib/curly/presenter_not_found.rb
|
50
51
|
lib/curly/railtie.rb
|
@@ -55,15 +56,43 @@ Gem::Specification.new do |s|
|
|
55
56
|
lib/generators/curly/controller/templates/presenter.rb.erb
|
56
57
|
lib/generators/curly/controller/templates/view.html.curly.erb
|
57
58
|
lib/rails/projections.json
|
58
|
-
spec/
|
59
|
+
spec/attribute_scanner_spec.rb
|
60
|
+
spec/collection_blocks_spec.rb
|
59
61
|
spec/compiler/collections_spec.rb
|
62
|
+
spec/compiler/context_blocks_spec.rb
|
60
63
|
spec/compiler_spec.rb
|
61
64
|
spec/component_compiler_spec.rb
|
65
|
+
spec/component_scanner_spec.rb
|
66
|
+
spec/components_spec.rb
|
67
|
+
spec/conditional_blocks_spec.rb
|
68
|
+
spec/dummy/.gitignore
|
69
|
+
spec/dummy/app/controllers/application_controller.rb
|
70
|
+
spec/dummy/app/controllers/dashboards_controller.rb
|
71
|
+
spec/dummy/app/helpers/application_helper.rb
|
72
|
+
spec/dummy/app/presenters/dashboards/collection_presenter.rb
|
73
|
+
spec/dummy/app/presenters/dashboards/item_presenter.rb
|
74
|
+
spec/dummy/app/presenters/dashboards/new_presenter.rb
|
75
|
+
spec/dummy/app/presenters/dashboards/partials_presenter.rb
|
76
|
+
spec/dummy/app/presenters/dashboards/show_presenter.rb
|
77
|
+
spec/dummy/app/presenters/layouts/application_presenter.rb
|
78
|
+
spec/dummy/app/views/dashboards/_item.html.curly
|
79
|
+
spec/dummy/app/views/dashboards/collection.html.curly
|
80
|
+
spec/dummy/app/views/dashboards/new.html.curly
|
81
|
+
spec/dummy/app/views/dashboards/partials.html.curly
|
82
|
+
spec/dummy/app/views/dashboards/show.html.curly
|
83
|
+
spec/dummy/app/views/layouts/application.html.curly
|
84
|
+
spec/dummy/config.ru
|
85
|
+
spec/dummy/config/application.rb
|
86
|
+
spec/dummy/config/boot.rb
|
87
|
+
spec/dummy/config/environment.rb
|
88
|
+
spec/dummy/config/environments/test.rb
|
89
|
+
spec/dummy/config/routes.rb
|
62
90
|
spec/generators/controller_generator_spec.rb
|
63
|
-
spec/
|
91
|
+
spec/integration/application_layout_spec.rb
|
64
92
|
spec/integration/collection_blocks_spec.rb
|
65
|
-
spec/integration/
|
66
|
-
spec/integration/
|
93
|
+
spec/integration/context_blocks_spec.rb
|
94
|
+
spec/integration/partials_spec.rb
|
95
|
+
spec/parser_spec.rb
|
67
96
|
spec/presenter_spec.rb
|
68
97
|
spec/scanner_spec.rb
|
69
98
|
spec/spec_helper.rb
|
data/lib/curly.rb
CHANGED
@@ -1,17 +1,19 @@
|
|
1
|
+
require 'curly/error'
|
2
|
+
|
1
3
|
module Curly
|
2
4
|
AttributeError = Class.new(Curly::Error)
|
3
5
|
|
4
|
-
class
|
5
|
-
def self.
|
6
|
+
class AttributeScanner
|
7
|
+
def self.scan(string)
|
6
8
|
return {} if string.nil?
|
7
|
-
new(string).
|
9
|
+
new(string).scan
|
8
10
|
end
|
9
11
|
|
10
12
|
def initialize(string)
|
11
13
|
@scanner = StringScanner.new(string)
|
12
14
|
end
|
13
15
|
|
14
|
-
def
|
16
|
+
def scan
|
15
17
|
attributes = scan_attributes
|
16
18
|
Hash[attributes]
|
17
19
|
end
|
data/lib/curly/compiler.rb
CHANGED
@@ -1,10 +1,8 @@
|
|
1
1
|
require 'curly/scanner'
|
2
|
+
require 'curly/parser'
|
2
3
|
require 'curly/component_compiler'
|
3
|
-
require 'curly/component_parser'
|
4
4
|
require 'curly/error'
|
5
5
|
require 'curly/invalid_component'
|
6
|
-
require 'curly/incorrect_ending_error'
|
7
|
-
require 'curly/incomplete_block_error'
|
8
6
|
|
9
7
|
module Curly
|
10
8
|
|
@@ -26,7 +24,16 @@ module Curly
|
|
26
24
|
# Raises IncompleteBlockError if a block is not completed.
|
27
25
|
# Returns a String containing the Ruby code.
|
28
26
|
def self.compile(template, presenter_class)
|
29
|
-
|
27
|
+
if presenter_class.nil?
|
28
|
+
raise ArgumentError, "presenter class cannot be nil"
|
29
|
+
end
|
30
|
+
|
31
|
+
tokens = Scanner.scan(template)
|
32
|
+
nodes = Parser.parse(tokens)
|
33
|
+
|
34
|
+
compiler = new(presenter_class)
|
35
|
+
compiler.compile(nodes)
|
36
|
+
compiler.code
|
30
37
|
end
|
31
38
|
|
32
39
|
# Whether the Curly template is valid. This includes whether all
|
@@ -44,34 +51,22 @@ module Curly
|
|
44
51
|
false
|
45
52
|
end
|
46
53
|
|
47
|
-
|
48
|
-
|
49
|
-
def initialize(template, presenter_class)
|
50
|
-
@template = template
|
54
|
+
def initialize(presenter_class)
|
51
55
|
@presenter_classes = [presenter_class]
|
56
|
+
@parts = []
|
52
57
|
end
|
53
58
|
|
54
|
-
def compile
|
55
|
-
|
56
|
-
|
57
|
-
end
|
58
|
-
|
59
|
-
tokens = Scanner.scan(template)
|
60
|
-
|
61
|
-
@blocks = []
|
62
|
-
|
63
|
-
parts = tokens.map do |type, value|
|
64
|
-
send("compile_#{type}", value)
|
65
|
-
end
|
66
|
-
|
67
|
-
if @blocks.any?
|
68
|
-
raise IncompleteBlockError.new(@blocks.pop)
|
59
|
+
def compile(nodes)
|
60
|
+
nodes.each do |node|
|
61
|
+
send("compile_#{node.type}", node)
|
69
62
|
end
|
63
|
+
end
|
70
64
|
|
65
|
+
def code
|
71
66
|
<<-RUBY
|
72
67
|
buffer = ActiveSupport::SafeBuffer.new
|
73
68
|
presenters = []
|
74
|
-
#{parts.join("\n")}
|
69
|
+
#{@parts.join("\n")}
|
75
70
|
buffer
|
76
71
|
RUBY
|
77
72
|
end
|
@@ -82,100 +77,114 @@ module Curly
|
|
82
77
|
@presenter_classes.last
|
83
78
|
end
|
84
79
|
|
85
|
-
def
|
86
|
-
compile_conditional_block
|
80
|
+
def compile_conditional(block)
|
81
|
+
compile_conditional_block("if", block)
|
87
82
|
end
|
88
83
|
|
89
|
-
def
|
90
|
-
compile_conditional_block
|
84
|
+
def compile_inverse_conditional(block)
|
85
|
+
compile_conditional_block("unless", block)
|
91
86
|
end
|
92
87
|
|
93
|
-
def
|
94
|
-
|
95
|
-
method_call = ComponentCompiler.
|
88
|
+
def compile_collection(block)
|
89
|
+
component = block.component
|
90
|
+
method_call = ComponentCompiler.compile(presenter_class, component)
|
96
91
|
|
97
|
-
|
98
|
-
counter = "#{
|
92
|
+
name = component.name.singularize
|
93
|
+
counter = "#{name}_counter"
|
99
94
|
|
100
95
|
begin
|
101
|
-
item_presenter_class = presenter_class.presenter_for_name(
|
96
|
+
item_presenter_class = presenter_class.presenter_for_name(name)
|
102
97
|
rescue NameError
|
103
98
|
raise Curly::Error,
|
104
|
-
"cannot enumerate `#{
|
99
|
+
"cannot enumerate `#{name}`, could not find matching presenter class"
|
105
100
|
end
|
106
101
|
|
107
|
-
|
108
|
-
@presenter_classes.push(item_presenter_class)
|
109
|
-
|
110
|
-
<<-RUBY
|
102
|
+
output <<-RUBY
|
111
103
|
presenters << presenter
|
112
104
|
items = Array(#{method_call})
|
113
105
|
items.each_with_index do |item, index|
|
114
|
-
item_options = options.merge(:#{
|
106
|
+
item_options = options.merge(:#{name} => item, :#{counter} => index + 1)
|
115
107
|
presenter = #{item_presenter_class}.new(self, item_options)
|
116
108
|
RUBY
|
109
|
+
|
110
|
+
@presenter_classes.push(item_presenter_class)
|
111
|
+
compile(block.nodes)
|
112
|
+
@presenter_classes.pop
|
113
|
+
|
114
|
+
output <<-RUBY
|
115
|
+
end
|
116
|
+
presenter = presenters.pop
|
117
|
+
RUBY
|
117
118
|
end
|
118
119
|
|
119
|
-
def compile_conditional_block(keyword,
|
120
|
-
|
121
|
-
method_call = ComponentCompiler.
|
120
|
+
def compile_conditional_block(keyword, block)
|
121
|
+
component = block.component
|
122
|
+
method_call = ComponentCompiler.compile(presenter_class, component)
|
122
123
|
|
123
|
-
|
124
|
+
unless component.name.end_with?("?")
|
125
|
+
raise Curly::Error, "conditional components must end with `?`"
|
126
|
+
end
|
124
127
|
|
125
|
-
<<-RUBY
|
128
|
+
output <<-RUBY
|
126
129
|
#{keyword} #{method_call}
|
127
130
|
RUBY
|
128
|
-
end
|
129
131
|
|
130
|
-
|
131
|
-
validate_block_end(component)
|
132
|
+
compile(block.nodes)
|
132
133
|
|
133
|
-
<<-RUBY
|
134
|
+
output <<-RUBY
|
134
135
|
end
|
135
136
|
RUBY
|
136
137
|
end
|
137
138
|
|
138
|
-
def
|
139
|
+
def compile_context(block)
|
140
|
+
component = block.component
|
141
|
+
method_call = ComponentCompiler.compile(presenter_class, component, type: block.type)
|
142
|
+
|
143
|
+
name = component.name
|
144
|
+
|
145
|
+
begin
|
146
|
+
item_presenter_class = presenter_class.presenter_for_name(name)
|
147
|
+
rescue NameError
|
148
|
+
raise Curly::Error,
|
149
|
+
"cannot use context `#{name}`, could not find matching presenter class"
|
150
|
+
end
|
151
|
+
|
152
|
+
output <<-RUBY
|
153
|
+
presenters << presenter
|
154
|
+
old_buffer, buffer = buffer, ActiveSupport::SafeBuffer.new
|
155
|
+
old_buffer << #{method_call} do |item|
|
156
|
+
item_options = options.merge(:#{name} => item)
|
157
|
+
presenter = #{item_presenter_class}.new(self, item_options.with_indifferent_access)
|
158
|
+
RUBY
|
159
|
+
|
160
|
+
@presenter_classes.push(item_presenter_class)
|
161
|
+
compile(block.nodes)
|
139
162
|
@presenter_classes.pop
|
140
|
-
validate_block_end(component)
|
141
163
|
|
142
|
-
<<-RUBY
|
164
|
+
output <<-RUBY
|
143
165
|
end
|
166
|
+
buffer = old_buffer
|
144
167
|
presenter = presenters.pop
|
145
168
|
RUBY
|
146
169
|
end
|
147
170
|
|
148
171
|
def compile_component(component)
|
149
|
-
|
150
|
-
method_call = ComponentCompiler.compile_component(presenter_class, name, identifier, attributes)
|
172
|
+
method_call = ComponentCompiler.compile(presenter_class, component)
|
151
173
|
code = "#{method_call} {|*args| yield(*args) }"
|
152
174
|
|
153
|
-
"buffer.concat(#{code.strip}.to_s)"
|
175
|
+
output "buffer.concat(#{code.strip}.to_s)"
|
154
176
|
end
|
155
177
|
|
156
178
|
def compile_text(text)
|
157
|
-
"buffer.safe_concat(#{text.inspect})"
|
179
|
+
output "buffer.safe_concat(#{text.value.inspect})"
|
158
180
|
end
|
159
181
|
|
160
182
|
def compile_comment(comment)
|
161
|
-
|
162
|
-
end
|
163
|
-
|
164
|
-
def validate_block_end(component)
|
165
|
-
name, identifier, attributes = ComponentParser.parse(component)
|
166
|
-
last_block = @blocks.pop
|
167
|
-
|
168
|
-
if last_block.nil?
|
169
|
-
raise Curly::Error, "block ending not expected"
|
170
|
-
end
|
171
|
-
|
172
|
-
unless last_block == [name, identifier]
|
173
|
-
raise Curly::IncorrectEndingError.new([name, identifier], last_block)
|
174
|
-
end
|
183
|
+
# Do nothing.
|
175
184
|
end
|
176
185
|
|
177
|
-
def
|
178
|
-
@
|
186
|
+
def output(code)
|
187
|
+
@parts << code
|
179
188
|
end
|
180
189
|
end
|
181
190
|
end
|