volt 0.7.18 → 0.7.19
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Readme.md +120 -82
- data/VERSION +1 -1
- data/lib/volt/cli.rb +1 -1
- data/lib/volt/controllers/model_controller.rb +0 -2
- data/lib/volt/models/persistors/model_store.rb +10 -5
- data/lib/volt/models/validations.rb +2 -2
- data/lib/volt/page/tasks.rb +1 -1
- data/lib/volt/reactive/reactive_value.rb +0 -5
- data/lib/volt/server/html_parser/view_scope.rb +1 -1
- data/lib/volt/server/socket_connection_handler.rb +1 -0
- data/spec/server/html_parser/view_parser_spec.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e52814d6fe0018d0ed9ff87c726b083c0d97c6d6
|
4
|
+
data.tar.gz: e45c97fe12819f7ab1abe17d9d0166379fa61a30
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fccff09f4fe9c5ddeaf5d41a8e8fe4e284c9f1af137a11578ba08f6eafc336a6b13e27c4b3bf54665e6d6933c6c3e4f328db1b574e514a49764d188a223a64d7
|
7
|
+
data.tar.gz: ee4387876d87956bb39127f2fad009f42fc7c1078e50b409a1fa3f4c88a2e91c1ebf4b267b56acdaa711faceff03e6f8d402ceb3beeb56462247f63bbf8a1c6e
|
data/Readme.md
CHANGED
@@ -1,26 +1,27 @@
|
|
1
1
|
[![Gem Version](https://badge.fury.io/rb/volt.png)](http://badge.fury.io/rb/volt)
|
2
2
|
[![Code Climate](https://codeclimate.com/github/voltrb/volt.png)](https://codeclimate.com/github/voltrb/volt)
|
3
3
|
[![Build Status](https://travis-ci.org/voltrb/volt.png?branch=master)](https://travis-ci.org/voltrb/volt)
|
4
|
+
[![Volt Chat](https://badges.gitter.im/voltrb/volt.png)](https://gitter.im/voltrb/volt)
|
5
|
+
[![Pledgie](https://pledgie.com/campaigns/26731.png?skin_name=chrome)](https://pledgie.com/campaigns/26731)
|
4
6
|
|
5
|
-
|
6
|
-
|
7
|
-
---
|
7
|
+
=======
|
8
|
+
** For the current status of volt, read: http://voltframework.com/blog
|
8
9
|
|
9
10
|
# Volt
|
10
11
|
|
11
|
-
Volt is a
|
12
|
+
Volt is a Ruby web framework where your ruby code runs on both the server and the client (via [opal](https://github.com/opal/opal)). The DOM automatically update as the user interacts with the page. Page state can be stored in the URL. If the user hits a URL directly, the HTML will first be rendered on the server for faster load times and easier indexing by search engines.
|
12
13
|
|
13
|
-
Instead of syncing data between the client and server via HTTP,
|
14
|
+
Instead of syncing data between the client and server via HTTP, Volt uses a persistent connection between the client and server. When data is updated on one client, it is updated in the database and any other listening clients (with almost no setup code needed).
|
14
15
|
|
15
|
-
Pages HTML is written in a handlebars
|
16
|
+
Pages HTML is written in a handlebars-like template language. Volt uses data flow/reactive programming to automatically and intelligently propagate changes to the DOM (or anything other code wanting to know when a value updates). When something in the DOM changes, Volt intelligently updates only the nodes that need to be changed.
|
16
17
|
|
17
18
|
See some demo videos here:
|
18
|
-
- [
|
19
|
-
- [
|
20
|
-
- [
|
19
|
+
- [Volt Todos Example](https://www.youtube.com/watch?v=6ZIvs0oKnYs)
|
20
|
+
- [Build a Blog with Volt](https://www.youtube.com/watch?v=c478sMlhx1o)
|
21
|
+
- [Reactive Values in Volt](https://www.youtube.com/watch?v=yZIQ-2irY-Q)
|
21
22
|
|
22
23
|
Check out demo apps:
|
23
|
-
- https://github.com/voltrb/
|
24
|
+
- https://github.com/voltrb/todos3
|
24
25
|
- https://github.com/voltrb/blog
|
25
26
|
- https://github.com/voltrb/contactsdemo
|
26
27
|
|
@@ -30,10 +31,10 @@ Check out demo apps:
|
|
30
31
|
Volt has the following goals:
|
31
32
|
|
32
33
|
1. Developer happiness
|
33
|
-
2. Write once on the client and
|
34
|
+
2. Write once on the client and server
|
34
35
|
3. Automatic data syncing between client and server
|
35
36
|
4. Apps are built as nested components. Components can be shared (via gems)
|
36
|
-
5. Concurrent. Volt provides tools to simplify concurrency. Component rendering is done in parallel on the server
|
37
|
+
5. Concurrent. Volt provides tools to simplify concurrency. Component rendering is done in parallel on the server
|
37
38
|
6. Intelligent asset management
|
38
39
|
7. Secure (shouldn't need to be said, but it does)
|
39
40
|
8. Be fast/light
|
@@ -44,18 +45,16 @@ Volt has the following goals:
|
|
44
45
|
|
45
46
|
Many of the core Volt features are implemented. We still have a bit to go before 1.0, most of it involving models.
|
46
47
|
|
47
|
-
1.
|
48
|
-
2.
|
49
|
-
3.
|
50
|
-
4.
|
51
|
-
5. Full managed render loop (for fast rendering)
|
52
|
-
6. Fix N+1 issue with some reactive values (I know how to fix, just haven't gotten around to doing it)
|
48
|
+
1. Reactive model queries
|
49
|
+
2. Reactive Enumerators with Blocks (.map .count, etc…)
|
50
|
+
3. Full managed render loop (for fast rendering)
|
51
|
+
4. Fix N+1 issue with some reactive values (I know how to fix it, just haven't gotten around to doing it)
|
53
52
|
|
54
53
|
# VOLT guide
|
55
54
|
|
56
|
-
This guide will take you through creating a basic web application in Volt. This tutorial assumes a basic knowledge of
|
55
|
+
This guide will take you through creating a basic web application in Volt. This tutorial assumes a basic knowledge of Ruby and web development.
|
57
56
|
|
58
|
-
To get started, install
|
57
|
+
To get started, install Volt:
|
59
58
|
|
60
59
|
gem install volt
|
61
60
|
|
@@ -63,11 +62,11 @@ Then create a new project:
|
|
63
62
|
|
64
63
|
volt new project_name
|
65
64
|
|
66
|
-
This will setup a basic project. Now
|
65
|
+
This will setup a basic project. Now let's run the server:
|
67
66
|
|
68
67
|
volt server
|
69
68
|
|
70
|
-
You can access the
|
69
|
+
You can access the Volt console with:
|
71
70
|
|
72
71
|
volt console
|
73
72
|
|
@@ -90,15 +89,16 @@ You can access the volt console with:
|
|
90
89
|
3. [Model Events](#model-events)
|
91
90
|
4. [Automatic Model Conversion](#automatic-model-conversion)
|
92
91
|
5. [Controllers](#controllers)
|
93
|
-
6. [
|
92
|
+
6. [Tasks](#tasks)
|
93
|
+
7. [Components](#components)
|
94
94
|
1. [Dependencies](#dependencies)
|
95
95
|
2. [Assets](#assets)
|
96
96
|
3. [Component Generator](#component-generator)
|
97
97
|
4. [Provided Components](#provided-components)
|
98
98
|
1. [Notices](#notices)
|
99
99
|
2. [Flash](#flash)
|
100
|
-
|
101
|
-
|
100
|
+
8. [Controls](#controls)
|
101
|
+
9. [Routes](#routes)
|
102
102
|
1. [Routes file](#routes-file)
|
103
103
|
|
104
104
|
|
@@ -106,10 +106,10 @@ You can access the volt console with:
|
|
106
106
|
|
107
107
|
Volt is still a work in progress, but early feedback is appreciated. Use the following to communicate with the developers, someone will get back to you very quickly:
|
108
108
|
|
109
|
-
- **If you need help**: post on [stackoverflow.com](http://www.stackoverflow.com)
|
109
|
+
- **If you need help**: post on [stackoverflow.com](http://www.stackoverflow.com). Be sure to tag your question with `voltrb`.
|
110
110
|
- **If you found a bug**: post on [github issues](https://github.com/voltrb/volt/issues)
|
111
111
|
- **If you have an idea or need a feature**: post on [github issues](https://github.com/voltrb/volt/issues)
|
112
|
-
- **If you want to discuss
|
112
|
+
- **If you want to discuss Volt**: use #voltrb on freenode.
|
113
113
|
|
114
114
|
|
115
115
|
# Rendering
|
@@ -123,7 +123,7 @@ For example when a user clicks to add a new todo item to a todo list, we might c
|
|
123
123
|
|
124
124
|
The idea of "reactive programming" has been used to simplify maintaining the DOM. The idea is instead of having event handlers that manage a model (or JavaScript object) and manage the DOM, we have event handlers that manage reactive data models. We describe our DOM layer in a declarative way so that it automatically knows how to render our data models.
|
125
125
|
|
126
|
-
## Reactive
|
126
|
+
## Reactive Values
|
127
127
|
|
128
128
|
To build bindings, Volt provides the ReactiveValue class. This wraps any object in a reactive interface. To create a ReactiveValue, simply pass the object you want to wrap as the first argument to new.
|
129
129
|
|
@@ -132,9 +132,9 @@ To build bindings, Volt provides the ReactiveValue class. This wraps any object
|
|
132
132
|
# => @"my object"
|
133
133
|
```
|
134
134
|
|
135
|
-
When
|
135
|
+
When `#inspect` is called on a ReactiveValue (like in the console), an '@' is placed in front of the value's inspect string, so you know it's reactive.
|
136
136
|
|
137
|
-
When you call a method on a ReactiveValue, you get back a new reactive value that depends on the previous one. It remembers how it was created and you can call
|
137
|
+
When you call a method on a ReactiveValue, you get back a new reactive value that depends on the previous one. It remembers how it was created and you can call `#cur` on it any time to get its current value, which will be computed based off of the first reactive value. (Keep in mind below that + is a method call, the same as `a.+(b)` in ruby.)
|
138
138
|
|
139
139
|
```ruby
|
140
140
|
a = ReactiveValue.new(1)
|
@@ -156,9 +156,9 @@ When you call a method on a ReactiveValue, you get back a new reactive value tha
|
|
156
156
|
# => 7
|
157
157
|
```
|
158
158
|
|
159
|
-
This provides the backbone for reactive programming. We setup computation/flow graphs instead of doing an actual calculation. Calling
|
159
|
+
This provides the backbone for reactive programming. We setup computation/flow graphs instead of doing an actual calculation. Calling `#cur` (or `#inspect`, `#to_s`, etc..) runs the computation and returns the current value at that time, based on all of its dependencies.
|
160
160
|
|
161
|
-
|
161
|
+
ReactiveValues also let you setup listeners and trigger events:
|
162
162
|
|
163
163
|
```ruby
|
164
164
|
a = ReactiveValue.new(0)
|
@@ -167,7 +167,7 @@ ReactiveValue's also let you setup listeners and trigger events:
|
|
167
167
|
# => A Changed
|
168
168
|
```
|
169
169
|
|
170
|
-
These events propagate to any reactive
|
170
|
+
These events propagate to any reactive values created off of a reactive value:
|
171
171
|
|
172
172
|
```ruby
|
173
173
|
a = ReactiveValue.new(1)
|
@@ -180,7 +180,7 @@ These events propagate to any reactive value's created off of a reactive value.
|
|
180
180
|
|
181
181
|
This event flow lets us know when an object has changed, so we can update everything that depended on that object.
|
182
182
|
|
183
|
-
Lastly, we can also pass in other reactive
|
183
|
+
Lastly, we can also pass in other reactive values as arguments to methods on a reactive value. The dependencies will be tracked for both and events will propagate down from both. (Also, note that calling `#cur=` to update the current value triggers a "changed" event.)
|
184
184
|
|
185
185
|
```ruby
|
186
186
|
a = ReactiveValue.new(1)
|
@@ -200,27 +200,27 @@ Lastly, we can also pass in other reactive value's as arguments to methods on a
|
|
200
200
|
# => C changed
|
201
201
|
```
|
202
202
|
|
203
|
-
### ReactiveValue
|
203
|
+
### ReactiveValue Gotchas
|
204
204
|
|
205
|
-
There are a few simple things to keep in mind with
|
205
|
+
There are a few simple things to keep in mind with ReactiveValues. In order to make them mostly compatible with other Ruby objects, two methods do not return another ReactiveValue.
|
206
206
|
|
207
207
|
to_s and inspect
|
208
208
|
|
209
|
-
If you want these to be used reactively, see the section on [with](#with)
|
209
|
+
If you want these to be used reactively, see the section on [with](#with).
|
210
210
|
|
211
|
-
Also, due to a small limitation in ruby,
|
211
|
+
Also, due to a small limitation in ruby, ReactiveValues always are truthy. See the [truthy checks](#truthy-checks-true-false-or-and-and) section on how to check for truth.
|
212
212
|
|
213
|
-
When passing something that may contain reactive values to a JS function, you can call
|
213
|
+
When passing something that may contain reactive values to a JS function, you can call ```#deep_cur``` on any object to get back a copy that will have all reactive values turned into their current value.
|
214
214
|
|
215
215
|
### Current Status
|
216
216
|
|
217
|
-
NOTE: currently
|
217
|
+
NOTE: currently ReactiveValues are not complete. At the moment, they do not handle methods that are passed blocks (or procs, lambdas). This is planned, but not complete. At the moment you can use [with](#with) to accomplish similar things.
|
218
218
|
|
219
219
|
### Truthy Checks: .true?, .false?, .or, and .and
|
220
220
|
|
221
221
|
Because a method on a reactive value always returns another reactive value, and because only nil and false are false in ruby, we need a way to check if a ReactiveValue is truthy in our code. The easiest way to do this is by calling .true? on it. It will return a non-wrapped boolean. .nil? and .false? do as you would expect.
|
222
222
|
|
223
|
-
One common place we use a truthy check is in setting up default values with || (logical or) Volt provides a
|
223
|
+
One common place we use a truthy check is in setting up default values with || (logical or) Volt provides a convenient method that does the same thing `#or`, but works with ReactiveValues.
|
224
224
|
|
225
225
|
Instead of
|
226
226
|
|
@@ -234,14 +234,14 @@ Simply use:
|
|
234
234
|
a.or(b)
|
235
235
|
```
|
236
236
|
|
237
|
-
|
237
|
+
`#and` works the same way as &&. #and and #or let you maintain the reactivity all of the way through.
|
238
238
|
|
239
239
|
|
240
240
|
### With
|
241
241
|
|
242
|
-
Normally when you want to have a value that depends on another value, but transforms it somehow, you simply call your transform method on the ReactiveValue. However sometimes the transform is not directly on the
|
242
|
+
Normally when you want to have a value that depends on another value, but transforms it somehow, you simply call your transform method on the ReactiveValue. However sometimes the transform is not directly on the ReactiveValues object.
|
243
243
|
|
244
|
-
You can call
|
244
|
+
You can call `#with` on any ReactiveValue. `#with` will return a new ReactiveValue that depends on the current ReactiveValue. `#with` takes a block, the first argument to the block will be the cur value of the ReactiveValue you called `#with` on. Any additional arguments to `#with` will be passed in after the first one. If you pass another ReactiveValue as an argument to `#with`, the returned ReactiveValue will depend on the argument ReactiveValue as well, and the block will receive the arguments cur value.
|
245
245
|
|
246
246
|
```ruby
|
247
247
|
a = ReactiveValue.new(5)
|
@@ -252,7 +252,7 @@ You can call .with on any ReactiveValue. .with will return a new ReactiveValue
|
|
252
252
|
|
253
253
|
# Views
|
254
254
|
|
255
|
-
Views in Volt
|
255
|
+
Views in Volt use a templating language similar to handlebars. They can be broken up into sections. A section header looks like the following:
|
256
256
|
|
257
257
|
```html
|
258
258
|
<:Body>
|
@@ -260,11 +260,11 @@ Views in Volt are use a templating language similar to handlebars. They can be
|
|
260
260
|
|
261
261
|
Section headers should start with a capital letter so as not to be confused with [controls](#controls). Section headers do not use closing tags. If section headers are not provided, the Body section is assumed.
|
262
262
|
|
263
|
-
|
263
|
+
Sections help you split up different parts of the same content (title and body usually), but within the same file.
|
264
264
|
|
265
265
|
## Bindings
|
266
266
|
|
267
|
-
|
267
|
+
Once you understand the basics of ReactiveValues, we can discuss bindings. 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 }.
|
268
268
|
|
269
269
|
### Content binding
|
270
270
|
|
@@ -274,7 +274,7 @@ The most basic binding is a content binding:
|
|
274
274
|
<p>{some_method}<p>
|
275
275
|
```
|
276
276
|
|
277
|
-
The content binding runs the
|
277
|
+
The content binding runs the Ruby code between { and }, then renders the return value. If the returned value is a ReactiveValue, it will update the value updated whenever a 'changed' event is triggered on the reactive value.
|
278
278
|
|
279
279
|
### If binding
|
280
280
|
|
@@ -288,7 +288,7 @@ An if binding lets you provide basic flow control.
|
|
288
288
|
|
289
289
|
Blocks are closed with a {/}
|
290
290
|
|
291
|
-
When the
|
291
|
+
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.
|
292
292
|
|
293
293
|
If bindings can also have #elsif and #else blocks.
|
294
294
|
|
@@ -312,7 +312,7 @@ For iteration over objects, the each binding is provided.
|
|
312
312
|
{/}
|
313
313
|
```
|
314
314
|
|
315
|
-
Above, if _items
|
315
|
+
Above, if _items were an array, the block would be rendered for each item, setting 'item' to the value of the array element.
|
316
316
|
|
317
317
|
You can also access the position of the item in the array with the #index method.
|
318
318
|
|
@@ -330,7 +330,7 @@ For the array: ['one', 'two', 'three'] this would print:
|
|
330
330
|
|
331
331
|
You can do {index + 1} to correct the zero offset.
|
332
332
|
|
333
|
-
When items are removed or added to the array, the #each binding automatically and intelligently
|
333
|
+
When items are removed or added to the array, the #each binding automatically and intelligently adds or removes the items from/to the DOM.
|
334
334
|
|
335
335
|
## Attribute Bindings
|
336
336
|
|
@@ -340,19 +340,19 @@ Bindings can also be placed inside of attributes.
|
|
340
340
|
<p class="{#if _is_cool?}cool{/}">Text</p>
|
341
341
|
```
|
342
342
|
|
343
|
-
There are some special features provided to make
|
343
|
+
There are some special features provided to make elements work as "two way bindings":
|
344
344
|
|
345
345
|
```html
|
346
346
|
<input type="text" value="{_name}" />
|
347
347
|
```
|
348
348
|
|
349
|
-
In the example above, if _name changes, the field will update and if the field is updated, _name will be changed
|
349
|
+
In the example above, if _name changes, the field will update, and if the field is updated, _name will be changed:
|
350
350
|
|
351
351
|
```html
|
352
352
|
<input type="checkbox" checked="{_checked}" />
|
353
353
|
```
|
354
354
|
|
355
|
-
If the value of a checked attribute is true, the checkbox will be shown checked.
|
355
|
+
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.
|
356
356
|
|
357
357
|
-- TODO: select boxes
|
358
358
|
|
@@ -360,7 +360,7 @@ If you have a controller at app/home/controller/index_controller.rb, and a view
|
|
360
360
|
|
361
361
|
## Escaping
|
362
362
|
|
363
|
-
When you need to use { and } outside of bindings
|
363
|
+
When you need to use { and } outside of bindings, anything in a triple mustache will be escaped and not processed as a binding:
|
364
364
|
|
365
365
|
```html
|
366
366
|
{{{ bindings look like: {this} }}}
|
@@ -370,7 +370,7 @@ When you need to use { and } outside of bindings. Anything in a triple mustache
|
|
370
370
|
|
371
371
|
Volt's concept of a model is slightly different from many frameworks where a model is the name for the ORM to the database. In Volt a model is a class where you can store data easily. Models can be created with a "Persistor", which is responsible for storing the data in the model. Models created without a persistor, simply store the data in the classes instance. Lets first see how to use a model.
|
372
372
|
|
373
|
-
Volt comes with many built-in models
|
373
|
+
Volt comes with many built-in models; one is called `page`. If you call `#page` on a controller, you will get access to the model. Models provided by Volt are automatically wrapped in a ReactiveValue so update events can be tracked.
|
374
374
|
|
375
375
|
```ruby
|
376
376
|
page._name = 'Ryan'
|
@@ -378,7 +378,7 @@ Volt comes with many built-in models, one is called 'page'. If you call #page o
|
|
378
378
|
# => @'Ryan'
|
379
379
|
```
|
380
380
|
|
381
|
-
Models act like a hash that you can access with getters and setters that start with an _
|
381
|
+
Models act like a hash that you can access with getters and setters that start with an _ . If an underscore method is called that hasn't yet been assigned, you will get back a "nil model". Prefixing with an underscore makes sure we don't accidentally try to call a method that doesn't exist and get back nil model instead of raising an exception. There is no need to define which fields a model has. Fields behave similarly to a hash, but with a different access and assignment syntax.
|
382
382
|
|
383
383
|
Models also let you nest data without creating the intermediate models:
|
384
384
|
|
@@ -393,7 +393,7 @@ Models also let you nest data without creating the intermediate models:
|
|
393
393
|
|
394
394
|
Nested data is automatically setup when assigned. In this case, page._settings is a model that is part of the page model.
|
395
395
|
|
396
|
-
You can also append to a model if
|
396
|
+
You can also append to a model if it's not defined yet. In Volt models, plural properties are assumed to contain arrays (or more specifically, ArrayModels).
|
397
397
|
|
398
398
|
```ruby
|
399
399
|
page._items << 'item 1'
|
@@ -408,7 +408,7 @@ ArrayModels can be appended to and accessed just like regular arrays.
|
|
408
408
|
|
409
409
|
## Provided Collections
|
410
410
|
|
411
|
-
Above I mentioned that Volt comes with many default collection models accessible from a controller. Each stores in a different location.
|
411
|
+
Above, I mentioned that Volt comes with many default collection models accessible from a controller. Each stores in a different location.
|
412
412
|
|
413
413
|
| Name | Storage Location |
|
414
414
|
|-----------|---------------------------------------------------------------------------|
|
@@ -423,11 +423,11 @@ Above I mentioned that Volt comes with many default collection models accessible
|
|
423
423
|
|
424
424
|
## Reactive Models
|
425
425
|
|
426
|
-
Because all models provided by Volt are wrapped in a ReactiveValue, you can register listeners on them and be updated when values change. You can also call methods on their values and get updates when the
|
426
|
+
Because all models provided by Volt are wrapped in a ReactiveValue, you can register listeners on them and be updated when values change. You can also call methods on their values and get updates when the sources change. Bindings also setup listeners. Models should be the main place you store all data in Volt. While you can use ReactiveValues manually, most of the time you will want to just use something like the controller model.
|
427
427
|
|
428
428
|
## Model Events
|
429
429
|
|
430
|
-
Models trigger events when their data is updated. Currently models emit three events: changed, added, and removed. For example:
|
430
|
+
Models trigger events when their data is updated. Currently, models emit three events: changed, added, and removed. For example:
|
431
431
|
|
432
432
|
```ruby
|
433
433
|
model = Model.new
|
@@ -467,9 +467,9 @@ For convenience, when placing a hash inside of another model, it is automaticall
|
|
467
467
|
# => Model
|
468
468
|
```
|
469
469
|
|
470
|
-
Models are accessed differently from hashes. Instead of using model[:symbol] to access, you call a method model.method_name
|
470
|
+
Models are accessed differently from hashes. Instead of using `model[:symbol]` to access, you call a method `model.method_name`. This provides a dynamic unified store where setters and getters can be added without changing any access code.
|
471
471
|
|
472
|
-
You can get a
|
472
|
+
You can get a Ruby hash back out by calling `#to_h` on a Model.
|
473
473
|
|
474
474
|
### Array -> ArrayModel
|
475
475
|
|
@@ -487,7 +487,7 @@ Arrays inside of models are automatically converted to an instance of ArrayModel
|
|
487
487
|
```
|
488
488
|
|
489
489
|
|
490
|
-
To convert a Model or an ArrayModel back to a normal hash, call .to_h or .to_a respectively. To convert them to a JavaScript Object (for passing to some JavaScript code), call
|
490
|
+
To convert a Model or an ArrayModel back to a normal hash, call .to_h or .to_a respectively. To convert them to a JavaScript Object (for passing to some JavaScript code), call `#to_n` (to native).
|
491
491
|
|
492
492
|
```ruby
|
493
493
|
user = Model.new
|
@@ -520,7 +520,7 @@ A controller can be any class in Volt, however it is common to have that class i
|
|
520
520
|
end
|
521
521
|
```
|
522
522
|
|
523
|
-
2. Calling self.
|
523
|
+
2. Calling `self.model=` in a method:
|
524
524
|
|
525
525
|
```ruby
|
526
526
|
class TodosController < ModelController
|
@@ -530,15 +530,15 @@ A controller can be any class in Volt, however it is common to have that class i
|
|
530
530
|
end
|
531
531
|
```
|
532
532
|
|
533
|
-
In methods, the
|
533
|
+
In methods, the `#model` method returns the current model.
|
534
534
|
|
535
535
|
See the [provided collections](#provided-collections) section for a list of the available collection models.
|
536
536
|
|
537
537
|
You can also provide your own object to model.
|
538
538
|
|
539
|
-
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
|
539
|
+
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.
|
540
540
|
|
541
|
-
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
|
541
|
+
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}```
|
542
542
|
|
543
543
|
Controllers in the app/home component do not need to be namespaced, all other components should namespace controllers like so:
|
544
544
|
|
@@ -554,7 +554,7 @@ Here "auth" would be the component name.
|
|
554
554
|
|
555
555
|
## Reactive Accessors
|
556
556
|
|
557
|
-
The default ModelController proxies any missing methods to its model. Since models are wrapped in
|
557
|
+
The default ModelController proxies any missing methods to its model. Since models are wrapped in ReactiveValues, they return ReactiveValues by default. Sometimes you need to store additional data reactively in the controller outside of the model. (Though often you may want to condier doing another control/controller). In this case, you can add a ```reactive_accessor```. These behave just like ```attr_accessor``` except the values assigned and returned are wrapped in a ReactiveValue. Updates update the existing ReactiveValue.
|
558
558
|
|
559
559
|
```ruby
|
560
560
|
class Contacts < ModelController
|
@@ -564,6 +564,44 @@ The default ModelController proxies any missing methods to its model. Since mod
|
|
564
564
|
|
565
565
|
Now from the view we can bind to _query while also changing in and out the model. You can also use ```reactive_reader``` and ```reactive_writer```
|
566
566
|
|
567
|
+
# Tasks
|
568
|
+
|
569
|
+
Sometimes you need to explicitly execute some code on the server. Volt solves this problem through *tasks*. You can define your own tasks by dropping a class into your component's ```tasks``` folder.
|
570
|
+
|
571
|
+
```ruby
|
572
|
+
# app/main/tasks/logging_tasks.rb
|
573
|
+
|
574
|
+
class LoggingTasks
|
575
|
+
def initialize(channel=nil, dispatcher=nil)
|
576
|
+
@channel = channel
|
577
|
+
@dispatcher = dispatcher
|
578
|
+
end
|
579
|
+
|
580
|
+
def log(message)
|
581
|
+
puts message
|
582
|
+
end
|
583
|
+
end
|
584
|
+
```
|
585
|
+
|
586
|
+
To invoke a task from a controller use ```tasks.call```.
|
587
|
+
|
588
|
+
```ruby
|
589
|
+
class Contacts < ModelController
|
590
|
+
def hello
|
591
|
+
tasks.call('LoggingTasks', 'log', 'Hello World!')
|
592
|
+
end
|
593
|
+
end
|
594
|
+
```
|
595
|
+
|
596
|
+
You can also pass a block to ```tasks.call``` that will receive the return value of your task as soon as it's done.
|
597
|
+
|
598
|
+
```ruby
|
599
|
+
tasks.call('MathTasks', 'add', 23, 5) do |result|
|
600
|
+
# result should be 28
|
601
|
+
alert result
|
602
|
+
end
|
603
|
+
```
|
604
|
+
|
567
605
|
# Components
|
568
606
|
|
569
607
|
Apps are made up of Components. Each folder under app/ is a component. When you visit a route, it loads all of the files in the component on the front end, so new pages within the component can be rendered without a new http request. If a URL is visited that routes to a different component, the request will be loaded as a normal page load and all of that components files will be loaded. You can think of components as the "reload boundary" between sections of your app.
|
@@ -593,7 +631,7 @@ Note above though that jquery and bootstrap are currently included by default.
|
|
593
631
|
|
594
632
|
**Note, asset management is still early, and likely will change quite a bit**
|
595
633
|
|
596
|
-
In
|
634
|
+
In Volt, assets such as JavaScript and CSS (or sass) are automatically included on the page for you. Anything placed inside of a components asset/js or assets/css folder is served at /assets/{js,css} (via [Sprockets](https://github.com/sstephenson/sprockets)). Link and script tags are automatically added for each css and js file in assets/css and assets/js respectively. Files are included in their lexical order, so you can add numbers in front if you need to change the load order.
|
597
635
|
|
598
636
|
Any JS/CSS from an included component or component gem will be included as well. By default [bootstrap](http://getbootstrap.com/) is provided by the volt-bootstrap gem.
|
599
637
|
|
@@ -617,7 +655,7 @@ Remove the path: option in the gemfile if you wish to use the rubygems version.
|
|
617
655
|
|
618
656
|
## Provided Components
|
619
657
|
|
620
|
-
Volt provides a few components to make web developers lives easier.
|
658
|
+
Volt provides a few components to make web developers' lives easier.
|
621
659
|
|
622
660
|
### Notices
|
623
661
|
|
@@ -641,7 +679,7 @@ These messages will show for 5 seconds, then disappear (both from the screen and
|
|
641
679
|
|
642
680
|
# Controls
|
643
681
|
|
644
|
-
Everyone wishes that we could predict the scope and required features for each part of our application, but in the real world, things we don't expect to grow large often do and things we think will be large don't end up that way. Controls let you quickly setup reusable code/views. The location of the
|
682
|
+
Everyone wishes that we could predict the scope and required features for each part of our application, but in the real world, things we don't expect to grow large often do and things we think will be large don't end up that way. Controls let you quickly setup reusable code/views. The location of the controls code can be moved as it grows without changing the way controls are invoked.
|
645
683
|
|
646
684
|
To render a control, simply use a tag like so:
|
647
685
|
|
@@ -675,7 +713,7 @@ Each part is explained below:
|
|
675
713
|
Views are composed of sections. Sections start with a ```<:SectionName>``` and are not closed. Volt will look first for a section in the same view.
|
676
714
|
|
677
715
|
2. views
|
678
|
-
Next Volt will look for a view file
|
716
|
+
Next Volt will look for a view file with the control name. If found, it will render the body section of that view.
|
679
717
|
|
680
718
|
3. view folder
|
681
719
|
Failing above, Volt will look for a view folder with the control name, and an index.html file within that folder. It will render the :body section of that view. If a controller exists for the view folder, it will make a new instance of that controller and render in that instance.
|
@@ -701,7 +739,7 @@ The above would search the following:
|
|
701
739
|
| :body | index.html | comments | blog |
|
702
740
|
| :body | index.html | comments | gems/blog |
|
703
741
|
|
704
|
-
Once the view file for the control or template is found, it will look for a matching controller. If the control is specified as a local template, an empty ModelController will be used. If a controller is found and loaded, a
|
742
|
+
Once the view file for the control or template is found, it will look for a matching controller. If the control is specified as a local template, an empty ModelController will be used. If a controller is found and loaded, a corresponding "action" method will be called on it if its exists. Action methods default to "index" unless the component or template path has two parts, in which case the last part is the action.
|
705
743
|
|
706
744
|
# Control Arguments/Attributes
|
707
745
|
|
@@ -739,9 +777,9 @@ Routes are specified on a per-component basis in the config/routes.rb file. Rou
|
|
739
777
|
|
740
778
|
Routes take two arguments, a path, and a params hash. When a new URL is loaded and the path is matched on a route, the params will be set to the params provided for that route.
|
741
779
|
|
742
|
-
When the params are changed, the URL will be set to the path for the route
|
780
|
+
When the params are changed, the URL will be set to the path for the route whose params hash matches.
|
743
781
|
|
744
|
-
Route
|
782
|
+
Route paths can also contain variables similar to bindings:
|
745
783
|
|
746
784
|
```ruby
|
747
785
|
get "/todos/{_index}", _view: 'todos'
|
@@ -749,22 +787,22 @@ Route path's can also contain variables similar to bindings.
|
|
749
787
|
|
750
788
|
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.
|
751
789
|
|
752
|
-
If ```params._view```
|
790
|
+
If ```params._view``` were 'todos' and ```params._index``` were not nil, the route would be matched.
|
753
791
|
|
754
792
|
Routes are matched top to bottom in a routes file.
|
755
793
|
|
756
794
|
## Debugging
|
757
795
|
|
758
|
-
An in browser irb is in the works. We also have source maps support, but they are currently disabled
|
796
|
+
An in browser irb is in the works. We also have source maps support, but they are currently disabled by default. To enable them run:
|
759
797
|
|
760
798
|
MAPS=true volt s
|
761
799
|
|
762
|
-
|
800
|
+
This feature is disabled by default because (due to the volume of pages rendered) it slows down page rendering. We're working with the opal and sprockets teams to make it so everything is still served in one big source maps file (which would show the files as they originated on disk)
|
763
801
|
|
764
802
|
|
765
803
|
## Channel
|
766
804
|
|
767
|
-
Controllers provide a
|
805
|
+
Controllers provide a `#channel` method, that you can use to get the status of the connection to the backend. Channel is provided in a ReactiveValue, and when the status changes, the changed events are triggered. It provides the following:
|
768
806
|
|
769
807
|
| method | description |
|
770
808
|
|-------------|-----------------------------------------------------------|
|
@@ -788,7 +826,7 @@ Volt provides a data store collection on the front-end and the back-end. In sto
|
|
788
826
|
store._things
|
789
827
|
```
|
790
828
|
|
791
|
-
**Work in
|
829
|
+
**Work in progress**
|
792
830
|
|
793
831
|
| state | events bound | description |
|
794
832
|
|-------------|--------------|--------------------------------------------------------------|
|
@@ -799,4 +837,4 @@ store._things
|
|
799
837
|
|
800
838
|
# Contributing
|
801
839
|
|
802
|
-
You want to contribute? Great! Thanks for being awesome! At the moment, we have a big internal todo list, hop on
|
840
|
+
You want to contribute? Great! Thanks for being awesome! At the moment, we have a big internal todo list, hop on https://gitter.im/voltrb/volt so we don't duplicate work. Pull requests are always welcome, but asking about helping on gitter should save some duplication.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.7.
|
1
|
+
0.7.19
|
data/lib/volt/cli.rb
CHANGED
@@ -65,11 +65,16 @@ module Persistors
|
|
65
65
|
@model.attributes[:"#{path[-4].singularize}_id"] = source._id
|
66
66
|
end
|
67
67
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
68
|
+
if !collection
|
69
|
+
puts "Attempting to save model directly on store."
|
70
|
+
raise "Attempting to save model directly on store."
|
71
|
+
else
|
72
|
+
@tasks.call('StoreTasks', 'save', collection, self_attributes) do |errors|
|
73
|
+
if errors.size == 0
|
74
|
+
promise.resolve
|
75
|
+
else
|
76
|
+
promise.reject(errors)
|
77
|
+
end
|
73
78
|
end
|
74
79
|
end
|
75
80
|
end
|
@@ -73,7 +73,7 @@ module Validations
|
|
73
73
|
if klass
|
74
74
|
validate_with(merge, klass, field_name, args)
|
75
75
|
else
|
76
|
-
raise "
|
76
|
+
raise "validation type #{validation} is not specified."
|
77
77
|
end
|
78
78
|
end
|
79
79
|
end
|
@@ -95,4 +95,4 @@ module Validations
|
|
95
95
|
puts "Unable to find #{validation} validator"
|
96
96
|
end
|
97
97
|
end
|
98
|
-
end
|
98
|
+
end
|
data/lib/volt/page/tasks.rb
CHANGED
@@ -136,10 +136,6 @@ class ReactiveValue < BasicObject
|
|
136
136
|
end
|
137
137
|
end
|
138
138
|
end
|
139
|
-
#
|
140
|
-
# def respond_to?(name, include_private=false)
|
141
|
-
# [:event_added, :event_removed].include?(name) || super
|
142
|
-
# end
|
143
139
|
|
144
140
|
def respond_to_missing?(name, include_private=false)
|
145
141
|
cur.respond_to?(name)
|
@@ -428,5 +424,4 @@ class ReactiveManager
|
|
428
424
|
def setter!(setter=nil, &block)
|
429
425
|
@setter = setter || block
|
430
426
|
end
|
431
|
-
|
432
427
|
end
|
@@ -114,7 +114,7 @@ describe ViewParser do
|
|
114
114
|
view = ViewParser.new(html, "main/main/main")
|
115
115
|
|
116
116
|
expect(view.templates).to eq({
|
117
|
-
"main/main/main/body/__template/0" => {
|
117
|
+
"main/main/main/body/__each0/__template/0" => {
|
118
118
|
"html" => "\n <p><!-- $0 --><!-- $/0 --></p>\n ",
|
119
119
|
"bindings" => {
|
120
120
|
0 => [
|
@@ -126,7 +126,7 @@ describe ViewParser do
|
|
126
126
|
"html" => " <div class=\"main\">\n <!-- $0 --><!-- $/0 -->\n </div>\n",
|
127
127
|
"bindings" => {
|
128
128
|
0 => [
|
129
|
-
"lambda { |__p, __t, __c, __id| EachBinding.new(__p, __t, __c, __id, Proc.new { _items }, \"item\", \"main/main/main/body/__template/0\") }"
|
129
|
+
"lambda { |__p, __t, __c, __id| EachBinding.new(__p, __t, __c, __id, Proc.new { _items }, \"item\", \"main/main/main/body/__each0/__template/0\") }"
|
130
130
|
]
|
131
131
|
}
|
132
132
|
}
|
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.7.
|
4
|
+
version: 0.7.19
|
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-
|
11
|
+
date: 2014-09-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|