flavour_saver 4.0.2 → 5.0.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/.github/workflows/ci.yml +14 -0
- data/.standard.yml +1 -0
- data/CHANGELOG.md +57 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +48 -5
- data/README.md +153 -271
- data/flavour_saver.gemspec +17 -19
- data/lib/flavour_saver/error.rb +5 -0
- data/lib/flavour_saver/helpers.rb +19 -18
- data/lib/flavour_saver/lexer.rb +123 -50
- data/lib/flavour_saver/nodes.rb +107 -19
- data/lib/flavour_saver/parser.rb +339 -161
- data/lib/flavour_saver/partial.rb +4 -6
- data/lib/flavour_saver/rails_partial.rb +10 -4
- data/lib/flavour_saver/runtime.rb +76 -81
- data/lib/flavour_saver/template.rb +3 -5
- data/lib/flavour_saver/version.rb +1 -1
- data/lib/flavour_saver.rb +17 -17
- data/spec/acceptance/backtrack_spec.rb +9 -9
- data/spec/acceptance/comment_spec.rb +7 -7
- data/spec/acceptance/custom_block_helper_spec.rb +12 -12
- data/spec/acceptance/custom_helper_spec.rb +8 -8
- data/spec/acceptance/ensure_no_rce_spec.rb +64 -53
- data/spec/acceptance/handlebars_qunit_spec.rb +295 -297
- data/spec/acceptance/if_else_spec.rb +6 -6
- data/spec/acceptance/multi_level_with_spec.rb +10 -11
- data/spec/acceptance/nesting_depth_spec.rb +34 -0
- data/spec/acceptance/one_character_identifier_spec.rb +8 -8
- data/spec/acceptance/raw_block_spec.rb +7 -7
- data/spec/acceptance/sections_spec.rb +17 -14
- data/spec/acceptance/segment_literals_spec.rb +5 -5
- data/spec/acceptance/simple_expression_spec.rb +9 -9
- data/spec/acceptance/subexpression_spec.rb +18 -17
- data/spec/acceptance/unless_spec.rb +19 -6
- data/spec/lib/flavour_saver/lexer_spec.rb +103 -89
- data/spec/lib/flavour_saver/nodes_spec.rb +85 -0
- data/spec/lib/flavour_saver/parser_spec.rb +155 -95
- data/spec/lib/flavour_saver/runtime_spec.rb +94 -96
- data/spec/lib/flavour_saver/template_spec.rb +1 -2
- metadata +19 -48
data/README.md
CHANGED
|
@@ -1,376 +1,258 @@
|
|
|
1
1
|
# FlavourSaver
|
|
2
2
|
|
|
3
|
-
[
|
|
3
|
+
[](https://rubygems.org/gems/flavour_saver)
|
|
4
|
+
[](https://github.com/FlavourSaver/FlavourSaver/actions/workflows/ci.yml)
|
|
5
|
+
[](LICENSE)
|
|
4
6
|
|
|
5
|
-
[
|
|
6
|
-

|
|
7
|
-
[](https://codeclimate.com/github/FlavourSaver/FlavourSaver/maintainability)
|
|
7
|
+
[Handlebars](https://handlebarsjs.com) templates in pure Ruby. Use the same templates on the server and in the browser.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
Please use it, break it, and send issues/PR's for improvement.
|
|
14
|
-
|
|
15
|
-
## Status
|
|
16
|
-
|
|
17
|
-
This project is currently in maintenance mode. Here's what this means:
|
|
18
|
-
|
|
19
|
-
* The maintainers of this project are not actively developing new features
|
|
20
|
-
* Issues and pull requests are still welcome
|
|
21
|
-
* New versions of the gem will be released as changes are merged
|
|
22
|
-
|
|
23
|
-
## License
|
|
24
|
-
|
|
25
|
-
FlavourSaver is Copyright (c) 2013 Resistor Limited and licensed under the terms
|
|
26
|
-
of the MIT Public License (see the LICENSE file included with this distribution
|
|
27
|
-
for more details).
|
|
9
|
+
- Works with Rails, and with Sinatra or anything else that uses [Tilt](https://github.com/jeremyevans/tilt)
|
|
10
|
+
- One runtime dependency (`tilt`), no native extensions
|
|
11
|
+
- Ruby 3.1+, tested on 3.1 through 4.0
|
|
28
12
|
|
|
29
13
|
## Installation
|
|
30
14
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
And then execute:
|
|
15
|
+
```ruby
|
|
16
|
+
# Gemfile
|
|
17
|
+
gem "flavour_saver"
|
|
18
|
+
```
|
|
36
19
|
|
|
37
|
-
|
|
20
|
+
## Quick start
|
|
38
21
|
|
|
39
|
-
|
|
22
|
+
```ruby
|
|
23
|
+
require "flavour_saver"
|
|
40
24
|
|
|
41
|
-
|
|
25
|
+
Person = Struct.new(:name, :admin)
|
|
42
26
|
|
|
43
|
-
|
|
27
|
+
FlavourSaver.evaluate("Hello {{name}}!", Person.new("Ana", true))
|
|
28
|
+
# => "Hello Ana!"
|
|
29
|
+
```
|
|
44
30
|
|
|
45
|
-
|
|
46
|
-
[Tilt](https://github.com/jeremyevans/tilt) templating library, meaning that it
|
|
47
|
-
should work with anything that has Tilt support (Sinatra, etc) and has a
|
|
48
|
-
native Rails template handler.
|
|
31
|
+
`FS` is an alias for `FlavourSaver`.
|
|
49
32
|
|
|
50
|
-
|
|
33
|
+
The context can be any object. Templates read its public methods: `{{name}}` calls `context.name`. Use a Struct, a `Data` class, a model or a presenter.
|
|
51
34
|
|
|
52
|
-
|
|
35
|
+
> [!NOTE]
|
|
36
|
+
> A plain Hash doesn't work as the context, because `{{name}}` calls a method. Hash values can be read with segment literals: `{{settings.[theme]}}` calls `settings["theme"]` (string keys only).
|
|
53
37
|
|
|
54
|
-
|
|
38
|
+
### With Tilt
|
|
55
39
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
compatible with `Hash` and hashlike objects.
|
|
62
|
-
- with list arguments (`{{method arg1 "arg2"}}`)
|
|
63
|
-
- with hash arguments (`{{method foo=bar bar="baz"}}`)
|
|
64
|
-
- with list and hash arguments (`{{method arg1 arg2 foo=bar bar="baz"}}`)
|
|
65
|
-
provided that the hash is the last argument.
|
|
66
|
-
- Comments (`{{! a comment}}`)
|
|
67
|
-
- Expression output is HTML escaped
|
|
68
|
-
- Safe expressions
|
|
69
|
-
- Expressions wrapped in triple-stashes are not HTML escaped (`{{{an expression}}}`)
|
|
70
|
-
- Block expressions
|
|
71
|
-
- Simple API for adding block helpers.
|
|
72
|
-
- Block expressions with inverse blocks
|
|
73
|
-
- Inverse blocks
|
|
74
|
-
- Partials
|
|
75
|
-
- Raw content (`{{{{raw}}}} not parsed or validated {{{{/raw}}}}`)
|
|
76
|
-
- Subexpressions (`{{sum 1 (sum 1 1)}}` returns `3`)
|
|
40
|
+
```ruby
|
|
41
|
+
template = Tilt["handlebars"].new { "{{greeting}}, {{name}}!" }
|
|
42
|
+
template.render(Struct.new(:greeting, :name).new("Hi", "Ana"))
|
|
43
|
+
# => "Hi, Ana!"
|
|
44
|
+
```
|
|
77
45
|
|
|
78
|
-
|
|
46
|
+
`.hbs` and `.handlebars` files are registered with Tilt, so `Tilt.new("welcome.hbs")` works too.
|
|
79
47
|
|
|
80
|
-
|
|
48
|
+
### With Rails
|
|
81
49
|
|
|
82
|
-
|
|
50
|
+
Put templates in `app/views` with a `.hbs` or `.handlebars` extension. See [Rails](#rails).
|
|
83
51
|
|
|
84
|
-
|
|
52
|
+
## Syntax
|
|
85
53
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
{{
|
|
90
|
-
|
|
54
|
+
| Syntax | Example |
|
|
55
|
+
| --- | --- |
|
|
56
|
+
| Expression (HTML-escaped) | `{{name}}` |
|
|
57
|
+
| Raw output | `{{{bio}}}` or `{{&bio}}` |
|
|
58
|
+
| Path | `{{author.name}}` |
|
|
59
|
+
| Array index / hash key | `{{posts.[0].title}}`, `{{settings.[theme]}}` |
|
|
60
|
+
| Parent context | `{{../title}}` |
|
|
61
|
+
| Root context | `{{@root.title}}` |
|
|
62
|
+
| Comment | `{{! hidden }}`, `{{!-- hidden --}}` |
|
|
63
|
+
| Section / inverted section | `{{#posts}}…{{/posts}}`, `{{^posts}}none{{/posts}}` |
|
|
64
|
+
| Helper with arguments | `{{link "Home" href="/"}}` |
|
|
65
|
+
| Subexpression | `{{sum 1 (sum 2 3)}}` |
|
|
66
|
+
| Partial | `{{> user_card author}}` |
|
|
67
|
+
| Raw block (not parsed) | `{{{{raw}}}} {{left as is}} {{{{/raw}}}}` |
|
|
91
68
|
|
|
92
|
-
|
|
69
|
+
## Built-in helpers
|
|
93
70
|
|
|
94
|
-
|
|
95
|
-
|
|
71
|
+
| Helper | Does |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| `#if` / `#unless` | Renders the block when the value is truthy / falsy. Supports `{{else}}`. `nil`, `false`, `0` and empty collections are falsy. |
|
|
74
|
+
| `#each` | Renders the block for each item. Exposes `@index`, `@first`, `@last`, and `@key` for hashes. |
|
|
75
|
+
| `#with` | Renders the block with the value as the context. |
|
|
76
|
+
| `this` | The current context. |
|
|
77
|
+
| `log` | Writes to `FlavourSaver.logger` at debug level. |
|
|
96
78
|
|
|
97
79
|
```handlebars
|
|
98
|
-
{{#each
|
|
99
|
-
{{
|
|
80
|
+
{{#each posts}}
|
|
81
|
+
{{@index}}. {{title}}{{#if @last}} (latest){{/if}}
|
|
100
82
|
{{/each}}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
### #if
|
|
104
|
-
|
|
105
|
-
Takes a single argument and yields the contents of the block if that argument
|
|
106
|
-
is truthy.
|
|
107
83
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
Hi {{person.name}}!
|
|
111
|
-
{{/if}}
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
It can also handle a special case `{{else}}` expression:
|
|
115
|
-
|
|
116
|
-
```handlebars
|
|
117
|
-
{{#if person}}
|
|
118
|
-
Hi {{person.name}}!
|
|
84
|
+
{{#if author.admin}}
|
|
85
|
+
Admin
|
|
119
86
|
{{else}}
|
|
120
|
-
|
|
87
|
+
Member
|
|
121
88
|
{{/if}}
|
|
122
89
|
```
|
|
123
90
|
|
|
124
|
-
|
|
91
|
+
On Rails, `FlavourSaver.logger` is `Rails.logger`. Elsewhere, set one before using `log`:
|
|
125
92
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
In JavaScript `this` is a native keyword, in Ruby not-so-much. FlavourSaver's `this` helper
|
|
131
|
-
returns `self`:
|
|
132
|
-
|
|
133
|
-
```handlebars
|
|
134
|
-
{{#each names}}
|
|
135
|
-
{{this}}
|
|
136
|
-
{{/each}}
|
|
93
|
+
```ruby
|
|
94
|
+
require "logger"
|
|
95
|
+
FlavourSaver.logger = Logger.new($stdout)
|
|
137
96
|
```
|
|
138
97
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
Writes log output. The destination can be changed by assigning a `Logger` instance to
|
|
142
|
-
`FlavourSaver.logger=`. On Rails `FlavourSaver.logger` automatically points at
|
|
143
|
-
`Rails.logger`.
|
|
98
|
+
## Custom helpers
|
|
144
99
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
Additional helpers can easy be added by calling `FS.register_helper`, eg:
|
|
100
|
+
Register a helper with a block. Positional arguments come first; `key=value` arguments arrive as a final hash with symbol keys.
|
|
148
101
|
|
|
149
102
|
```ruby
|
|
150
|
-
FS.register_helper(:
|
|
103
|
+
FS.register_helper(:shout) { |text| text.upcase }
|
|
104
|
+
FS.register_helper(:greet) { |name, options| "#{options[:greeting]}, #{name}" }
|
|
151
105
|
```
|
|
152
106
|
|
|
153
|
-
Now if you were to render the following template:
|
|
154
|
-
|
|
155
107
|
```handlebars
|
|
156
|
-
|
|
108
|
+
{{shout "hi"}} {{! HI }}
|
|
109
|
+
{{greet "Ana" greeting="Hola"}} {{! Hola, Ana }}
|
|
157
110
|
```
|
|
158
111
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
```html
|
|
162
|
-
<h1>Hello world!</h1>
|
|
163
|
-
```
|
|
112
|
+
Helper output is HTML-escaped. Return an `html_safe` string (from ActiveSupport) to skip escaping.
|
|
164
113
|
|
|
165
|
-
###
|
|
114
|
+
### Block helpers
|
|
166
115
|
|
|
167
|
-
|
|
168
|
-
the helper implementation can call `yield.contents` one or more times, with an
|
|
169
|
-
optional argument setting the context of the block execution:
|
|
116
|
+
A block helper takes the template block as `&block`. `block.call.contents` renders the main section, optionally with a new context. `block.call.inverse` renders the `{{else}}` section.
|
|
170
117
|
|
|
171
118
|
```ruby
|
|
172
|
-
FS.register_helper(:
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
119
|
+
FS.register_helper(:admins) do |people, &block|
|
|
120
|
+
admins = people.select(&:admin)
|
|
121
|
+
if admins.any?
|
|
122
|
+
admins.map { |person| block.call.contents(person) }.join
|
|
123
|
+
else
|
|
124
|
+
block.call.inverse
|
|
125
|
+
end
|
|
176
126
|
end
|
|
177
127
|
```
|
|
178
128
|
|
|
179
|
-
Which when called with the following template:
|
|
180
|
-
|
|
181
129
|
```handlebars
|
|
182
|
-
{{#
|
|
183
|
-
|
|
184
|
-
{{
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
would result in the following output:
|
|
188
|
-
```
|
|
189
|
-
hello
|
|
190
|
-
hello
|
|
191
|
-
hello
|
|
130
|
+
{{#admins people}}
|
|
131
|
+
<li>{{name}}</li>
|
|
132
|
+
{{else}}
|
|
133
|
+
<li>No admins</li>
|
|
134
|
+
{{/admins}}
|
|
192
135
|
```
|
|
193
136
|
|
|
194
|
-
|
|
137
|
+
You can also register a method. Inside a method, `yield` works the same way as `block.call`:
|
|
195
138
|
|
|
196
139
|
```ruby
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
yield.contents person
|
|
200
|
-
end
|
|
140
|
+
def twice
|
|
141
|
+
yield.contents * 2
|
|
201
142
|
end
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
Which could be used like so:
|
|
205
143
|
|
|
206
|
-
|
|
207
|
-
{{#list_people people}}
|
|
208
|
-
<b>{{name}}<b><br />
|
|
209
|
-
Age: {{age}}<br />
|
|
210
|
-
Sex: {{sex}}<br />
|
|
211
|
-
{{/list_people}}
|
|
144
|
+
FS.register_helper(method(:twice))
|
|
212
145
|
```
|
|
213
146
|
|
|
214
|
-
|
|
215
|
-
a second set of block contents (called `inverse`) which can be yielded to the output:
|
|
216
|
-
|
|
217
|
-
```ruby
|
|
218
|
-
FS.register_helper(:isFemale) do |person,&block|
|
|
219
|
-
if person.sex == 'female'
|
|
220
|
-
block.call.contents
|
|
221
|
-
else
|
|
222
|
-
block.call.inverse
|
|
223
|
-
end
|
|
224
|
-
end
|
|
225
|
-
```
|
|
147
|
+
### Subexpressions
|
|
226
148
|
|
|
227
|
-
|
|
149
|
+
Wrap a helper call in parentheses to use its result as an argument:
|
|
228
150
|
|
|
229
151
|
```ruby
|
|
230
|
-
|
|
231
|
-
if person.sex == 'female'
|
|
232
|
-
yield.contents
|
|
233
|
-
else
|
|
234
|
-
yield.inverse
|
|
235
|
-
end
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
FS.register_helper(method(:isFemale))
|
|
152
|
+
FS.register_helper(:sum) { |a, b| a + b }
|
|
239
153
|
```
|
|
240
154
|
|
|
241
|
-
Which could be used like so:
|
|
242
|
-
|
|
243
155
|
```handlebars
|
|
244
|
-
{{
|
|
245
|
-
|
|
246
|
-
{{else}}
|
|
247
|
-
{{person.name}} is male.
|
|
248
|
-
{{/isFemale}}
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
### Subexpressions
|
|
252
|
-
|
|
253
|
-
You can use a subexpression as any value for a helper, and it will be executed before it is ran. You can also nest them, and use them in assignment of variables.
|
|
254
|
-
|
|
255
|
-
Below are some examples, utilizing a "sum" helper than adds together two numbers.
|
|
256
|
-
|
|
156
|
+
{{sum (sum 5 10) (sum 2 3)}} {{! 20 }}
|
|
157
|
+
{{chart total=(sum boys girls)}}
|
|
257
158
|
```
|
|
258
|
-
{{sum (sum 5 10) (sum 2 (sum 1 4))}}
|
|
259
|
-
#=> 22
|
|
260
159
|
|
|
261
|
-
|
|
262
|
-
#=> its more
|
|
160
|
+
## Partials
|
|
263
161
|
|
|
264
|
-
|
|
162
|
+
```ruby
|
|
163
|
+
FS.register_partial(:user_card, "<b>{{name}}</b>")
|
|
265
164
|
```
|
|
266
165
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
Sometimes you don't want a section of content to be evaluted as handlebars, such as when you want to display it in a page that renders with handlebars. FlavourSaver offers a `raw` helper, that will allow you to pass anything through wrapped in those elements, and it will not be evaluated.
|
|
270
|
-
|
|
271
|
-
```
|
|
272
|
-
{{{{raw}}}}
|
|
273
|
-
{{if} this tries to parse, it will break on syntax
|
|
274
|
-
{{{{/raw}}}}
|
|
275
|
-
=> {{if} this tries to parse, it will break on syntax
|
|
166
|
+
```handlebars
|
|
167
|
+
{{#each people}}{{> user_card this}}{{/each}}
|
|
276
168
|
```
|
|
277
169
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
### Using Partials
|
|
281
|
-
|
|
282
|
-
Handlebars allows you to register a partial either as a function or a string template with
|
|
283
|
-
the engine before compiling, FlavourSaver retains this behaviour (with the notable exception
|
|
284
|
-
of within Rails - see below).
|
|
285
|
-
|
|
286
|
-
To register a partial you call `FlavourSaver.register_partial` with a name and a string:
|
|
170
|
+
A partial can also be a block that receives the context and returns a string:
|
|
287
171
|
|
|
288
172
|
```ruby
|
|
289
|
-
|
|
173
|
+
FS.register_partial(:user_card) { |person| "<b>#{person.name}</b>" }
|
|
290
174
|
```
|
|
291
175
|
|
|
292
|
-
|
|
176
|
+
On Rails, partials come from your views instead. See below.
|
|
293
177
|
|
|
294
|
-
|
|
295
|
-
{{#each people}}{{> my_partial this}}{{/each}}
|
|
296
|
-
```
|
|
297
|
-
|
|
298
|
-
## Using with Rails
|
|
299
|
-
|
|
300
|
-
One potential gotcha of using FlavourSaver with Rails is that FlavourSaver doesn't let you
|
|
301
|
-
have any access to the controller's instance variables. This is done to maintain compatibility
|
|
302
|
-
with the original JavaScript implementation of Handlebars so that templates can be used on
|
|
303
|
-
both the server and client side without any change.
|
|
304
|
-
|
|
305
|
-
When accessing controller instance variables you should access them by way of a helper method
|
|
306
|
-
or a presenter object.
|
|
178
|
+
## Rails
|
|
307
179
|
|
|
308
|
-
|
|
309
|
-
the current user's session cookie and stores it in the controller's `@current_user` instance
|
|
310
|
-
variable.
|
|
180
|
+
FlavourSaver registers `.hbs` and `.handlebars` template handlers with Action View.
|
|
311
181
|
|
|
312
|
-
|
|
182
|
+
**No instance variables.** Templates can't see controller instance variables. This keeps them portable to Handlebars.js. Expose data through helper methods or a presenter:
|
|
313
183
|
|
|
314
184
|
```ruby
|
|
315
|
-
|
|
316
|
-
|
|
185
|
+
# app/helpers/application_helper.rb
|
|
186
|
+
module ApplicationHelper
|
|
187
|
+
def current_user
|
|
188
|
+
Current.user
|
|
189
|
+
end
|
|
317
190
|
end
|
|
318
191
|
```
|
|
319
192
|
|
|
320
|
-
Which would mean that you are able to access it in your template:
|
|
321
|
-
|
|
322
193
|
```handlebars
|
|
323
194
|
{{#if current_user}}
|
|
324
195
|
Welcome back, {{current_user.first_name}}!
|
|
325
196
|
{{/if}}
|
|
326
197
|
```
|
|
327
198
|
|
|
328
|
-
|
|
199
|
+
**Call view helpers through `@root` inside blocks.** The view is the root context. Inside `{{#each}}` or `{{#with}}`, names are looked up on the current item, so `{{post_url id}}` renders blank. Use `@root` to reach the view:
|
|
329
200
|
|
|
330
|
-
|
|
201
|
+
```handlebars
|
|
202
|
+
{{#each posts}}
|
|
203
|
+
<a href="{{@root.post_url id}}">{{title}}</a>
|
|
204
|
+
{{/each}}
|
|
205
|
+
```
|
|
331
206
|
|
|
332
|
-
|
|
207
|
+
**Partials use `render`.** You don't register partials in Rails. The partial syntax maps to Rails partials, and the argument is passed as `object:`:
|
|
333
208
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
209
|
+
| Template | Rails call |
|
|
210
|
+
| --- | --- |
|
|
211
|
+
| `{{> user_card}}` | `render partial: "user_card", object: <current context>` |
|
|
212
|
+
| `{{> user_card author}}` | `render partial: "user_card", object: author` |
|
|
337
213
|
|
|
338
|
-
|
|
214
|
+
## Errors
|
|
215
|
+
|
|
216
|
+
Invalid templates raise a subclass of `FlavourSaver::Error`:
|
|
217
|
+
|
|
218
|
+
```ruby
|
|
219
|
+
begin
|
|
220
|
+
FS.evaluate("{{#if ok}}unclosed", context)
|
|
221
|
+
rescue FlavourSaver::Error => e
|
|
222
|
+
# FlavourSaver::Parser::NotInLanguage, ::UnbalancedBlockError,
|
|
223
|
+
# or FlavourSaver::Lexer::LexingError
|
|
224
|
+
end
|
|
339
225
|
```
|
|
340
226
|
|
|
341
|
-
|
|
227
|
+
## Security
|
|
342
228
|
|
|
343
|
-
|
|
344
|
-
able to be used. When running inside Rails FlavourSaver modifies this behaviour to use Rails'
|
|
345
|
-
render partial helper:
|
|
229
|
+
A template can only call registered helpers and the public methods of its context. Methods every object inherits, like `send`, `instance_eval` or `class`, raise `FlavourSaver::ForbiddenMethodException`. Private methods like `system` and `eval` can't be reached at all. Even so, only pass objects you're comfortable exposing to template authors.
|
|
346
230
|
|
|
347
|
-
|
|
348
|
-
{{> my_partial}}
|
|
349
|
-
```
|
|
231
|
+
## Not supported yet
|
|
350
232
|
|
|
351
|
-
|
|
233
|
+
These Handlebars features don't parse or don't behave like Handlebars.js yet. Pull requests are welcome.
|
|
352
234
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
235
|
+
- Whitespace control (`{{~foo~}}`)
|
|
236
|
+
- `{{else if …}}` chains
|
|
237
|
+
- Block parameters (`{{#each items as |item|}}`)
|
|
238
|
+
- `{{else}}` inside `{{#each}}` and `{{#with}}`
|
|
239
|
+
- Inline partials, partial blocks, dynamic partials, and hash arguments on partials
|
|
240
|
+
- The `lookup` helper
|
|
241
|
+
- `./` paths
|
|
242
|
+
|
|
243
|
+
## Contributing
|
|
356
244
|
|
|
357
|
-
|
|
358
|
-
context of the partial. In Rails this behaviour would be confusing and non-standard, so
|
|
359
|
-
instead any argument passed to the partial is evaluated and passed to the partial's
|
|
360
|
-
`:object` argument:
|
|
245
|
+
Bug reports and pull requests are welcome on [GitHub](https://github.com/FlavourSaver/FlavourSaver/issues).
|
|
361
246
|
|
|
362
|
-
```
|
|
363
|
-
|
|
247
|
+
```sh
|
|
248
|
+
git clone https://github.com/FlavourSaver/FlavourSaver.git
|
|
249
|
+
cd FlavourSaver
|
|
250
|
+
bundle install
|
|
251
|
+
bundle exec rspec
|
|
364
252
|
```
|
|
365
253
|
|
|
366
|
-
|
|
367
|
-
render :partial => 'my_partial', :object => my_context
|
|
368
|
-
```
|
|
254
|
+
Changes are listed in the [CHANGELOG](CHANGELOG.md).
|
|
369
255
|
|
|
370
|
-
##
|
|
256
|
+
## License
|
|
371
257
|
|
|
372
|
-
|
|
373
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
374
|
-
3. Commit your changes (`git commit -am 'Added some feature'`)
|
|
375
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
|
376
|
-
5. Create new Pull Request
|
|
258
|
+
MIT. Copyright (c) 2013 Resistor Limited. See [LICENSE](LICENSE).
|
data/flavour_saver.gemspec
CHANGED
|
@@ -1,33 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
require File.expand_path('../lib/flavour_saver/version', __FILE__)
|
|
1
|
+
require File.expand_path("../lib/flavour_saver/version", __FILE__)
|
|
3
2
|
|
|
4
3
|
Gem::Specification.new do |gem|
|
|
5
|
-
gem.authors
|
|
6
|
-
gem.email
|
|
7
|
-
gem.description
|
|
8
|
-
gem.summary
|
|
9
|
-
gem.homepage
|
|
10
|
-
gem.license
|
|
11
|
-
gem.metadata
|
|
12
|
-
"bug_tracker_uri"
|
|
13
|
-
"changelog_uri"
|
|
14
|
-
"homepage_uri"
|
|
15
|
-
"source_code_uri"
|
|
4
|
+
gem.authors = ["Clayton Passmore", "James Harton"]
|
|
5
|
+
gem.email = ["ctpassmore+flavoursaver@gmail.com"]
|
|
6
|
+
gem.description = "FlavourSaver is a pure-ruby implementation of the Handlebars templating language"
|
|
7
|
+
gem.summary = "Handlebars.js without the .js"
|
|
8
|
+
gem.homepage = "http://github.com/FlavourSaver/FlavourSaver"
|
|
9
|
+
gem.license = "MIT"
|
|
10
|
+
gem.metadata = {
|
|
11
|
+
"bug_tracker_uri" => "http://github.com/FlavourSaver/FlavourSaver/issues",
|
|
12
|
+
"changelog_uri" => "http://github.com/FlavourSaver/FlavourSaver/blob/master/CHANGELOG.md",
|
|
13
|
+
"homepage_uri" => "http://github.com/FlavourSaver/FlavourSaver",
|
|
14
|
+
"source_code_uri" => "http://github.com/FlavourSaver/FlavourSaver"
|
|
16
15
|
}
|
|
17
16
|
|
|
18
|
-
gem.files
|
|
19
|
-
gem.executables
|
|
20
|
-
gem.
|
|
21
|
-
gem.name = "flavour_saver"
|
|
17
|
+
gem.files = `git ls-files`.split($\)
|
|
18
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
|
19
|
+
gem.name = "flavour_saver"
|
|
22
20
|
gem.require_paths = ["lib"]
|
|
23
|
-
gem.version
|
|
21
|
+
gem.version = FlavourSaver::VERSION
|
|
24
22
|
|
|
25
23
|
gem.required_ruby_version = ">= 3.1.0"
|
|
26
24
|
|
|
27
25
|
gem.add_development_dependency "rake"
|
|
28
26
|
gem.add_development_dependency "rspec"
|
|
27
|
+
gem.add_development_dependency "standard"
|
|
29
28
|
gem.add_development_dependency "activesupport", "~> 7.2.0"
|
|
30
29
|
|
|
31
|
-
gem.add_dependency "rltk", "< 3.0"
|
|
32
30
|
gem.add_dependency "tilt", "~> 2.6"
|
|
33
31
|
end
|