curly-templates 2.3.2 → 2.4.0
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +41 -10
- data/curly-templates.gemspec +2 -2
- data/lib/curly.rb +1 -1
- data/lib/curly/compiler.rb +2 -2
- data/lib/curly/component_scanner.rb +5 -2
- data/lib/curly/parser.rb +31 -7
- data/lib/curly/presenter.rb +10 -0
- data/lib/curly/scanner.rb +12 -12
- data/spec/compiler/collections_spec.rb +37 -9
- data/spec/compiler/context_blocks_spec.rb +55 -0
- data/spec/component_scanner_spec.rb +14 -3
- data/spec/dummy/app/presenters/layouts/application_presenter.rb +12 -0
- data/spec/dummy/app/views/layouts/application.html.curly +3 -0
- data/spec/integration/application_layout_spec.rb +3 -0
- data/spec/integration/collection_blocks_spec.rb +3 -0
- data/spec/integration/context_blocks_spec.rb +3 -0
- data/spec/integration/partials_spec.rb +3 -0
- data/spec/presenter_spec.rb +25 -0
- data/spec/scanner_spec.rb +13 -13
- data/spec/spec_helper.rb +16 -9
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8f4fe4c21493bebaaa569720e2a43f78e2d80438
|
4
|
+
data.tar.gz: 24606b4dc84bf910492ae70e9264c4887c463b6e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0213f00bcaf7842aa05ef052dd6ceb74b7b4c03b7ee7cfe546df3ba4e77cc73d57b2c5c5d46a89544567e6ec5fcfe98d2b9297953ffed8eb0a0abceb64aba96b
|
7
|
+
data.tar.gz: 231542411d1447d7b2139f4637f1d40014e14ee19b1313bf2f05c65ff17608faf658f26750024373ff03d3cae179d57989c9716480dd43b836e36dca41652187
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
### Unreleased
|
2
2
|
|
3
|
+
### Curly 2.4.0 (February 24, 2015)
|
4
|
+
|
5
|
+
* Add an `exposes_helper` class methods to Curly::Presenter. This allows exposing
|
6
|
+
helper methods as components.
|
7
|
+
|
8
|
+
*Jeremy Rodi*
|
9
|
+
|
10
|
+
* Add a shorthand syntax for using components within a context. This allows you
|
11
|
+
to write `{{author:name}}` rather than `{{@author}}{{name}}{{/author}}`.
|
12
|
+
|
13
|
+
*Daniel Schierbeck*
|
14
|
+
|
3
15
|
### Curly 2.3.2 (January 13, 2015)
|
4
16
|
|
5
17
|
* Fix an issue that caused presenter parameters to get mixed up.
|
data/README.md
CHANGED
@@ -11,16 +11,17 @@ to a presenter class.
|
|
11
11
|
1. [Installing](#installing)
|
12
12
|
2. [How to use Curly](#how-to-use-curly)
|
13
13
|
1. [Identifiers](#identifiers)
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
2. [Attributes](#attributes)
|
15
|
+
3. [Conditional blocks](#conditional-blocks)
|
16
|
+
4. [Collection blocks](#collection-blocks)
|
17
|
+
5. [Context blocks](#context-blocks)
|
18
|
+
6. [Setting up state](#setting-up-state)
|
19
|
+
7. [Escaping Curly syntax](#escaping-curly-syntax)
|
20
|
+
8. [Comments](#comments)
|
21
21
|
3. [Presenters](#presenters)
|
22
|
-
1. [Layouts and
|
23
|
-
2. [
|
22
|
+
1. [Layouts and content blocks](#layouts-and-content-blocks)
|
23
|
+
2. [Rails helper methods](#rails-helper-methods)
|
24
|
+
3. [Examples](#examples)
|
24
25
|
4. [Caching](#caching)
|
25
26
|
|
26
27
|
|
@@ -327,6 +328,22 @@ def author(&block)
|
|
327
328
|
end
|
328
329
|
```
|
329
330
|
|
331
|
+
#### Context shorthand syntax
|
332
|
+
|
333
|
+
If you find yourself opening a context block just in order to use a single component,
|
334
|
+
e.g. `{{@author}}{{name}}{{/author}}`, you can use the _shorthand syntax_ instead:
|
335
|
+
`{{author:name}}`. This works for all component types, e.g.
|
336
|
+
|
337
|
+
```html
|
338
|
+
{{#author:admin?}}
|
339
|
+
<p>The author is an admin!</p>
|
340
|
+
{{/author:admin?}}
|
341
|
+
```
|
342
|
+
|
343
|
+
The syntax works for nested contexts as well, e.g. `{{comment:author:name}}`. Any
|
344
|
+
identifier and attributes are passed to the target component, which in this example
|
345
|
+
would be `{{name}}`.
|
346
|
+
|
330
347
|
|
331
348
|
### Setting up state
|
332
349
|
|
@@ -439,7 +456,7 @@ have any side effects, though, as an important part of Curly is the idempotence
|
|
439
456
|
templates.
|
440
457
|
|
441
458
|
|
442
|
-
### Layouts and
|
459
|
+
### Layouts and content blocks
|
443
460
|
|
444
461
|
Both layouts and content blocks (see [`content_for`](http://api.rubyonrails.org/classes/ActionView/Helpers/CaptureHelper.html#method-i-content_for))
|
445
462
|
use `yield` to signal that content can be inserted. Curly works just like ERB, so calling
|
@@ -478,6 +495,19 @@ end
|
|
478
495
|
```
|
479
496
|
|
480
497
|
|
498
|
+
### Rails helper methods
|
499
|
+
|
500
|
+
In order to make a Rails helper method available as a component in your template,
|
501
|
+
use the `exposes_helper` method:
|
502
|
+
|
503
|
+
```ruby
|
504
|
+
class Layouts::ApplicationPresenter < Curly::Presenter
|
505
|
+
# The components {{sign_in_path}} and {{root_path}} are made available.
|
506
|
+
exposes_helper :sign_in_path, :root_path
|
507
|
+
end
|
508
|
+
```
|
509
|
+
|
510
|
+
|
481
511
|
### Examples
|
482
512
|
|
483
513
|
Here is a simple Curly template – it will be looked up by Rails automatically.
|
@@ -667,6 +697,7 @@ Thanks to [Zendesk](http://zendesk.com/) for sponsoring the work on Curly.
|
|
667
697
|
- Jeremy Rodi ([@redjazz96](https://github.com/redjazz96))
|
668
698
|
- Alisson Cavalcante Agiani ([@thelinuxlich](https://github.com/thelinuxlich))
|
669
699
|
- Łukasz Niemier ([@hauleth](https://github.com/hauleth))
|
700
|
+
- Cristian Planas ([@Gawyn](https://github.com/Gawyn))
|
670
701
|
|
671
702
|
|
672
703
|
Build Status
|
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.
|
8
|
-
s.date = '2015-
|
7
|
+
s.version = '2.4.0'
|
8
|
+
s.date = '2015-02-24'
|
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."
|
data/lib/curly.rb
CHANGED
data/lib/curly/compiler.rb
CHANGED
@@ -107,7 +107,7 @@ module Curly
|
|
107
107
|
items = Array(#{method_call})
|
108
108
|
items.each_with_index do |item, index|
|
109
109
|
options = options.merge("#{name}" => item, "#{counter}" => index + 1)
|
110
|
-
presenter =
|
110
|
+
presenter = ::#{item_presenter_class}.new(self, options)
|
111
111
|
RUBY
|
112
112
|
|
113
113
|
@presenter_classes.push(item_presenter_class)
|
@@ -160,7 +160,7 @@ module Curly
|
|
160
160
|
buffer << #{method_call} do |item|
|
161
161
|
options = options.merge("#{name}" => item)
|
162
162
|
buffer = ActiveSupport::SafeBuffer.new
|
163
|
-
presenter =
|
163
|
+
presenter = ::#{item_presenter_class}.new(self, options)
|
164
164
|
RUBY
|
165
165
|
|
166
166
|
@presenter_classes.push(item_presenter_class)
|
@@ -4,7 +4,10 @@ module Curly
|
|
4
4
|
class ComponentScanner
|
5
5
|
def self.scan(component)
|
6
6
|
first, rest = component.strip.split(/\s+/, 2)
|
7
|
-
|
7
|
+
contexts = first.split(":")
|
8
|
+
name_and_identifier = contexts.pop
|
9
|
+
|
10
|
+
name, identifier = name_and_identifier.split(".", 2)
|
8
11
|
|
9
12
|
if identifier && identifier.end_with?("?")
|
10
13
|
name += "?"
|
@@ -13,7 +16,7 @@ module Curly
|
|
13
16
|
|
14
17
|
attributes = AttributeScanner.scan(rest)
|
15
18
|
|
16
|
-
[name, identifier, attributes]
|
19
|
+
[name, identifier, attributes, contexts]
|
17
20
|
end
|
18
21
|
end
|
19
22
|
end
|
data/lib/curly/parser.rb
CHANGED
@@ -3,20 +3,21 @@ require 'curly/incorrect_ending_error'
|
|
3
3
|
|
4
4
|
class Curly::Parser
|
5
5
|
class Component
|
6
|
-
attr_reader :name, :identifier, :attributes
|
6
|
+
attr_reader :name, :identifier, :attributes, :contexts
|
7
7
|
|
8
|
-
def initialize(name, identifier = nil, attributes = {})
|
9
|
-
@name, @identifier, @attributes = name, identifier, attributes
|
8
|
+
def initialize(name, identifier = nil, attributes = {}, contexts = [])
|
9
|
+
@name, @identifier, @attributes, @contexts = name, identifier, attributes, contexts
|
10
10
|
end
|
11
11
|
|
12
12
|
def to_s
|
13
|
-
[name, identifier].compact.join(".")
|
13
|
+
contexts.map {|c| c + ":" }.join << [name, identifier].compact.join(".")
|
14
14
|
end
|
15
15
|
|
16
16
|
def ==(other)
|
17
17
|
other.name == name &&
|
18
18
|
other.identifier == identifier &&
|
19
|
-
other.attributes == attributes
|
19
|
+
other.attributes == attributes &&
|
20
|
+
other.contexts == contexts
|
20
21
|
end
|
21
22
|
|
22
23
|
def type
|
@@ -77,7 +78,8 @@ class Curly::Parser
|
|
77
78
|
|
78
79
|
def closed_by?(component)
|
79
80
|
self.component.name == component.name &&
|
80
|
-
self.component.identifier == component.identifier
|
81
|
+
self.component.identifier == component.identifier &&
|
82
|
+
self.component.contexts == component.contexts
|
81
83
|
end
|
82
84
|
|
83
85
|
def to_s
|
@@ -125,7 +127,20 @@ class Curly::Parser
|
|
125
127
|
end
|
126
128
|
|
127
129
|
def parse_component(*args)
|
128
|
-
|
130
|
+
component = Component.new(*args)
|
131
|
+
|
132
|
+
# If the component is namespaced by a list of context names, open a context
|
133
|
+
# block for each.
|
134
|
+
component.contexts.each do |context|
|
135
|
+
parse_context_block_start(context)
|
136
|
+
end
|
137
|
+
|
138
|
+
tree << component
|
139
|
+
|
140
|
+
# Close each context block in the namespace.
|
141
|
+
component.contexts.reverse.each do |context|
|
142
|
+
parse_block_end(context)
|
143
|
+
end
|
129
144
|
end
|
130
145
|
|
131
146
|
def parse_conditional_block_start(*args)
|
@@ -146,6 +161,11 @@ class Curly::Parser
|
|
146
161
|
|
147
162
|
def parse_block(type, *args)
|
148
163
|
component = Component.new(*args)
|
164
|
+
|
165
|
+
component.contexts.each do |context|
|
166
|
+
parse_context_block_start(context)
|
167
|
+
end
|
168
|
+
|
149
169
|
block = Block.new(type, component)
|
150
170
|
tree << block
|
151
171
|
@stack.push(block)
|
@@ -159,6 +179,10 @@ class Curly::Parser
|
|
159
179
|
raise Curly::IncorrectEndingError,
|
160
180
|
"block `#{block}` cannot be closed by `#{component}`"
|
161
181
|
end
|
182
|
+
|
183
|
+
component.contexts.reverse.each do |context|
|
184
|
+
parse_block_end(context)
|
185
|
+
end
|
162
186
|
end
|
163
187
|
|
164
188
|
def parse_comment(comment)
|
data/lib/curly/presenter.rb
CHANGED
@@ -292,6 +292,16 @@ module Curly
|
|
292
292
|
self.default_values = self.default_values.merge(default_values)
|
293
293
|
end
|
294
294
|
end
|
295
|
+
|
296
|
+
def exposes_helper(*methods)
|
297
|
+
methods.each do |method_name|
|
298
|
+
define_method(method_name) do |*args|
|
299
|
+
@_context.public_send(method_name, *args)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
alias_method :exposes_helpers, :exposes_helper
|
295
305
|
end
|
296
306
|
|
297
307
|
private
|
data/lib/curly/scanner.rb
CHANGED
@@ -95,45 +95,45 @@ module Curly
|
|
95
95
|
|
96
96
|
def scan_conditional_block_start
|
97
97
|
if value = scan_until_end_of_curly
|
98
|
-
name, identifier, attributes = ComponentScanner.scan(value)
|
98
|
+
name, identifier, attributes, contexts = ComponentScanner.scan(value)
|
99
99
|
|
100
|
-
[:conditional_block_start, name, identifier, attributes]
|
100
|
+
[:conditional_block_start, name, identifier, attributes, contexts]
|
101
101
|
end
|
102
102
|
end
|
103
103
|
|
104
104
|
def scan_context_block_start
|
105
105
|
if value = scan_until_end_of_curly
|
106
|
-
name, identifier, attributes = ComponentScanner.scan(value)
|
106
|
+
name, identifier, attributes, contexts = ComponentScanner.scan(value)
|
107
107
|
|
108
|
-
[:context_block_start, name, identifier, attributes]
|
108
|
+
[:context_block_start, name, identifier, attributes, contexts]
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
112
112
|
def scan_collection_block_start
|
113
113
|
if value = scan_until_end_of_curly
|
114
|
-
name, identifier, attributes = ComponentScanner.scan(value)
|
115
|
-
[:collection_block_start, name, identifier, attributes]
|
114
|
+
name, identifier, attributes, contexts = ComponentScanner.scan(value)
|
115
|
+
[:collection_block_start, name, identifier, attributes, contexts]
|
116
116
|
end
|
117
117
|
end
|
118
118
|
|
119
119
|
def scan_inverse_block_start
|
120
120
|
if value = scan_until_end_of_curly
|
121
|
-
name, identifier, attributes = ComponentScanner.scan(value)
|
122
|
-
[:inverse_conditional_block_start, name, identifier, attributes]
|
121
|
+
name, identifier, attributes, contexts = ComponentScanner.scan(value)
|
122
|
+
[:inverse_conditional_block_start, name, identifier, attributes, contexts]
|
123
123
|
end
|
124
124
|
end
|
125
125
|
|
126
126
|
def scan_block_end
|
127
127
|
if value = scan_until_end_of_curly
|
128
|
-
name, identifier, attributes = ComponentScanner.scan(value)
|
129
|
-
[:block_end, name, identifier]
|
128
|
+
name, identifier, attributes, contexts = ComponentScanner.scan(value)
|
129
|
+
[:block_end, name, identifier, {}, contexts]
|
130
130
|
end
|
131
131
|
end
|
132
132
|
|
133
133
|
def scan_component
|
134
134
|
if value = scan_until_end_of_curly
|
135
|
-
name, identifier, attributes = ComponentScanner.scan(value)
|
136
|
-
[:component, name, identifier, attributes]
|
135
|
+
name, identifier, attributes, contexts = ComponentScanner.scan(value)
|
136
|
+
[:component, name, identifier, attributes, contexts]
|
137
137
|
end
|
138
138
|
end
|
139
139
|
|
@@ -19,7 +19,8 @@ describe Curly::Compiler do
|
|
19
19
|
item2 = double("item2", name: "bar")
|
20
20
|
|
21
21
|
template = "<ul>{{*items}}<li>{{name}}</li>{{/items}}</ul>"
|
22
|
-
expect(render(template, items: [item1, item2])).
|
22
|
+
expect(render(template, locals: { items: [item1, item2] })).
|
23
|
+
to eql "<ul><li>foo</li><li>bar</li></ul>"
|
23
24
|
end
|
24
25
|
|
25
26
|
it "allows attributes on collection blocks" do
|
@@ -39,7 +40,8 @@ describe Curly::Compiler do
|
|
39
40
|
item2 = double("item2", name: "bar", status: "inactive")
|
40
41
|
|
41
42
|
template = "<ul>{{*items status=active}}<li>{{name}}</li>{{/items}}</ul>"
|
42
|
-
expect(render(template, items: [item1, item2])).
|
43
|
+
expect(render(template, locals: { items: [item1, item2] })).
|
44
|
+
to eql "<ul><li>foo</li></ul>"
|
43
45
|
end
|
44
46
|
|
45
47
|
it "fails if the component doesn't support enumeration" do
|
@@ -84,7 +86,8 @@ describe Curly::Compiler do
|
|
84
86
|
item2 = double("item2")
|
85
87
|
|
86
88
|
template = "<ul>{{*items}}<li>{{index}}</li>{{/items}}</ul>"
|
87
|
-
expect(render(template, items: [item1, item2])).
|
89
|
+
expect(render(template, locals: { items: [item1, item2] })).
|
90
|
+
to eql "<ul><li>1</li><li>2</li></ul>"
|
88
91
|
end
|
89
92
|
|
90
93
|
it "restores the previous scope after exiting the collection block" do
|
@@ -111,7 +114,8 @@ describe Curly::Compiler do
|
|
111
114
|
item = double("item", name: "foo", parts: [part])
|
112
115
|
|
113
116
|
template = "{{*items}}{{*parts}}{{identifier}}{{/parts}}{{name}}{{/items}}{{title}}"
|
114
|
-
expect(render(template, items: [item])).
|
117
|
+
expect(render(template, locals: { items: [item] })).
|
118
|
+
to eql "XfooInventory"
|
115
119
|
end
|
116
120
|
|
117
121
|
it "passes the parent presenter's options to the nested presenter" do
|
@@ -130,7 +134,8 @@ describe Curly::Compiler do
|
|
130
134
|
item2 = double(name: "bar")
|
131
135
|
|
132
136
|
template = "{{*items}}{{prefix}}: {{name}}; {{/items}}"
|
133
|
-
expect(render(template, prefix: "SKU", items: [item1, item2])).
|
137
|
+
expect(render(template, locals: { prefix: "SKU", items: [item1, item2] })).
|
138
|
+
to eql "SKU: foo; SKU: bar; "
|
134
139
|
end
|
135
140
|
|
136
141
|
it "compiles nested collection blocks" do
|
@@ -153,7 +158,8 @@ describe Curly::Compiler do
|
|
153
158
|
item2 = double("item2", name: "item2", parts: [double(identifier: "C"), double(identifier: "D")])
|
154
159
|
|
155
160
|
template = "{{*items}}{{name}}: {{*parts}}{{identifier}}{{/parts}}; {{/items}}"
|
156
|
-
expect(render(template, items: [item1, item2])).
|
161
|
+
expect(render(template, locals: { items: [item1, item2] })).
|
162
|
+
to eql "item1: AB; item2: CD; "
|
157
163
|
end
|
158
164
|
end
|
159
165
|
|
@@ -169,7 +175,7 @@ describe Curly::Compiler do
|
|
169
175
|
end
|
170
176
|
|
171
177
|
def comment(&block)
|
172
|
-
block.call("
|
178
|
+
block.call("foo!")
|
173
179
|
end
|
174
180
|
|
175
181
|
def form(&block)
|
@@ -190,13 +196,35 @@ describe Curly::Compiler do
|
|
190
196
|
it "allows re-using assign names in collection blocks" do
|
191
197
|
options = { "comment" => "first post!" }
|
192
198
|
template = "{{*comments}}{{/comments}}{{@form}}{{comment}}{{/form}}"
|
193
|
-
expect(render(template, options)).to eql "first post!"
|
199
|
+
expect(render(template, locals: options)).to eql "first post!"
|
194
200
|
end
|
195
201
|
|
196
202
|
it "allows re-using assign names in context blocks" do
|
197
203
|
options = { "comment" => "first post!" }
|
198
204
|
template = "{{@comment}}{{/comment}}{{@form}}{{comment}}{{/form}}"
|
199
|
-
expect(render(template, options)).to eql "first post!"
|
205
|
+
expect(render(template, locals: options)).to eql "first post!"
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
context "using namespaced names" do
|
210
|
+
before do
|
211
|
+
define_presenter "Layouts::ShowPresenter" do
|
212
|
+
def comments
|
213
|
+
["hello", "world"]
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
define_presenter "Layouts::ShowPresenter::CommentPresenter" do
|
218
|
+
presents :comment
|
219
|
+
|
220
|
+
attr_reader :comment
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
it "renders correctly" do
|
225
|
+
template = "{{*comments}}{{comment}} {{/comments}}"
|
226
|
+
expect(render(template, presenter: "Layouts::ShowPresenter")).
|
227
|
+
to eql "hello world "
|
200
228
|
end
|
201
229
|
end
|
202
230
|
end
|
@@ -22,11 +22,34 @@ describe Curly::Compiler do
|
|
22
22
|
def field
|
23
23
|
%(<input type="text" value="#{@text_field.upcase}">).html_safe
|
24
24
|
end
|
25
|
+
|
26
|
+
def value?
|
27
|
+
true
|
28
|
+
end
|
25
29
|
end
|
26
30
|
|
27
31
|
render('{{@form}}{{@text_field}}{{field}}{{/text_field}}{{/form}}').should == '<form><input type="text" value="YO"></form>'
|
28
32
|
end
|
29
33
|
|
34
|
+
it "compiles using the right presenter" do
|
35
|
+
define_presenter "Layouts::SomePresenter" do
|
36
|
+
|
37
|
+
def contents(&block)
|
38
|
+
block.call("hello, world")
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
define_presenter "Layouts::SomePresenter::ContentsPresenter" do
|
43
|
+
presents :contents
|
44
|
+
|
45
|
+
def contents
|
46
|
+
@contents
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
render("foo: {{@contents}}{{contents}}{{/contents}}", presenter: "Layouts::SomePresenter").should == 'foo: hello, world'
|
51
|
+
end
|
52
|
+
|
30
53
|
it "fails if the component is not a context block" do
|
31
54
|
define_presenter do
|
32
55
|
def form
|
@@ -48,4 +71,36 @@ describe Curly::Compiler do
|
|
48
71
|
render('{{@dust}}{{/dust}}')
|
49
72
|
}.to raise_exception(Curly::Error)
|
50
73
|
end
|
74
|
+
|
75
|
+
it "fails if the component is not a context block" do
|
76
|
+
expect { render('{{@invalid}}yo{{/invalid}}') }.to raise_exception(Curly::Error)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "compiles shorthand context components" do
|
80
|
+
define_presenter do
|
81
|
+
def tree(&block)
|
82
|
+
yield
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
define_presenter "TreePresenter" do
|
87
|
+
def branch(&block)
|
88
|
+
yield
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
define_presenter "BranchPresenter" do
|
93
|
+
def leaf
|
94
|
+
"leaf"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
render('{{tree:branch:leaf}}').should == "leaf"
|
99
|
+
end
|
100
|
+
|
101
|
+
it "requires shorthand blocks to be closed with the same set of namespaces" do
|
102
|
+
expect do
|
103
|
+
render('{{#tree:branch}}{{/branch}}{{/tree}}')
|
104
|
+
end.to raise_exception(Curly::IncorrectEndingError)
|
105
|
+
end
|
51
106
|
end
|
@@ -3,19 +3,30 @@ describe Curly::ComponentScanner do
|
|
3
3
|
scan('hello.world weather="sunny"').should == [
|
4
4
|
"hello",
|
5
5
|
"world",
|
6
|
-
{ "weather" => "sunny" }
|
6
|
+
{ "weather" => "sunny" },
|
7
|
+
[]
|
8
|
+
]
|
9
|
+
end
|
10
|
+
|
11
|
+
it "allows context namespaces" do
|
12
|
+
scan('island:beach:hello.world').should == [
|
13
|
+
"hello",
|
14
|
+
"world",
|
15
|
+
{},
|
16
|
+
["island", "beach"]
|
7
17
|
]
|
8
18
|
end
|
9
19
|
|
10
20
|
it "allows a question mark after the identifier" do
|
11
|
-
scan('hello.world?').should == ["hello?", "world", {}]
|
21
|
+
scan('hello.world?').should == ["hello?", "world", {}, []]
|
12
22
|
end
|
13
23
|
|
14
24
|
it 'allows spaces before and after component' do
|
15
25
|
scan(' hello.world weather="sunny" ').should == [
|
16
26
|
"hello",
|
17
27
|
"world",
|
18
|
-
{ "weather" => "sunny" }
|
28
|
+
{ "weather" => "sunny" },
|
29
|
+
[]
|
19
30
|
]
|
20
31
|
end
|
21
32
|
|
@@ -10,6 +10,9 @@ describe "Context blocks", type: :request do
|
|
10
10
|
<title>Dummy app</title>
|
11
11
|
</head>
|
12
12
|
<body>
|
13
|
+
<header>
|
14
|
+
<h1>Dummy app</h1>
|
15
|
+
</header>
|
13
16
|
<form accept-charset="UTF-8" action="/new" method="post"><input name="utf8" type="hidden" value="✓" />
|
14
17
|
<div class="field">
|
15
18
|
<b>Name</b> <input id="dashboard_name" name="dashboard[name]" type="text" value="test" />
|
data/spec/presenter_spec.rb
CHANGED
@@ -5,6 +5,8 @@ describe Curly::Presenter do
|
|
5
5
|
end
|
6
6
|
end
|
7
7
|
|
8
|
+
exposes_helper :foo
|
9
|
+
|
8
10
|
include MonkeyComponents
|
9
11
|
|
10
12
|
presents :midget, :clown, default: nil
|
@@ -74,6 +76,29 @@ describe Curly::Presenter do
|
|
74
76
|
end
|
75
77
|
end
|
76
78
|
|
79
|
+
describe ".exposes_helper" do
|
80
|
+
let(:context) { double("context") }
|
81
|
+
subject {
|
82
|
+
CircusPresenter.new(context,
|
83
|
+
midget: "Meek Harolson",
|
84
|
+
clown: "Bubbles")
|
85
|
+
}
|
86
|
+
|
87
|
+
it "allows a method as a component" do
|
88
|
+
CircusPresenter.component_available?(:foo)
|
89
|
+
end
|
90
|
+
|
91
|
+
it "delegates the call to the context" do
|
92
|
+
context.should receive(:foo).once
|
93
|
+
subject.should_not receive(:method_missing)
|
94
|
+
subject.foo
|
95
|
+
end
|
96
|
+
|
97
|
+
it "doesn't delegate other calls to the context" do
|
98
|
+
expect { subject.bar }.to raise_error
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
77
102
|
describe ".presenter_for_path" do
|
78
103
|
it "returns the presenter class for the given path" do
|
79
104
|
presenter = double("presenter")
|
data/spec/scanner_spec.rb
CHANGED
@@ -2,14 +2,14 @@ describe Curly::Scanner, ".scan" do
|
|
2
2
|
it "returns the tokens in the source" do
|
3
3
|
scan("foo {{bar}} baz").should == [
|
4
4
|
[:text, "foo "],
|
5
|
-
[:component, "bar", nil, {}],
|
5
|
+
[:component, "bar", nil, {}, []],
|
6
6
|
[:text, " baz"]
|
7
7
|
]
|
8
8
|
end
|
9
9
|
|
10
10
|
it "scans components with identifiers" do
|
11
11
|
scan("{{foo.bar}}").should == [
|
12
|
-
[:component, "foo", "bar", {}]
|
12
|
+
[:component, "foo", "bar", {}, []]
|
13
13
|
]
|
14
14
|
end
|
15
15
|
|
@@ -53,44 +53,44 @@ describe Curly::Scanner, ".scan" do
|
|
53
53
|
|
54
54
|
it "scans context block tags" do
|
55
55
|
scan('{{@search_form}}{{query_field}}{{/search_form}}').should == [
|
56
|
-
[:context_block_start, "search_form", nil, {}],
|
57
|
-
[:component, "query_field", nil, {}],
|
58
|
-
[:block_end, "search_form", nil]
|
56
|
+
[:context_block_start, "search_form", nil, {}, []],
|
57
|
+
[:component, "query_field", nil, {}, []],
|
58
|
+
[:block_end, "search_form", nil, {}, []]
|
59
59
|
]
|
60
60
|
end
|
61
61
|
|
62
62
|
it "scans conditional block tags" do
|
63
63
|
scan('foo {{#bar?}} hello {{/bar?}}').should == [
|
64
64
|
[:text, "foo "],
|
65
|
-
[:conditional_block_start, "bar?", nil, {}],
|
65
|
+
[:conditional_block_start, "bar?", nil, {}, []],
|
66
66
|
[:text, " hello "],
|
67
|
-
[:block_end, "bar?", nil]
|
67
|
+
[:block_end, "bar?", nil, {}, []]
|
68
68
|
]
|
69
69
|
end
|
70
70
|
|
71
71
|
it "scans conditional block tags with parameters and attributes" do
|
72
72
|
scan('{{#active.test? name="test"}}yo{{/active.test?}}').should == [
|
73
|
-
[:conditional_block_start, "active?", "test", { "name" => "test" }],
|
73
|
+
[:conditional_block_start, "active?", "test", { "name" => "test" }, []],
|
74
74
|
[:text, "yo"],
|
75
|
-
[:block_end, "active?", "test"]
|
75
|
+
[:block_end, "active?", "test", {}, []]
|
76
76
|
]
|
77
77
|
end
|
78
78
|
|
79
79
|
it "scans inverse block tags" do
|
80
80
|
scan('foo {{^bar?}} hello {{/bar?}}').should == [
|
81
81
|
[:text, "foo "],
|
82
|
-
[:inverse_conditional_block_start, "bar?", nil, {}],
|
82
|
+
[:inverse_conditional_block_start, "bar?", nil, {}, []],
|
83
83
|
[:text, " hello "],
|
84
|
-
[:block_end, "bar?", nil]
|
84
|
+
[:block_end, "bar?", nil, {}, []]
|
85
85
|
]
|
86
86
|
end
|
87
87
|
|
88
88
|
it "scans collection block tags" do
|
89
89
|
scan('foo {{*bar}} hello {{/bar}}').should == [
|
90
90
|
[:text, "foo "],
|
91
|
-
[:collection_block_start, "bar", nil, {}],
|
91
|
+
[:collection_block_start, "bar", nil, {}, []],
|
92
92
|
[:text, " hello "],
|
93
|
-
[:block_end, "bar", nil]
|
93
|
+
[:block_end, "bar", nil, {}, []]
|
94
94
|
]
|
95
95
|
end
|
96
96
|
|
data/spec/spec_helper.rb
CHANGED
@@ -14,23 +14,30 @@ module CompilationSupport
|
|
14
14
|
presenter_class
|
15
15
|
end
|
16
16
|
|
17
|
-
def render(source,
|
18
|
-
|
19
|
-
unless defined?(ShowPresenter)
|
20
|
-
|
21
|
-
|
17
|
+
def render(source, options = {}, &block)
|
18
|
+
presenter = options.fetch(:presenter) do
|
19
|
+
define_presenter("ShowPresenter") unless defined?(ShowPresenter)
|
20
|
+
"ShowPresenter"
|
21
|
+
end.constantize
|
22
22
|
|
23
|
-
|
23
|
+
virtual_path = options.fetch(:virtual_path) do
|
24
|
+
presenter.name.underscore.gsub(/_presenter\z/, "")
|
24
25
|
end
|
25
26
|
|
26
|
-
identifier =
|
27
|
+
identifier = options.fetch(:identifier) do
|
28
|
+
defined?(Rails.root) ? "#{Rails.root}/#{virtual_path}.html.curly" : virtual_path
|
29
|
+
end
|
30
|
+
|
31
|
+
details = { virtual_path: virtual_path }
|
32
|
+
details.merge! options.fetch(:details, {})
|
33
|
+
|
27
34
|
handler = Curly::TemplateHandler
|
28
|
-
details = { virtual_path: 'show' }
|
29
35
|
template = ActionView::Template.new(source, identifier, handler, details)
|
30
36
|
view = ActionView::Base.new
|
37
|
+
view.lookup_context.stub(:find_template) { source }
|
31
38
|
|
32
39
|
begin
|
33
|
-
template.render(view, locals, &block)
|
40
|
+
template.render(view, options.fetch(:locals, {}), &block)
|
34
41
|
rescue ActionView::Template::Error => e
|
35
42
|
raise e.original_exception
|
36
43
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: curly-templates
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Schierbeck
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-02-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|