curtain 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +311 -17
- data/TODO.md +7 -0
- data/curtain.gemspec +2 -3
- data/erubs_example.rb +103 -0
- data/example.erb +4 -0
- data/example.slim +2 -0
- data/lib/curtain/caching.rb +9 -0
- data/lib/curtain/erubis.rb +33 -0
- data/lib/curtain/erubis_template.rb +23 -0
- data/lib/curtain/form_builder.rb +218 -0
- data/lib/curtain/form_helpers.rb +23 -0
- data/lib/curtain/html_helpers.rb +180 -0
- data/lib/curtain/output_buffer.rb +10 -0
- data/lib/curtain/rendering.rb +9 -30
- data/lib/curtain/templating.rb +5 -1
- data/lib/curtain/version.rb +1 -1
- data/lib/curtain.rb +18 -1
- data/test/basic_test.rb +66 -0
- data/test/cache_test.rb +17 -0
- data/test/examples/{body.erb → basic/erb/body.erb} +0 -0
- data/test/examples/basic/erb/index.erb +1 -0
- data/test/examples/basic/erb/layout.erb +3 -0
- data/test/examples/{subdir → basic/erb/subdir}/index.erb +0 -0
- data/test/examples/{test.erb → basic/erb/test.erb} +0 -0
- data/test/examples/basic/slim/body.slim +5 -0
- data/test/examples/basic/slim/index.slim +1 -0
- data/test/examples/basic/slim/layout.slim +2 -0
- data/test/examples/basic/slim/subdir/index.slim +1 -0
- data/test/examples/basic/slim/test.slim +3 -0
- data/test/examples/cache/erb/cache.erb +3 -0
- data/test/examples/cache/slim/cache.slim +2 -0
- data/test/examples/form/Rakefile +56 -0
- data/test/examples/form/account.html +101 -0
- data/test/examples/form/account.rb +11 -0
- data/test/examples/form/account.yml +11 -0
- data/test/examples/form/account_view.rb +3 -0
- data/test/examples/form/account_with_data.html +100 -0
- data/test/examples/form/erb/account.erb +84 -0
- data/test/examples/form/erb/account_with_fields.erb +0 -0
- data/test/examples/form/erb/bootstrap.erb +15 -0
- data/test/examples/form/slim/account.slim +65 -0
- data/test/examples/form/slim/bootstrap.slim +11 -0
- data/test/examples/html/erb/content_tag.erb +1 -0
- data/test/examples/html/erb/content_tag_with_content.erb +1 -0
- data/test/examples/html/erb/content_tag_with_content_and_attributes.erb +1 -0
- data/test/examples/html/erb/content_tag_with_content_block_and_attributes.erb +3 -0
- data/test/examples/html/erb/content_tag_with_content_block_with_nested_tags.erb +3 -0
- data/test/examples/html/erb/empty_content_tag.erb +1 -0
- data/test/examples/html/erb/void_tag.erb +1 -0
- data/test/examples/html/erb/void_tag_with_attributes.erb +1 -0
- data/test/examples/html/slim/content_tag.slim +1 -0
- data/test/examples/html/slim/content_tag_with_content.slim +1 -0
- data/test/examples/html/slim/content_tag_with_content_and_attributes.slim +1 -0
- data/test/examples/html/slim/content_tag_with_content_block_and_attributes.slim +2 -0
- data/test/examples/html/slim/content_tag_with_content_block_with_nested_tags.slim +2 -0
- data/test/examples/html/slim/empty_content_tag.slim +1 -0
- data/test/examples/html/slim/void_tag.slim +1 -0
- data/test/examples/html/slim/void_tag_with_attributes.slim +1 -0
- data/test/form_test.rb +44 -0
- data/test/html_test.rb +32 -0
- data/test/test_helper.rb +31 -2
- metadata +101 -34
- data/test/curtain_test.rb +0 -119
- data/test/examples/index.erb +0 -1
- data/test/examples/layout.erb +0 -1
- data/test/examples/registration.mustache +0 -48
- data/test/examples/simple.erb +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: deb1542f4574f0d23da36aab00e9dc2841c2f1b5
|
4
|
+
data.tar.gz: 6fd652b3eae67688a634f9f84b9b9bec81026427
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c07f312c43a694d7dc14bfa70f685f9ff23f2f5a86a1739023ec18b4e5c538228ae7e929711862151c433e068401aa51c161554b1ee69e517bd4fb8b9d6ba033
|
7
|
+
data.tar.gz: 172fd6af7e87c927aea65a218fdec517901ffc561b57d6fce5bedcb4ce861bf79d4a648b6dc6b4680cc6f6cffd7913bf70b6a43c63283c3379cedc1b894eb9ab
|
data/README.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Curtain is a template rendering framework for Ruby. It is built on top of [Tilt](https://github.com/rtomayko/tilt). Curtain is not tied to any web framework like Rails or Sinatra, so it can be used standalone in any Ruby project.
|
4
4
|
|
5
|
-
|
5
|
+
# Installation
|
6
6
|
|
7
7
|
Add this line to your application's Gemfile:
|
8
8
|
|
@@ -16,16 +16,20 @@ Or install it yourself as:
|
|
16
16
|
|
17
17
|
$ gem install curtain
|
18
18
|
|
19
|
-
|
19
|
+
# Usage
|
20
|
+
|
21
|
+
## Rendering Templates
|
20
22
|
|
21
23
|
To use Curtain, you define a view and then have that view render templates:
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
+
**hello.erb**
|
26
|
+
|
27
|
+
``` rhtml
|
25
28
|
<h1><%= msg %></h1>
|
26
29
|
```
|
27
30
|
|
28
|
-
|
31
|
+
**my_view.rb**
|
32
|
+
|
29
33
|
``` ruby
|
30
34
|
class MyView
|
31
35
|
include Curtain
|
@@ -46,21 +50,60 @@ view.render("hello") # => <h1>Hello, World!</h1>
|
|
46
50
|
The template is rendered in the scope of the view object, so any methods defined in the view are available to the template. You don't have to create a subclass if you don't need to:
|
47
51
|
|
48
52
|
``` ruby
|
49
|
-
Curtain::View.new.render("hello", :
|
53
|
+
Curtain::View.new.render("hello", msg: "Hello, World!")
|
50
54
|
```
|
51
55
|
|
52
56
|
There is an equivalent shortcut available:
|
53
57
|
|
54
58
|
``` ruby
|
55
|
-
Curtain.render("hello", :
|
59
|
+
Curtain.render("hello", msg: "Hello, World!")
|
60
|
+
```
|
61
|
+
|
62
|
+
## Partials
|
63
|
+
|
64
|
+
Because `render` is a method on the view object, you can call it from within the template to render one template from another:
|
65
|
+
|
66
|
+
**main.erb**
|
67
|
+
|
68
|
+
``` rhtml
|
69
|
+
<%= render "partial" %>
|
56
70
|
```
|
57
71
|
|
58
|
-
|
72
|
+
**partial.erb**
|
73
|
+
|
74
|
+
``` rhtml
|
75
|
+
<h2>I'm a Partial!</h2>
|
76
|
+
```
|
77
|
+
|
78
|
+
```
|
79
|
+
Curtain.render("main") # => "<h2>I'm a Partial!</h2>"
|
80
|
+
```
|
81
|
+
|
82
|
+
You can also pass local variables to the partial:
|
83
|
+
|
84
|
+
**main.erb**
|
85
|
+
|
86
|
+
``` rhtml
|
87
|
+
<%= render "partial", greeting: 'Hello' %>
|
88
|
+
```
|
59
89
|
|
60
|
-
|
90
|
+
**partial.erb**
|
61
91
|
|
62
|
-
|
63
|
-
|
92
|
+
``` rhtml
|
93
|
+
<h2><%= greeting %>, I'm a Partial!</h2>
|
94
|
+
```
|
95
|
+
|
96
|
+
```
|
97
|
+
Curtain.render("main") # => "<h2>Hello, I'm a Partial!</h2>"
|
98
|
+
```
|
99
|
+
|
100
|
+
## Variables
|
101
|
+
|
102
|
+
If you don't want to define a subclass of `Curtain::View` and add attributes to it, you can also use variables. `Curtain::View` supports the hash-like Ruby methods `[]` and `[]=` to define variables that will act as locals in when the template is rendered:
|
103
|
+
|
104
|
+
**hello.erb**
|
105
|
+
|
106
|
+
``` rhtml
|
64
107
|
<h1><%= msg %></h1>
|
65
108
|
```
|
66
109
|
|
@@ -72,15 +115,17 @@ view.render(:hello) # => "<h1>Hello</h1>"
|
|
72
115
|
|
73
116
|
Note that unlike locals, variables exist throughout nested scope of render calls:
|
74
117
|
|
75
|
-
|
76
|
-
|
118
|
+
**main.erb**
|
119
|
+
|
120
|
+
``` rhtml
|
77
121
|
foo: <%= foo %>
|
78
122
|
bar: <%= bar %>
|
79
123
|
<%= render "partial" %>
|
80
124
|
```
|
81
125
|
|
82
|
-
|
83
|
-
|
126
|
+
**partial.erb**
|
127
|
+
|
128
|
+
``` rhtml
|
84
129
|
foo: <%= foo %>
|
85
130
|
bar: <%= bar %>
|
86
131
|
```
|
@@ -94,12 +139,261 @@ view[:foo] = "foo"
|
|
94
139
|
view.render :bar => "bar"
|
95
140
|
```
|
96
141
|
|
97
|
-
This example would result in an error. As the main template is first rendered, foo is defined as "foo" because it is a variable,
|
142
|
+
This example would result in an error. As the main template is first rendered, foo is defined as "foo" because it is a variable, bar is "bar" because it is passed in as a local. Then the partial template is rendered, and foo is still defined as "foo" because it is a variable, but since bar was a local passed to the rendering of main, it doesn't carry through to the rendering of partial.
|
143
|
+
|
144
|
+
## HTML Tag Methods
|
145
|
+
|
146
|
+
*NOTE: The HTML Tag Methods are only supported in [ERB][erb] and [Slim][slim]*
|
147
|
+
|
148
|
+
`Curtain::View` has HTML tag methods to make generating HTML easier. There is a method for every valid HTML tag. The methods for [void tags][void-tags] take an optional Hash of attributes:
|
149
|
+
|
150
|
+
**photo.erb**
|
151
|
+
|
152
|
+
``` rhtml
|
153
|
+
<%= img src: image_path %>
|
154
|
+
```
|
155
|
+
|
156
|
+
``` ruby
|
157
|
+
Curtain.render('photo', image_path: '/pic.jpg') # => <img src="/pic.jpg">
|
158
|
+
```
|
159
|
+
|
160
|
+
The methods for tags that may have body content accept the body content as a string in the first argument:
|
161
|
+
|
162
|
+
**link.erb**
|
163
|
+
|
164
|
+
``` rhtml
|
165
|
+
<%= a 'Home', href: path %>
|
166
|
+
```
|
167
|
+
|
168
|
+
``` ruby
|
169
|
+
Curtain.render('link', path: '/') # => <a href="/">Home</a>
|
170
|
+
```
|
171
|
+
|
172
|
+
or you can pass the body content as a block:
|
173
|
+
|
174
|
+
**link.erb**
|
175
|
+
|
176
|
+
``` rhtml
|
177
|
+
<%= a href: path, class: 'btn btn-default' do %>
|
178
|
+
<span class="glyphicon glyphicon-home"></span> Home
|
179
|
+
<% end %>
|
180
|
+
```
|
181
|
+
|
182
|
+
``` ruby
|
183
|
+
Curtain.render('link', path: '/') # => <a href="/" class="btn btn-default"><span class="glyphicon glyphicon-home"></span> Home</a>
|
184
|
+
```
|
185
|
+
|
186
|
+
## Form Tag Methods
|
187
|
+
|
188
|
+
There are some extra tag methods related to forms, as well as enhanced functionality for the form-related tags.
|
189
|
+
|
190
|
+
### Input Types
|
191
|
+
|
192
|
+
You can create an `input` tag using the `input` method:
|
193
|
+
|
194
|
+
``` rhtml
|
195
|
+
<%= input type: 'text', name: 'first_name' %>
|
196
|
+
```
|
197
|
+
|
198
|
+
but there are shortcut methods for all the valid types for input. Here are some examples:
|
199
|
+
|
200
|
+
``` rhtml
|
201
|
+
<%= text :first_name %>
|
202
|
+
<%= file :photo %>
|
203
|
+
<%= email :email %>
|
204
|
+
<%= password :password %>
|
205
|
+
<%= url :website %>
|
206
|
+
```
|
207
|
+
|
208
|
+
The equivalent to those examples are:
|
209
|
+
|
210
|
+
``` rhtml
|
211
|
+
<%= input type: 'text', name: 'first_name' %>
|
212
|
+
<%= input type: 'file', name: 'photo' %>
|
213
|
+
<%= input type: 'email', name: 'email' %>
|
214
|
+
<%= input type: 'password', name: 'password' %>
|
215
|
+
<%= input type: 'url', name: 'website' %>
|
216
|
+
```
|
217
|
+
|
218
|
+
The one exception to this pattern is that this:
|
219
|
+
|
220
|
+
``` rhtml
|
221
|
+
<%= submit "Save" %>
|
222
|
+
```
|
223
|
+
|
224
|
+
generates:
|
225
|
+
|
226
|
+
``` html
|
227
|
+
<button type="submit">Save</button>
|
228
|
+
```
|
229
|
+
|
230
|
+
### Forms
|
231
|
+
|
232
|
+
The `form` tag has some special behavior as well. You can use it without any special behavior with no arguments:
|
233
|
+
|
234
|
+
``` rhtml
|
235
|
+
<%= form do %>
|
236
|
+
<% end %>
|
237
|
+
```
|
238
|
+
|
239
|
+
which generates:
|
240
|
+
|
241
|
+
``` html
|
242
|
+
<form>
|
243
|
+
</form>
|
244
|
+
```
|
245
|
+
|
246
|
+
or you can pass a Hash of attributes:
|
247
|
+
|
248
|
+
``` rhtml
|
249
|
+
<%= form method: 'patch', action: '/register' do %>
|
250
|
+
<% end %>
|
251
|
+
```
|
252
|
+
|
253
|
+
which generates:
|
254
|
+
|
255
|
+
``` html
|
256
|
+
<form method="post" action="/register">
|
257
|
+
<input type="hidden" name="_method" value="patch">
|
258
|
+
</form>
|
259
|
+
```
|
260
|
+
|
261
|
+
Because browsers only natively support `GET` and `POST` methods, a hidden parameter is generated for the method if you use something other than `GET` or `POST`.
|
98
262
|
|
99
|
-
|
263
|
+
### Form Builders
|
264
|
+
|
265
|
+
When creating forms, it is common to want to have a form field for each attribute of an object and have the value of each input set to the value of the correspoding attribute of the object. You can do that using a Form Builder. Assuming you have something like this in the view:
|
266
|
+
|
267
|
+
``` ruby
|
268
|
+
@person = Person.new(id: 1, first_name: 'Paul')
|
269
|
+
```
|
270
|
+
|
271
|
+
You can use a form builder to create a form using the `for` attribute of the `form` tag method:
|
272
|
+
|
273
|
+
``` rhtml
|
274
|
+
<%= form for: @person do %>
|
275
|
+
<%= text :first_name %>
|
276
|
+
<% end %>
|
277
|
+
```
|
278
|
+
|
279
|
+
This generates:
|
280
|
+
|
281
|
+
``` html
|
282
|
+
<form method="post" action="/people/1">
|
283
|
+
<input type="hidden" name="_method" value="PATCH">
|
284
|
+
<input type="text" name="person[first_name]" value="Paul">
|
285
|
+
</form>
|
286
|
+
```
|
287
|
+
|
288
|
+
There are a few things that happen when you use the `for` attribute. First, the method and action are inferred based on the object. If the object has an ID, it is assumed to be a record that already been saved, therefore the method will be `patch` and the action will be the pluralized class name with the ID at the end. If the object does not have an ID, it is assumed to be a new record, so the `post` method is used and the action is just the pluralized class name. You can override the generated method by passing explict values for `method` and `action`
|
289
|
+
|
290
|
+
``` rhtml
|
291
|
+
<%= form for: @person, method: 'post', action: '/profile' do %>
|
292
|
+
<% end %>
|
293
|
+
```
|
294
|
+
|
295
|
+
The second thing that happens when you use a form builder is that the input names are prefixed with the underscored name of the class and the attribute is in brackets. This is so that all parameters related to the object will be collected into one Hash in the parameters. In the example above, `person` is the underscored name of the class and the attribute is `first_name`, so the name of the input ends up being `person[first_name]`. If you want to use something other than the underscored name of the class, you can use the `as` attributesin the `form` method:
|
296
|
+
|
297
|
+
``` rhtml
|
298
|
+
<%= form for: @person, as: 'account' do %>
|
299
|
+
<%= text :first_name %>
|
300
|
+
<% end %>
|
301
|
+
```
|
302
|
+
|
303
|
+
This will result in the input name being `account[first_name]`.
|
304
|
+
|
305
|
+
Finally, the value is set based on the value of the attribute of the object the form is for. As seen in the example above, `value` is `Paul`, because that is that is the value returned by `@person.first_name`.
|
306
|
+
|
307
|
+
### Fields
|
308
|
+
|
309
|
+
When building forms, it is common to want to have a label and errors messages next to each input. This pattern is supported by the `_field` methods. This example:
|
310
|
+
|
311
|
+
``` rhtml
|
312
|
+
<%= form for: @person do %>
|
313
|
+
<%= text_field :first_name %>
|
314
|
+
<% end %>
|
315
|
+
```
|
316
|
+
|
317
|
+
produces the following HTML:
|
318
|
+
|
319
|
+
``` html
|
320
|
+
<form method="post" action="/people">
|
321
|
+
<div class="form-field">
|
322
|
+
<label for="first_name">First Name</label>
|
323
|
+
<input type="text" name="first_name" value="" id="first_name">
|
324
|
+
<span class="error">is required</span>
|
325
|
+
</div>
|
326
|
+
</form>
|
327
|
+
```
|
328
|
+
|
329
|
+
The content of the label can be customized with the `label` attribute. The error only shows up if the form object has an error on that attribute.
|
330
|
+
|
331
|
+
There is a corresponding `_field` method for each input tag, so `text_field`, `email_field`, `password_field`, etc. There are also `_field` methods for the other non-input form tag methods, such as `select_field` and `textarea_field`.
|
332
|
+
|
333
|
+
### Custom Form Builders
|
334
|
+
|
335
|
+
The markup produced by the `_field` methods is controlled by the form builder. The form builder is a class that can be overriden and customized. You can use a different form builder for a specific form:
|
336
|
+
|
337
|
+
``` rhtml
|
338
|
+
<%= form builder: MyFormBuilder do %>
|
339
|
+
```
|
340
|
+
|
341
|
+
The class passed to the `builder` attribute should be a subclass of `Curtain::FormBuilder`. You can also control which form builder is used in a more broad manner. The default form builder is determined by the `default_form_builder` method on the `View` class. `Curtain::View.default_form_builder` returns `Curtain::FormBuilder`. You can set the default form builder on your view like this:
|
342
|
+
|
343
|
+
``` ruby
|
344
|
+
class MyView < Curtain::View
|
345
|
+
default_form_builder MyFormBuilder
|
346
|
+
end
|
347
|
+
```
|
348
|
+
|
349
|
+
This works throughout the inhereitence chain, so you can create a single view in your application that all views inheret from and then set the default_form_builder on that view in order to make all forms in your application use your custom form builder.
|
350
|
+
|
351
|
+
### Pre-built Form Builders
|
352
|
+
|
353
|
+
There are already form builders for the [Bootstrap][bootstrap] and [Foundation][foundation] CSS frameworks. To use them, install the gem for the one you wish to use, [curtain-bootstrap][curtain-bootstrap] or [curtain-foundation][curtain-foundation], and then set the default form builder on the view you would like to use them on:
|
354
|
+
|
355
|
+
``` ruby
|
356
|
+
class ApplicationView < Curtain::View
|
357
|
+
default_form_builder Curtain::Bootstrap::FormBuilder
|
358
|
+
end
|
359
|
+
```
|
360
|
+
|
361
|
+
See the documentation for each of those projects to see exactly what markup they generate.
|
362
|
+
|
363
|
+
## Caching
|
364
|
+
|
365
|
+
Curtain has built-in support for caching via the `cache` method. You use the method like this:
|
366
|
+
|
367
|
+
``` rhtml
|
368
|
+
<%= cache 'hourly-stats', expires_in: 1.hour do %>
|
369
|
+
<% get_report_data.each do |row| %>
|
370
|
+
...
|
371
|
+
<% end %>
|
372
|
+
<% end %>
|
373
|
+
```
|
374
|
+
|
375
|
+
To use caching, you just set the cache on the view class:
|
376
|
+
|
377
|
+
``` ruby
|
378
|
+
Curtain::View.cache = Dalli::Client.new
|
379
|
+
```
|
380
|
+
|
381
|
+
You can use anything supported by the [Cache][cache] library for the cache.
|
382
|
+
|
383
|
+
# Contributing
|
100
384
|
|
101
385
|
1. Fork it
|
102
386
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
103
387
|
3. Commit your changes (`git commit -am 'Added some feature'`)
|
104
388
|
4. Push to the branch (`git push origin my-new-feature`)
|
105
389
|
5. Create new Pull Request
|
390
|
+
|
391
|
+
[tilt]: http://github.com/rtomayko/tilt
|
392
|
+
[erb]: http://www.kuwata-lab.com/erubis/#Introduction
|
393
|
+
[slim]: http://slim-lang.com
|
394
|
+
[bootstrap]: http://getbootstrap.com
|
395
|
+
[foundation]: http://foundation.zurb.com
|
396
|
+
[curtain-bootstrap]: http://rubygems.org/gems/curtain-bootstrap
|
397
|
+
[curtain-foundation]: http://rubygems.org/gems/curtain-foundation
|
398
|
+
[cache]: http://rubygems.org/gems/cache
|
399
|
+
[void-tags]: http://www.w3.org/TR/html-markup/syntax.html#void-element
|
data/TODO.md
ADDED
data/curtain.gemspec
CHANGED
@@ -12,13 +12,12 @@ Gem::Specification.new do |gem|
|
|
12
12
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
13
13
|
gem.name = "curtain"
|
14
14
|
gem.require_paths = ["lib"]
|
15
|
-
gem.version = "0.
|
15
|
+
gem.version = "0.3.0"
|
16
16
|
|
17
17
|
gem.add_runtime_dependency "activesupport"
|
18
18
|
gem.add_runtime_dependency "tilt"
|
19
19
|
|
20
20
|
gem.add_development_dependency "erubis"
|
21
|
-
gem.add_development_dependency "
|
21
|
+
gem.add_development_dependency "glam"
|
22
22
|
gem.add_development_dependency "slim"
|
23
|
-
gem.add_development_dependency "mustache"
|
24
23
|
end
|
data/erubs_example.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'active_support/core_ext'
|
2
|
+
require 'erubis'
|
3
|
+
require 'tilt'
|
4
|
+
require 'temple'
|
5
|
+
|
6
|
+
module Curtain
|
7
|
+
class OutputBuffer < ActiveSupport::SafeBuffer
|
8
|
+
def <<(value)
|
9
|
+
super(value.to_s)
|
10
|
+
end
|
11
|
+
alias :append= :<<
|
12
|
+
alias :safe_append= :<<
|
13
|
+
alias :safe_contact :<<
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module Curtain
|
18
|
+
class Erubis < ::Erubis::Eruby
|
19
|
+
def add_text(src, text)
|
20
|
+
return if text.empty?
|
21
|
+
src << "@output_buffer.safe_concat('" << escape_text(text) << "');"
|
22
|
+
end
|
23
|
+
|
24
|
+
BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
|
25
|
+
|
26
|
+
def add_expr_literal(src, code)
|
27
|
+
if code =~ BLOCK_EXPR
|
28
|
+
src << '@output_buffer.append= ' << code
|
29
|
+
else
|
30
|
+
src << '@output_buffer.append= (' << code << ');'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_expr_escaped(src, code)
|
35
|
+
if code =~ BLOCK_EXPR
|
36
|
+
src << "@output_buffer.safe_append= " << code
|
37
|
+
else
|
38
|
+
src << "@output_buffer.safe_concat((" << code << ").to_s);"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def add_postamble(src)
|
43
|
+
src << '@output_buffer.to_s'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
module Curtain
|
49
|
+
class ErubisTemplate < Tilt::Template
|
50
|
+
DEFAULT_OUTPUT_VARIABLE = '@output_buffer'
|
51
|
+
|
52
|
+
def self.engine_initialized?
|
53
|
+
defined? ::ERB
|
54
|
+
end
|
55
|
+
|
56
|
+
def initialize_engine
|
57
|
+
require_template_library 'erubis'
|
58
|
+
end
|
59
|
+
|
60
|
+
def prepare
|
61
|
+
@engine = Curtain::Erubis.new(data, options)
|
62
|
+
end
|
63
|
+
|
64
|
+
def precompiled_template(locals)
|
65
|
+
@engine.src
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
Tilt.prefer Curtain::ErubisTemplate, 'erb'
|
71
|
+
class View
|
72
|
+
attr_accessor :output_buffer
|
73
|
+
|
74
|
+
def initialize
|
75
|
+
@output_buffer = Curtain::OutputBuffer.new
|
76
|
+
end
|
77
|
+
|
78
|
+
def capture
|
79
|
+
original_buffer = @output_buffer
|
80
|
+
@output_buffer = Curtain::OutputBuffer.new
|
81
|
+
yield
|
82
|
+
ensure
|
83
|
+
@output_buffer = original_buffer
|
84
|
+
end
|
85
|
+
|
86
|
+
def form(attrs={}, &body)
|
87
|
+
%{<form>#{capture(&body)}</form>}.html_safe
|
88
|
+
end
|
89
|
+
|
90
|
+
def input(attrs={})
|
91
|
+
%{<input#{attrs.map{|n,v| %{ #{n}="#{v}"} }.join}/>}.html_safe
|
92
|
+
end
|
93
|
+
|
94
|
+
def a(attrs={}, &body)
|
95
|
+
%{<a#{Array(attrs).map{|n,v| %{ #{n}="#{v}"} }.join}>#{capture(&body)}</a>}.html_safe
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
template = Tilt.new('example.erb', :buffer => '@output_buffer', :use_html_safe => true, :disable_capture => true, :generator => Temple::Generators::RailsOutputBuffer)
|
100
|
+
puts template.render(View.new)
|
101
|
+
|
102
|
+
template = Tilt.new('example.slim', :buffer => '@output_buffer', :use_html_safe => true, :disable_capture => true, :generator => Temple::Generators::RailsOutputBuffer)
|
103
|
+
puts template.render(View.new)
|
data/example.erb
ADDED
data/example.slim
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'erubis'
|
2
|
+
require 'curtain/erubis_template'
|
3
|
+
|
4
|
+
module Curtain
|
5
|
+
class Erubis < ::Erubis::Eruby
|
6
|
+
def add_text(src, text)
|
7
|
+
return if text.empty?
|
8
|
+
src << "@output_buffer.safe_concat('" << escape_text(text) << "');"
|
9
|
+
end
|
10
|
+
|
11
|
+
BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
|
12
|
+
|
13
|
+
def add_expr_literal(src, code)
|
14
|
+
if code =~ BLOCK_EXPR
|
15
|
+
src << '@output_buffer.append= ' << code
|
16
|
+
else
|
17
|
+
src << '@output_buffer.append= (' << code << ');'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_expr_escaped(src, code)
|
22
|
+
if code =~ BLOCK_EXPR
|
23
|
+
src << "@output_buffer.safe_append= " << code
|
24
|
+
else
|
25
|
+
src << "@output_buffer.safe_concat((" << code << ").to_s);"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def add_postamble(src)
|
30
|
+
src << '@output_buffer.to_s'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Curtain
|
2
|
+
class ErubisTemplate < Tilt::Template
|
3
|
+
DEFAULT_OUTPUT_VARIABLE = '@output_buffer'
|
4
|
+
|
5
|
+
def self.engine_initialized?
|
6
|
+
defined? ::ERB
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize_engine
|
10
|
+
require_template_library 'erubis'
|
11
|
+
end
|
12
|
+
|
13
|
+
def prepare
|
14
|
+
@engine = Curtain::Erubis.new(data, options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def precompiled_template(locals)
|
18
|
+
@engine.src
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Tilt.prefer Curtain::ErubisTemplate, 'erb'
|