Context: I am a somewhat experienced Rails developer who is relatively inexperienced with Javascript.
I first heard about the Volt Framework on the Ruby Rogues podcast episode about it and was immediately fascinated. I think that the separation of front-end and back-end parts of applications is one of the biggest pain points in web development, and a framework that tries to eliminate that pain point sounded great. Building a web app with separate languages and frameworks for front and backend parts can feel as foolish and cumbersome as it would be building the outside and inside of a house separately and hoping they fit together at the end.
I downloaded the gem and fired it up and started fiddling, and haven’t stopped fiddling for a few months now. Here’s what stands out about it to me:
Components are awesome
I really like components. The app folder of a Volt app looks a lot different from that of a Rails app. Where a Rails app folder has controller, model, and view folders at the bottom level, a Volt app has component folders. Each component folder is its own self-contained quasi-application, with model, controller, view, and other folders inside it. They’re somewhat analogous to Rails engines, but much easier to work with. They can be used for things large and small.
In Rails:
app/controllers/
app/models/
app/views/
In Volt:
app/component_1/controllers
app/component_1/models
app/component_1/views
app/component_2/controllers
app/component_2/models
app/component_2/views
app/component_3/controllers
app/component_3/models
app/component_3/views
Though the above makes it look like more folders happen in Volt, the reality is I find myself with a lot fewer things in a controllers folder in Volt than in Rails. In Volt, I’m likely to have a component for each model, which means all the controller, model, and view code for one conceptual part of the application is under the same folder. I find this organization more intuitive than the Rails folder structure.
One of the other things I have found myself doing with components is separating little view elements into their own components. In one app I found myself wanting to make little keyboard keys display on the screen, so I made a component for that called string_as_key_combo. Essentially, it’s a little view element that takes in a string value and parses and displays it as keyboard keys. What this means in practice is if I have a string, say ctrl+alt+delete, I can display that string as keyboard keys in a view with this code:
<:string_as_key_combo value=“ctrl+alt+delete” />
Result:
Though a css file, a teensy controller, and a view file all go into making the output, they exist only in their own component instead of cluttering up the folders that contain my main app functionality.
Separating out little view parts like this is surprisingly easy to do, and it keeps the application code less cluttered. The app I’ve been working on has several components like this for special selectors, forms, and information displays. For the curious, I made the string_as_key_combo component into a gem (gem-izing a component is trivially simple to do in volt): volt-string_as_key_combo. There is more to learn from than the demo apps
This brings me to another thing about Volt. Learning the framework is challenging right now. There are only a few demo apps, and they don’t cover nearly everything you might want to do. However, because Volt apps are made up of components and volt gems are also in the form of components, there is a lot more available for reference than just the demo apps. Every Volt gem (which is a short but growing list) can teach you things about how the framework works. Every Volt gem is a little Volt app, and is constructed like one, so you can learn how to write Volt apps from Volt gems. A few things I couldn’t pick up from the demo apps I picked up from the volt-editable-text gem, for example. Reactive variables are great
Much like Angular (which has clearly has some influence on the Volt framework), in Volt you can use the same variable in multiple places and it will automatically sync its value between all of those places when it changes, even between browsers. This is easier shown than told, so check out an intro video to get a better idea of how this works in practice.
I love this feature. Getting data to sync is one of those conceptually simple but in practice difficult problems that begs to be abstracted away, and Volt does a fine job of this. Going back to manually writing code to sync data is a lot more annoying after the simplicity of doing it in Volt. However, The different types of variables were hard for me to get a hang of
A lot of the reference help I’ve needed in working with Volt has been figuring out how the different variables and methods work. What precisely are the distinctions between a reactive_accessor, an attr_accessor, an attrs variable, and any or all of those things when referenced using a method, or when fed from one component into another. Under what circumstances will a reactive variable sync or not sync?
I’ve been developing an instinct for all of these things, but it was tricky at first. Sometimes things wouldn’t sync when I expected them to, or would sync when I didn’t expect them to, or would already be defined when I didn’t expect them to, or not be defined when I thought they should be.
The string_as_key_combo component was a good example: if I pass in a reactive variable to that component, will it still be reactive once it gets there (yes)? Do I need an accessor for it in the new component (no)? Will it still sync if I set it this way? That way? The provided collections are great
Persisting something for the life of a page, or a session, or in the database is trivially easy in Volt. You just store it in the page, cookies/local_store, or store collections respectively. You can even set and get params variables from the url in a similar way. Figuring out the relationships between controllers and views was tricky
I had a fair amount of trouble getting a hang of where I needed to put a view file and what I needed to name it in order to get it to connect to a particular controller. I also have had some trouble keeping my views files small. Separating things into partials (or the equivalent thereof) has been a tricky thing to figure out because every view and sub-view has its own controller. While there are times this is useful, there are other times I just have a big view file I want to divvy up a little, but without the hassle of considering whether the new view files will still have access to the same controller methods and variables they did when I had them in a single huge, unwieldy view file. Deployment is tricky at present
A byproduct of working with an early-stage framework is that while I have gotten it to work on Heroku at certain points, new versions and functionality are coming fast enough that it isn’t consistently working on deployment. I expect this will become less of a problem over time.
All-in-all
I’m very excited about Volt. It makes a lot of hard things easy and does so without doing too much of the reverse. I think it’s on track to accomplish Ryan Stout’s goal of keeping web development simple enough to be done by very small, even one-person teams for some projects, and if it doesn’t end up successful, I will be very disappointed if some of its ideas about app organization don’t succeed on their own. It has its quirks at the moment, but even with them, it’s an exciting thing to be working with. Would fiddle with again.
Finally, the community in the Volt Gitter chatroom has been very friendly so far. Frameworks create communities, and I think it’s a good sign that Ryan and other people on the Volt team have made an effort to be available to learners, and to be thoughtful and helpful in response to their questions, regardless of experience levels. A good framework requires a good community, and a good community requires that the framework’s builders accept a certain responsibility for cultivating a good community by setting examples of how to treat the people who are inside it and the people who are new to it. I think the Volt team is doing a good job of this.