flavour_saver 0.3.3
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 +7 -0
- data/.gitignore +16 -0
- data/.travis.yml +11 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +81 -0
- data/Guardfile +12 -0
- data/LICENSE +22 -0
- data/README.md +339 -0
- data/Rakefile +2 -0
- data/flavour_saver.gemspec +28 -0
- data/lib/flavour_saver/helpers.rb +118 -0
- data/lib/flavour_saver/lexer.rb +127 -0
- data/lib/flavour_saver/nodes.rb +177 -0
- data/lib/flavour_saver/parser.rb +183 -0
- data/lib/flavour_saver/partial.rb +29 -0
- data/lib/flavour_saver/rails_partial.rb +10 -0
- data/lib/flavour_saver/runtime.rb +269 -0
- data/lib/flavour_saver/template.rb +19 -0
- data/lib/flavour_saver/version.rb +3 -0
- data/lib/flavour_saver.rb +78 -0
- data/spec/acceptance/backtrack_spec.rb +14 -0
- data/spec/acceptance/comment_spec.rb +12 -0
- data/spec/acceptance/custom_block_helper_spec.rb +35 -0
- data/spec/acceptance/custom_helper_spec.rb +15 -0
- data/spec/acceptance/ensure_no_rce_spec.rb +26 -0
- data/spec/acceptance/handlebars_qunit_spec.rb +911 -0
- data/spec/acceptance/if_else_spec.rb +17 -0
- data/spec/acceptance/multi_level_with_spec.rb +15 -0
- data/spec/acceptance/one_character_identifier_spec.rb +13 -0
- data/spec/acceptance/runtime_run_spec.rb +27 -0
- data/spec/acceptance/sections_spec.rb +25 -0
- data/spec/acceptance/segment_literals_spec.rb +26 -0
- data/spec/acceptance/simple_expression_spec.rb +13 -0
- data/spec/fixtures/backtrack.hbs +4 -0
- data/spec/fixtures/comment.hbs +1 -0
- data/spec/fixtures/custom_block_helper.hbs +3 -0
- data/spec/fixtures/custom_helper.hbs +1 -0
- data/spec/fixtures/if_else.hbs +5 -0
- data/spec/fixtures/multi_level_if.hbs +12 -0
- data/spec/fixtures/multi_level_with.hbs +11 -0
- data/spec/fixtures/one_character_identifier.hbs +1 -0
- data/spec/fixtures/sections.hbs +9 -0
- data/spec/fixtures/simple_expression.hbs +1 -0
- data/spec/lib/flavour_saver/lexer_spec.rb +187 -0
- data/spec/lib/flavour_saver/parser_spec.rb +277 -0
- data/spec/lib/flavour_saver/runtime_spec.rb +190 -0
- data/spec/lib/flavour_saver/template_spec.rb +5 -0
- metadata +243 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9d12597ba5058729046ad752a861932197e839cf
|
4
|
+
data.tar.gz: e1975090088071c306eff750d6f45b9155bf0cce
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d09d2f9c685d5fb79c1e86df08d01865f4b108551c67e45e754dae94483f8f739e133cf744ba0d0d9548e9365e83f579b47bad21043927b80694bba36cc6e493
|
7
|
+
data.tar.gz: 97dc6cd9a74bca558a20f0cca63ff552e7fc0a69a9a94ea38dedbc21c74e4b25c5433f288823e74db8ac69b5e36a6b9607ee9f4493347f66e909cce99eb6d37e
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
flavour_saver (0.3.3)
|
5
|
+
rltk (~> 2.2.0)
|
6
|
+
tilt
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
activesupport (4.0.0)
|
12
|
+
i18n (~> 0.6, >= 0.6.4)
|
13
|
+
minitest (~> 4.2)
|
14
|
+
multi_json (~> 1.3)
|
15
|
+
thread_safe (~> 0.1)
|
16
|
+
tzinfo (~> 0.3.37)
|
17
|
+
atomic (1.1.14)
|
18
|
+
coderay (1.0.9)
|
19
|
+
diff-lcs (1.2.4)
|
20
|
+
ffi (1.9.0)
|
21
|
+
formatador (0.2.4)
|
22
|
+
guard (1.8.3)
|
23
|
+
formatador (>= 0.2.4)
|
24
|
+
listen (~> 1.3)
|
25
|
+
lumberjack (>= 1.0.2)
|
26
|
+
pry (>= 0.9.10)
|
27
|
+
thor (>= 0.14.6)
|
28
|
+
guard-bundler (1.0.0)
|
29
|
+
bundler (~> 1.0)
|
30
|
+
guard (~> 1.1)
|
31
|
+
guard-rspec (3.1.0)
|
32
|
+
guard (>= 1.8)
|
33
|
+
rspec (~> 2.13)
|
34
|
+
i18n (0.6.5)
|
35
|
+
listen (1.3.1)
|
36
|
+
rb-fsevent (>= 0.9.3)
|
37
|
+
rb-inotify (>= 0.9)
|
38
|
+
rb-kqueue (>= 0.2)
|
39
|
+
lumberjack (1.0.4)
|
40
|
+
method_source (0.8.2)
|
41
|
+
minitest (4.7.5)
|
42
|
+
multi_json (1.8.0)
|
43
|
+
pry (0.9.12.2)
|
44
|
+
coderay (~> 1.0.5)
|
45
|
+
method_source (~> 0.8)
|
46
|
+
slop (~> 3.4)
|
47
|
+
rake (10.1.0)
|
48
|
+
rb-fsevent (0.9.3)
|
49
|
+
rb-inotify (0.9.2)
|
50
|
+
ffi (>= 0.5.0)
|
51
|
+
rb-kqueue (0.2.0)
|
52
|
+
ffi (>= 0.5.0)
|
53
|
+
rltk (2.2.1)
|
54
|
+
ffi (>= 1.0.0)
|
55
|
+
rspec (2.14.1)
|
56
|
+
rspec-core (~> 2.14.0)
|
57
|
+
rspec-expectations (~> 2.14.0)
|
58
|
+
rspec-mocks (~> 2.14.0)
|
59
|
+
rspec-core (2.14.5)
|
60
|
+
rspec-expectations (2.14.3)
|
61
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
62
|
+
rspec-mocks (2.14.3)
|
63
|
+
slop (3.4.6)
|
64
|
+
thor (0.18.1)
|
65
|
+
thread_safe (0.1.3)
|
66
|
+
atomic
|
67
|
+
tilt (1.4.1)
|
68
|
+
tzinfo (0.3.37)
|
69
|
+
|
70
|
+
PLATFORMS
|
71
|
+
ruby
|
72
|
+
|
73
|
+
DEPENDENCIES
|
74
|
+
activesupport
|
75
|
+
flavour_saver!
|
76
|
+
guard-bundler
|
77
|
+
guard-rspec
|
78
|
+
rake
|
79
|
+
rspec-core
|
80
|
+
rspec-expectations
|
81
|
+
rspec-mocks
|
data/Guardfile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
guard 'rspec', version: 2 do
|
2
|
+
watch(%r{^spec/.+_spec\.rb$})
|
3
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
4
|
+
watch('spec/spec_helper.rb') { "spec" }
|
5
|
+
watch(%r{^spec/fixtures/(.+)\.(hbs|handlebars)}) { |m| "spec/acceptance/#{m[1]}_spec.rb" }
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
guard 'bundler' do
|
10
|
+
watch('Gemfile')
|
11
|
+
watch(/^.+\.gemspec/)
|
12
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Resistor Limited.
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,339 @@
|
|
1
|
+
# FlavourSaver
|
2
|
+
|
3
|
+
[Handlebars.js](http://handlebarsjs.com) without the `.js`
|
4
|
+
|
5
|
+
[](https://travis-ci.org/jamesotron/FlavourSaver)
|
6
|
+
[](https://gemnasium.com/jamesotron/FlavourSaver)
|
7
|
+
[](https://codeclimate.com/github/jamesotron/FlavourSaver)
|
8
|
+
|
9
|
+
## WAT?
|
10
|
+
|
11
|
+
FlavourSaver is a ruby-based implementation of the [Handlebars.js](http://handlebars.js)
|
12
|
+
templating language. FlavourSaver supports Handlebars template rendering natively on
|
13
|
+
Rails and on other frameworks (such as Sinatra) via Tilt.
|
14
|
+
|
15
|
+
Please use it, break it, and send issues/PR's for improvement.
|
16
|
+
|
17
|
+
## License
|
18
|
+
|
19
|
+
FlavourSaver is Copyright (c) 2013 Resistor Limited and licensed under the terms
|
20
|
+
of the MIT Public License (see the LICENSE file included with this distribution
|
21
|
+
for more details).
|
22
|
+
|
23
|
+
## Installation
|
24
|
+
|
25
|
+
Add this line to your application's Gemfile:
|
26
|
+
|
27
|
+
gem 'flavour_saver'
|
28
|
+
|
29
|
+
And then execute:
|
30
|
+
|
31
|
+
$ bundle
|
32
|
+
|
33
|
+
Or install it yourself as:
|
34
|
+
|
35
|
+
$ gem install flavour_saver
|
36
|
+
|
37
|
+
## Usage
|
38
|
+
|
39
|
+
FlavourSaver provides an interface to the amazing
|
40
|
+
[Tilt](https://github.com/rtomayko/tilt) templating library, meaning that it
|
41
|
+
should work with anything that has Tilt support (Sinatra, etc) and has a
|
42
|
+
native Rails template handler.
|
43
|
+
|
44
|
+
## Status
|
45
|
+
|
46
|
+
FlavourSaver is in its infancy, your pull requests are greatly appreciated.
|
47
|
+
|
48
|
+
Currently supported:
|
49
|
+
|
50
|
+
- Full support of Mustache and Handlebars templates.
|
51
|
+
- Expressions:
|
52
|
+
- with object-paths (`{{some.method.chain}}`)
|
53
|
+
- containing object-literals (`{{object.['index'].method}}`):
|
54
|
+
Ruby's `:[](index)` method is called for literals, making FlavourSaver
|
55
|
+
compatible with `Hash` and hashlike objects.
|
56
|
+
- with list arguments (`{{method arg1 "arg2"}}`)
|
57
|
+
- with hash arguments (`{{method foo=bar bar="baz"}}`)
|
58
|
+
- with list and hash arguments (`{{method arg1 arg2 foo=bar bar="baz"}}`)
|
59
|
+
provided that the hash is the last argument.
|
60
|
+
- Comments (`{{! a comment}}`)
|
61
|
+
- Expression output is HTML escaped
|
62
|
+
- Safe expressions
|
63
|
+
- Expressions wrapped in triple-stashes are not HTML escaped (`{{{an expression}}}`)
|
64
|
+
- Block expressions
|
65
|
+
- Simple API for adding block helpers.
|
66
|
+
- Block expressions with inverse blocks
|
67
|
+
- Inverse blocks
|
68
|
+
- Partials
|
69
|
+
|
70
|
+
## Helpers
|
71
|
+
|
72
|
+
FlavourSaver implements the following helpers by default:
|
73
|
+
|
74
|
+
### #with
|
75
|
+
|
76
|
+
Yields its argument into the context of the block contents:
|
77
|
+
|
78
|
+
```handlebars
|
79
|
+
{{#with person}}
|
80
|
+
{{name}}
|
81
|
+
{{/with}}
|
82
|
+
```
|
83
|
+
|
84
|
+
### #each
|
85
|
+
|
86
|
+
Takes a single collection argument and yields the block's contents once
|
87
|
+
for each member of the collection:
|
88
|
+
|
89
|
+
```handlebars
|
90
|
+
{{#each people}}
|
91
|
+
{{name}}
|
92
|
+
{{/each}}
|
93
|
+
```
|
94
|
+
|
95
|
+
### #if
|
96
|
+
|
97
|
+
Takes a single argument and yields the contents of the block if that argument
|
98
|
+
is truthy.
|
99
|
+
|
100
|
+
```handlebars
|
101
|
+
{{#if person}}
|
102
|
+
Hi {{person.name}}!
|
103
|
+
{{/if}}
|
104
|
+
```
|
105
|
+
|
106
|
+
It can also handle a special case `{{else}}` expression:
|
107
|
+
|
108
|
+
```handlebars
|
109
|
+
{{#if person}}
|
110
|
+
Hi {{person.name}}!
|
111
|
+
{{else}}
|
112
|
+
Nobody to say hi to.
|
113
|
+
{{/if}}
|
114
|
+
```
|
115
|
+
|
116
|
+
### #unless
|
117
|
+
|
118
|
+
Exactly the same is `#if` but backwards.
|
119
|
+
|
120
|
+
### this
|
121
|
+
|
122
|
+
In JavaScript `this` is a native keyword, in Ruby not-so-much. FlavourSaver's `this` helper
|
123
|
+
returns `self`:
|
124
|
+
|
125
|
+
```handlebars
|
126
|
+
{{#each names}}
|
127
|
+
{{this}}
|
128
|
+
{{/each}}
|
129
|
+
```
|
130
|
+
|
131
|
+
### log
|
132
|
+
|
133
|
+
Writes log output. The destination can be changed by assigning a `Logger` instance to
|
134
|
+
`FlavourSaver.logger=`. On Rails `FlavourSaver.logger` automatically points at
|
135
|
+
`Rails.logger`.
|
136
|
+
|
137
|
+
### Adding additional helpers
|
138
|
+
|
139
|
+
Additional helpers can easy be added by calling `FS.register_helper`, eg:
|
140
|
+
|
141
|
+
```ruby
|
142
|
+
FS.register_helper(:whom) { 'world' }
|
143
|
+
```
|
144
|
+
|
145
|
+
Now if you were to render the following template:
|
146
|
+
|
147
|
+
```handlebars
|
148
|
+
<h1>Hello {{whom}}!</h1>
|
149
|
+
```
|
150
|
+
|
151
|
+
You would receive the following output:
|
152
|
+
|
153
|
+
```html
|
154
|
+
<h1>Hello world!</h1>
|
155
|
+
```
|
156
|
+
|
157
|
+
### Adding block helpers
|
158
|
+
|
159
|
+
Creating a block helper works exactly like adding a regular helper, except that
|
160
|
+
the helper implementation can call `yield.contents` one or more times, with an
|
161
|
+
optional argument setting the context of the block execution:
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
FS.register_helper(:three_times) do
|
165
|
+
yield.contents
|
166
|
+
yield.contents
|
167
|
+
yield.contents
|
168
|
+
end
|
169
|
+
```
|
170
|
+
|
171
|
+
Which when called with the following template:
|
172
|
+
|
173
|
+
```handlebars
|
174
|
+
{{#three_times}}
|
175
|
+
hello
|
176
|
+
{{/three_times}}
|
177
|
+
```
|
178
|
+
|
179
|
+
would result in the following output:
|
180
|
+
```
|
181
|
+
hello
|
182
|
+
hello
|
183
|
+
hello
|
184
|
+
```
|
185
|
+
|
186
|
+
Implementing a simple iterator is dead easy:
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
FS.register_helper(:list_people) do |people|
|
190
|
+
people.each do |person|
|
191
|
+
yield.contents person
|
192
|
+
end
|
193
|
+
end
|
194
|
+
```
|
195
|
+
|
196
|
+
Which could be used like so:
|
197
|
+
|
198
|
+
```handlebars
|
199
|
+
{{#list_people people}}
|
200
|
+
<b>{{name}}<b><br />
|
201
|
+
Age: {{age}}<br />
|
202
|
+
Sex: {{sex}}<br />
|
203
|
+
{{/list_people}}
|
204
|
+
```
|
205
|
+
|
206
|
+
Block helpers can also contain an `{{else}}` statement, which, when used creates
|
207
|
+
a second set of block contents (called `inverse`) which can be yielded to the output:
|
208
|
+
|
209
|
+
```ruby
|
210
|
+
FS.register_helper(:isFemale) do |person,&block|
|
211
|
+
if person.sex == 'female'
|
212
|
+
block.call.contents
|
213
|
+
else
|
214
|
+
block.call.inverse
|
215
|
+
end
|
216
|
+
end
|
217
|
+
```
|
218
|
+
|
219
|
+
You can also register an existing method:
|
220
|
+
|
221
|
+
```ruby
|
222
|
+
def isFemale(person)
|
223
|
+
if person.sex == 'female'
|
224
|
+
yield.contents
|
225
|
+
else
|
226
|
+
yield.inverse
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
FS.register_helper(method(:isFemale))
|
231
|
+
```
|
232
|
+
|
233
|
+
Which could be used like so:
|
234
|
+
|
235
|
+
```handlebars
|
236
|
+
{{#isFemale person}}
|
237
|
+
{{person.name}} is female.
|
238
|
+
{{else}}
|
239
|
+
{{person.name}} is male.
|
240
|
+
{{/isFemale}}
|
241
|
+
```
|
242
|
+
|
243
|
+
### Using Partials
|
244
|
+
|
245
|
+
Handlebars allows you to register a partial either as a function or a string template with
|
246
|
+
the engine before compiling, FlavourSaver retains this behaviour (with the notable exception
|
247
|
+
of within Rails - see below).
|
248
|
+
|
249
|
+
To register a partial you call `FlavourSaver.register_partial` with a name and a string:
|
250
|
+
|
251
|
+
```ruby
|
252
|
+
FlavourSaver.register_partial(:my_partial, "{{this}} is a partial")
|
253
|
+
```
|
254
|
+
|
255
|
+
You can then use this partial within your templates:
|
256
|
+
|
257
|
+
```handlebars
|
258
|
+
{{#each people}}{{> my_partial this}}{{/each}}
|
259
|
+
```
|
260
|
+
|
261
|
+
## Using with Rails
|
262
|
+
|
263
|
+
One potential gotcha of using FlavourSaver with Rails is that FlavourSaver doesn't let you
|
264
|
+
have any access to the controller's instance variables. This is done to maintain compatibility
|
265
|
+
with the original JavaScript implementation of Handlebars so that templates can be used on
|
266
|
+
both the server and client side without any change.
|
267
|
+
|
268
|
+
When accessing controller instance variables you should access them by way of a helper method
|
269
|
+
or a presenter object.
|
270
|
+
|
271
|
+
For example, in `ApplicationController.rb` you may have a `before_filter` which authenticates
|
272
|
+
the current user's session cookie and stores it in the controller's `@current_user` instance
|
273
|
+
variable.
|
274
|
+
|
275
|
+
To access this variable you could create a simple helper method in `ApplicationHelpers`:
|
276
|
+
|
277
|
+
```ruby
|
278
|
+
def current_user
|
279
|
+
@current_user
|
280
|
+
end
|
281
|
+
```
|
282
|
+
|
283
|
+
Which would mean that you are able to access it in your template:
|
284
|
+
|
285
|
+
```handlebars
|
286
|
+
{{#if current_user}}
|
287
|
+
Welcome back, {{current_user.first_name}}!
|
288
|
+
{{/if}}
|
289
|
+
```
|
290
|
+
|
291
|
+
## Using the Tilt Interface Directly
|
292
|
+
|
293
|
+
You can use the registered Tilt interface directly to render template strings with a hash of template variables.
|
294
|
+
|
295
|
+
The Tilt template's `render` method expects an object that can respond to messages using dot notation. In the following example, the template variable `{{foo}}` will result in a call to `.foo` on the `data` object. For this reason the `data` object can't be a simple hash. A model would work, but if you have a plain old Ruby hash, use it to create a new OpenStruct object, which will provide the dot notation needed.
|
296
|
+
|
297
|
+
```ruby
|
298
|
+
template = Tilt['handlebars'].new { "{{foo}} {{bar}}" }
|
299
|
+
data = OpenStruct.new foo: "hello", bar: "world"
|
300
|
+
|
301
|
+
template.render data # => "hello world"
|
302
|
+
```
|
303
|
+
|
304
|
+
### Special behaviour of Handlebars' partial syntax
|
305
|
+
|
306
|
+
In Handlebars.js all partial templates must be pre-registered with the engine before they are
|
307
|
+
able to be used. When running inside Rails FlavourSaver modifies this behaviour to use Rails'
|
308
|
+
render partial helper:
|
309
|
+
|
310
|
+
```handlebars
|
311
|
+
{{> my_partial}}
|
312
|
+
```
|
313
|
+
|
314
|
+
Will be translated into:
|
315
|
+
|
316
|
+
```ruby
|
317
|
+
render :partial => 'my_partial'
|
318
|
+
```
|
319
|
+
|
320
|
+
Handlebars allows you to send a context object into the partial, which sets the execution
|
321
|
+
context of the partial. In Rails this behaviour would be confusing and non-standard, so
|
322
|
+
instead any argument passed to the partial is evaluated and passed to the partial's
|
323
|
+
`:object` argument:
|
324
|
+
|
325
|
+
```handlebars
|
326
|
+
{{> my_partial my_context}}
|
327
|
+
```
|
328
|
+
|
329
|
+
```ruby
|
330
|
+
render :partial => 'my_partial', :object => my_context
|
331
|
+
```
|
332
|
+
|
333
|
+
## Contributing
|
334
|
+
|
335
|
+
1. Fork it
|
336
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
337
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
338
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
339
|
+
5. Create new Pull Request
|