Front-end developers face a variety of challenges when creating sites that have repetitive solutions. All sites use a similar DOM skeleton, as well as many common features and attributes. Volo aims to remove some of the effort out of the process, allowing developers to focus on the new aspects of a project, as apposed to the same.
If you add a JavaScript file that isn’t AMD compatible, Volo will try and convert it for you.
Volo is an application that aids front-end developers by automating their processes. It lets you create projects based on a template; you can extend these templates and setup custom commands for automating repetitive tasks.
Volo was created with a very special relationship to GitHub; it understands GIT repos, as well as branches and tags. There is no special Volo package or repository; if something is on GitHub, you can use it in your project. This is what sets Volo apart from similar applications.
Volo was created by James Burke, the same James Burke that created the very popular require.js library.
It may not surprise you that Volo has a very opinionated workflow; it assumes you want to use proper AMD for files, as well as providing presets on where to put files.
Is this a bad thing? Not really--you should be doing this anyway. Besides, you can override the commands if you need to do so.
So let's begin by going through some of Volo's commands.
Creating Projects
You'll probably use the create
command first. I mentioned that Volo is deeply rooted in GitHub, but interestingly enough, Volo doesn't actually use GIT. You usually just want a snapshot of a repo, not its entire commit history; so Volo only downloads the repo's files without the actual GIT data.
The simplest method of referencing a template is to just provide the repo's name. For example, if you want to copy a repo named 'starterTemplate', you can simply type the following command:
volo create myApp starterTemplate
Assuming the repo is the first result returned by GitHub, Volo will download and "install" the template to a new directory called myApp
.
This is a little risky, especially if you prefer to name your repos with very generic titles. Thankfully, you can specify more details to narrow the result set. The filtering system is fairly advanced when it comes to deciphering your input. Basically, you can use:
volo create appName userName/repoName/[branch|tag]
You can also omit certain pieces of information. For example, you can omit the GitHub user name, causing Volo to search for the first project with the given name and branch.
Alternatively, you can specify the user name but omit the version/branch, and Volo will download the latest tagged version.
If no tags are found, Volo falls back to the master branch.
Furthermore, you're not required to specify an exact version. Typing an expression like ">1.0
" tells Volo to download a version greater than 1.0.
Last but not least, you don't have to use a repo from Github; you simply pass the URL to the zip archive you want to use as your template. To copy a local folder, you can use the following format:
local:/path/to/directory
So there are a variety of options you can use to select your desired repo, and you can even omit the repo name to use Volo's own default template.
Searching and Retrieving Repo Information
Unsure who the owner of a popular repo is? Use the search
command.
volo search repoName
It uses the same GitHub search as create
; so you can be sure that the first repo in your search results is the repo Volo will download.
The search
command is only for finding a repo or retrieving its information. As a result, it lacks some of the functionality found in the create
command. For example, James created a few official templates that you can use, but you may not know their names.
Executing
volo search volojs/template
displays a few good options to use.
You don't have to be too accurate with your search criteria; Volo does a decent job finding what you want. It's surprisingly fast.
If you want more infoformation about a repo, such as version numbers or links, you can use the appropriately named info
command. One of the results from volo search volojs/template
is a repo named 'volojs/create-responsive-template'. You can retrieve more information about this repo by typing:
volo info volojs/create-responsive-template
Using the create
, search
, and info
commands, we can assume that we created our app. So what's next?
Adding Files
Next, we need to add dependencies. This is fairly easy, and it uses the same options as the create
command with a few additions. Besides being able to specify a GitHub user, branch/tag or URL to a zip, the add
command lets you append "#<filename>
" to intall a specific file from the repo. Many repos tend to have the JaveScript library that you want, but it is packaged together with demos and documentation. More often than not, you want just the library, and the add
command lets you do this.
Let's pretend you want to download a library called 'foo.js'. You can simply type:
volo add user/fooRepo/v1.5#foo.js
This downloads the entire zip file from GitHub, extracts only the foo.js
file and adds it to your project. This hashtag feature is not specific to filenames; you can pass a folder name to add only the specified folder to your project.
Earlier I mentioned that Volo expects you to practice proper AMD. So if you add a JavaScript file that isn't AMD compatible, Volo will try and convert it for you. The process is pretty simple.
It begins by asking what dependencies your JavaScript file uses.
You can either pass the dependecies' names in a comma separated list, or you can optionally give them a variable name. Here is an example of what you may type for a jQuery plugin:
jquery1.9=jQuery,jqueryui
This tells Volo that the specified library requires the file named jquery1.9
and to pass it under the variable name jQuery
. Then we add a comma to declare the next dependency: jqueryui
.
After you finish declaring your dependencies, you can declare what you want to export from your script. You don't usually export anything in a jQuery plugin; that's handled by jQuery itself. But if you have a JavaScript library that does need to export something, you would just type the name of the desired variable in your script. You can use this conversion feature on the fly with the amdify
command:
volo amdify <filename>
Just pass the desired filename.
You can also automatically rename a downloaded file. For example, you may want to add a file named jquery.pluginName.1.3.min.js
, but it's not a convenient file to reference in your scripts. You can pass a new name as the final parameter to the add
command, and Volo will rename the file.
To sum up, we can install this pseudo jQuery plugin from within a repo by typing the following:
volo add userName/RepoName/v1.3#jquery.pluginName.1.3.min.js newName.js
Volo will download the specified repo, extract only the given file for the plugin, ask you how to convert it to be AMD compliant and place it in your project under the new name. It's a lot of control and automation in a very easy to use command.
Now let's move on to Volofiles.
Volofiles
Volofiles allow you to automate tasks by creating macro-like commands. For example, CoffeeScript and SCSS users may manually issue the following commands to compile their files:
coffee -c coffee-lib/ -o lib/ coffee -c app.coffee sass css/main.scss css/main.css
This is a lot of commands to repeatedly execute. Naturally, one can add these commands to a bash file, but automating multiple operations more than likely involves a semi-complicated bash file or multiple bash files. With Volo, you create a file named volofile
. Inside this file, you create a JavaScript object that contains the commands you want to execute. Then you can simply call the them as if they were native commands.
Here is a sample volofile
using common parameters:
module.exports = { 'hi': { 'summary' : 'A command to say Hello', 'doc' : 'Enter Doc Text Here or Load a file', 'validate' : function(namedArgs, arg1){ if(namedArgs.cancel == "true"){ return new Error('The "cancel" parameter has been set'); } }, 'run' : [ 'echo "Hello World"', 'echo "How are you Today?"' ] }, 'nextCommand' : { … } }
You could then type volo -h
to view these commands and their summary. Entering volo hi -h
shows whatever is in the doc
field. The validate
key is optional, and you can use it for testing purposes to optionally exit if there is a problem. Last but not least, we have the run
property, which contains the list of command to execute. You can enter a simple string or an array of strings, and Volo will process and run each one accordingly.
With the above command, I would be able to type:
volo hi
To get the output, or:
volo hi cancel=true
To trigger the if
statement and exit with the provided error message. This is probably the most basic of examples, and you have many more options available when creating your own commands.
I mentioned Volo processes each string in the run
property. The above sample uses the 'last-resort' setting. Volo first looks for certain characteristics in your commands, and it only executes the string in the standard terminal if it cannot determine what you want. You have two options before that though, and they are named 'v
' and 'n
'.
The 'v
' and 'n
' Keywords
These two keywords allow you to inject custom functionality into your run strings. If you need to read files, execute processes, or other OS-specific tasks, you want to use the 'v
' keyword. Just writing the command (e.g. rm file.js
) only works on operating systems that support the given command. By using v
's commands, you assure cross-platform support of your volofile
. For example, you can add the following run string if you want to copy a file from one location to another:
run : 'v.copyFile file_one file_two'
This command is cross-platform. To see the full list of 'v
' options, take a look at the source file. You can add parameters with a space (like in the terminal) instead of using brackets.
Next we have the n
keyword, which maps to executable node.js modules. If you specify a command under the n
keyword, Volo checks if it can find the node.js executable in the current project's private node_modules
folder, and it falls back to the global node.js modules folder if one is not found.
Node.js, or more specifically NPM, has a very large collection of quality development tools created by many smart and creative people.
Volo leverages that enormous resource by allowing you to plugin their executables right into your Volofiles. James Burke has even created a template for creating your own node.js modules specifically for Volo, but we will get to that in a minute.
An example of using the n
keyword is the CoffeeScript module. If you want to compile a directory containing CoffeeScript files, we could write the following command:
'run' : 'n.coffee -c coffee-lib/ -o lib/'
Even if CoffeeScript isn't globally installed, you can ensure it will run on all OSs from the local node_modules
folder.
Now you can use the command template I mentioned (download here, by the way) to create a reusable Volo command to use in all your projects. For example, if you have a module that backs up your app, you may want to include it in all your projects.
Create a standard NPM module that exports the aforementioned properties (
summary
,run
etc) and require the module into your Volofile.
So, with your package added to NPM, you can just type:
npm install commandName
And in your volofile, just type:
module.exports = { 'commandName': require('node_modules/commandName/index') };
Where index.js
is the name of your extension. Because your file exports the necessary properties, the options will be directly injected into your volofile under the given command name. I wouldn't really call it a custom feature (it's standard JavaScript), but it's still nice to know you have this option.
Now with the theory out of the way, let's look into a real-world example. We'll build an app using Volo.
Getting Started
Volo is a NPM package, so installing it is as simple as:
(sudo) npm install -g volo
For this article, I thought I would create a small web page for generating a map of your area, showing who the FourSquare mayors of your favorite sites. It's not an incredible deep idea, but it will put Volo through the paces.
So to begin, we need a repo. You can find a number of starter templates, created specifically for Volo, by simply searching GitHub. The default template comes with the bare necessities: a simple HTML skeleton and a JavaScript folder with an AMD barebones setup.
You also get a volofile with a build command for compiling the project's JavaScript and CSS files.
I'm not going to use the standard template, as I would like to have a few more resources. After a quick GitHub search, I found an official template that contains the defaults, the bootstrap framework, and other resources.
So to create our new project (which I named 'turf'), you can simply type the following in a terminal window:
volo create turf volojs/create-responsive-template cd turf
This creates the project folder and downloads a snapshot of the specified repo. Next, to download other resources. We will obviously use jQuery, so let's add it:
volo add jquery
We also need a way to display a map on the page. Google Maps comes to mind, so we'll use a library named gmaps. The JavaScript file contains the entire library; therefore, we don't really need the rest of the repo. Additionally, you can see that the library is stored within a variable named GMaps
, and it requires the Google maps API as a dependency.
There is one small problem with this: the Google Maps API is not AMD compliant and is an asynchronous library.
When you load the single URL, it continues to load other resources on its own. This makes using standard require.js a problem, but there's a plugin that handles this exact situation. It's part of a repo called "requirejs-plugins", but once again, we don't need the entire repo. So, type the following in the terminal window:
volo add requirejs-plugins#src/async.js
With the async plugin, we can load our special asynchronous dependencies.
We have two options to install the actual plugin:
- Convert the library to be AMD-compliant.
- Use require.js' shim feature for adding non-AMD files.
I'll show you how to use both options. To convert the file, just add it. Volo will automatically detect that the file needs to be converted:
volo add HPNeo/gmaps#gmaps.js
Volo was created with a very special relationship to GitHub; it understands GIT repos, as well as branches and tags.
According to the project's Github page, it relies on the google maps API. There is a small issue with entering the URL because it contains unsupported characters (the equals sign). So let's use a placeholder; just type googlemaps
and hit enter. Next, we need to specify what we want to export. Type GMaps
(with the two capital letters; that's the name of the variable) and hit enter. Volo will finish converting it and add it to the js/lib
folder.
Why the js/lib
folder? If you take a look at the package.json
file in the root of your project, you will see an entry called baseUrl
under the amd
property. This tells Volo where to put all downloaded JavaScript files. There is actually a number of locations Volo looks in to decide where to put stuff:
-
baseDir
property under an entry namedvolo
-
baseUrl
property again undervolo
-
baseUrl
underamd
like we have here - a
js
folder in your current directory - a
scripts
folder in your current directory
If none of these locations exist, Volo puts the file in the current directory.
With the module installed, we still need to replace the placeholder we created for the Google Maps API. Open the www/js/lib/gmaps.js
file and replace the placeholder in the first (or second) line with the following:
async!http://maps.google.com/maps/api/js?sensor=false&libraries=places
This tells Volo to include the async plugin and pass it the URL for the Google Maps API.
The GMaps module is now fully installed, but it requires you to enter specific longitude and latitude coordinates--something most users will not know. So we should have some kind of autocomplete plugin that converts location names to coordinates.
After another quick Github search, I found a jQuery plugin called geocomplete by ubilabs. It too is not AMD compliant, but we will use require.js' shim feature. In the terminal type the following command:
volo add -amdoff ubilabs/geocomplete#jquery.geocomplete.js geocomplete
The amdoff
option prevents Volo from converting the file, and the ending parameter renames the file from jquery.geocomplete.js
to geocomplete.js
. This is not mandatory, but it makes referencing it more convenient.
Now open the www/js/app.js
file, and replace the require.js config declaration at the top:
requirejs.config({ baseUrl: 'js/lib', paths: { app: '../app' }, shim: { 'geocomplete' : { deps: ['jquery', 'async!http://maps.google.com/maps/api/js?sensor=false&libraries=places'] } } });
Just to run through the settings that were already there, baseUrl
tells require.js where to look for relative files, and the paths.app
option tells require.js that if a module name has a reference to a location named "app", then to replace with what is specified ('../app').
The shim
section is what I added to the file. It tells require.js to first load jQuery and the Google Maps API when someone loads a file named geocomplete
. You could optionally set another property called exports
, the name of the variable to export. We don't need to export anything because this is a plugin and it attaches to the jQuery object.
At this point, we have the files necessary to display the map and retrieve the necessary coordinates. We haven't written any code, but we do have all the dependencies.
The Outline
To begin prototyping our app, we need to write some HTML. Open up the index.html
file and remove everything inside the body tags except the <script />
element at the bottom. You should be left with a file like the following:
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="css/app.css" rel="stylesheet"> </head> <body> <script async data-main="js/app" src="js/lib/require.js" charset="utf-8"></script> </body> </html>
Next, add a text input field for the search box and a placeholder for the actual map. Your new body area should look something like this:
<body> <div class="container"> <div class="row"> <div class="span8 offset2"> <div class="row text-center"> <input class="input-xxlarge" id="search"> </div> <div class="row text-center"> <div id="map"></div> </div> </div> </div> </div> <script async data-main="js/app" src="js/lib/require.js" charset="utf-8"></script> </body>
It's a little more than the basics so that it conforms to bootstrap's CSS rules. I gave the text input an ID of search
and the map's <div />
an ID of map
. Now let's open the JavaScript file.
Volo is an application that aids front-end developers by automating their processes.
I'll break it down for those of you who are new to require.js. The primary reason to use something like require.js is to make sure your scripts' dependencies are loaded when, and only when, your script actually needs them. This increases the overall speed of your site and simplifies your HTML files (you don't need reference many scripts).
You can see the script at the bottom of the HTML file points to the require.js library, and it also has a data-main
attribute that automatically runs once require.js is ready.
If you open js/app.js
, our modified config section and a require statement at the bottom. Notice that you don't need the file extension. In the index file, we referenced js/app
(without the .js
), and here load app/main
.
Normally, a reference to app/main
would map to a file called main.js
inside a folder called app
(all relative to the js/lib
folder). But remember in the config, we said any reference to a location called app
, should be replaced with ../app
. Instead of searching for js/lib/app/main.js
, require.js will retrieve js/app/main.js
.
Next, open js/app/main.js
, remove everything and type the following:
define(['jquery', 'gmaps', 'geocomplete'], function ($, gmaps) { $(document).ready(function () { $("#search").geocomplete(); }); });
The first parameter is an array of our dependencies. The second is a function with parameter names for jQuery ($
) and GMaps (gmaps
). Remember that geocomplete is a jQuery plugin, so there's no reason to give it a corresponding parameter..
The function passed as the second parameter executes after the dependencies finish loading.
Inside this function, we execute the geocomplete()
method when the document is ready.
You can now open the page in your browser, and if all went well, the text box should say "Enter a Location". Start typing, and you'll see some results.
Next, we need to handle the event when a match is found, i.e. when the user presses the Enter key on a place's name. These events are: geocode:result
and the geocode:error
. Add the code to handle these events:
$(document).ready(function () { $("#search").geocomplete() .bind("geocode:result", function(event, result){ //On Result }) .bind("geocode:error", function(event, result){ alert("Location not Found"); }); });
The next step is to retrieve the latitude and longitude and generate a new map. The lat/long are stored in properties named geometry.location.hb
and geometry.location.ib
, respectively. Add the following code inside the geocode:result
handler:
var lat = result.geometry.location.hb; var lng = result.geometry.location.ib; var map = new gmaps({ div : "#map", lat : lat, lng : lng, height : '380px' });
We store the coordinates and load a new map. Creaing a map is simple: we pass the container's ID along with the coordinates and height.
You should now be able to search and display a map. Next, we need to interface with Foursquare and display the "mayors" of your city.
Interfacing with Foursquare
We first need an interface to the foursquare API. To save time, and to stay on topic, I created a repo that we can clone. So in the terminal window, type:
git clone https://github.com/gmanricks/MayorAPI www/foursquare
This downloads the repo and places it in a folder named foursquare
under the www
directory.
Next, open foursquare.php
and insert your client-id and client-secret into the class' constants. If you don't already have this information, you can get it from Foursquare by registering an app.
The other file in the repo (data.php
) retrieves the map coordinates via GET parameters and returns a JSON object. So to add the mayors to the map, we need to rewrite the geocomplete:result
event handler:
.bind("geocode:result", function(event, result){ var lat = result.geometry.location.hb; var lng = result.geometry.location.ib; $.get("foursquare/data.php?lat=" + lat + "&lng=" + lng, function(d){ var map = new gmaps({ div : "#map", lat : lat, lng : lng, height : '380px' }); var places = JSON.parse(d); if(places.error){ alert("The rate limit has been hit"); } else{ for (var index in places) { var place = places[index]; if(place.mayor.photo){ var html = '<div class="mayor">' + '<div class="person">' + '<img class="img-circle img-polaroid" src="' + place.mayor.photo.prefix + "60x60" + place.mayor.photo.suffix + '" />' + '</div>' + '<div class="label label-inverse">' + place.mayor.name + '</div>' + '</div>'; map.drawOverlay({ lat : place.lat, lng : place.lng, content : html }); } } } }); })
We first define the lat
and lng
variables, but instead of immediately creating the map, we wait for our Ajax request to complete. We're making about eleven API calls behind the scenes, so it might take ten or fifteen seconds.
Next, we make sure the rate limit hasn't been hit and alert the user appropriately. Finally, we cycle through the results, adding each one to the map using a command from the GMaps library.
You can now open the page and test it out; everything should work! With a little styling and some design, it could look something like this:
Optimizing with Volo
Our app is complete, and the last step is to optimize it. Let's first delete the files we do not use. In the js/app/
folder, you can remove everything but the main.js
file. In the js/lib/
folder, remove the files named appCache.js
and network.js
. You also don't need the manifest.webapp
file, but you could keep if you so desire.
Now open the volofile
, and you can see that the template came with a pretty complicated build command for optimizing and setting up the project's JavaScript and CSS files. We don't need such an elaborate script, so delete everything from the file and replace it with the following:
module.exports = { 'build' : { 'summary' : 'Command to compile the source files', 'run' : [ 'v.rm www-built', 'v.copyDir www www-built', 'v.rm www-built/js', 'node tools/r.js -o baseUrl=www/js/lib paths.app=../app paths.requireLib=require name=app include=requireLib out=www-built/js/lib/require.js', 'v.rm www-built/css', 'node tools/r.js -o cssIn=www/css/app.css out=www-built/css/app.css' ] } };
Volo is an omni-tool: you get what you put into it.
This is much more simple. Here, we create a command named build
that removes the previous built folde if it exists. It then copies the entire www
folder as a base for our compiled version. Next, we delete the js
directory (we'll replace it with the optimized version).
Then we run require.js' optimizer to compile the JavaScript files into a single file. You may notice it renames the final product to require.js
; this is kind of a "hack" because it's not really the require.js library. But since our HTML file has a reference to require.js
already, it's easier to rename the Javascript file than to parsing all HTML files and change the script references.
Once that command completes, we remove the CSS folder (again to replace it with the optimized version). And last but not least, we run the require.js optimizer again, this time for the CSS.
You can now execute volo build
from your terminal window and Volo will run this series of commands.
In your browser, you can navigate to the www-built
folder instead of the www
folder to make sure it still works. On my computer, building the project cut the file size in half, from just under a megabyte to ~400kb. You can probably get it even smaller if you minify the CSS.
Summary
In this article, I taught you Volo's syntax and we built an example application. We learned how to use Volo to create a project, add dependencies, add custom commands for automating common tasks and optimize an entire project.
To sum up, Volo is an omni-tool: you get what you put into it. You have a few, but powerful, commands, but Volo's true power comes from it's incredible extendibility and ultimately its community.
So what do you think about Volo? Will you be creating custom Volo commands? Leave a comment with your questions and opinions.
Thank you for reading.
Comments