Volt is a Ruby framework designed for data rich applications. Both the server and client sides are written in Ruby (which is then compiled to JS using OPAL), so this allows the developer to write very dynamic applications without having to write a single line of Javascript code. If you’re a Ruby fan like me, you’ll love this framework.

In an attempt to make web applications a lot more dynamic, front-end Javascript frameworks like Angular.js, Backbone.js and Ember.js have gained a lot of popularity. However, these frameworks often require a back-end application to be useful, so they are used in conjunction with web frameworks like Ruby on Rails and Django.

On the other hand, Ruby framework Volt is capable of managing the back-end and a dynamic front-end. Since both functionalities are tightly integrated into its core (in fact, Volt is more like an MVVM architecture, leveraging the advantages of data bindings), it enables the developer to build these applications quickly.

A very cool feature that comes out of the box is Volt’s real-time feature. If you ever made real-time applications, you know the process can be challenging – you probably implemented AJAX-polling, web sockets, Server-Sent Events (SSE) or even used external services, adding complexity to the application and even incurring additional costs. Unlike other frameworks, Volt keeps a connection with the server alive (via web sockets), so instead of making Ajax requests for each action, it pushes changes instantly to all clients. No configuration is needed for this to work.

Meet Volt, A Promising Ruby Framework For Dynamic Applications

Using Volt To Create A Chat Application

In this Ruby framework tutorial I am going to take you through the process of creating a real-time application using Volt, and what better way than a chat application to demonstrate its capabilities, since chat remains the number one use case of real time applications.

First of all, let’s install Volt and MongoDB. The latter process will not be covered in detail:

gem install volt
 
brew install mongodb
mkdir -p /data/db 

(create dbpath)

chown `id -u` /data/db (change the owner to have the proper dbpath permissions)

Now we’re ready to create our first app, lets call it ‘chat’. We can do that easily in a couple of lines:

volt new chat
cd chat

The document structure has some similarities to Rails. The main difference Rails users will notice is that we have an extra folder inside app that contains the rest of the folders like assets, controllers, models and views, this extra folder is a ‘Component’.

A Component is an isolated section of the app. All pages inside a Component are rendered without reloading the page since all files for that component are loaded with the initial http request, so if we visit a page of a different component, a new http request will be made and the page will be ‘reloaded’. For this example let’s use the default component called ‘main’.

Let’s start the server by executing ‘volt server’ command in console, and see how it looks in the browser by navigating to localhost:3000:

volt server

Also don’t forget to start MongoDB in console:

mongod

We can notice that Volt comes with a number of default pages, including ‘Home’ and ‘About’. These can be customized right away.

The other thing worth mentioning is the login button at the top right side of the page. Volt has a “user” functionality integrated to the framework via the ‘volt-user-templates’ gem, which provides a way to register and authenticate users, right out of the box.

Getting Started

Now, let’s start working on our app. First of all we don’t need the ‘About’ page so we can go ahead and delete the following: The app/main/views/main/about.html file, the about action in app/main/controllers/main_controller.rb, remove the /about route in app/main/config/routes.rb and the nav link in app/main/views/main/main.html.

<ul class="nav nav-pills pull-right">
  <:nav href="/" text="Home" />
  <:user-templates:menu />
</ul>

Now let’s get down to business and start by listing all registered users:

<:Body>
  <h1>Home</h1>
  <div class="row">
	<div class="col-md-4">
  	{{ _users.each do |user| }}
    	<div class="contact">
      	{{user._name}}
    	</div>
  	{{ end }}
	</div>
  </div>

Now all registered users are being listed in the homepage. Note that the code written inside {{ }} is Ruby code that gets executed. This way we can iterate over user collection and print each one out.

As you may have noticed, ‘users’ is the name of the collection where all users are stored; something to have in mind is that attributes are accessed with an underscore ‘’ prepended to the attribute name. For this to work, we first need to add a line of code at the top of the main_controller.rb file:

model :store

Volt comes with multiple collection models accessible from the controller, and each of them stores the information in a different place. The store collection model stores the data in the data store, and here we are specifying the controller to use that one (right now the only data store supported is MongoDB). Let’s create a couple of users to see what it looks like.

Right now there is nothing exciting about this page, we’re just listing the registered users. Now I would like to be able to select a user to send a message to, remove the name of the currently logged in user from the list (since he shouldn’t be able to send messages to himself), show the list only to authenticated users and show a ‘landing’ page to non-authenticated users:

<:Body>
  <h1>Home</h1>
  {{ if Volt.user }}
	<div class="row">
  	<div class="col-md-4">
    	{{ _users.each do |user| }}
      	{{ if user._id != Volt.user._id }}
        	<div class="contact {{ if params._user_id == user._id }} active {{ end }}" e-click="select_conversation(user)">
          	{{user._name}}
        	</div>
      	{{ end }}
    	{{ end }}
  	</div>
	</div>
  {{ else }}
	<p>This is a sample application built with Volt to demonstrate its real-time capabilities. Please log in to access it.</p>
  {{ end }}

Volt.user return the current (logged in) user or nil.

The e-click attribute lets us select a method from the controller that will be called when that element is clicked.

Attributes And CSS

In fact, all ‘e-‘ attributes are event binders in Volt, so for example we can add e-submit to a form to choose the action that will be called on the controller. We are going to add the ‘selected’ user’s ID to the parameters so we can know which one has been selected and add a class called ‘active’ which we can later style.

Now let’s create the select_conversation method in the controller:

def select_conversation(user)
  params._user_id = user._id
end

And that’s it – if you check out the page again, you can see that the URL changes every time you click on a user’s name. Also, the class ‘active’ is being added to that element, so let’s add some CSS to make it visible (I’ll go ahead and add the CSS for items we will add later on):

.conversation{
  form{
	input{
  	margin: 10px 0 5px 0;
	}
  }
}
.contact{
  width:100%;
  padding:5px;
  margin: 4px 0;
  font-size:15px;
  cursor:pointer;
  &:hover{
	background-color: #FAFAFA;
  }
  &.active{
	background-color: #337ab7;
	color: #FFF;
  }
  .badge{
	background-color: #900;
  }
}

.message{
  max-width: 80%;
  padding:10px 15px;
  margin: 5px 0;
  background-color: #FEFEFE;
  border: 1px solid #E7E7E7;
  border-radius: 5px;
  float: left;
  clear:both;
  &.sent{
	background-color: #E4F3DB;
	border: 1px solid #B7D0A7;
	float: right;
  }
  p{
	margin:0;
  }
}

Now let’s create a form on the right side to send messages to each user:

<:Body>
  <h1>Home</h1>
  {{ if Volt.user }}
	<div class="row">
  	<div class="col-md-4">
    	{{ _users.each do |user| }}
      	{{ if user._id != Volt.user._id }}
        	<div class="contact {{ if params._user_id == user._id }} active {{ end }}" e-click="select_conversation(user)">
          	{{user._name}}
	        </div>
      	{{ end }}
    	{{ end }}
  	</div>
  	{{ if params._user_id }}
    	<div class="col-md-8 well conversation">
      	{{ current_conversation.each do |message| }}
        	<div class="message {{ if message._sender_id == Volt.user._id }} sent {{ end }}">
          	<p>{{ message._text }}</p>
        	</div>
      	{{ end }}
 
      	{{ if current_conversation.count == 0 }}
        	<p>You have no messages yet. Start chatting!</p>
      	{{ else }}
        	<div class="clearfix"></div>
      	{{ end }}
 
      	<form e-submit="send_message" role="form">
        	<div class="form-group">
          	<input class="form-control" type="text" placeholder="Write a message" value="{{ page._new_message }}" />
          	<button type="submit" class="btn btn-primary pull-right">Submit</button>
        	</div>
 
      	</form>
    	</div>
  	{{ end }}
	</div>
  {{ else }}
	<p>This is a sample application built with Volt to demonstrate its real-time capabilities. Please log in to access it.</p>
  {{ end }}

First, we’re checking if there is a user selected before displaying the form, then we display all messages from the current conversation (the conversation with the selected user) from a method in the controller we’re going to define in a bit, and at the bottom we’re displaying a form for sending new messages.

Notice that the value of the input is an attribute we’re creating on the page collection model since we don’t want it to be stored in the data store. Now let’s define the current_conversation and send_message methods in the controller:

def send_message
  unless page._new_message.strip.empty?
	_messages << { sender_id: Volt.user._id, receiver_id: params._user_id, text: page._new_message }
	page._new_message = ''
  end
end
def current_conversation
  _messages.find({ "$or" => [{ sender_id: Volt.user._id, receiver_id: params._user_id }, { sender_id: params._user_id, receiver_id: Volt.user._id }] })
end

In the send_message method we add a new message to the collection if the message is not blank (we’re checking inline so we don’t have to mess with validations at the moment), then we set the page ._new_message to ‘’ so we empty the input field.

We might want to add that line to the end of the select_conversation method as well. The current conversation method just queries the _messages collection for messages between the selected user and the current user.

Wrap Up With Real-Time Notifications

To finish, I would like to have some kind of notification system, so users could see when other users are messaging them.

Let’s add a new collection called _notifications and create a new one after each message is sent:

def send_message
  unless page._new_message.strip.empty?
	_messages << { sender_id: Volt.user._id, receiver_id: params._user_id, text: page._new_message }
	_notifications << { sender_id: Volt.user._id, receiver_id: params._user_id }
	page._new_message = ''
  end
end
def select_conversation(user)
  params._user_id = user._id
  unread_notifications_from(user).then do |results|
	results.each do |notification|
  	_notifications.delete(notification)
	end
  end
  page._new_message = ''
end
def unread_notifications_from(user)
  _notifications.find({ sender_id: user._id, receiver_id: Volt.user._id })
end

Also, we need to delete notifications from after a user selects the conversation and sees the new messages, so I added that part to the select_conversation method.

Let’s add a notification counter right next to the user name:

<div class="contact {{ if params._user_id == user._id }} active {{ end }}" e-click="select_conversation(user)">
  {{user._name}}
  {{ if unread_notifications_from(user).count > 0 }}
	<span class="badge">
  	{{ unread_notifications_from(user).count }}
	</span>
  {{ end }}
</div>

Now the app is ready, you can open a couple of browsers and start testing the real-time capabilities of Volt.

Volt Is Definitely Worth A Try

Even though the Volt framework is not as mature and robust as most of the popular Ruby frameworks that have been around for years (at the moment of Volt is still in beta), it is worth considering and studying.

In case you are interested, use this Ruby framework tutorial to take Volt out for a spin. Keep an eye on further developments, as Volt looks like a very promising Ruby framework even at this early stage of development.

There are a lot of cool new features in the pipeline and I’m pretty sure Volt will become more relevant over the next couple of years, as more people start experimenting with it. Due to a number of innovative features, many developers could fall in love with Volt and use it for their next Ruby project.

About the author

Amaury Andres Peniche Gonzalez, Colombia
member since November 2, 2012
Amaury is a systems engineer and production engineer with experience in front- and back-end development, computer graphics, and networking. He has developed software extensively for large companies. He believes in simplicity, quality, productivity, agile methodologies, and responsibility. [click to continue...]
Hiring? Meet the Top 10 Freelance Ruby Developers for Hire in September 2016

Comments

Juan Méndez
Interesante pero discutible, se puede discutir con mayor profundidad. Entiendo tu punto
Rafael Pérez
Interesting article Amaury. Is there a way to use it in conjunction with Rails?
Emiro Surmay
Wow! This looks like a gamechanger
Santiago Contreras Tascon
amazing!!! a great way to make things more dinymic, this can improve any code.
Amaury Peniche
Hey Rafael, actually that seems to be a popular request and there is a project called "Third-Rail" (https://gitter.im/catprintlabs/third-rail) aiming to accomplish this, but last time I checked, it wasn't possible yet. However, I don't think thats the best idea, but lets wait and see how this comes up!
Martin
I'm curious how this compares to MeteorJS.
Amaury Peniche
Hey Martin, actually they have a lot of similarities since they both follow the 'Isomorphic' paradigm, and that is sharing the same code in the Server and the Client, which basically means one programming language in every part of the app, in the case of MeteorJS is Javascript while Volt uses Ruby. It has become very complex/tedious to define the Models/Controllers/Views/Routes for the back-end and front-end in two different programming languages for the same app, and this is exactly the problem that frameworks like MeteorJS and Volt are tackling.
Belisario Solana
great article, direct and specific !! is it possible to deploy to heroku ?
Paul Hrimiuc
Well, I think you should've put some links and some images with application, altghout nice platform, let's hope it doesn't get lose.
Amaury Peniche
Thanks Belisario, yes, it is possible to deploy to Heroku, in the framework documentation there is a brief explanation of how to do so: http://docs.voltframework.com/en/deployment/heroku.html#
Rafael Pérez
Hi Amaury, thanks for your answer. I'll check the link about Third-Rail
R2BeefStew
Great article, it has my attention.
KOTP
Looks familar :) http://rubylearning.com/blog/2015/02/11/meet-volt-a-promising-ruby-framework-for-dynamic-applications/
komrath
So it is Meteor running on Ruby instead of NodeJS? :D
biodiscover
I wonder which command is regarding to `mkdir -p /data/db` in Linux.
Amaury Peniche
When you think of Isomorphic app development you think on JS as the programming language (as it is the language of the browser), actually, besides MeteorJS there are other JS frameworks that follow the same paradigm... one of the cool things about Volt is that the language is Ruby!
Amaury Peniche
Here is a manual for installing MongoDB in different Linux distributions: http://docs.mongodb.org/manual/administration/install-on-linux/
alainpilon
Fix the code indentation. Makes reviewing the code more complex than it should be.
Jonathan Bello
What are some real world applications that have been built using this framework?
Amaury Peniche
Hey Jonathan, Volt is still in Beta, so I don't think there are many of them
Sergio Tapia
Meteor has latency compensation, a tighter build system that auto-detects everything, a mini-mongo mongodb client-side implementation, super simple named templates and real-time baked in, hot-code reload, auto-reload seamless deploys to production. I've never used Volt but this should give you the key features Meteor has.
disqus_SAAcNqC5Jy
You should update 'Volt.user' to 'Volt.current_user' and I think that notification badge is diplayed even if I follow any given conversation
Brad
"It has become very complex/tedious to define the Models/Controllers/Views/Routes for the back-end and front-end in two different programming languages for the same app," This is very powerful, for two reasons that come to mind right now: * Quicker prototyping, proof of concept, or MVP apps. * Teaching/learning - MUCH easier for someone to learn how to build useful apps. Flatter learning curve.
Brad
I would love to hear more opinions on this. Why it is or is not a game changer. Just would like to hear the opinions of experienced programmers.
Jakub Zak
Hi I am giving this a go, and I am experiencing really weird behaviour where comparison of the ids is behaving conversely. Attaching two screenshots with the IDs printed out and the code implementation. I registered 2 users only in the app and both users can see them selfs in the list of users.
Barchiel
NOOBIE!!!!
Barchiel
PROBABLY BECAUSE...UH...YOU'RE WEIRD
Barchiel
COOL BEANS!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Darin Haener
Hey. I just figured out the issue to that problem. Volt.current_user.id returns a promise, so you can't directly compare the two like that. You can however use Volt.current_user_id. The other option is to do something like: Volt.current_user.then do |current_user| current_user.id == user.id end
comments powered by Disqus
Subscribe
The #1 Blog for Engineers
Get the latest content first.
No spam. Just great engineering and design posts.
The #1 Blog for Engineers
Get the latest content first.
Thank you for subscribing!
You can edit your subscription preferences here.
Trending articles
Relevant technologies
About the author
Amaury Andres Peniche Gonzalez
JavaScript Developer
Amaury is a systems engineer and production engineer with experience in front- and back-end development, computer graphics, and networking. He has developed software extensively for large companies. He believes in simplicity, quality, productivity, agile methodologies, and responsibility.