curly-templates 2.3.2 → 2.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d5d6cde1c07741229dc8ea78114e54af3f8203c1
4
- data.tar.gz: c06295601f10d2b265334ef2e3a67255fd00b3f6
3
+ metadata.gz: 8f4fe4c21493bebaaa569720e2a43f78e2d80438
4
+ data.tar.gz: 24606b4dc84bf910492ae70e9264c4887c463b6e
5
5
  SHA512:
6
- metadata.gz: bff53c1b1165f5fbbf2882a172ac47b274bae2b9a635c757870b226e0087ebaeab035b856c55c33cde288d74999d9d5d5dd034d272faecaba8eb0b5319a2b30c
7
- data.tar.gz: 3907be2a2506af3b65c0d2cdb787b83472f2b35d60f3dcef649fb1234585d9eca08714875b7ab975c9bacd16ec17f7aabf80a1e795b57e75626d0b511d2ed0a6
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
- 1. [Attributes](#attributes)
15
- 1. [Conditional blocks](#conditional-blocks)
16
- 1. [Collection blocks](#collection-blocks)
17
- 1. [Context blocks](#context-blocks)
18
- 1. [Setting up state](#setting-up-state)
19
- 2. [Escaping Curly syntax](#escaping-curly-syntax)
20
- 2. [Comments](#comments)
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 Content Blocks](#layouts-and-content-blocks)
23
- 2. [Examples](#examples)
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 Content Blocks
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
@@ -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.3.2'
8
- s.date = '2015-01-13'
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
@@ -25,7 +25,7 @@
25
25
  #
26
26
  # See Curly::Presenter for more information on presenters.
27
27
  module Curly
28
- VERSION = "2.3.2"
28
+ VERSION = "2.4.0"
29
29
 
30
30
  # Compiles a Curly template to Ruby code.
31
31
  #
@@ -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 = #{item_presenter_class}.new(self, options)
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 = #{item_presenter_class}.new(self, options)
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
- name, identifier = first.split(".", 2)
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
- tree << Component.new(*args)
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)
@@ -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])).to eql "<ul><li>foo</li><li>bar</li></ul>"
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])).to eql "<ul><li>foo</li></ul>"
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])).to eql "<ul><li>1</li><li>2</li></ul>"
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])).to eql "XfooInventory"
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])).to eql "SKU: foo; SKU: bar; "
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])).to eql "item1: AB; item2: CD; "
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("viagra!")
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
 
@@ -6,4 +6,16 @@ class Layouts::ApplicationPresenter < Curly::Presenter
6
6
  def content
7
7
  yield
8
8
  end
9
+
10
+ def header(&block)
11
+ block.call
12
+ end
13
+
14
+ class HeaderPresenter < Curly::Presenter
15
+
16
+ def title
17
+ "Dummy app"
18
+ end
19
+ end
20
+
9
21
  end
@@ -3,6 +3,9 @@
3
3
  <title>{{title}}</title>
4
4
  </head>
5
5
  <body>
6
+ {{@header}}<header>
7
+ <h1>{{title}}</h1>
8
+ </header>{{/header}}
6
9
  {{content}}
7
10
  </body>
8
11
  </html>
@@ -8,6 +8,9 @@ describe "Using Curly for the application layout", type: :request do
8
8
  <title>Dummy app</title>
9
9
  </head>
10
10
  <body>
11
+ <header>
12
+ <h1>Dummy app</h1>
13
+ </header>
11
14
  <h1>Dashboard</h1>
12
15
  <p>Hello, World!</p>
13
16
  <p>Welcome!</p>
@@ -8,6 +8,9 @@ describe "Collection blocks", type: :request do
8
8
  <title>Dummy app</title>
9
9
  </head>
10
10
  <body>
11
+ <header>
12
+ <h1>Dummy app</h1>
13
+ </header>
11
14
  <ul>
12
15
 
13
16
  <li>uno</li>
@@ -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="&#x2713;" />
14
17
  <div class="field">
15
18
  <b>Name</b> <input id="dashboard_name" name="dashboard[name]" type="text" value="test" />
@@ -8,6 +8,9 @@ describe "Using Curly for Rails partials", type: :request do
8
8
  <title>Dummy app</title>
9
9
  </head>
10
10
  <body>
11
+ <header>
12
+ <h1>Dummy app</h1>
13
+ </header>
11
14
  <ul>
12
15
  <li>One (yo)</li>
13
16
  <li>Two (yo)</li>
@@ -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, locals = {}, presenter_class = nil, &block)
18
- if presenter_class.nil?
19
- unless defined?(ShowPresenter)
20
- define_presenter("ShowPresenter")
21
- end
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
- presenter_class = ShowPresenter
23
+ virtual_path = options.fetch(:virtual_path) do
24
+ presenter.name.underscore.gsub(/_presenter\z/, "")
24
25
  end
25
26
 
26
- identifier = "show"
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.3.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-01-13 00:00:00.000000000 Z
11
+ date: 2015-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack