volt 0.8.5 → 0.8.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -0
- data/Readme.md +65 -38
- data/VERSION +1 -1
- data/app/volt/views/notices/index.html +13 -13
- data/docs/JAVASCRIPT_COMPONENTS.md +2 -0
- data/lib/volt/router/routes.rb +4 -4
- data/lib/volt/server/html_parser/attribute_scope.rb +3 -3
- data/lib/volt/server/html_parser/each_scope.rb +9 -5
- data/lib/volt/server/html_parser/sandlebars_parser.rb +9 -6
- data/lib/volt/server/html_parser/textarea_scope.rb +2 -2
- data/lib/volt/server/html_parser/view_scope.rb +36 -24
- data/spec/apps/kitchen_sink/app/main/config/routes.rb +1 -1
- data/spec/apps/kitchen_sink/app/main/views/main/bindings.html +53 -30
- data/spec/apps/kitchen_sink/app/main/views/main/main.html +4 -4
- data/spec/integration/bindings_spec.rb +10 -0
- data/spec/router/routes_spec.rb +16 -16
- data/spec/server/html_parser/sandlebars_parser_spec.rb +4 -3
- data/spec/server/html_parser/view_parser_spec.rb +17 -17
- data/templates/project/app/main/views/main/main.html.tt +4 -4
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cc39b7688dcb264420fc550e644030913c5c3a02
|
4
|
+
data.tar.gz: 1cc906e44c38dbb1cb76839f128c484107ed11a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 336d0323272ede308bce021ef3b790271b4c87734697d3344872b566de11a817fb7ae00c3930e51725d3b2b3d98fe302b274f3404005534f229fa8f6b34b2fcd
|
7
|
+
data.tar.gz: 62e5f58fb0d95740b833aa2265b556e7e585ec9c8b79603864dbec3c8f1a4dd2b05864ca150c5c38b649985128c9e19980715535ab4438fa383e842903b410ea
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
# 0.8.6 - Oct 5, 2014
|
2
|
+
|
3
|
+
- Major changes to the templating system (to address common concerns and make things simpler).
|
4
|
+
1. All binding now takes place between {{ and }} instead of { and } (double stash instead of single stash) Escaping is still with a tripple stash {{{ escap{{ed}} }}} => escap{{ed}}
|
5
|
+
2. Bindings can now be (almost) any ruby code. No more #'s at the beginning. Blocks are now closed with {{ end }}
|
6
|
+
If's are now: {{ if _something }} ... {{ elsif _other }} .. {{ else }} .. {{ end }}
|
7
|
+
Each's are now: {{ _items.each do |item| }} ... {{ end }}
|
8
|
+
Template bindings are now: {{ template "path" }} (along with other options)
|
9
|
+
|
10
|
+
Each should use do notation not brackets. Also, .each is not actually called, the binding is parsed and converted into a EachBinding. Other Eneumerable methods do not work at this time, only each. (more coming soon)
|
11
|
+
3. Bindings in routes now use double stashes as well get '/products/{{ _name }}/info'
|
12
|
+
4. To help clean things up, we reccomend spaces between {{ and }}
|
13
|
+
|
14
|
+
|
1
15
|
# 0.8.4 - Oct 4, 2014
|
2
16
|
|
3
17
|
- Added configuration for databases.
|
data/Readme.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
[![Gem Version](https://badge.fury.io/rb/volt.
|
2
|
-
[![Code Climate](
|
3
|
-
[![Build Status](
|
1
|
+
[![Gem Version](https://badge.fury.io/rb/volt.svg)](http://badge.fury.io/rb/volt)
|
2
|
+
[![Code Climate](http://img.shields.io/codeclimate/github/voltrb/volt.svg)](https://codeclimate.com/github/voltrb/volt)
|
3
|
+
[![Build Status](http://img.shields.io/travis/voltrb/volt/master.svg)](https://travis-ci.org/voltrb/volt)
|
4
4
|
[![Inline docs](http://inch-ci.org/github/voltrb/volt.svg?branch=master)](http://inch-ci.org/github/voltrb/volt)
|
5
|
-
[![Volt Chat](https://badges.gitter.im/
|
5
|
+
[![Volt Chat](https://badges.gitter.im/Join Chat.svg)](https://gitter.im/voltrb/volt)
|
6
6
|
|
7
7
|
** For the current status of volt, read: http://voltframework.com/blog
|
8
8
|
|
@@ -213,52 +213,52 @@ Sections help you split up different parts of the same content (title and body u
|
|
213
213
|
|
214
214
|
## Bindings
|
215
215
|
|
216
|
-
|
216
|
+
In Volt, you code your views in a handlebars like template language. Volt provides several bindings, which handle rendering of something for you. Content bindings are anything inbetween {{ and }}.
|
217
217
|
|
218
218
|
### Content binding
|
219
219
|
|
220
220
|
The most basic binding is a content binding:
|
221
221
|
|
222
222
|
```html
|
223
|
-
<p>{some_method}<p>
|
223
|
+
<p>{{ some_method }}<p>
|
224
224
|
```
|
225
225
|
|
226
|
-
The content binding runs the Ruby code between { and }, then renders the return value.
|
226
|
+
The content binding runs the Ruby code between {{ and }}, then renders the return value. Any time the data a content binding relies on changes, the binding will run again and update the text
|
227
227
|
|
228
228
|
### If binding
|
229
229
|
|
230
230
|
An if binding lets you provide basic flow control.
|
231
231
|
|
232
232
|
```html
|
233
|
-
{
|
233
|
+
{{ if _some_check? }}
|
234
234
|
<p>render this</p>
|
235
|
-
{
|
235
|
+
{{ end }}
|
236
236
|
```
|
237
237
|
|
238
|
-
Blocks are closed with a {
|
238
|
+
Blocks are closed with a {{ end }}
|
239
239
|
|
240
240
|
When the if binding is rendered, it will run the ruby code after #if. If the code is true it will render the code below. Again, if the returned value is reactive, it will update as that value changes.
|
241
241
|
|
242
242
|
If bindings can also have #elsif and #else blocks.
|
243
243
|
|
244
244
|
```html
|
245
|
-
{
|
245
|
+
{{ if _condition_1? }}
|
246
246
|
<p>condition 1 true</p>
|
247
|
-
{
|
247
|
+
{{ elsif _condition_2? }}
|
248
248
|
<p>condition 2 true</p>
|
249
|
-
{
|
249
|
+
{{ else }}
|
250
250
|
<p>neither true</p>
|
251
|
-
{
|
251
|
+
{{ end }}
|
252
252
|
```
|
253
253
|
|
254
254
|
### Each binding
|
255
255
|
|
256
|
-
For iteration over objects,
|
256
|
+
For iteration over objects, you can use .each
|
257
257
|
|
258
258
|
```html
|
259
|
-
{
|
260
|
-
<p>{item}</p>
|
261
|
-
{
|
259
|
+
{{ _items.each do |item| }}
|
260
|
+
<p>{{ item }}</p>
|
261
|
+
{{ end }}
|
262
262
|
```
|
263
263
|
|
264
264
|
Above, if _items were an array, the block would be rendered for each item, setting 'item' to the value of the array element.
|
@@ -266,9 +266,9 @@ Above, if _items were an array, the block would be rendered for each item, setti
|
|
266
266
|
You can also access the position of the item in the array with the #index method.
|
267
267
|
|
268
268
|
```html
|
269
|
-
{
|
270
|
-
<p>{index}. {item}</p>
|
271
|
-
{
|
269
|
+
{{ each _items as item }}
|
270
|
+
<p>{{ index }}. {{ item }}</p>
|
271
|
+
{{ end }}
|
272
272
|
```
|
273
273
|
|
274
274
|
For the array: ['one', 'two', 'three'] this would print:
|
@@ -277,7 +277,7 @@ For the array: ['one', 'two', 'three'] this would print:
|
|
277
277
|
1. two
|
278
278
|
2. three
|
279
279
|
|
280
|
-
You can do {index + 1} to correct the zero offset.
|
280
|
+
You can do {{ index + 1 }} to correct the zero offset.
|
281
281
|
|
282
282
|
When items are removed or added to the array, the #each binding automatically and intelligently adds or removes the items from/to the DOM.
|
283
283
|
|
@@ -286,19 +286,19 @@ When items are removed or added to the array, the #each binding automatically an
|
|
286
286
|
Bindings can also be placed inside of attributes.
|
287
287
|
|
288
288
|
```html
|
289
|
-
<p class="{
|
289
|
+
<p class="{{ if _is_cool? }}cool{{ end }}">Text</p>
|
290
290
|
```
|
291
291
|
|
292
292
|
There are some special features provided to make elements work as "two way bindings":
|
293
293
|
|
294
294
|
```html
|
295
|
-
<input type="text" value="{_name}" />
|
295
|
+
<input type="text" value="{{ _name }}" />
|
296
296
|
```
|
297
297
|
|
298
298
|
In the example above, if _name changes, the field will update, and if the field is updated, _name will be changed:
|
299
299
|
|
300
300
|
```html
|
301
|
-
<input type="checkbox" checked="{_checked}" />
|
301
|
+
<input type="checkbox" checked="{{ _checked }}" />
|
302
302
|
```
|
303
303
|
|
304
304
|
If the value of a checked attribute is true, the checkbox will be shown checked. If it's checked or unchecked, the value will be updated to true or false.
|
@@ -306,8 +306,8 @@ If the value of a checked attribute is true, the checkbox will be shown checked.
|
|
306
306
|
Radio buttons bind to a checked state as well, except instead of setting the value to true or false, they set it to a supplied field value.
|
307
307
|
|
308
308
|
```html
|
309
|
-
<input type="radio" checked="{_radio}" value="one" />
|
310
|
-
<input type="radio" checked="{_radio}" value="two" />
|
309
|
+
<input type="radio" checked="{{ _radio }}" value="one" />
|
310
|
+
<input type="radio" checked="{{ _radio }}" value="two" />
|
311
311
|
```
|
312
312
|
|
313
313
|
When a radio button is checked, whatever checked is bound to is set to the field's value. When the checked binding value is changed, any radio buttons where the binding's value matches the fields value are checked. NOTE: This seems to be the most useful behaviour for radio buttons.
|
@@ -315,7 +315,7 @@ When a radio button is checked, whatever checked is bound to is set to the field
|
|
315
315
|
Select boxes can be bound to a value (while not technically a property, this is another convient behavior we add).
|
316
316
|
|
317
317
|
```html
|
318
|
-
<select value="{_rating}">
|
318
|
+
<select value="{{ _rating }}">
|
319
319
|
<option value="1">*</option>
|
320
320
|
<option value="2">**</option>
|
321
321
|
<option value="3">***</option>
|
@@ -328,12 +328,20 @@ When the selected option of the select above changes, ```_rating``` is changed t
|
|
328
328
|
|
329
329
|
If you have a controller at app/home/controller/index_controller.rb, and a view at app/home/views/index/index.html, all methods called are called on the controller.
|
330
330
|
|
331
|
+
### Template Bindings
|
332
|
+
|
333
|
+
All views/*.html files are templates that can be rendered inside of other views using the template binding.
|
334
|
+
|
335
|
+
```html
|
336
|
+
{{ template "header" }}
|
337
|
+
```
|
338
|
+
|
331
339
|
## Escaping
|
332
340
|
|
333
|
-
When you need to use { and } outside of bindings, anything in a triple mustache will be escaped and not processed as a binding:
|
341
|
+
When you need to use {{ and }} outside of bindings, anything in a triple mustache will be escaped and not processed as a binding:
|
334
342
|
|
335
343
|
```html
|
336
|
-
{{{ bindings look like: {this} }}}
|
344
|
+
{{{ bindings look like: {{this}} }}}
|
337
345
|
```
|
338
346
|
|
339
347
|
# Models
|
@@ -693,6 +701,8 @@ A controller can be any class in Volt, however it is common to have that class i
|
|
693
701
|
end
|
694
702
|
```
|
695
703
|
|
704
|
+
When a model is set, any missing methods will be proxied to the model. This lets you bind within the views without prefixing the model object every time. It also lets you change out the current model and have the views update automatically.
|
705
|
+
|
696
706
|
In methods, the `#model` method returns the current model.
|
697
707
|
|
698
708
|
See the [provided collections](#provided-collections) section for a list of the available collection models.
|
@@ -701,7 +711,7 @@ You can also provide your own object to model.
|
|
701
711
|
|
702
712
|
In the example above, any methods not defined on the TodosController will fall through to the provided model. All views in views/{controller_name} will have this controller as the target for any Ruby run in their bindings. This means that calls on self (implicit or with self.) will have the model as their target (after calling through the controller). This lets you add methods to the controller to control how the model is handled, or provide extra methods to the views.
|
703
713
|
|
704
|
-
Volt is more similar to an MVVM architecture than an MVC architecture. Instead of the controllers passing data off to the views, the controllers are the context for the views. When using a ModelController, the controller automatically forwards all methods it does not handle to the model. This is convenient since you can set a model in the controller and then access its properties directly with methods in bindings. This lets you do something like ```{_name}``` instead of something like ```{@model._name}```
|
714
|
+
Volt is more similar to an MVVM architecture than an MVC architecture. Instead of the controllers passing data off to the views, the controllers are the context for the views. When using a ModelController, the controller automatically forwards all methods it does not handle to the model. This is convenient since you can set a model in the controller and then access its properties directly with methods in bindings. This lets you do something like ```{{ _name }}``` instead of something like ```{{ @model._name }}```
|
705
715
|
|
706
716
|
Controllers in the app/home component do not need to be namespaced, all other components should namespace controllers like so:
|
707
717
|
|
@@ -905,23 +915,40 @@ Once the view file for the control or template is found, it will look for a matc
|
|
905
915
|
|
906
916
|
# Control Arguments/Attributes
|
907
917
|
|
908
|
-
Like other html tags, controls can be passed attributes. These are then converted into
|
918
|
+
Like other html tags, controls can be passed attributes. These are then converted into an object that is passed as the first argument to the initialize method on the controller. The standard ModelController's initialize will then assign the object to the attrs property which can be accessed with ```#attrs``` This makes it easy to access attributes passed in.
|
909
919
|
|
910
920
|
```html
|
911
921
|
|
912
922
|
<:Body>
|
913
923
|
|
914
924
|
<ul>
|
915
|
-
{
|
916
|
-
<:todo name="{todo._name}" />
|
917
|
-
{
|
925
|
+
{{ _todos.each do |todo| }}
|
926
|
+
<:todo name="{{ todo._name }}" />
|
927
|
+
{{ end }}
|
918
928
|
</ul>
|
919
929
|
|
920
930
|
<:Todo>
|
921
|
-
<li>{
|
922
|
-
|
931
|
+
<li>{{ attrs.name }}</li>
|
923
932
|
```
|
924
933
|
|
934
|
+
Instead of passing in individual attributes, you can also pass in a Model object with the "model" attribute and it will be set as the model for the controller.
|
935
|
+
|
936
|
+
```html
|
937
|
+
<:Body>
|
938
|
+
<ul>
|
939
|
+
{{ _todos.each do |todo| }}
|
940
|
+
<:todo model="{{ todo }}" />
|
941
|
+
{{ end }}
|
942
|
+
</ul>
|
943
|
+
|
944
|
+
<:Todo>
|
945
|
+
<li>
|
946
|
+
{{ _name }} -
|
947
|
+
{{ if _complete }}
|
948
|
+
Complete
|
949
|
+
{{ end }}
|
950
|
+
</li>
|
951
|
+
```
|
925
952
|
|
926
953
|
# Routes
|
927
954
|
|
@@ -944,7 +971,7 @@ When the params are changed, the URL will be set to the path for the route whose
|
|
944
971
|
Route paths can also contain variables similar to bindings:
|
945
972
|
|
946
973
|
```ruby
|
947
|
-
get "/todos/{_index}", _view: 'todos'
|
974
|
+
get "/todos/{{ _index }}", _view: 'todos'
|
948
975
|
```
|
949
976
|
|
950
977
|
In the case above, if any URL matches /todos/*, (where * is anything but a slash), it will be the active route. ```params._view``` would be set to 'todos', and ```params._index``` would be set to the value in the path.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.8.
|
1
|
+
0.8.6
|
@@ -1,20 +1,20 @@
|
|
1
1
|
<:Body>
|
2
|
-
{
|
2
|
+
{{ if page._reloading }}
|
3
3
|
<div class="notices alert alert-info">Reloading...</div>
|
4
|
-
{
|
5
|
-
{
|
4
|
+
{{ end }}
|
5
|
+
{{ if channel.status == :reconnecting }}
|
6
6
|
<div class="notices alert alert-info">
|
7
|
-
Connection Lost... {channel.error}...
|
8
|
-
{
|
7
|
+
Connection Lost... {{ channel.error }}...
|
8
|
+
{{ if channel.reconnect_interval }} Reconnecting in {{ (channel.reconnect_interval / 1000.0).round }} sec{{ end }}
|
9
9
|
</div>
|
10
|
-
{
|
11
|
-
{
|
10
|
+
{{ end }}
|
11
|
+
{{ if page._reconnected }}
|
12
12
|
<div class="notices alert alert-success">Reconnected!</div>
|
13
|
-
{
|
14
|
-
{
|
13
|
+
{{ end }}
|
14
|
+
{{ if !flash.empty? }}
|
15
15
|
<div class="notices alert alert-info" e-click="flash.clear">
|
16
|
-
{
|
17
|
-
<p>{notice}</p>
|
18
|
-
{
|
16
|
+
{{ flash._notices.each do |notice| }}
|
17
|
+
<p>{{ notice }}</p>
|
18
|
+
{{ end }}
|
19
19
|
</div>
|
20
|
-
{
|
20
|
+
{{ end }}
|
data/lib/volt/router/routes.rb
CHANGED
@@ -157,7 +157,7 @@ class Routes
|
|
157
157
|
|
158
158
|
parts.each_with_index do |part, index|
|
159
159
|
if has_binding?(part)
|
160
|
-
params[part[
|
160
|
+
params[part[2...-2].strip.to_sym] = index
|
161
161
|
|
162
162
|
# Set the part to be '*' (anything matcher)
|
163
163
|
part = '*'
|
@@ -178,7 +178,7 @@ class Routes
|
|
178
178
|
if has_binding?(part)
|
179
179
|
# Setup a nil param that can match anything, but gets
|
180
180
|
# assigned into the url
|
181
|
-
params[part[
|
181
|
+
params[part[2...-2].strip.to_sym] = nil
|
182
182
|
end
|
183
183
|
end
|
184
184
|
|
@@ -197,7 +197,7 @@ class Routes
|
|
197
197
|
url = parts.map do |part|
|
198
198
|
val = if has_binding?(part)
|
199
199
|
# Get the
|
200
|
-
binding = part[
|
200
|
+
binding = part[2...-2].strip.to_sym
|
201
201
|
input_params.delete(binding)
|
202
202
|
else
|
203
203
|
part
|
@@ -254,7 +254,7 @@ class Routes
|
|
254
254
|
|
255
255
|
# Check if a string has a binding in it
|
256
256
|
def has_binding?(string)
|
257
|
-
string.index('{') && string.index('}')
|
257
|
+
string.index('{{') && string.index('}}')
|
258
258
|
end
|
259
259
|
|
260
260
|
end
|
@@ -32,8 +32,8 @@ module AttributeScope
|
|
32
32
|
end
|
33
33
|
|
34
34
|
def process_attribute(tag_name, attributes, attribute_name, value)
|
35
|
-
parts = value.split(/(\{[^\}]+\})/).reject(&:blank?)
|
36
|
-
binding_count = parts.count {|p| p[0] == '{' && p[-1] == '}'}
|
35
|
+
parts = value.split(/(\{\{[^\}]+\}\})/).reject(&:blank?)
|
36
|
+
binding_count = parts.count {|p| p[0] == '{' && p[1] == '{' && p[-2] == '}' && p[-1] == '}'}
|
37
37
|
|
38
38
|
# if this attribute has bindings
|
39
39
|
if binding_count > 0
|
@@ -69,7 +69,7 @@ module AttributeScope
|
|
69
69
|
|
70
70
|
# Add an attribute binding on the tag, bind directly to the getter in the binding
|
71
71
|
def add_single_attribute(id, attribute_name, parts)
|
72
|
-
getter = parts[0][
|
72
|
+
getter = parts[0][2...-2].strip
|
73
73
|
|
74
74
|
# if getter.index('@')
|
75
75
|
# raise "Bindings currently do not support instance variables"
|
@@ -1,18 +1,22 @@
|
|
1
1
|
class EachScope < ViewScope
|
2
2
|
def initialize(handler, path, content)
|
3
3
|
super(handler, path)
|
4
|
-
@content, @variable_name = content.strip.split(/ as /)
|
4
|
+
# @content, @variable_name = content.strip.split(/ as /)
|
5
|
+
|
6
|
+
@content, @variable_name = content.split(/.each\s+do\s+\|/)
|
7
|
+
|
8
|
+
@variable_name = @variable_name.gsub(/\|/, '')
|
5
9
|
end
|
6
|
-
|
10
|
+
|
7
11
|
def close_scope
|
8
12
|
binding_number = @handler.scope[-2].binding_number
|
9
13
|
@handler.scope[-2].binding_number += 1
|
10
14
|
@path += "/__template/#{binding_number}"
|
11
|
-
|
15
|
+
|
12
16
|
super
|
13
|
-
|
17
|
+
|
14
18
|
@handler.html << "<!-- $#{binding_number} --><!-- $/#{binding_number} -->"
|
15
19
|
@handler.scope.last.save_binding(binding_number, "lambda { |__p, __t, __c, __id| EachBinding.new(__p, __t, __c, __id, Proc.new { #{@content} }, #{@variable_name.inspect}, #{@path.inspect}) }")
|
16
|
-
|
20
|
+
|
17
21
|
end
|
18
22
|
end
|
@@ -82,9 +82,12 @@ class SandlebarsParser
|
|
82
82
|
end
|
83
83
|
|
84
84
|
text(@html[1])
|
85
|
-
elsif (binding = @html.scan(/\{/))
|
85
|
+
elsif (binding = @html.scan(/\{\{/))
|
86
86
|
# We are in text mode and matched the start of a binding
|
87
87
|
start_binding
|
88
|
+
elsif (text = @html.scan(/\{/))
|
89
|
+
# A single { outside of a binding
|
90
|
+
text(text)
|
88
91
|
elsif (text = @html.scan(/(?:[^\<\{]+)/))
|
89
92
|
# matched text up until the next html tag
|
90
93
|
text(text)
|
@@ -106,16 +109,16 @@ class SandlebarsParser
|
|
106
109
|
binding = ''
|
107
110
|
open_count = 1
|
108
111
|
|
109
|
-
# scan until we reach a { or }
|
112
|
+
# scan until we reach a {{ or }}
|
110
113
|
loop do
|
111
|
-
binding << @html.scan_until(/(
|
114
|
+
binding << @html.scan_until(/(\{\{|\}\}|\n|\Z)/)
|
112
115
|
|
113
116
|
match = @html[1]
|
114
|
-
if match == '}'
|
117
|
+
if match == '}}'
|
115
118
|
# close
|
116
119
|
open_count -= 1
|
117
120
|
break if open_count == 0
|
118
|
-
elsif match == '{'
|
121
|
+
elsif match == '{{'
|
119
122
|
# open more
|
120
123
|
open_count += 1
|
121
124
|
elsif match == "\n" || @html.eos?
|
@@ -127,7 +130,7 @@ class SandlebarsParser
|
|
127
130
|
end
|
128
131
|
end
|
129
132
|
|
130
|
-
binding = binding[0..-
|
133
|
+
binding = binding[0..-3]
|
131
134
|
@handler.binding(binding) if @handler.respond_to?(:binding)
|
132
135
|
end
|
133
136
|
|
@@ -6,7 +6,7 @@ class TextareaScope < ViewScope
|
|
6
6
|
end
|
7
7
|
|
8
8
|
def add_binding(content)
|
9
|
-
@html << "{#{content}}"
|
9
|
+
@html << "{{#{content}}}"
|
10
10
|
end
|
11
11
|
|
12
12
|
def close_scope(pop=true)
|
@@ -15,7 +15,7 @@ class TextareaScope < ViewScope
|
|
15
15
|
|
16
16
|
attributes = @attributes
|
17
17
|
|
18
|
-
if @html[/\{[^\}]+\}/]
|
18
|
+
if @html[/\{\{[^\}]+\}\}/]
|
19
19
|
# If the html inside the textarea has a binding, process it as
|
20
20
|
# a value attribute.
|
21
21
|
attributes['value'] = @html
|
@@ -20,33 +20,42 @@ class ViewScope
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def add_binding(content)
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
23
|
+
content = content.strip
|
24
|
+
index = content.index(/[ \(]/)
|
25
|
+
if index
|
26
|
+
first_symbol = content[0...index]
|
27
|
+
args = content[index..-1].strip
|
28
|
+
|
29
|
+
case first_symbol
|
30
|
+
when 'if'
|
31
|
+
add_if(args)
|
32
|
+
when 'elsif'
|
33
|
+
add_else(args)
|
34
|
+
when 'else'
|
35
|
+
if args.blank?
|
35
36
|
add_else(nil)
|
36
37
|
else
|
37
|
-
raise "
|
38
|
+
raise "else does not take a conditional, #{content} was provided."
|
39
|
+
end
|
40
|
+
when 'template'
|
41
|
+
add_template(args)
|
42
|
+
else
|
43
|
+
if content =~ /.each\s+do\s+\|/
|
44
|
+
add_each(content)
|
45
|
+
else
|
46
|
+
add_content_binding(content)
|
38
47
|
end
|
39
|
-
when '#each'
|
40
|
-
add_each(content)
|
41
|
-
when '#template'
|
42
|
-
add_template(content)
|
43
48
|
end
|
44
|
-
when '/'
|
45
|
-
# close binding
|
46
|
-
close_scope
|
47
49
|
else
|
48
|
-
|
49
|
-
|
50
|
+
case content
|
51
|
+
when 'end'
|
52
|
+
# Close the binding
|
53
|
+
close_scope
|
54
|
+
when 'else'
|
55
|
+
add_else(nil)
|
56
|
+
else
|
57
|
+
add_content_binding(content)
|
58
|
+
end
|
50
59
|
end
|
51
60
|
end
|
52
61
|
|
@@ -71,6 +80,9 @@ class ViewScope
|
|
71
80
|
end
|
72
81
|
|
73
82
|
def add_template(content)
|
83
|
+
# Strip ( and ) from the outsides
|
84
|
+
content = content.strip.gsub(/^\(/, '').gsub(/\)$/, '')
|
85
|
+
|
74
86
|
@handler.html << "<!-- $#{@binding_number} --><!-- $/#{@binding_number} -->"
|
75
87
|
save_binding(@binding_number, "lambda { |__p, __t, __c, __id| TemplateBinding.new(__p, __t, __c, __id, #{@path.inspect}, Proc.new { [#{content}] }) }")
|
76
88
|
|
@@ -100,8 +112,8 @@ class ViewScope
|
|
100
112
|
|
101
113
|
data_hash = []
|
102
114
|
attributes.each_pair do |name, value|
|
103
|
-
parts = value.split(/(\{[^\}]+\})/).reject(&:blank?)
|
104
|
-
binding_count = parts.count {|p| p[0] == '{' && p[-1] == '}'}
|
115
|
+
parts = value.split(/(\{\{[^\}]+\}\})/).reject(&:blank?)
|
116
|
+
binding_count = parts.count {|p| p[0] == '{' && p[1] == '{' && p[-2] == '}' && p[-1] == '}'}
|
105
117
|
|
106
118
|
# if this attribute has bindings
|
107
119
|
if binding_count > 0
|
@@ -4,32 +4,31 @@
|
|
4
4
|
<:Body>
|
5
5
|
<h1>Bindings</h1>
|
6
6
|
|
7
|
-
|
8
7
|
<h2>Text</h2>
|
9
8
|
<table class="table">
|
10
9
|
<tr>
|
11
10
|
<td>Page</td>
|
12
|
-
<td><input type="text" id="pageName1" value="{page._name}"></td>
|
13
|
-
<td><input type="text" id="pageName2" value="{page._name}"></td>
|
14
|
-
<td id="pageName3">{page._name}</td>
|
11
|
+
<td><input type="text" id="pageName1" value="{{ page._name }}"></td>
|
12
|
+
<td><input type="text" id="pageName2" value="{{ page._name }}"></td>
|
13
|
+
<td id="pageName3">{{ page._name }}</td>
|
15
14
|
</tr>
|
16
15
|
<tr>
|
17
16
|
<td>Params</td>
|
18
|
-
<td><input type="text" id="paramsName1" value="{params._name}"></td>
|
19
|
-
<td><input type="text" id="paramsName2" value="{params._name}"></td>
|
20
|
-
<td id="paramsName3">{params._name}</td>
|
17
|
+
<td><input type="text" id="paramsName1" value="{{ params._name }}"></td>
|
18
|
+
<td><input type="text" id="paramsName2" value="{{ params._name }}"></td>
|
19
|
+
<td id="paramsName3">{{ params._name }}</td>
|
21
20
|
</tr>
|
22
21
|
<tr>
|
23
22
|
<td>Routes</td>
|
24
|
-
<td><input type="text" id="routesName1" value="{params._route_test}"></td>
|
25
|
-
<td><input type="text" id="routesName2" value="{params._route_test}"></td>
|
26
|
-
<td id="routesName3">{params._route_test}</td>
|
23
|
+
<td><input type="text" id="routesName1" value="{{ params._route_test }}"></td>
|
24
|
+
<td><input type="text" id="routesName2" value="{{ params._route_test }}"></td>
|
25
|
+
<td id="routesName3">{{ params._route_test }}</td>
|
27
26
|
</tr>
|
28
27
|
<tr>
|
29
28
|
<td>Local Store</td>
|
30
|
-
<td><input type="text" id="localstoreName1" value="{local_store._name}"></td>
|
31
|
-
<td><input type="text" id="localstoreName2" value="{local_store._name}"></td>
|
32
|
-
<td id="localstoreName3">{local_store._name}</td>
|
29
|
+
<td><input type="text" id="localstoreName1" value="{{ local_store._name }}"></td>
|
30
|
+
<td><input type="text" id="localstoreName2" value="{{ local_store._name }}"></td>
|
31
|
+
<td id="localstoreName3">{{ local_store._name }}</td>
|
33
32
|
</tr>
|
34
33
|
</table>
|
35
34
|
|
@@ -38,15 +37,15 @@
|
|
38
37
|
<table class="table">
|
39
38
|
<tr>
|
40
39
|
<td>Page</td>
|
41
|
-
<td><input type="checkbox" id="pageCheck1" checked="{page._check}" /></td>
|
42
|
-
<td><input type="checkbox" id="pageCheck2" checked="{page._check}" /></td>
|
43
|
-
<td id="pageCheck3">{page._check}</td>
|
40
|
+
<td><input type="checkbox" id="pageCheck1" checked="{{ page._check }}" /></td>
|
41
|
+
<td><input type="checkbox" id="pageCheck2" checked="{{ page._check }}" /></td>
|
42
|
+
<td id="pageCheck3">{{ page._check }}</td>
|
44
43
|
</tr>
|
45
44
|
<tr>
|
46
45
|
<td>Params</td>
|
47
|
-
<td><input type="checkbox" id="paramsCheck1" checked="{params._check}" /></td>
|
48
|
-
<td><input type="checkbox" id="paramsCheck2" checked="{params._check}" /></td>
|
49
|
-
<td id="paramsCheck3">{params._check}</td>
|
46
|
+
<td><input type="checkbox" id="paramsCheck1" checked="{{ params._check }}" /></td>
|
47
|
+
<td><input type="checkbox" id="paramsCheck2" checked="{{ params._check }}" /></td>
|
48
|
+
<td id="paramsCheck3">{{ params._check }}</td>
|
50
49
|
</tr>
|
51
50
|
</table>
|
52
51
|
|
@@ -55,14 +54,14 @@
|
|
55
54
|
<table class="table">
|
56
55
|
<tr>
|
57
56
|
<td>Page</td>
|
58
|
-
<td><input type="radio" id="pageRadio1" checked="{page._radio}" value="one" /></td>
|
59
|
-
<td><input type="radio" id="pageRadio2" checked="{page._radio}" value="two" /></td>
|
60
|
-
<td id="pageRadio3">{page._radio}</td>
|
57
|
+
<td><input type="radio" id="pageRadio1" checked="{{ page._radio }}" value="one" /></td>
|
58
|
+
<td><input type="radio" id="pageRadio2" checked="{{ page._radio }}" value="two" /></td>
|
59
|
+
<td id="pageRadio3">{{ page._radio }}</td>
|
61
60
|
</tr>
|
62
61
|
<tr>
|
63
62
|
<td></td>
|
64
|
-
<td><input type="radio" checked="{page._radio}" value="one" /></td>
|
65
|
-
<td><input type="radio" checked="{page._radio}" value="two" /></td>
|
63
|
+
<td><input type="radio" checked="{{ page._radio }}" value="one" /></td>
|
64
|
+
<td><input type="radio" checked="{{ page._radio }}" value="two" /></td>
|
66
65
|
<td></td>
|
67
66
|
</tr>
|
68
67
|
</table>
|
@@ -72,16 +71,16 @@
|
|
72
71
|
<tr>
|
73
72
|
<td>Page</td>
|
74
73
|
<td>
|
75
|
-
<select id="pageSelect1" value="{page._select}">
|
74
|
+
<select id="pageSelect1" value="{{ page._select }}">
|
76
75
|
<option value="one">One</option>
|
77
76
|
<option value="two">Two</option>
|
78
77
|
<option value="three">Three</option>
|
79
78
|
</select>
|
80
79
|
</td>
|
81
80
|
<td>
|
82
|
-
<input type="text" id="pageSelect2" value="{page._select}" />
|
81
|
+
<input type="text" id="pageSelect2" value="{{ page._select }}" />
|
83
82
|
</td>
|
84
|
-
<td id="pageSelect3">{page._select}</td>
|
83
|
+
<td id="pageSelect3">{{ page._select }}</td>
|
85
84
|
</tr>
|
86
85
|
</table>
|
87
86
|
|
@@ -90,9 +89,33 @@
|
|
90
89
|
<table class="table">
|
91
90
|
<tr>
|
92
91
|
<td>Page</td>
|
93
|
-
<td><textarea id="textareaName1">{page._textarea_name}</textarea></td>
|
94
|
-
<td><textarea id="textareaName2">{page._textarea_name}</textarea></td>
|
95
|
-
<td id="textareaName3">{page._textarea_name}</td>
|
92
|
+
<td><textarea id="textareaName1">{{ page._textarea_name }}</textarea></td>
|
93
|
+
<td><textarea id="textareaName2">{{ page._textarea_name }}</textarea></td>
|
94
|
+
<td id="textareaName3">{{ page._textarea_name }}</td>
|
95
|
+
</tr>
|
96
|
+
</table>
|
97
|
+
|
98
|
+
<h2>URL</h2>
|
99
|
+
<table class="table">
|
100
|
+
<tr>
|
101
|
+
<td>Path</td>
|
102
|
+
<td id="urlPath">{{ url.path }}</td>
|
103
|
+
</tr>
|
104
|
+
<tr>
|
105
|
+
<td>Query</td>
|
106
|
+
<td id="urlPath">{{ url.query }}</td>
|
107
|
+
<td><input type="text" value="{{ url.query }}" /></td>
|
108
|
+
</tr>
|
109
|
+
<tr>
|
110
|
+
<td>Fragment</td>
|
111
|
+
<td id="urlPath">{{ url.fragment }}</td>
|
112
|
+
<td><input type="text" value="{{ url.fragment }}" /></td>
|
96
113
|
</tr>
|
97
114
|
</table>
|
98
115
|
|
116
|
+
|
117
|
+
<h2>Content</h2>
|
118
|
+
|
119
|
+
<p id="escapeContent">{{{this is {{escaped}}}}}</p>
|
120
|
+
|
121
|
+
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<:Title>
|
2
|
-
{
|
2
|
+
{{ template main_path, "title", {controller_group: 'main'} }} - KitchenSink
|
3
3
|
|
4
4
|
<:Body>
|
5
5
|
<div class="container">
|
@@ -14,7 +14,7 @@
|
|
14
14
|
|
15
15
|
<:volt:notices />
|
16
16
|
|
17
|
-
{
|
17
|
+
{{ template main_path, 'body', {controller_group: 'main'} }}
|
18
18
|
|
19
19
|
<div class="footer">
|
20
20
|
<p>© Company 2014</p>
|
@@ -23,7 +23,7 @@
|
|
23
23
|
</div>
|
24
24
|
|
25
25
|
<:Nav>
|
26
|
-
<li class="{
|
27
|
-
<a href="{attrs.href}">{attrs.text}</a>
|
26
|
+
<li class="{{ if url.path.split('/')[1] == attrs.href.split('/')[1] }}active{{ end }}">
|
27
|
+
<a href="{{ attrs.href }}">{{ attrs.text }}</a>
|
28
28
|
</li>
|
29
29
|
|
@@ -192,5 +192,15 @@ if ENV['BROWSER']
|
|
192
192
|
end
|
193
193
|
end
|
194
194
|
end
|
195
|
+
|
196
|
+
describe "content escaping" do
|
197
|
+
it 'should escape in a tripple stash' do
|
198
|
+
visit '/'
|
199
|
+
|
200
|
+
click_link 'Bindings'
|
201
|
+
|
202
|
+
expect(find('#escapeContent')).to have_content('this is {{escaped}}')
|
203
|
+
end
|
204
|
+
end
|
195
205
|
end
|
196
206
|
end
|
data/spec/router/routes_spec.rb
CHANGED
@@ -19,8 +19,8 @@ describe Routes do
|
|
19
19
|
|
20
20
|
it "should setup indiect routes" do
|
21
21
|
routes do
|
22
|
-
get '/blog/{_id}/edit', _view: 'blog/edit'
|
23
|
-
get '/blog/{_id}', _view: 'blog/show'
|
22
|
+
get '/blog/{{ _id }}/edit', _view: 'blog/edit'
|
23
|
+
get '/blog/{{ _id }}', _view: 'blog/show'
|
24
24
|
end
|
25
25
|
|
26
26
|
indirect_routes = @routes.instance_variable_get(:@indirect_routes)
|
@@ -41,11 +41,11 @@ describe Routes do
|
|
41
41
|
it "should match routes" do
|
42
42
|
routes do
|
43
43
|
get "/blog", _view: 'blog'
|
44
|
-
get '/blog/{_id}', _view: 'blog/show'
|
45
|
-
get '/blog/{_id}/draft', _view: 'blog/draft', _action: 'draft'
|
46
|
-
get '/blog/{_id}/edit', _view: 'blog/edit'
|
47
|
-
get '/blog/tags/{_tag}', _view: 'blog/tag'
|
48
|
-
get '/login/{_name}/user/{_id}', _view: 'login', _action: 'user'
|
44
|
+
get '/blog/{{ _id }}', _view: 'blog/show'
|
45
|
+
get '/blog/{{ _id }}/draft', _view: 'blog/draft', _action: 'draft'
|
46
|
+
get '/blog/{{ _id }}/edit', _view: 'blog/edit'
|
47
|
+
get '/blog/tags/{{ _tag }}', _view: 'blog/tag'
|
48
|
+
get '/login/{{ _name }}/user/{{ _id }}', _view: 'login', _action: 'user'
|
49
49
|
end
|
50
50
|
|
51
51
|
params = @routes.url_to_params('/blog')
|
@@ -74,10 +74,10 @@ describe Routes do
|
|
74
74
|
it "should setup param matchers" do
|
75
75
|
routes do
|
76
76
|
get "/blog", _view: 'blog'
|
77
|
-
get '/blog/{_id}', _view: 'blog/show'
|
78
|
-
get '/blog/{_id}/edit', _view: 'blog/edit'
|
79
|
-
get '/blog/tags/{_tag}', _view: 'blog/tag'
|
80
|
-
get '/login/{_name}/user/{_id}', _view: 'login', _action: 'user'
|
77
|
+
get '/blog/{{ _id }}', _view: 'blog/show'
|
78
|
+
get '/blog/{{ _id }}/edit', _view: 'blog/edit'
|
79
|
+
get '/blog/tags/{{ _tag }}', _view: 'blog/tag'
|
80
|
+
get '/login/{{ _name }}/user/{{ _id }}', _view: 'login', _action: 'user'
|
81
81
|
end
|
82
82
|
|
83
83
|
param_matches = @routes.instance_variable_get(:@param_matches)
|
@@ -94,10 +94,10 @@ describe Routes do
|
|
94
94
|
it "should go from params to url" do
|
95
95
|
routes do
|
96
96
|
get "/blog", _view: 'blog'
|
97
|
-
get '/blog/{_id}', _view: 'blog/show'
|
98
|
-
get '/blog/{_id}/edit', _view: 'blog/edit'
|
99
|
-
get '/blog/tags/{_tag}', _view: 'blog/tag'
|
100
|
-
get '/login/{_name}/user/{_id}', _view: 'login', _action: 'user'
|
97
|
+
get '/blog/{{ _id }}', _view: 'blog/show'
|
98
|
+
get '/blog/{{ _id }}/edit', _view: 'blog/edit'
|
99
|
+
get '/blog/tags/{{ _tag }}', _view: 'blog/tag'
|
100
|
+
get '/login/{{ _name }}/user/{{ _id }}', _view: 'login', _action: 'user'
|
101
101
|
end
|
102
102
|
|
103
103
|
url, params = @routes.params_to_url({_view: 'blog/show', _id: '55'})
|
@@ -162,7 +162,7 @@ describe Routes do
|
|
162
162
|
|
163
163
|
routes do
|
164
164
|
get '/', _controller: 'index'
|
165
|
-
get '/blog/{_id}', _controller: 'blog'
|
165
|
+
get '/blog/{{ _id }}', _controller: 'blog'
|
166
166
|
end
|
167
167
|
|
168
168
|
params = @routes.url_to_params('/blog/20')
|
@@ -1,5 +1,6 @@
|
|
1
1
|
if RUBY_PLATFORM == 'opal'
|
2
2
|
else
|
3
|
+
require 'spec_helper'
|
3
4
|
require 'benchmark'
|
4
5
|
require 'volt/server/html_parser/sandlebars_parser'
|
5
6
|
|
@@ -103,21 +104,21 @@ describe SandlebarsParser do
|
|
103
104
|
end
|
104
105
|
|
105
106
|
it "should raise an exception on an unclosed binding at the end of the document" do
|
106
|
-
html = "<p>testing with {nested"
|
107
|
+
html = "<p>testing with {{nested"
|
107
108
|
|
108
109
|
handler = HTMLHandler.new
|
109
110
|
expect { SandlebarsParser.new(html, handler) }.to raise_error(HTMLParseError)
|
110
111
|
end
|
111
112
|
|
112
113
|
it "should raise an exception on an unclosed binding" do
|
113
|
-
html = "<p>testing with {nested </p>\n<p>ok</p>"
|
114
|
+
html = "<p>testing with {{nested </p>\n<p>ok</p>"
|
114
115
|
|
115
116
|
handler = HTMLHandler.new
|
116
117
|
expect { SandlebarsParser.new(html, handler) }.to raise_error(HTMLParseError)
|
117
118
|
end
|
118
119
|
|
119
120
|
it "should report the line number" do
|
120
|
-
html = "\n\n<p>some paragraph</p>\n\n<p>testing with {nested </p>\n<p>ok</p>"
|
121
|
+
html = "\n\n<p>some paragraph</p>\n\n<p>testing with {{nested </p>\n<p>ok</p>"
|
121
122
|
|
122
123
|
handler = HTMLHandler.new
|
123
124
|
expect { SandlebarsParser.new(html, handler) }.to raise_error(HTMLParseError, "unclosed binding: {nested </p> on line: 5")
|
@@ -5,7 +5,7 @@ require 'volt/server/html_parser/view_parser'
|
|
5
5
|
|
6
6
|
describe ViewParser do
|
7
7
|
it "should parse content bindings" do
|
8
|
-
html = "<p>Some {content} binding, {name}</p>"
|
8
|
+
html = "<p>Some {{ content }} binding, {{ name }}</p>"
|
9
9
|
|
10
10
|
view = ViewParser.new(html, "main/main/main")
|
11
11
|
|
@@ -24,13 +24,13 @@ describe ViewParser do
|
|
24
24
|
html = <<-END
|
25
25
|
<p>
|
26
26
|
Some
|
27
|
-
{
|
27
|
+
{{ if showing == :text }}
|
28
28
|
text
|
29
|
-
{
|
29
|
+
{{ elsif showing == :button }}
|
30
30
|
<button>Button</button>
|
31
|
-
{
|
31
|
+
{{ else }}
|
32
32
|
<a href="">link</a>
|
33
|
-
{
|
33
|
+
{{ end }}
|
34
34
|
</p>
|
35
35
|
END
|
36
36
|
|
@@ -63,13 +63,13 @@ describe ViewParser do
|
|
63
63
|
html = <<-END
|
64
64
|
<p>
|
65
65
|
Some
|
66
|
-
{
|
67
|
-
{
|
66
|
+
{{ if showing == :text }}
|
67
|
+
{{ if sub_item }}
|
68
68
|
sub item text
|
69
|
-
{
|
70
|
-
{
|
69
|
+
{{ end }}
|
70
|
+
{{ else }}
|
71
71
|
other
|
72
|
-
{
|
72
|
+
{{ end }}
|
73
73
|
</p>
|
74
74
|
END
|
75
75
|
|
@@ -105,9 +105,9 @@ describe ViewParser do
|
|
105
105
|
it "should parse each bindings" do
|
106
106
|
html = <<-END
|
107
107
|
<div class="main">
|
108
|
-
{
|
109
|
-
<p>{item}</p>
|
110
|
-
{
|
108
|
+
{{ _items.each do |item| }}
|
109
|
+
<p>{{ item }}</p>
|
110
|
+
{{ end }}
|
111
111
|
</div>
|
112
112
|
END
|
113
113
|
|
@@ -137,7 +137,7 @@ describe ViewParser do
|
|
137
137
|
|
138
138
|
it "should parse a single attribute binding" do
|
139
139
|
html = <<-END
|
140
|
-
<div class="{main_class}">
|
140
|
+
<div class="{{ main_class }}">
|
141
141
|
</div>
|
142
142
|
END
|
143
143
|
|
@@ -148,7 +148,7 @@ describe ViewParser do
|
|
148
148
|
|
149
149
|
it "should parse multiple attribute bindings in a single attribute" do
|
150
150
|
html = <<-END
|
151
|
-
<div class="start {main_class} {awesome_class} string">
|
151
|
+
<div class="start {{ main_class }} {{ awesome_class }} string">
|
152
152
|
</div>
|
153
153
|
END
|
154
154
|
|
@@ -179,7 +179,7 @@ describe ViewParser do
|
|
179
179
|
|
180
180
|
it "should parse a template" do
|
181
181
|
html = <<-END
|
182
|
-
{
|
182
|
+
{{ template "/home/temp/path" }}
|
183
183
|
END
|
184
184
|
|
185
185
|
view = ViewParser.new(html, "main/main/main")
|
@@ -259,7 +259,7 @@ describe ViewParser do
|
|
259
259
|
|
260
260
|
it "should setup bindings for textarea values" do
|
261
261
|
html = <<-END
|
262
|
-
<textarea name="cool">{awesome}</textarea>
|
262
|
+
<textarea name="cool">{{ awesome }}</textarea>
|
263
263
|
END
|
264
264
|
|
265
265
|
view = ViewParser.new(html, "main/main/main")
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<:Title>
|
2
|
-
{
|
2
|
+
{{ template main_path, "title", {controller_group: 'main'} }}
|
3
3
|
|
4
4
|
<:Body>
|
5
5
|
<div class="container">
|
@@ -13,7 +13,7 @@
|
|
13
13
|
|
14
14
|
<:volt:notices />
|
15
15
|
|
16
|
-
{
|
16
|
+
{{ template main_path, 'body', {controller_group: 'main'} }}
|
17
17
|
|
18
18
|
<div class="footer">
|
19
19
|
<p>© Company 2014</p>
|
@@ -22,7 +22,7 @@
|
|
22
22
|
</div>
|
23
23
|
|
24
24
|
<:Nav>
|
25
|
-
<li class="{
|
26
|
-
<a href="{attrs.href}">{attrs.text}</a>
|
25
|
+
<li class="{{ if url.path.split('/')[1] == attrs.href.split('/')[1] }}active{{ end }}">
|
26
|
+
<a href="{{ attrs.href }}">{{ attrs.text }}</a>
|
27
27
|
</li>
|
28
28
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: volt
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Stout
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-10-
|
11
|
+
date: 2014-10-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -368,6 +368,7 @@ files:
|
|
368
368
|
- bin/volt
|
369
369
|
- docs/FAQ.md
|
370
370
|
- docs/GETTING_STARTED.md
|
371
|
+
- docs/JAVASCRIPT_COMPONENTS.md
|
371
372
|
- docs/WHY.md
|
372
373
|
- docs/volt-logo.jpg
|
373
374
|
- lib/volt.rb
|