Volt 0.9.0 is here, and its amazing
Ok, we might be biased, but 0.9.0 has a ton of new stuff in it. But here be dragons, or more precisely some breaking changes. Lets start off with what’s new and we’ll get to the breaking changes later though.
permissions
It took us longer than we expected, but we now have a full permissions system that is simple to use and keeps your data safe. We provide a simple API to specify how and when data can be accessed. In the permissions API, you can respond anytime a model is created, read, updated, or deleted (CRUD). At each point, you can either allow or disallow the action, or you can specify which fields are allowed or denied.
Running permissions at the model level gives us a few nice things:
You don’t duplicate logic in other areas. The models are the single authority on how they can be accessed and changed.
Because of #1, you can’t accidentally forget to check a permissions (aka more secure)
Because the logic for permissions is in one place, we can do things like automatically expose a rest api for models.
See the full docs on permissions here.
has_many and belongs_to
This is pretty standard for ORM’s, and we now have it in our models. has_many :through and embedded documents are still on the todo list, but they should be out soon.
delayed rendering
Commonly in Volt, you will assign a Promise or an unloaded collection to the model property of a controller. Controllers have a .loaded? method that returns true once the promise is resolved, or the model/collection’s .loaded? method returns true. Now view bindings will wait until .loaded? returns true before changing the rendered template. The existing controller won’t have before/after_action_remove until after the new controller is loaded. This prevents you from needing to check if the model is loaded before using it.
http endpoints
Previously, there wasn’t an easy way to provide REST api endpoints. Now you can write HttpControllers that behave similar to a controller in rails or another server side framework. The routes let you specify which routes respond with client code, and which respond with JSON, XML, text, or raw html. Thanks @jfahrer :-)
file upload
Along with http endpoints comes file upload. More docs on this coming soon.
sort on store queries
You can now specify a sort order for queries on store. Just call .order on a query or collection on store and it will be passed down to .sort in mongo. (We went with .order since .sort is already an Enum method.
more generators
We added a lot of generators for different things.
Also, previously we had a top level generate and a top level gem command. We moved the gem command under generate so you can now do: bundle exec volt g ... without writing all of generate out (since its a big word :-)
improved data fetching
Previously in Volt, when you used the store collection, data would be loaded once it is used to generate the value for a binding. This means initially something like an ArrayModel will be empty and when the data loads from the server, the data is added and everything re-renders. We still do this by default, but now we have some helpful methods to make it easier to interact with data when its ready. The first is .fetch You can call .fetch on a collection or query and it will return a promise. (Previously you could call .then on the collection) You can either do something like:
store._posts.fetch.then do |posts| ... end
Or you can pass fetch a block, which is the same as doing .fetch.then And it will still return a promise.
Second, we now have a .fetch_first method that returns a promise that resolves the first item. You can also pass a block like .fetch
bindings support promises
This is a pretty useful change. Now you can pass promises directly to bindings and they will update when the promise resolves. This lets you do something like this:
{{ store._users.fetch_first.then(&:_name) }}
sync on promises
If you are writing code that is only going to run on the server (in a Task for example), you can call .sync on a model and it will block until the promise resolves and return the resolve value. If the promise is rejected, it will raise the error as an exception. This lets you treat code that uses promises as synchronous code on the server. (Keep in mind that this doesn’t work on the client, though we have some plans for that)
yield in tags
You can now pass content into tags and render it inside of another view using {{ yield }} See the docs for more info.
more validators
We added validators for format, email, and phone validators. Thanks @lexun!
each_with_index
It works in bindings and gets reactively bound.
improved test helpers
Each component now gets its own folder in the specs folder. We added helpers for accessing the page and store collection inside of rspec. If you call store inside of a spec, it will return the store collection. Also, after the test finishes it will reset the database. Unfortunately, since capybara adds a page method, we’re currently using the_page as a method to get the page collection in a spec. We’re working on figuring out a good way to resolve the conflict.
Also now you don’t need to manually check for ENV[’BROWSER’] when writing integration tests. Just having type: :feature is enough, we use rspec’s filtering to only run those specs when running with a browser.
We also moved the gem dependencies for testing out to projects Gemfile, so you could change out rspec if you wanted.
filtered logging
Tasks and anything that uses tasks like logging in and querying/updating models log any calls to the STDOUT. To add security, you can now specify arguments that should be filtered. This will filter the specified keys from any hashes passed as arguments to Tasks. It also works with nested hashes. By default ‘password’ is filtered.
opal 0.7
Volt now runs on Opal 0.7 out of the box.
template bindings have been renamed to view
now you use
{{ view "path/for/view" }}
instead of {{ template "path/for/view" }} This is more consistent since views are kept in the view folder.
renaming some core gems
We made a mistake in the original way we were naming some gems and have decided to rename them to follow the conventions. The two you have to think about are:
volt-user-templates is now volt-user_templates
volt-bootstrap-jumbotron-theme is now volt-bootstrap_jumbotron_theme
{action}_removed rename
Now you can create methods for before_{action}_remove and after_{action}_remove This gives you more flexibility in how to handle cleanup.
plural model attributes always return an ArrayModel
This might seem like a bit of a strange one, but internally it simplifies a lot of Volt’s code and I think makes expectations clearer. Now anytime you call ._some_things (where the method name is plural), you will get back an ArrayModel.
main_path includes a component param
To make it easier to render paths to other components besides main, we now include a “component” param in the default main_path
No more NilModel’s
One thing we want in Volt is the ability to use nested collections without setting them up before hand. This lets you bind to nested collections from the view and provide a quick way to sync from the view to other places.
Previously you might have seen code like this in a view:
<input value=“{{ page._new_todo._label }}” />
When you called page._new_todo, you would get back a NilModel, that would expand when you used _label. While this was nice, it had the major downside that the NilModel was truthy (instead of falsy). So if you did something like:
it would always evaluate true. In 0.9.0, we move to explicit expanding. You can simply put a ! on the end of a field access and it will generate an empty Model if the value isn’t already set to something.
<input value=“{{ page._new_todo!._label }}” />
This means you never need to use .true? or .false? (and they have been removed)
Volt.user is now Volt.current_user
Its longer, but we think more clear on what it does.
underscore’s are no longer required in routes
This was a vestige of the past and now its gone. Now the only place you use underscores is when you want to access a model property that you haven’t setup a field for or for accessing ArrayModels on a model.
.find is now .where
On store .where passes to mongo’s .find We use .where because it doesn’t conflict with .find in Enum. We’re also planning to start work on adding support for more databases (like postgres)
Volt::TaskHandler is now just Volt::Task
Again, legacy fix
Controllers in main are now namespaced
They weren’t before, but it makes since to namespace controllers in all components.
raw html in bindings
By default all strings passed to content bindings is rendered as text and not html. Now you can put raw at the start of a content binding to have it rendered as HTML. With great power comes great responsibility. Use wisely because this can open you up to xss attacks. We recommend never using raw on user generated text.
go renamed to redirect_to
We renamed the go method to redirect_to since its more descriptive and to keep things consistent with expectations.
We also fixed a lot of bugs, improved error messages, added tests, refactored lots of code, and improved docs.
0.9.0 has a ton of changes in it. But it really sets the stage for 1.0. We took the opportunity to break a few things to make everything simpler, more consistent, and cleaner. We don’t expect to have any major breaking changes for 1.0 (since we’re getting them out of the way now) We still have a few minor “core” things left (like has_many through, embedded models)
Here’s some of the “major” things we’re working on for 1.0
switch to faye
Right now Volt has to run on thin because of the websocket/fallback implementation we run. The plan is to switch to faye, which would also allow easier multi-node deployments. This would let Volt run on any rack-hijack compatible server (most of them)
data provider api
We’ve been working on abstracting the database specifics of the store collection. We’re pretty close to having a fully implemented “data provider api” that would allow anyone to easily implement a store adaptor for any database or data provider. This means you could query and update anything from a postgres database to a custom news feed service.
more docs, tutorials, videos
Now that 0.9 is out, there shouldn't be too many more breaking changes, so I think its time we start working on a really large demo app and more videos/tutorials. Expect to see better resources within the next few weeks.
Overall I think the future of volt is bright, I want to thank all of those who have helped us get this far. If your interested in contributing or have any questions, be sure to hit me (@ryanstout) up in the gitter chat room any time.