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 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