Brian Sam-Bodden's complete blog can be found at: http://www.integrallis.com/blog/

Items:   1 to 5 of 16   Next »

Tuesday, December 27, 2011

For years the software community has been pushing the MVC architectural pattern to organize and separate the concerns of our applications. So far we seem to have done a decent job of accomplishing that based on the enforcement of the pattern in the most successful web frameworks such as Rails, Grails, JSF, Struts and many others. The last frontier for MVC seems to be the sometimes convoluted world of JavaScript; the client tier of our web applications. Although frameworks like jQuery, Prototype, Scriptaculous, ExtJS, DOJO and others have greatly helped in cleaning up and structuring the client tier, there's still much to be desired. In recent years several micro-frameworks have appeared that aim to put an end to the madness of the JavaScript client tier world. In this article we'll explore the most prominent players and see how their usage impacts modern web development.

Blurting out a solution when so many do not understand the problem would not help. So, let’s start at the beginning… Before the web, but not that long ago, we had client-server applications in which the client was typically a desktop application and the server was a monolithic application that babysat a database. In those days, mastery of programming environments like Delphi, Visual Basic, Powerbuilder and Oracle Forms was in hot demand. Visual component environments allowed, to an extent, the separation of presentation logic from the database. I lived through that period and I remember the feeling that the user interface portions of the application seemed pretty orderly via the use of components but the server side was a disaster.

Then along came the web and, overwhelmed by the new environment, protocols, and stateless nature of the new beast, we somehow forgot everything we’ve learned in the past. The first applications were monolithic messes both in the front-end and the back (remember CGI scripts?). Then eventually we came to our senses and a myriad of web frameworks started to appear. Most of the frameworks implemented a version of the Model-View-Controller (MVC) pattern.

So while things got better at the web server level, the UI remained the wild frontier of web development, where JavaScript was the lingua franca and not many could speak it. JavaScript, an amazingly powerful but misused and misunderstood language, was caught smack in the middle of the so-called browser wars. These wars were centered around the differences and inconsistencies on the browser’s DOM (Document Object Model), which also made for a mess at development time. Slowly, the community reacted by creating frameworks like jQuery, Scriptaculous, Prototype, MooTools, Dojo, Ext JS and others that could hide the warts of the DOM implementations (and also mask some of the ugly parts of JavaScript).

These frameworks have simplified the way we deal with the DOM and JavaScript. They’ve given us the ability to encapsulate visual aspects of the DOM and deal with events in a uniform fashion. But in spite of our best efforts, complex client-side functionality still gets messy very fast. Applications typically end up as a spaghetti mess of callbacks between the DOM, your code and the server-side application. A more structured approach to building rich JavaScript applications is needed.

Model-View-Controller

The MVC pattern made its first appearance in 1979 when Trygve Reenskaug described a new architecture for developing interactive applications while working on Smalltalk at Xerox PARC. MVC breaks a functional slice of an application into three basic components: models, views and controllers.

In the MVC world, the model is responsible for maintaining an aspect of the state of the application. This “state” could be transient, lasting only while dealing with an interaction with the user or persistent and stored outside of the application’s realm. The model is responsible for encapsulating the operations relevant to the entity it represents. This encapsulation carries with it the responsibility of maintaining the integrity of the data that represents the state of the model.

The view encompasses a user interface element that typically reflects the state of the underlying model. Finally, the controller handles input events from the UI and determines a set of operations to be performed upon the model. Changes to the state of the model are then reflected on the view. How coupled the components of the MVC triumvirate are depends on implementation specific decisions.

A Simple Example: A Shopping Cart

To illustrate the problems the new breed of MVC frameworks aim to solve, I will first build a dynamic single page JavaScript application without them; a simple drag-n-drop jQuery based shopping cart.

Figure Spine Bsb 1
Figure BSB-1 – jQuery Shopping Cart Project

As shown in Figure BSB-1 the project consists of an HTML page called shopping_cart.html which uses the JavaScript files integrallis.commerce.js and integrallis.jquery.cart.js. These files are an attempt to separate the business objects from the visual aspects of the application. In integrallis.commerce.js I’ve implemented a simple shopping cart as shown in Listing BSB-1 (the full listing for this example can be found at https://github.com/bsbodden/jquery-shopping-cart)

var INTEGRALLIS = {};
INTEGRALLIS.commerce = {};

INTEGRALLIS.commerce.ShoppingCart = function(options) {
	// initializes the cart
	this.items = {};
	
	// callbacks
	var defaults = {
		removeAll: function() {},
		updateTotal: function() {},
       	updateItemQuantity: function() {},
       	removeItem: function() {}
	};
	
	// merge options with defaults
	for (property in defaults) { 
		if (!options.hasOwnProperty(property)) { 
			options[property] = defaults[property]
		} 
	}
	
	this.options = options;
}

// removes all items from the cart
INTEGRALLIS.commerce.ShoppingCart.prototype.clear = function() {
	this.items = {};
	this.options.removeAll();
	this.options.updateTotal();
}

// returns the value of all items in the cart
INTEGRALLIS.commerce.ShoppingCart.prototype.total = function() {
	var sum = 0.0;
	for (var index in this.items) {
		var item = this.items[index];
		sum = sum + (item.price * item.quantity);
	}
	return sum;
}

// returns the number of unique items in the cart
INTEGRALLIS.commerce.ShoppingCart.prototype.itemsCount = function() {
	var size = 0, key;
    for (key in this.items) {
        if (this.items.hasOwnProperty(key)) size++;
    }

	return size;
}

// returns whether the cart is empty or not
INTEGRALLIS.commerce.ShoppingCart.prototype.isEmpty = function() {
	return this.itemsCount() == 0;
}
	
// adds a new item to the cart or increases the quantity of an existing item
INTEGRALLIS.commerce.ShoppingCart.prototype.add = function(id, price, quantity) {
	var is_new;
	quantity = (typeof(quantity) != 'undefined') ? quantity : 1;
	if (this.items.hasOwnProperty(id)) {
		var item = this.items[id];
		item.quantity = item.quantity + quantity;
		this.options.updateItemQuantity(id);
		this.options.updateTotal();
		is_new = false;
	}
	else {
	    this.items[id] = { quantity : quantity, price : price };
	    this.options.updateTotal();
	    is_new = true;
    }
    return is_new;
}
		
// increases the quantity of an item in the cart
INTEGRALLIS.commerce.ShoppingCart.prototype.increase = function(id, quantity) {
	quantity = (typeof(quantity) != 'undefined') ? quantity : 1;
	if (this.items.hasOwnProperty(id)) {
		var item = this.items[id];
		item.quantity = item.quantity + quantity;
		this.options.updateItemQuantity(id);
		this.options.updateTotal();
	}
}
		
// decreases the quantity of an item in the cart
INTEGRALLIS.commerce.ShoppingCart.prototype.decrease = function(id, quantity) {
	quantity = (typeof(quantity) != 'undefined') ? quantity : 1;
	if (this.items.hasOwnProperty(id)) {
		var item = this.items[id];
		if (item.quantity >= quantity) {
		    item.quantity = item.quantity - quantity;
	    }
	    else {
		    item.quantity = 0;
	    }
		this.options.updateItemQuantity(id);
		if (item.quantity == 0) {
			delete this.items[id];
			this.options.removeItem(id);
		}
		this.options.updateTotal();
	}
}
		
// returns the quantity of an item in the cart
INTEGRALLIS.commerce.ShoppingCart.prototype.quantityFor = function(id) {
	return this.items.hasOwnProperty(id) ? this.items[id].quantity : 0;
} 

Listing BSB-1

The shopping cart provides the ability to maintain a list of items and their associated quantities as well as their total value. In order to reflect changes to the cart, I’ve created a set of callback functions (initialized to no-op functions) in the Constructor function of the cart. In integrallis.jquery.cart.js shown in Listing BOD-2, I wire the shopping cart object to the shopping_cart.html page. I start by instantiating the cart and passing the shopping cart UI callback functions. The callbacks use jQuery to update elements of the page to reflect changes in the shopping cart.

Interaction with the cart will occur when the user drops an HTML element representing an item into the cart “drop” area. To accomplish the drag-n-drop functionality I’m using jQuery’s draggable and droppable functions to enable the elements with class “product” to be dragged and dropped into the element with id “cart”.

// create a shopping cart instance, set all callbacks
var myShoppingCart = new INTEGRALLIS.commerce.ShoppingCart({ removeItem : removeItem,
	                                                         removeAll : removeAll,
	                                                         updateTotal : updateCartTotal,
	                                                         updateItemQuantity : updateItemQuantity });
	
// shopping cart UI callbacks

function updateItemQuantity(id) {
	$('#cart #' + id)
	    .children('#qty')
	    .text(myShoppingCart.quantityFor(id))
	    .effect("highlight", {}, 1500);		
}

function updateCartTotal() {
	$('#total').text(myShoppingCart.total()).effect("highlight", {}, 1500);
}

function removeItem(id) {
	$('#cart #' + id).effect("puff", {}, "slow", function(){ $(this).remove(); });
}

function removeAll() {
	$('#cart .product').effect("puff", {}, "slow", function(){ $('#cart').empty(); });
}

function decorateForCart(item, id) {
	   item.append(' (1)')
           .append(' +')
           .append(' -');
       item.children('#add').click(function() {
	       myShoppingCart.increase(id);
       });
       item.children('#remove').click(function() {
	       myShoppingCart.decrease(id);
       });
}

$(document).ready(function() {
	// make products draggable
	$(".product").draggable({ helper: 'clone', opacity: "0.5" });
	
	// allow products to be dropped in the cart
	$("#cart").droppable({ accept: '.product', 
	                       drop: function(ev, ui) {
						       var item_dropped = ui.draggable;
						       var id = item_dropped.attr('id');
					           var price = item_dropped.attr('price');	      
						       if (myShoppingCart.add(id, price)) {
							       var item = item_dropped.clone();
							       decorateForCart(item, id);
							       $(this).append(item);
						       }
		                   }
	});
	
	// jquery-fy the 'clear shopping cart' button
	$('#dump').button().click(function() { myShoppingCart.clear() });
});

Listing BSB-2

Finally to put the cart to the test in shopping_cart.html where I provide a div representing the shopping cart and a list of “products” that can be dropped into the cart (Listing BOD-3). The “data” for the products is taken from the attributes of the <li> “product” elements.

<html>
<head>
    <link href='http://fonts.googleapis.com/css?family=Muli' rel='stylesheet' type='text/css'>
    <link type="text/css" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/themes/cupertino/jquery-ui.css" rel="stylesheet" />
    <link type="text/css" href="styles/shopping_cart.css" rel="stylesheet" />
    
    <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
    <script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.min.js"></script>
    <script src="src/integrallis.commerce.js"></script>
    <script src="src/integrallis.jquery.cart.js"></script>
</head>
<body>
    <h2>jQuery Shopping Cart</h2>

    <h3>Available Products</h3>
    
    <ul class="products" id="products">
        <li class="product ui-state-default" id="item_0" price="100.0">Product 0 - $100</li>
        <li class="product ui-state-default" id="item_1" price="110.0">Product 1 - $110</li>
        <li class="product ui-state-default" id="item_2" price="120.0">Product 2 - $120</li>
    </ul>

    <h3>Your Shopping Cart</h3>

    <div id="cart">
    </div>
    <p>Total: $<span id="total">0.00</span></p>
    <button id="dump">Dump the cart!</button>
</body>
</html>

Listing BSB-3

As the elements are dropped into the cart they are cloned and decorated with a couple of links to increase/decrease the quantity of the particular item in the cart.

In the callbacks we are dealing with dynamically updating the total value of the cart and increasing and decreasing the displayed quantities of an item. Also notice that if we drop an item that already exists in the cart, rather than creating a new item we simply increase the quantity of the existing item. Similarly, when an item’s quantity reaches zero we remove the item from the cart. All these operations are performed with jQuery’s animation capabilities to keep the user aware of changes to the cart.

Figure Spine Bsb 2 Trans Sm

Figure BSB-2 – jQuery Shopping Cart in Action

Shortcomings of the jQuery Shopping Cart

I am obviously taking some shortcuts for the sake of the example such as assuming that the available products magically appeared in the page (in a real application they would be retrieved from a server). But other than that, at first glance the implementation of the shopping cart seems to work as advertised and even seems well thought-out. That is, until we start asking the hard questions…

From the usability point of view, the first thing that jumps to mind is, what happens when the user clicks the refresh button? Since everything is just being kept in memory the answer is that it simply goes away. The user will end up with an empty shopping cart. The same applies to the back button; both will have the effect of dumping the cart!

One of the features that simple page applications try to achieve is the minimization of round trips to the server. Since the page is not to be refreshed by the user actions on the page, AJAX calls will have to be made to send the state of the cart back for the server for storage.

On the code organization front, even though I have taken steps to separate the business logic from the UI interaction code it is still obvious that for anything more complex we would end up with an unmanageable mess of UI callbacks all of which are currently living in the global JavaScript scope.

Another glaring problem is the coupling of the concepts of the shopping cart and the items in the shopping cart. The items are simple JavaScript objects with no behavior. Their reflection on the UI is controlled by the decorateForCart function as shown in Listing BSB-2. Ideally our cart items would be good object-oriented citizens and encapsulate both data and behavior.

Although it might seem that I’m being nitpicky about this implementation, the small issues above are representative of the types of problems that can rapidly turn a JavaScript application into an unbearable mess.

MVC to the Rescue: Introducing Spine

Spine is one of the many lightweight JavaScript micro MVC framework choices that have popped into the scene.

From the Spine Website:

"The library is written in CoffeeScript, but doesn't necessarily require CoffeeScript to develop applications. You can use CoffeeScript or JavaScript, whichever language you're most familiar with. Spine is tiny, the library comes in at around 500 lines of CoffeeScript, that's about 2K minified & compressed. Being lightweight and simple is fundamental to Spine."

Spine’s take on the MVC pattern might be slightly confusing for those accustomed to more traditional MVC implementations. Spine only provides Models and Controllers.

Figure BSB-3 shows the typical directory structure used for a Spine application. The application consists of 1 model, 2 controllers and a JavaScript file to bootstrap the application.

Figure Spine Bsb 3

Figure BSB-3 – jQuery/Spine Shopping Cart Project

Models

In Spine, a model represents an entity, which has state (data) and logic to manipulate said data. A model can be saved, retrieved and even queried for (to an extent). Models can be stored in memory (the default) or use any of the storage modules provided such as Ajax or HTML5 Local Storage. Listing BSB-4 shows the basic Item model that represents an item in our shopping cart application.

// Create the Item model.
var Item = Spine.Model.sub();
Item.configure("Item", "name", "pid", "price", "quantity");

Listing BSB-4

A Spine model “class” is created using the setup method of Spine.Model, which takes the name of the model and an array of properties. To make the model persist between page reloads we extend Item with the Spine.Model.Local module as shown in Listing BSB-5.

// Persist model between page reloads.
Item.extend(Spine.Model.Local);

Listing BSB-5

The extend method adds class properties to the model. We now have an object that can be created, saved and retrieved from the browser local storage. 

To add behavior to our model we use the include method which adds instance properties. Listing BSB-6 shows the four methods that we need for our Item model; increase, decrease, total and label.

// Instance methods
Item.include({
    //
    total: function() {
        return (this.price * this.quantity);
    },
    //
    increase: function(quantity) {
        quantity = (typeof(quantity) != 'undefined') ? quantity : 1;
        this.quantity = this.quantity + quantity;
		this.trigger("quantityChanged");
    },
    //
    decrease: function(quantity) {
        quantity = (typeof(quantity) != 'undefined') ? quantity : 1;
        if (this.quantity >= quantity) {
            this.quantity = this.quantity - quantity;
        }
        else {
            this.quantity = 0;
        }
		this.trigger("quantityChanged");
    },
    // 
    label: function() {
        return (this.name + " - $" + this.price);
    }
    
});

Listing BSB-6

To instantiate an Item we use the create method, which takes object literals for the parameters. A new model can be persisted using the save method. When a model is saved it is assigned an identifier that can be retrieved via the id property. All three are demonstrated in Listing BSB-7.

var item = new Item({name: "Product 1", pid: "0001" , price: 100.0, quantity: 1});
item.save();
alert("Just saved Item with id => " + item.id);    

Listing BSB-7

Controllers

Controllers in Spine are a combination of a traditional MVC controller and a view. Therefore controllers are in charge of rendering and manipulating one or more models in the context of the controller’s functionality.

Our first Spine controller will deal with the rendering and manipulation of an individual Item. The CartItem controller will deal with user interface events to increase and decrease the quantity of an Item while keeping the user abreast of the changes. Listing BSB-8 shows the CartItem controller contained in the file cart_item.js.

jQuery(function($){
    window.CartItem = Spine.Controller.sub({ 
        
        init: function(){
            var cartItem = this;
            
            this.item.bind("quantityChanged", function() { cartItem.updateQty() });
            
            $('#item_' + this.item.pid + ' .add').live('click', function(e) { 
                cartItem.add(); 
                e.preventDefault(); 
            });
            
            $('#item_' + this.item.pid + ' .remove').live('click', function(e) { 
                cartItem.remove(); 
                e.preventDefault(); 
            });
        },
        
        render: function(){
            this.el = $.mustache($("#cartItem").html(), this.item);
            
            return this;
        },
    
        // event handlers
        add: function(e) {
            this.item.increase();
        },
            
        remove: function(e) {
            this.item.decrease();
        },
    
        // ui methods
        updateQty: function() {
            $('#item_' + this.item.pid + ' #qty')
                .text(this.item.quantity)
                .effect("highlight", {}, 1500);
        }
    });
})

Listing BSB-8

Spine controllers are created using the create method of Spine.Controller, which takes an object literal that sets a wide variety of properties. In Listing BSB-8 we see the first property passed is named proxied and takes an array of Strings. The values are the name of the controller methods that need to be guaranteed to execute in the controller’s context (otherwise you’ll noticed that sometimes the this variable points to something other than the controller).

Events

In the controller’s init method we bind a custom event called "quantityChanged" of the enclosing Item model to the controller’s updateQty method.

this.item.bind("quantityChanged", function() { cartItem.updateQty() }); 

Listing BSB-9.1

The model’s "quantityChanged" is triggered in both the increase and decrease methods of the Item model:

this.trigger("quantityChanged");

Listing BSB-9.2

Also, in the init method, we wire the ‘add’ and ‘remove’ links of the CartItem to the add and remove methods which invoke the underlying Item model add and remove methods.

Rendering

To render the associated view for the CartItem controller we use the render method. In the render method we use jQuery to locate the element with id cartItem, which is a template, embedded in the HTML page as shown in Listing BSB-10.
render: function(){
            this.el = $.mustache($("#cartItem").html(), this.item);
            
            return this;
        }

Listing BSB-10

The cartItem template is a Mustache.js Template that enables the creation of markup templates containing binding expressions. The mustache method clones the template contents and replaces the binding expressions ({{exp}}) with the values of the object passed, in our case in Listing BSB-11 the controller’s enclosed Item model.

    
    <!-- Mustache :-{)~ Template for CartItem -->
    <script type="text/x-mustache-tmpl" id="cartItem">
        <li class="product ui-state-default" id="item_{{pid}}" price="{{price}}">
            {{label}}
            (<span id="qty">{{quantity}}</span>)
            <a href="#" class="add">+</a>
            <a href="#" class="remove">-</a>
        </li>
    </script>

Listing BSB-11

To test our controller we need to instantiate an Item model and pass it to the controller. We can then render the controller on the DOM of an HTML page as shown in Listing BSB-12.

    
	var item = new Item({name: "Product 1", pid: "0001" , price: 100.0, quantity: 1});
	var view = new CartItem({item: item});
	$("#item").html(view.render().el);

Listing BSB-12

A controller has an el (element) property that reflects the DOM element that will be the target of the rendering. The test page shown in Figure BSB-4 reveals that the CartItem controller accomplishes the functionality required to deal with a single Item model.

Figure Spine Bsb 4

Figure BSB-4 – CartItem Controller in Action

Shopping Cart Controller

With the CartItem controller in place we can now concentrate on the ShoppingCart controller (shown in Listing BSB-13), which will manage a collection of Item models.

Internally the ShoppingCart keeps the items dropped in the items property, which is initialized with an object literal in the init method. Also in the init method we set up the drop area for the drag-n-drop operation.

Persistence

In the init method we also query for any existing Item models that were previously saved by using the all method of the model (Item.all()). If any items are found then we simply add them to the cart using the addItem method.

Methods

The cart provides several aptly named methods to fulfill its functionality. The clear method iterates over all the contained Item models and invokes the destroy method which the cart listens to in order to animate the removal of rendered ItemCarts from the cart.

The methods total, isEmpty and itemsCount return the total dollar amount for the cart, whether the cart is empty and the count of all unique items in the cart respectively.

The drop method handles drop events similarly to the pure jQuery shopping cart implementation; it either creates a new item or increases the quantity of an existing item. The main difference being that the drop creates an Item model that is then rendered using the CartItem controller. Concerns separated!

    
jQuery(function($){
    window.ShoppingCart = Spine.Controller.sub({
        el: $("#theCart"),
        
        init: function() {
            var cart = this;
            this.items = {};
            $.each(Item.all(), function(){ cart.addItem(this); });
            this.el.droppable({ accept: '.product', drop: this.proxy(this.drop) });
            $('#dump', this.el).live('click', function() { cart.clear(); });
        },
        
        // removes all items from the cart
        clear: function() {
            $.each(this.items, function(){ this.destroy(); });
            this.items = {};
            this.updateCartTotal(); 
        },
        
        total: function() {
            var sum = 0.0;
            $.each(this.items, function(){ sum += this.total(); });

            return sum;
        },
        
        isEmpty: function() {
            return this.itemsCount() == 0;
        },
        
        itemsCount: function() {
            var size = 0;
            var items = this.items;
            $.each(items, function(){ if (items.hasOwnProperty(this)) size++; });

            return size;
        },
        
        drop: function(ev, ui) {
            var item_dropped = ui.draggable;
            var pid = item_dropped.attr('id');
            var price = item_dropped.attr('price');
            var name = item_dropped.attr('name');

            if (this.items.hasOwnProperty(pid)) {
                this.items[pid].increase();
            }
            else {
                var item = Item.create({name: name, pid: pid, price: price, quantity: 1});
                this.addItem(item);
                $(".items").append(CartItem.init({item: item}).render().el);
            }
        },
        
        render: function() {
            this.el.html($.mustache($("#shoppingCart").html(), {}));
            
            $('#dump').button();

            $.each(this.items, function(){ 
                $(".items").append(CartItem.init({item: this}).render().el);
            });
            
            this.updateCartTotal();
        },
        
        removeItem: function(item) {
            $('#item_' + item.pid).effect("puff", {}, "slow", function(){ $(this).remove(); });
        },
        
        updateCartTotal: function() {
            $('#total').text(this.total()).effect("highlight", {}, 1500);
        },
        
        removeIfQuantityZero: function(item) {
            if (item.quantity == 0) {
                this.removeItem(item);
                delete this.items[item.pid];
                item.destroy();
            }
        },
        
        addItem: function(item) {
            this.items[item.pid] = item;
            item.bind("quantityChanged", this.proxy(this.updateCartTotal));
            item.bind("quantityChanged", this.proxy(this.removeIfQuantityZero));
			item.bind("quantityChanged", function() { item.save() });
            item.bind("destroy", this.proxy(this.removeItem));
            item.save();
            this.updateCartTotal();
        }

    });
})

Listing BSB-13

At the end of Listing BSB-13 we can see that the addItem method sets several listeners to various events on the enclosed Item models. One of the advantages of frameworks like Spine is the ability to deal with standard changes in related models without having to tightly couple their implementation to the controllers. Also, note that in addItem we save the created models so that we can later retrieve them in case of a page refresh.

Tying It All Together

Finally, to kick everything in motion the application.js file begins by fetching any previously stored Item records, making the sample products draggable, creating a cart and rendering it.

  
jQuery(function($){
	Item.fetch();
	$(".product").draggable({ helper: 'clone', opacity: "0.5" });
	var cart = new ShoppingCart();
	cart.render();
});

Listing BSB-14

Conclusions

In this article we’ve covered the basics of single page web applications. These applications are nothing new; we’ve attempted them in the past but now we have frameworks like jQuery to deal with browser disparities and Spine to inject the MVC sense of order.

The new breed of MVC frameworks allows us to create clean, simple, componentized and event-oriented applications. We can separate our applications’ logic into cleanly defined areas of concern, cache data on the client side, minimize AJAX calls and provide many features that we would otherwise have to implement ourselves. We can now build rich state of the art JavaScript applications that can rival their desktop counterparts.

References

About The Author

Brian Sam-Bodden is avid developer, author and recognized international speaker that has spent over fifteen years working with object technologies. 

He is the president and chief software architect for Integrallis, where he focuses on building great applications with Ruby, Rails, Groovy, Grails and Java. Brian is the creator of many Ruby open source utilities including the JRuby XML tool “Excemel”, the Component-Oriented Ruby Framework “Trellis” and numerous Ruby on Rails Plugins.

He is also the author of Beginning POJOs: Spring, Hibernate, JBoss and Tapestry and has also co-authored the Apress Java title Enterprise Java Development on a Budget: Leveraging Java Open Source Technologies and contributed to O'reilly's 97 Things Every Project Manager Should Know.

Recommend Brian on Working With Rails


Saturday, December 18, 2010

Clojure is a relatively new, dynamic Lisp that runs on the JVM. Clojure, being a Lisp, is extremely malleable and extensible, allowing Clojure the language and the programmer the ability to create powerful yet consistent abstractions. Clojure out-of-the-box comes with a set of these "mini-languages" and gives the programmer the ability to create new ones easily. In this article we will discuss some of these mini-languages, and how you can use them to write idiomatic Clojure code.

This article by Michael Fogus attempts to list several technical mini-languages that Clojure provides. Fogus is a well known Clojure hacker and enthusiast and author of The Joy of Clojure. We will take some of the more interesting of these and explore them in detail - I will provide some background and attempt to illuminate their idiomatic usage with some examples.

On a side note, if you have been exploring Clojure for a while, I strongly urge you to pick up The Joy of Clojure by Michael Fogus and Chris Houser. Most other books about Clojure in the market (perhaps with the exception of Clojure In Action by Amit Rathore) work at explaining the "what" and "how" of Clojure, The Joy of Clojure attempts to teach the "why" of Clojure. I recognize this is a bold statement, but I have certainly found that this book does a very good job of explaining "the Clojure way" and is a treat to read .

Mini-languages

A mini-language is much like an internal DSL, or an embedded DSL. A mini-language allows you, the developer, to create higher-level abstractions to express a problem space or a specific domain. This allows you to build the language up toward the problem space rather than tear down the problem to fit within the confines of the language. Paul Graham, a well known entrepreneur, venture capitalist, founder of the Y Combinator incubator, and a Lisp proponent, has a very nice essay explaining this premise, if you are so inclined.

It should be noted that a mini-language is not meant to be a complete language. In fact, most mini-languages are very context-specific, and target a specific spot within the language or domain. We explored one such use-case in one of my earlier articles with NFJS, The Magazine. In that article we wrote a DSL that allowed for the manipulation of a Finite State Machine. We used Groovy's malleable syntax to define the various states that the state machine is allowed to be in, and the events that cause it to transition from one state to another. Listing GAN-1 demonstrates this.

class GitFileStateMachine extends StateMachine {
    {
      gsmInitialState “unmodified”

      gsmState “unmodified”
      gsmState “modified”
      gsmState “staged”

      gsmEvent “edit”, {
        transitions from:”unmodified”, to:”modified”
      }

      gsmEvent “add”, {
        transitions from:”modified”, to:”staged”
      }

      gsmEvent “commit”, {
        transitions from:”staged”, to:”unmodified”
      }
  } 
}

Listing GAN-1

On the other hand, consider the enhanced for loop that was introduced in Java 5. This too is an example of a mini-language - one that lets you succinctly express the act of iterating over the items in an Iterable.

You might have already concluded that these two examples are slightly different. In one case we are using the language to build an abstraction to work with a particular domain more expressively, while in the second case, the mini-language is "baked" into its host. Regardless, the intent of the mini-language remains the same: it gives us the ability to convey our intent unambiguously.

So let's get on with it, shall we? Fire up your trusted Clojure repl so that you can follow along…

Destructuring

As Michael notes in his article, destructuring is one of the more comprehensive and powerful mini-languages that Clojure provides. This is an example of a mini-language that is "baked" into the language. Destructuring allows you to extract pieces and parts of a collection and bind them to local symbols. We will start with a simple case [See Listing GAN-2], and proceed to different use-cases. Note that although some of the examples use the let form, you can use destructuring anywhere you have a binding form.

; define a collection
(def breakfast ["scrambled eggs" "bacon" "coffee"])

; use destructuring to extract the pieces
(let [[main side caffeinated_drink] breakfast]
     (println "I had" main "with" side "and a" caffeinated_drink))

; output at the repl
;> "I had scrambled eggs with bacon and a coffee"
;> nil

Listing GAN-2

Notice that we name each item in the collection by listing their respective symbols within a vector inside the let's binding vector. The assignment happens sequentially across the items in the collection that you are destructuring. If there are fewer items in the collection than the number of bindings you provide, then the extra symbols will be nil, and if there are more, then they simply won't get bound.

Naturally this approach works well when you know exactly how many items are to be in a collection. More often than not, that is not the case. Furthermore, you are usually interested in the first item, or n number of items in the collection, as well as the rest of the items in the collection, or the whole collection. No worries, we’ve got you covered :) [See Listing-GAN3]

; define a collection
(def breakfast ["scrambled eggs" "bacon" "coffee" "orange juice"])

; use destructuring for a few pieces
; and capture the rest using the & 
(let [[main side & drinks] breakfast]
     (println "I had" main "with" side "and" (count drinks) "drinks"))

; output at the repl
;> I had scrambled eggs with bacon and 2 drinks
;> nil

; capture the entire collection as well using the :as keyword
(let [[main side & drinks :as breakfast-food] breakfast]
     (println "I had" main "with" side "and" (count drinks) "drinks")
     (println "A breakfast with" (count breakfast-food) "items is very  filling"))

;> I had scrambled eggs with bacon and 2 drinks
;> A breakfast with 4 items is very filling
;> nil

Listing GAN-3

I should point out two things of note here. First, although we are using a vector to define the items in our breakfast (which is the idiomatic approach to defining a list of items), this form of destructuring will work with any sequential construct, like lists, or even Strings! The second is a little more subtle. It does seem to appear that the & and :as keyword do somewhat similar things (that is, capture a portion or the whole collection vs. capturing distinct items); they do so in different ways. The & returns a seq view of the remaining items while the :as keyword keeps the type of collection being destructured untouched. For those who like to write code by manipulating the magnetic bits on their hard-drives, & uses the clojure.core/nthnext function to capture the remaining items. You can find its documentation here. See Listing GAN-4, where we explore this just a little bit more.

(let [[main side & drinks :as breakfast-food] breakfast]
     (println "class of the original collection:" (class breakfast))
     (println "class of the & binding:" (class drinks))
     (println "class of the :as binding:" (class breakfast-food)))

;> class of the original collection: clojure.lang.PersistentVector
;> class of the & binding: clojure.lang.PersistentVector$ChunkedSeq
;> class of the :as binding: clojure.lang.PersistentVector
;> nil

Listing GAN-4

That takes care of sequential artifacts. What about associative constructs, like maps? It just so happens that you can destructure maps using their keys. The syntax looks a little contrived at first, but you will soon see why this is actually a useful feature. Let's start with Listing GAN-5:

; define a map of breakfast-items
(def breakfast-items {:main "scrambled eggs", :side "bacon", :drinks ["coffee" "orange juice"]})

; notice we are using a map inside the binding vector
(let [{main :main side :side  drinks :drinks} breakfast-items]
     (println "I had" main "with" side "and" (count drinks) "drinks"))

;> I had scrambled eggs with bacon and 2 drinks
;> nil

Listing GAN-5

The thing to note here is that the local symbol is to the left, and the key that you are looking up is to the right, which is consistent with the usual binding forms that you see everywhere in Clojure: the symbol being assigned is to the left of the value it's being assigned to. But there is another reason which we will see in the following sections (See "Putting it Together" if you just can't wait).

If the verbosity of the code in Listing GAN-5 bothers you, look no further than Listing GAN-6. Clojure provides a :keys keyword to eliminate some of that.

; define a map of breakfast-items
(def breakfast-items {:main "scrambled eggs", 
                      :side "bacon", 
                      :drinks 
                      ["coffee" "orange juice"]})

; notice that we have the :keys keyword followed by a vector inside
; the binding vector
(let [{:keys [main side drinks]} breakfast-items]
     (println "I had" main "with" side "and" (count drinks) "drinks"))

;> I had scrambled eggs with bacon and 2 drinks
;> nil

Listing GAN-6

In this case rather than using a vector for destructuring, we are using a map. The :keys keyword tells Clojure to look up the keys in the map that have the same name as the ones listed in the vector that follow it, and bind it to local symbols with the same name. So in the case of [{:keys [main]}] Clojure will look for a key with the name main within the map, create a new symbol named main within the let scope, and associate the value of main from the map to this new symbol. This works well for the usual case when you know which keys should be supplied.

The associative destructuring also supports the :as keyword, which gives you a handle to the entire map. Use it to seek out key-value pairs for which you did not provide explicit destructuring. Furthermore, associative destructuring gives you another keyword - :or which lets you define a default value for a binding in case one or more keys do not exist in the map provided (without this the binding for that key would be nil). See Listing GAN-7 for a few examples.

 
; define a map of breakfast-items
(def breakfast-items {:main "scrambled eggs", 
                      :side "bacon", 
                      :drinks 
                      ["coffee" "orange juice"]})

; notice that we have the :or keyword followed by another map inside
; the binding map
; I realize I have a sweet tooth :) 
(let [{:keys [main side drinks dessert] 
     :or {dessert "strawberry danish"}} 
     breakfast-items]
     (println "I had" main "with" side "and" (count drinks) "drinks")
     (println "For dessert it was" dessert))

;> I had scrambled eggs with bacon and 2 drinks
;> For dessert it was strawberry danish
;> nil

; let's define a map with dessert included
(def breakfast-items-with-dessert {:main "scrambled eggs", 
                                   :side "bacon", 
                                   :drinks ["coffee" "orange juice"], 
                                   :dessert "pecan pie"})

; running the same let again
(let [{:keys [main side drinks dessert] 
     :or {dessert "strawberry danish"}} 
     breakfast-items-with-dessert]
     (println "I had" main "with" side "and" (count drinks) "drinks")
     (println "For dessert it was" dessert))

; we get
;> I had scrambled eggs with bacon and 2 drinks
;> For dessert it was pecan pie
;> nil

; throwing in the :as operator in the mix
(let [{:keys [main side drinks dessert] 
     :or {dessert "strawberry danish"} :as items} 
     breakfast-items-with-dessert]
     (println "I had" main "with" side "and" (count drinks) "drinks")
     (println "For dessert it was" dessert)
     (println "In all" (count items) "items including" 
       (count drinks) "drinks"))

;> I had scrambled eggs with bacon and 2 drinks
;> For dessert it was pecan pie
;> In all 4 items including 2 drinks
;> nil

Listing GAN-7

Once again, there a few points to note. The destructuring for drinks should not surprise you - the drinks binding within the let is the vector of drinks (thus we can do a count drinks on it). Also, the :as works in a similar fashion to associative destructuring, giving you the entire map within scope. For those who have had experience with other Lisps this seems like a way to get named parameters in Clojure (albeit the poor man's version :D). Clojure has no support for named parameters, but you can achieve a similar effect with associative destructuring (a technique that Rails uses with much success since Ruby too does not support named parameters).

Putting It All Together

I mentioned earlier that the associative destructuring syntax seems a little contrived. But there is a hidden gem in there; this becomes apparent when you start to mix and match the various kinds of destructuring available to you. In listings GAN-6 and GAN-7 we had a vector of drinks being mapped to the :drinks key inside the map. What if you wanted to destructure that vector along with the key-value pairs themselves? See Listing GAN-8 to see how to do this.

; define a map of breakfast-items
(def breakfast-items {:main "scrambled eggs", 
                      :side "bacon", 
                      :drinks 
                      ["coffee" "orange juice"]})

; we are back to the verbose associative destructuring form
; notice we are further destructuring drinks as a sequential destructuring
(let [{main :main, side :side, [first_drink second_drink] :drinks} 
     breakfast-items]
     (println "I had" main "with" side)
     (println "My first drink was" first_drink 
       "and my second was" second_drink))

;> I had scrambled eggs with bacon
;> My first drink was coffee and my second was orange juice
;> nil

Listing GAN-8

Perhaps now you see why this is a feature. Clojure can safely assume that anything to the left of the key name in the destructuring form is the symbol that is to be assigned. This could be the :keys keyword, or a symbol, or yet another destructuring form! The syntax remains consistent regardless of how you are using the destructuring form. (Hats off to The Joy of Clojure that explains this in great detail.)

There is a lot more to destructuring, but before we move on to the next mini-language, I should put out another disclaimer. Remember that you can use destructuring everywhere you can bind locals. This includes function signatures. Here, you have a choice - you could just choose to accept the collection as an argument and then destructure the argument inside the function body using a binding form, or you could expose the destructuring as part of the function signature. Consider the examples in Listing GAN-9.

; generic signature hiding how the coll is being used
(defn some-fn [coll]
     (let [[first second & rest] coll]
          ( ;body
           )))

; exposing the destructuring in the function signature
(defn some-fn [[first second & rest]]
     ( ;body
      ))

Listing GAN-9

You should consider how much of the internal working of the function you wish you expose to the end user, and this is something you will need to do on a case-by-case basis. The first option buys you a lot more flexibility but you need to ensure that you have documented the function well, and the second one can make it easier for callers of your API to see what is expected.

List Comprehensions

If you have been playing with Clojure for a while, you might have noticed that it does not provide you with a for loop. For those new to Clojure, this might be a little bit surprising. But Clojure gives you something more powerful - list comprehensions. Essentially, list comprehensions are a construct that let you create new lists, or rather sequences, based off existing ones. If you deem yourself math savvy, then this article from Wikipedia may just be up your alley. Otherwise, keep reading …

List comprehensions can be a little hard to wrap your head around but are incredibly powerful. To use list comprehensions, you need one or more sequences that you are operating on and potentially some predicates, or conditionals that items in the newly created sequences must conform to. Clojure's list comprehensions support the :when and :while keywords to specify your predicates. The :when keyword, true to its name, filters out elements from the final sequence that do not meet a specific criteria. The :while keyword, on the other hand, is more of a go, no-go situation. The evaluation of the list comprehension is halted when the first element fails the :while predicate. That's it. Armed with this knowledge, consider Listing GAN-10 for a few examples to get started.

; simple list comprehension
; I realize it's very contrived :)
(for [x (range 10)] x)

;> (0 1 2 3 4 5 6 7 8 9)

; using the :when to filter out odd items
(for [x (range 10) :when (even? x)] x)

;> (0 2 4 6 8)

; using the :while keyword - Note that the evaluation
; stops when the first item, in this case 1 fails the predicate
(for [x (range 10) :while (even? x)] x)

;> (0)

Listing GAN-10

Notice that, every time, you get a sequence as the result of the evaluation. I mentioned earlier that you could have one or more sequences that you are operating on. See Listing GAN-11 for some examples.

; let's list all the highest powered cards in a deck
; start by defining the highest cards and all the suits
(def high-cards ["A" "K" "Q" "J"])
(def suits ["clubs" "diamonds" "hearts" "spades"])

(for [c high-cards s suits] (list c s))

; truncated for brevity
;> (("A" "clubs") ("A" "diamonds") ("A" "hearts") ("A" "spades") ("K" "clubs")
;> ... ("J" "spades"))

Listing GAN-11

If you look carefully at the output in the previous listing, you will notice that the first four items in the resultant sequence are all "Aces" (4 in total) followed by the "Kings" and so on and so forth. When working with multiple sequences, the for list comprehension starts with the right most sequence and works its way left. Another way to look at this is to think of the right most sequence as the "inner" loop. Once the inner loop is exhausted, Clojure moves to the next item in the outer (left) sequence and repeats.

The list comprehension supports one more keyword, :let which lets you bind other symbols within its context (as shown in Listing GAN-12).

; list all the numbers and their sums where the sum is lesser than 10
(for [a (range 10) b (range 10) 
     :let [c (+ a b)] 
     :when (> 10 c)] (list a b c))

; truncated for brevity
;> ((0 0 0) (0 1 1) (0 2 2) (0 3 3) (0 4 4) ... (9 0 9))

Listing GAN-12

Using the :let keyword in this case makes the code, and our intent, clearer. Before I end this section on list comprehensions, I would like to point out a few things. One, you might be surprised to hear that list comprehensions in Clojure are not baked in, rather the for loop is the result of a macro expansion! Two, Clojure strives to be consistent, thus making the amount of context, and the number of rules that you need to keep in your head less. You will notice that the :let syntax is similar to other places where you might have used let. It's the let followed by a vector of bindings. You will find another great example if you are to compare the doseq construct with the for construct (Note that doseq is not a list comprehension). See Listing GAN-13 for an example.

; list all the numbers and their sums where the sum is less than 10
(doseq [a (range 10)
        b (range 10)
        :let [c (+ a b)]
        :when (> 10 c)]
        (println (list a b c)))

; truncated for brevity
;> (0 0 0)
;> (0 1 1)
;> (0 2 2)
;> (0 3 3)
;> (0 4 4)
;> (0 5 5)
;> ,,,

Listing GAN-13

doseq can be deemed equivalent to the "for each" that you may be used to in languages like Java. doseq runs immediately (which is why we println each list), while for creates a new sequence containing each of the new items (in line with the definition of a list comprehension). Furthermore, the do in doseq tells you that, if you are to have side-effects, doseq is the idiomatic way of doing it.

Pre and Post Conditions

Pre and Post conditions are a relatively new feature in Clojure and, as Michael correctly points out in his article, form the basis for contract programming. The pre and post conditions act as assert statements for your function, validating the input to and the return value from the function to verify that it conforms to a certain criteria. Let's start with an example (See Listing GAN-14).
; a function that calculates the square root of a number
(defn sq-root [x]
  {:pre [(pos? x)]
   :post [(pos? %)]}
  (Math/sqrt x))

Listing GAN-14

You can see that the pre and post conditions follow the argument vector of the function definition, and are declared as a map of keywords to vectors. (You can have more than one conditional check for each; just tack on more entries in the vectors). In Listing GAN-14 we are checking to see if the input value and the return value are positive numbers. Notice that we can capture the return value of the function using the % symbol. This is yet another example of how Clojure strives to be consistent in its syntax. The % sign, for those of you who have written anonymous functions (using the #() syntax), performs the same role - a means to capture a argument.

If a pre or a post condition were to fail, you would get an AssertionError, and the evaluation of the function would be aborted. Pah!, you say, there isn't anything here that a faithful assert can't do for me. Don't be quick to judge! We need to consider that the conditions are defined in a map, and this is one of those situations where Clojure's homoiconicity reveals its power. Think about it this way: what if you were to separate the creation of the conditional map from the function that it was applied to? It's simply a Clojure map. We could do that easily, right? Consider the examples in Listing GAN-15.

; constraints on positive numbers
(defn positives-only [f x]
   {:pre [(pos? x)]
    :post [(pos? %)]}
   (f x))

; apply those constraints to sq-root defined in Listing GAN-14
(positives-only sq-root 4)
;> 2.0

(positives-only sq-root -4)
;> AssertionError

Listing GAN-15

Notice how we decoupled the constraint itself from the function that we use it on. The constraints are no longer invasive on the function that is actually doing the heavy lifting, namely sq-roots. You could define other constraints and use them with your sq-root function, or define other mathematical functions and apply any of the constraints on them - mix and match, plug and play. Take it a step further: you could have a function that actually creates the constraint map based on certain arguments and invokes the delegate on your behalf! Can you say “aspects”?

The Thrush Twins … Kinda …

The thrush operators -> and ->>, in my opinion fill a very sweet spot within Clojure. Most Lisps inherently have code written "inside-out". See Listing GAN-16 for an example.

; calculating final amount due with simple interest
; for a principal of $1000 at 20% for 2 years
(def principal 1000)
(def rate 20)
(def years 2)

(+ principal (* principal (* (/ rate 100) years)))
;> 1400N

Listing GAN-16

In order to read the calculation in Listing GAN-16, you start with the inner-most nested form, and work your way outward. You first divide the rate by 100, multiply by the number of years, multiply by the principal, and then finally add it to the principal to get the amount you owe after 2 years. This is where the thrush operators can be handy. Thrush operators allow you to turn the code "outside-in", laying it out in a fashion that makes it easier to interpret the problem. See Listing GAN-17 to see how this works.

; calculating final amount due with simple interest
; for a principal of $1000 at 20% for 2 years
(def principal 1000)
(def rate 20)
(def years 2)

(-> 
  (/ rate 100)
  (* years)
  (* principal)
  (+ principal))

Listing GAN-17

The thrush operator threads the output of evaluating the first argument as the first argument of the second form, then takes the result of that evaluation and continues. Listing GAN-18 attempts to explain this by highlighting where values will be threaded using ",,,".

(-> 
  (/ rate 100)       ;             => 0.2
  (* ,,, years)      ;(* 0.2 2)    => 0.4
  (* ,,, principal)  ;(* 0.4 1000) => 400
  (+ ,,, principal)) ;(+ 400 1000) => 1400

Listing GAN-18

The double-thrash, that is ->> does something similar (See Listing GAN-19), except rather than threading the evaluation of the previous form as the first argument to the next, it threads it as the last. In our chosen example it does not change the output (since the last three forms are commutative mathematical operations). It goes without saying that this may not always be the case, for example if you had a division operation.

(->>
  (/ rate 100)       ;             => 0.2
  (* years ,,,)      ;(* 0.2 2)    => 0.4
  (* principal ,,,)  ;(* 0.4 1000) => 400
  (+ principal ,,,)) ;(+ 400 1000) => 1400

Listing GAN-19

A couple of things to note. The thrush operators, again, are just macros. Consequently, this has inspired other similar mini-languages that do similar but slightly different things (like allowing for nils). For example, check out this library currently in Clojure Contrib. Finally, there is a caveat: although thrush operators are really cool, they don't always do what you think they might do. See this article (again by Michael Fogus) that discusses this and gives you an alternative form that you can consider.

Thrush operators can make your Clojure code less Lispy (gasp!). In all seriousness thrush operators can make your code more readable and easier to follow.

So Many More, So Little Time …

Michael's blog post mentions several other mini-languages within Clojure. I, humbly, propose one more: Clojure's Java interop operators. Of course, there is the grand-daddy of them all, the macro, and it's very own mini-language, which makes a lot of the other mini-languages possible. Macros are a fundamental construct to all Lisps and Lisp's consistent syntax gives them their awe-inspiring and mind-bending prowess. Macros deserve a discussion of their own, but unfortunately, we have run out of time. Perhaps some other time?

Conclusion

Clojure's mini-languages aim to solve very specific concerns. Their use in Clojure is wide-spread, even in places where you would not expect them. Using these mini-languages makes your life as a Clojure developer easier and your code consistent and easier to read and maintain – all while being idiomatic Clojure code. Clojure encourages the creation of other mini-languages with the use of macros. I will leave you with one question: can you think of any other places in Clojure where a mini-language would be useful? Till next time …

Raju Gandhi

Raju is a Ruby/Java developer and a programming language geek. He has been writing software for the better part of a decade in several industries including education, finance, construction and the manufacturing sector. Raju has a graduate degree in Industrial Engineering from Ohio University. Raju is Integrallis' Clojure man and teaches Ruby/Rails, jQuery/JavaScript and of course Clojure.

In his spare time you will find Raju reading, watching movies, or playing with yet another programming language. Raju is the founder and host of inclojure, the Columbus Clojure User Group. He is affectionately known as @looselytyped on Twitter.


Thursday, November 18, 2010

So you are a Ruby developer who has been working with Ruby for a while with multiple projects using different flavors of Ruby and a whole plethora of gem dependencies. You even have multiple Rails projects using different versions of Rails that you need to manage and update. All the while, you have a set of gems and plugins that you have been investigating to see which ones best suit your needs. Up until now you have had to meticulously tweak your path each time so that you can run the version of Ruby that your current project requires, hoping that an install of a new gem won't create havoc with your existing dependencies.

Enter RVM. RVM is the "Ruby Version Manager" - a command line utility that lets you manage different versions of Ruby (including JRuby) as well as the specific gems you need for that version. RVM does this by allowing you to create a dedicated sandbox for multiple Ruby versions; or even project-specific sandboxes that contain the right version of Ruby and all the necessary gems for it. Any Ruby process running within this sandbox will only see these specific dependencies, making inter-project dependency conflicts a thing of the past.

With RVM, switching between projects merely entails waking up the correct sandbox for that project. You can even configure RVM to set the sandbox automatically when you cd into a project folder so that you are up and running quickly. Feel like dabbling with a newer version of a gem without corrupting your gem list? No worries. Have a new developer on your team that needs to be set up with the "right" environment? All they have to do is pull in your source code and “cd” into the project.

This article will give you a detailed introduction to RVM and show how you can use it to streamline your workflow, especially when working with multiple projects. We will briefly introduce Bundler, and how you can use it with RVM to sync your entire team throughout the development process.

Installation

Intrigued? Ready to give RVM a spin? This section will step you through installing RVM on your machine. If you are worried about corrupting your current JRuby (or Ruby) install, fret not! Installing RVM is, for the most part, non-intrusive. If you find that RVM does not fit your needs, uninstalling it (along with any Ruby and associated gems that you install with it) is simple.

So let's get started. A disclaimer here for all Windows users - RVM is not currently, and will probably never be supported on Windows. pik is a suggested alternative and you can find it here). We will start with installing RVM itself and then follow it up by installing JRuby.

Excited? Fire up your terminal and run the code in Listing GAN-1.

bash < <( curl http://rvm.beginrescueend.com/releases/rvm-install-head )

Listing GAN-1

This does a couple of things, including creating a ~/.rvm directory and installing RVM into it. After the install you need to make sure that your path is correctly set up to pick up RVM as a regular function. Open your profile file (~/.bash_profile or ~/.bashrc) with your favorite text editor (that would be emacs, yes? :) ) and insert the lines in Listing GAN-2 at the end.

# this will load RVM in your terminal every time you start one
# put this at the very end of your profile file
# notice that we are pointing to the rvm directory in your home folder
[[ -s "$HOME/.rvm/scripts/rvm" ]] && source "$HOME/.rvm/scripts/rvm"

Listing GAN-2

Just to make sure everything went as planned, open up a new terminal window and type in the code in Listing GAN-3. You should see the "Usage" section of RVM displayed in your terminal.

rvm

Listing GAN-3

While we are on the topic of installation, if you wish to upgrade to the latest release/version of RVM, you can run the code in Listing GAN-4.

# This will upgrade RVM to the latest code in the git repository
# and then run the install script within it
rvm update --head

# you can also run the following to get the latest version of the RVM gem
rvm update

Listing GAN-4

On a related note, the rvm command has a host of useful options. I have listed a few in Listing GAN-5 that I have found useful, but be sure to look over the help to see what else RVM has to offer.

RVM Command Description
rvm --v Display RVM’s version information
rvm --trace <command name> Puts RVM in ‘trace’ mode – Handy if something does not quite work
rvm install <ruby version> Installs a new (J)Ruby version
rvm info Displays information about the current Ruby version, install locations, and the gemset (if any) in use - we will discuss installing JRuby and gemsets in the following sections
rvm list Lists all the Ruby interpreters and implementations that RVM is managing for you
rvm gemset list Lists all the gemsets (to be discussed soon) that are installed for the current Ruby version in use

Listing GAN-5

While I am at it, here is another helpful tip: How often have you used the locally installed rdocs that most gems come packaged with? If the answer to that question is "never" or "almost never" or, even better, "I trust Google for that", then go ahead and put the code in Listing GAN-6 in your ~/.gemrc (or /etc/gemrc) file. This will prevent gems from installing their associated rdocs when you install them.

gem: --no-ri --no-rdoc

Listing GAN-6

Installing your (J)Rubies 

We now have RVM installed; let's install JRuby next. Note that if this is your first time using JRuby, you will need to have to Java installed. If you already have JRuby installed, no worries; RVM won't conflict with that. The code to run is in Listing GAN-7.

# let’s install jruby
rvm install jruby
# this will install JRuby in a directory under the $HOME/.rvm
# directory - mine is at $HOME/.rvm/gems/jruby-1.5.2
# rvm/gems/jruby-1.5.2

Listing GAN-7

Now, if you want to install other Ruby implementations, such as MRI, you will use the same command as in Listing GAN-7 except you substitute "jruby" with say "1.8.7" (for Ruby 1.8.7) or ree (for the Ruby Enterprise Edition).

# sets the default Ruby implementation for RVM to use
rvm --default jruby

# just to make sure it took, run the following to ask RVM 
# what it thinks the default is
rvm list default
# the output on my console look like this
# Default Ruby (for new shells)
#    jruby-1.5.2 [ x86_64-java ]

Listing GAN-8

Now What? 

If JRuby is the only Ruby implementation that you have installed, and you were using JRuby prior to this exercise, you must be wondering what this bought you. Well, to keep things interesting, open a new terminal and execute the code in Listing GAN-9.

# make sure you are using RVM 
rvm list # you should see a => next to the JRuby version

# make sure you are seeing the correct version
jruby -v # see the same version as you saw in the command above

# list the gems installed
gem list

Listing GAN-9

No reason to panic! Your previously installed gems are still safe and tucked away where you last had them. If you really want to make sure (and for another exercise in RVM), follow the code in listing GAN-10.

# ask RVM to use the system-installed Ruby implementation
rvm system

# make sure you get the right version
# assuming you had JRuby installed, else call ‘rvm use jruby’
jruby -v 

# list your gems 
gem list # you should see your previously installed list

Listing GAN-10

Listing GAN-10 demonstrates how you can fall back on the "default" Ruby implementation for your system. But I assure you, after you are done reading about and playing with RVM you will see no reason to. :)

How Does This Work?

RVM creates separate sandboxes for each of the Ruby implementations that it manages. One thing that RVM does is tweak your path so that the directory containing the Ruby implementation in "use" (via rvm use) is included. RVM will include this directory as the first item in your path, thereby over-riding a ‘system’ install, if you happen to have one. So if you were to execute echo $PATH after a rvm use jruby you will see what I mean, as in Listing GAN-11.

# set RVM to use the correct Ruby implementation
rvm use jruby

# echo your path
echo $PATH

# the first item on my path is  /<home_dir>/.rvm/gems/jruby-1.5.2/bin: ... 
# followed by other items in my path

Listing GAN-11

Now if you were to open a new terminal window and follow the code listed in Listing GAN-11, but with a different Ruby implementation like ree, and inspect your path, you would see that RVM has included the install directory for that installation of Ruby in your path (in place of the JRuby install directory). This means that each of those windows acts as a dedicated environment for independent Ruby setups. You can safely run jirb in one and irb in the other without those two ever conflicting with one another. Thus RVM allows you to have development level setups. But there is more: you can set up "gemsets" to install gems for specific implementations. That's next!

Understanding Gemsets

Let's start with a simple use-case. You are working on an existing Rails project, but you want to play with Rails 3 in your spare time. You want to install Rails 3 without affecting your existing Rails install. Yes, you could install them side-by-side, followed by some alias-ing magic in your profile so that you could execute the appropriate Rails command at the right time. Sound painful? With RVM by your side, you may cast aside your fears. RVM lets you create named gemsets. Gemsets let you create collections of installed gems that you can refer to. When you are working with a gemset, any and all gems you install will be installed within a compartmentalized and isolated sandbox. You can have the same gem with different version numbers in different gemsets with no worry of conflicts. If you find that a particular gem does not fit your needs, uninstall it! You can even blow away the entire gemset and start again if you desire.

Global, Default and Project-specific Gemsets

RVM has three different kinds of gemsets. It maintains a global gemset, which is a set of gems that will get installed every time you install a new Ruby implementation (e.g., rake is one that you would want for each Ruby install, yes?). The default gemset is a list of gems that will get installed every time you create a new gemset (we will discuss this next). Finally, there are project-specific gemsets that you can create for your specific projects.

Enough with the Talking! Show Me the Code Already!!!

Here we go. Start up a new terminal, follow the code in Listing GAN-12 and inspect the code just so you know what's going on. I have sprinkled in some comments to guide you along.

# make sure you are using JRuby - should be if you set it as 
# the default as we did in Listing GAN-8
rvm use jruby

# let's see what we have installed in default gemset
gem list
# lists the gems that were installed to support JRuby

# let's create a new gemset - notice the 'gemset create'
rvm gemset create my-rails-dependencies

# now, we need to use it - notice we have to use an @ in front of the name
# the @ tells rvm that this is the name of a gemset, vs. a ruby implementation
rvm use @my-rails-dependencies

# list some info
rvm info
# notice that it lists the gemset name at the very end

# let us install rails - Note: NO sudo!!!
# this will install rails inside the ‘my-rails-dependencies’ gemset
# you could potentially have another gemset with a different version of rails
# without the two ever conflicting!
gem install rails

# list the gems to make sure all is well
gem list

# just for grins - switch back to using the default gemset
rvm use jruby

# now list gems - notice, no rails!
gem list

Listing GAN-12

Pretty cool, huh? If you are feeling frisky, go ahead and create another gemset with a different name, install another gem, and switch back and forth between the two gemsets.

As a final note, you no longer have to (nor should be) sudo-ing. RVM, as I mentioned earlier, installs everything in your home directory. This also ensures that it is easy to delete gemsets and even entire Ruby installs if you ever need to.

While we are on the topic of deleting gemsets, let's go ahead and do that (see Listing GAN-13), with a small twist. :)

# Make sure you have the right implementation and gemset
rvm info

# now, let's export the set of gems you have installed
rvm gemset export my-rails-dependencies.gems
# this will export a my-rails-dependencies.gems file to your home directory

# now let's blow away the gemset
rvm gemset delete my-rails-dependencies
# rvm will ask you to confirm this. "yes" should do it :)

# create and switch to a new one in a one-liner
rvm use @some-other-gemset --create

# now import the previously exported gemset
rvm gemset import my-rails-dependencies.gems

# Notice that rvm is installing all the gems that it finds in the .gems file
# Voila!

Listing GAN-13

Did you see what we just did? Suppose you have a new developer on the team. You need a way to quickly set up her development environment. Once she has JRuby with RVM installed, you can export your gem file and send it her way, and they just "import" it. They will have the correct set of gems to work with, right down to the same versions!

Setting up Project-specific Gemsets

We have talked about RVM, ruby installs, and gemsets to manage the gem list for each of your projects. This would be enough if you had only one project and one gemset, but this is usually not the case. With multiple Ruby installs and multiple gemsets per install, you not only have to remember which combination applies to the project that you are currently working on, but you have to remember to switch to it. Obviously this is tedious, but more importantly error-prone. If you were to install a gem in the wrong gemset, well then you are back to where you were prior to using RVM! Well, as geeks we need to figure out a way to make this happen transparently. No worries, RVM has you covered.

Using a Project-specific .rvmrc File

RVM allows you to have a .rvmrc file in your project. When you cd into this project, RVM will inspect this file and set up your Ruby environment automagically for you. But first, we need to set up RVM so that it knows to look for the project-specific .rvmrc file (See Listing GAN-14) .

# cd into HOME
cd ~

# look for a .rvmrc file in your HOME directory
# if there isn't one, then create one
touch .rvmrc

# now set up RVM so it looks for the .rvmrc file
echo 'export rvm_gemset_create_on_use_flag=1' > .rvmrc
Listing GAN-14
Let's make sure we have a gemset to play with as shown in Listing GAN-15. This will be quick, I promise. :) 
# Let's make a gemset so we can try project-specific .rvmrc files out
# I am assuming you have JRuby being managed by RVM. 
# If not, change it here to use the correct one
rvm use jruby@project-specific-gemset –-create

Listing GAN-15

Now, we are going to pretend that test-project is the root directory for the source code of the twitter-killer application that you have lying on your hard-drive. Since you know you are going to need to scale quickly (wink wink, nudge nudge) you have decided to use JRuby. You are a cautious developer, and a recent RVM convert, so you are going to use the 'project-specific-gemset' gemset (which we created in Listing GAN-14) to stash all the gems specific to your stealth project. Ready? (See Listing GAN-16.)

# root directory of your application
mkdir test-project

# let's cd into it
cd test-project

# notice that the gemset name here is the same as the one 
# we created in Listing GAN-15
# the --create will cause RVM to create a gemset if it does 
# not already exist (the next time you cd into this project)
# the --rvmrc will create a .rvmrc file in the current directory
rvm jruby@project-specific-gemset --create --rvmrc

# if you are curious, 'less' the .rvmrc file that just got created

Listing GAN-16

Let's continue pretending and simulate a regular work-day. You come into work, open a new terminal window and execute the lines in Listing GAN-17.

# just to prove that I have no cards hidden in my sleeve 
rvm info
# pay attention to the gemset listed at the very end. It should be empty

# this is what you would normally start your day with
cd test-project

# Done! To prove my point
rvm info
# notice that you are using project-specific-gemset (listed at the end)

# now, let's cd out of the directory
cd .. 
rvm info
# notice that you are no longer using the project-specific-gemset gemset
# furthermore, if JRuby is not your default Ruby implementation RVM will
# have switched back to the default!

Listing GAN-17

A typical approach is to create the .rvmrc file for your projects and then check them into source control. Assuming the other developers on your project are using RVM with JRuby installed, getting them set up with the right Ruby implementation and the same gemset as everyone else now happens automatically. You no longer have to think about which gemset goes with a particular project, on your machine or anyone else's.

There are a couple of things you might have noticed. One, anyone else using the same .rvmrc file needs to have RVM and JRuby installed. Two, having the .rvmrc file will not install the gems that are needed for the project (more on that in the following sections). All the .rvmrc file does is switch to the correct Ruby implementation, and then switch to the correct gemset, creating it if necessary.

Finally, RVM is "intercepting" the cd command of your terminal so that it can execute the .rvmrc file when you switch into and out of directories. This is generally not an issue, but to all of you shell-scripters out there, it’s something to keep in mind. :)

Bundler

Bundler is a relatively new library (packaged as a gem) that can be used to manage the dependencies (including resolving the dependencies for your dependencies) for your Ruby and JRuby projects across all phases of development (e.g., development vs. testing). Bundler now comes as the default dependency management tool for Rails 3. We will not delve deep into the workings of Bundler in this article; rather I will introduce Bundler and demonstrate how you can use it with RVM.

The Gemfile

To declare your dependencies so that Bundler can manage them for you, you need a Gemfile at the root level of your project directory. Within this file you can tell Bundler the sources for your gems (e.g., http://rubygems.org or a Git repository URL) followed by a list of gems that your application depends on. You can also include specific version numbers if you so desire. Bundler also respects the notion of a "scope" or “group”, that is, gems that you need only for a specific phase of your development process, like testing.

When you invoke Bundler, it will inspect the Gemfile, connect to the sources you specified in the Gemfile, and pull in each of the gems that you have listed. Listing GAN-18 shows a sample Gemfile.

source 'http://rubygems.org'

gem 'rails', '3.0.0'

group :development, :test do
  gem "rspec-rails", ">= 2.0.0.beta.12"  
  gem 'webrat'
end

Listing GAN-18

As you can see, we are specifying a source for our gems, and then installing rails 3.0.0. We specify rspec-rails and webrat only as dependencies for the development and test phases. Now, if you were to run bundle install, or simply its alias bundle, Bundler will reach out to the inter-webs, and pull in the required dependencies. As simple as that!

Needless to say, Bundler offers a myriad of other neat features, but this is sufficient to get us started.

Some of you may have noticed that Bundler offers similar functionality to Maven or Ant with Ivy. Bundler’s ‘group’s are similar to Maven’s ‘scopes’ – they allow you to declare different dependencies for different phases of your development process (e.g. an in-memory database for development and testing vs. MySql for production).

Unfortunately there is no parallel to RVM within the Java eco-system. The reason for this lies in the manner in which your Java dependencies are resolved. Java has the notion of a classpath – Java will search this classpath for any and all dependencies, failing which you will get an error. Non-Bundler enabled Ruby applications look in the Ruby load path – this is where gemsets play a role. RVM ensures that the correct Ruby implementation along with the correct gemset lies in your load path. This way the Ruby interpreter can find all the necessary dependencies that your application requires.

RVM and Bundler, Sitting in a Tree …

So, now we have a tool that lets us neatly tuck away project-specific dependencies into a self-contained environment, and a tool that lets us declaratively specify those very dependencies. But before we go further, I need to make an admission.

Early on I mentioned a way to import and export your gemsets using RVM. Well, with Bundler you can declare your gem dependencies in your Gemfile. If your Gemfile (along with your project-specific .rvmrc file) is checked into your repository, there is no reason to export and import gemsets any more! When your fellow team members cd into the project root, the .rvmrc file will ensure that they are using the correct (J)Ruby version as well as the correct gemset (and create it if necessary). A simple bundle install will pull in the correct version of all gems that your application depends on and tuck it neatly inside that gemset. This is because Bundler is run within RVM, so it will install the gems where RVM tells it to. Done!

One More Thing …

You have that gleam in your eye; you can see a way of making yourself, and your team, more productive. But before you fire up your terminal to create your gemset and .rvmrc file, hold on. This time, I actually have a trick up my sleeve. :)

You see, the .rvmrc file is an executable file. When RVM sees that file, it merely executes every command listed in that file. What if there was a way to execute the bundle install command when a fellow developer cds into the project directory? As you might have guessed by now, there is! See Listing GAN-19 for an example of a project-specific .rvmrc file that does just that. (This was artfully stolen from the source code for www.tedxperth.com found here, although the comments in the code are mine.)

# use ree (Ruby Enterprise Edition) and switch to the
# tedxperth gemset creating it if necessary 
rvm use --create --install ree@tedxperth >/dev/null 2>&1

# attempts to look for an exported gemset list in the home directory
if [[ -s "./tedxperth.gems" ]]; then
  if ! rvm gemset import tedxperth.gems >/dev/null 2>&1; then
    echo "ERROR: Unable to bootstrap the gems" >&2
  fi
fi

# Finally, fires off the bundle install command
if command -v bundle >/dev/null && ! grep -q BUNDLE_FROZEN .bundle/config 2>/dev/null ; then
  bundle install >/dev/null 2>&1
fi

Listing GAN-19

Pretty cool, huh? Now you can concentrate on writing code that creates value instead of worrying about managing dependencies and conflicting gem versions.

Conclusion

Do you remember the time when you discovered Rails? I know I do. Knowing that I could go from having an idea to having a working site that I could start to play with opened up a whole new world of geeking and tinkering. RVM has had the same effect on me. Now, starting a new project or playing with a new gem no longer makes me apprehensive of breaking existing dependencies. RVM is a tool which, once you start using it, makes you wonder how you managed to do without it all this time. Bundler dovetails well within the RVM philosophy, and with that combination, you can easily manage and install any and all dependencies without affecting any other project setups on your machine.

Before I bid farewell, I would like to mention that RVM is donation-ware. If I have convinced you to use RVM, and you find that it fits well in your workflow and aids your productivity, I urge you to consider donating to the RVM effort. Now that you have earned your certification in gemology, what are you waiting for? Let's start mining!

Raju Gandhi

Raju is a Ruby/Java developer and a programming language geek. He has been writing software for the better part of a decade in several industries including education, finance, construction and the manufacturing sector. Raju has a graduate degree in Industrial Engineering from Ohio University. Raju is Integrallis' Clojure man and teaches Ruby/Rails, jQuery/JavaScript and of course Clojure.

In his spare time you will find Raju reading, watching movies, or playing with yet another programming language. Raju is the founder and host of inclojure, the Columbus Clojure User Group. He is affectionately known as @looselytyped on Twitter.


Friday, July 9, 2010

Today, Groovy is a mature language on the JVM that gives Java developers a dynamic, flexible and highly productive and expressive medium while allowing seamless integration with existing Java applications and libraries. One facility that Groovy lends itself very well to is the creation of DSLs. A DSL (Domain Specific Language) is a language that has been built to express a specific domain, and one that allows for a rich vocabulary that can be shared by programmers and business experts. A DSL offers a higher level of abstraction than the host language that it's written in. Groovy's rich yet liberal syntax makes it a perfect candidate for taking on such an endeavor.

In this article we will attempt to port a small part of a Ruby library called 'Acts As State Machine' (AASM) to Groovy. AASM is a library that allows for the creation and manipulation of a finite state machine while providing a very nice DSL. This will allow us to explore some of the steps and pitfalls that you might encounter when writing your very own DSL. You can find the source code for the Groovy State Machine library on the Integrallis GitHub account.

A gentle introduction to Finite State Machines

If you attended a Computer Science class and the mention of Finite State Machine (FSM) has you conjuring up images of complex flow diagrams and mathematical terminology, don't fret. Conceptually, a Finite State Machine consists of 3 basic constructs:

  • "states" that the machine can be in
  • "transitions" that cause the system to go from one state to another
  • "events" that fire the rules and cause the system to transition

That's it. Consider the simple example of a FSM modeling the stages that a file goes through when tracked by Git (a distributed source-code version control system). Git employs a two-phase commit system - a file that has been modified or created is first added to a "staging area" which represents what the next commit looks like. A commit essentially adds all the changes in the staging area to the "master", or the main trunk. Each file has an initial state of "unmodified". When the user "edits" a file, this event causes the file to transition to "modified". When the user "add"s the file, it transitions to "staged", and on "commit" transitions back to "unmodified" (see Figure GAN-1). Keep this example in mind as you read further on, because we will be using this example in the code samples.

Figure Gan 1

A sneak-peek at the DSL

AASM provides a very nice DSL in Ruby to interact with a FSM and we will attempt to reproduce the same in Groovy albeit with a few differences. These differences are because of a slightly different object system between Ruby and Groovy, but for the most part, the DSLs should look and behave in a similar fashion. Listing GAN-1 demonstrates how we can use the Groovy State Machine library to create a class that models the states a file transitions through when managed by Git (as described in the previous section).

class GitFileStateMachine extends StateMachine {
// note we are doing this inside an instance-initializer block
// this block, like Java will be run right after 
// the constructor is called
{
		gsmInitialState "unmodified" // initial state of the FSM

		// all the other states the FSM is allowed to be
		// these make the universal set of allowed states
		// for this state machine
		gsmState "unmodified"
		gsmState "modified"
		gsmState "staged"

		//define the events – 
		// notice the comma after the name of the event
		gsmEvent "edit", {
			transitions from:"unmodified", to:"modified"
		}

		gsmEvent "add", {
			transitions from:"modified", to:"staged"
		}

		gsmEvent "commit", {
			transitions from:"staged", to:"unmodified"
		}
	} // instance-initializer block ends here
}

Listing GAN-1

As you can see, the DSL provides a nice intuitive interface to declare states, events and transitions. But there is more to it! The DSL will also introduce additional methods allowing the user to interact, manipulate and query the state-machine. Listing GAN-2 shows off some of the additional functionality that the corresponding objects will expose.

// first create an instance
gitModel = new GitFileStateMachine()
// a method injected to test for initial state
assert true == gitModel.isUnModified() 
// fire edit event
gitModel.doEdit() 
// using Java's getter syntax
assert "modified" == gitModel.getCurrentState() 
// applying Groovy syntactic sugar
assert "modified" == gitModel.currentState 
// another method injected to test for current state
assertTrue gitModel.isModified() 
assert 2 == gitModel.states.size() 

Listing GAN-2

Now that you have had a sneak peek at the usage of the library, let’s sit back and look at some of the design and implementation steps you might want to consider when writing your own DSL.

Knowing where you are going

When writing a DSL, it often helps to know what the final result will look like. In the case of porting an existing library, it’s much simpler since you already have a good starting point, but this is usually not the case.

A DSL's role is to present an interface that is an abstraction higher (or lower depending on your perspective) from that of the programming language that most developers are used to. The objective in writing a DSL is to work towards a more "natural" language, usually one that closely maps to the domain that you are working with. The end objective may be to establish a consistent vocabulary that everybody speaks and understands, or have business experts and/or testers more involved in the development/testing life-cycle. Furthermore, often times a DSL is specifically designed to be both expressive and succinct while doing everything that the end user needs it to do.

As you can see from our state machine example in Listing GAN-1, the intent there is to make the language feel less Groovy-like and more English-like. But there lies a huge divide between the strict rules that govern programming languages and a context dependent natural language like English. If the end result of the DSL is fuzzy, you may end up biting off more than you bargained for. Furthermore, you may end up exposing more than you originally intended (See "The Law of Leaky Abstractions"). Occasionally it might help to sit down with a piece of paper and pen and hash out what the final DSL will look like.

There is another benefit to doing this. Sometimes, the tool that you are leveraging to write your DSL may not be capable of doing what you want it to do. Consider the following snippet of code in Listing GAN-3.

	
// define the events - notice the comma after the name of the event, 
// which in this case is “edit”
gsmEvent "edit", {
	transitions from:"unmodified", to:"modified"
}

Listing GAN-3

The comma after the edit is so that Groovy treats the String and the Closure as two arguments to the gsmEvent method. This is a consequence of how Groovy parses the code. An alternative to this would be to write the code –as in Listing GAN-4.

		
// define the events - no comma, but the name has to be enclosed in parentheses
gsmEvent("edit") {
	transitions from:"unmodified", to:"modified"
}

Listing GAN-4

I recognize that this is a trivial example, but scenarios like these may present themselves. If none of the solutions (like the ones above) are acceptable to you, then perhaps Groovy isn't the tool to use in this case. Alternatively, if you prefer to use Groovy, you know your options early on (In this case Listing GAN-3 or GAN-4) without having to invest a lot of time and effort.

Exploring the landscape

Languages like Groovy offer a very rich syntax and great pliability, while lending themselves to a very quick try-evaluate feedback loop. Highlighting the latter, you have two options, and you must exercise both of them - the Groovy Console (or shell) and testing. Attempting to see or understand how a feature of the language works is a matter of firing up the interactive shell and running some quick and dirty code. This isn't only applicable to exploring a feature, but also to seeing how different features of the language work together (e.g. having a closure supplied as an argument to a method in conjunction with named arguments, as in Listing GAN-8). It might be tempting to squeeze in something like the aforementioned example in a growing code-base and hope that your integration tests catch any errors, but this proves to be both difficult and unreliable. You would be attempting to understand how that feature is playing with everything else while keeping an eye on all the moving parts at the same time. Writing the simplest code possible with the least amount of environmental baggage to investigate how something works will save you lots of grief. Once you know how a feature works, and you have plugged it in your code, be sure to take the code samples you just punched out on the interactive shell and make appropriate tests out of them. Two for the price of one, yes?

Occasionally, you may come across a (new) feature in a language and it may not jump out at you as being very useful. Consider the with (also known as identity) method, defined as an extension method within Groovy's GDK for Object . This is defined as in Listing GAN-5.

	
public Object with(Closure closure)
Listing GAN-5
	
Most code samples on the web demonstrate the use of with as shown – in Listing GAN-6. 
	
def calendar = Calendar.instance
calendar.with {
	clear()
	set MONTH, JULY
	set DATE, 4
	set YEAR, 1776
}

Listing GAN-6

Unfortunately, it's not clear how this works from this example. Essentially, all the method invocations within the closure argument happen within the context of the containing Calendar object. It's the same as if you were to call each of those methods –as in Listing GAN-7.

	
def calendar = Calendar.instance
calendar.clear()
calendar.set(MONTH, JULY)
calendar.set(DATE, 4)
calendar.set(YEAR, 1776)

Listing GAN-7

Shown in Listing GAN-8 is how we are leveraging with when we implemented how to declare events for the state machine library.

	
class Event {
	Event(args = [:], String name, Closure c) {
		// some code here 
		if(c) this.with(c)	
	}
		
	private void transitions(args) {
		// some code here
	}
}

Listing GAN-8

Listing GAN-9 shows how the class in Listing GAN-8 is used.

	
// this method is declared inside StateMachine.groovy
	
def gsmEvent(options = [:], name, Closure transitions) {
	if(!events[name]) {
		events[name] = new Event(options, name, transitions)
	}
}

Listing GAN-9

Listing GAN-10 demonstrates the invocation of the method in Listing GAN-9.

	
gsmEvent "stage", { 
	transitions from:"modified", to:"staged" 
}

Listing GAN-10

Notice that the last argument passed to the Event constructor from within the gsmEvent method is a Closure that internally references a method named transitions. Inside the Event constructor, calling with with that closure causes that method to be executed within the context of the newly created Event object, thus calling the private transitions method.

In conclusion, learning what a language has to offer, and how it can be leveraged is the result of some experimentation, and lightweight tools like an interactive environment and tests are your friends. Furthermore, they need not be your tests! The Groovy source code is full of good examples, along with a whole test suite. Dive in! There truly is a pot of gold at the end of the green bar.

The glow of inspiration may be coming from behind you

Granted, once you write a little bit of Groovy code, Java begins to feel like a strait-jacket, where the compiler only seems to tighten its stranglehold over you with the red squiggly lines emitted by your IDE of choice. But there is a glimmer of hope. Like I said earlier, you can find inspiration in the form of good code samples in the Groovy source code, but you might find it in places that you seemingly have left behind.

Consider the snippet of code –in Listing GAN-11.

class GitFileStateMachine extends StateMachine {
	// note we are doing this inside an instance-initializer block
	// this block, like Java will be run right after
	// the constructor is called
	{
		// declare your states and events here
	}
	// instance-initializer block ends here
}

Listing GAN-11

The block of code in Listing GAN-11 that initializes the states and the events is surrounded by curly brackets. For those familiar with Java's static block, this is essentially the same thing, except this is usually referred to as an instance initializer - that's right, a block of code that gets executed after a constructor call. You could achieve the same effect by putting these lines of code inside your constructor, but it would detract from the English-like flow of the DSL.

It's prudent to remember that Groovy attempts to be an extension of the Java language, and all of the features that Java offers are available to you within Groovy. Taking a step back, and leveraging the entire ecosystem may prove beneficial.

Conclusion

Groovy's flexible structure, along with syntactic sugar can help create very intuitive DSLs that can make working with libraries and applications easier. Try to chart your path, get to know your tools and attempt to synthesize a solution using both Groovy's language features and development kit. Whether it is to make a task more fun, your fellow programmers more productive, or to allow for an easier feed-back loop between technical and non-technical staff, DSLs certainly have their place, and Groovy is certainly an ally. Ready, set, go DSL'ing!

Raju Gandhi

Raju is a Ruby/Java developer and a programming language geek. He has been writing software for the better part of a decade in several industries including education, finance, construction and the manufacturing sector. Raju has a graduate degree in Industrial Engineering from Ohio University. Raju is Integrallis' Clojure man and teaches Ruby/Rails, jQuery/JavaScript and of course Clojure.

In his spare time you will find Raju reading, watching movies, or playing with yet another programming language. Raju is the founder and host of inclojure, the Columbus Clojure User Group. He is affectionately known as @looselytyped on Twitter.


Friday, April 30, 2010

One of the factors that made Java hugely successful is the myriad of open source libraries and frameworks. The successful ones have had now a decade or more to mature and grow. A side effect of being successful is both intended and unintended complexity. In the next few pages I will show you how a JRuby Domain Specific Language (DSL) can breed new life into an old powerhouse Java library

In recent years it has become clear that the lasting legacy of Java is not Java “the language” but Java the platform and specially the open-source community that it has fostered over the last 15 years.

Many developers (including yours truly) experienced a great deal of success with the Java language but as the years went by it started to become apparent that the safety “features” of the language that made it so popular were getting in the way of progress. This became painfully clear in the area of Web Development and it became borderline embarrassing when we were introduced to the Ruby on Rails framework and witness the productivity boost that the combination of a dynamic language like Ruby and a DSL-based approach to a framework brought to the table.

Today, we recognize that the Java language is just another player in the world of the Java Virtual Machine. The JVM is an amazing and pervasive piece of software that in the early days of Java played second fiddle to the language. This is no more, everyone know acknowledges that this amazing, highly tuned, multiplatform computing engine dominates the present and permeates the future of computing.

As I write this article there are more than 200 language implementations available for the JVM, many of them now being used in the “enterprise” and not only in academia. I strongly believe that this decade marks the official beginning of the “polyglot” era of programming with the JVM smack in the center of this new world.

Ruby

Ruby is a dynamically typed language created by Yukihiro “Matz” Matsumoto. Ruby is a general purpose, multi-paradigm language inspired primarily by Smalltalk, Perl, Lisp and Eiffel.
As David Heinemeier Hansson puts it “Beauty leads to happiness, happiness leads to productivity, thus beauty leads to productivy”. If you have never worked with Ruby, after a few hours coding it will immediately hit you. Things just make sense! My first thought was “there is no way in hell this was build by committee!” Indeed it wasn’t. Matz set out to create a language for him to enjoy in which the main ideals were that programming should be fun and that the language should behave in predictable ways for those experienced with it core principles (this is often referred as the “Principle of Least Surprise”.

Rather than praise Ruby, let me show some quick examples the simplicity of Ruby. Say we have to do some basic, brain-dead file manipulation; load a text file and print its contents line by line. In Java we would do something along of the lines of:

package com.integrallis.braindeadio;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class MyFileReader {
    public static void main(String[] args) {
        try {
            BufferedReader in = new BufferedReader(new FileReader("read_me.txt"));
            String str;
            while ((str = in.readLine()) != null) {
                System.out.println(str);
            }
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Listing BSB-1

After years of doing Java we have come to accept code like that shown in Listing BSB-1. I equate this code to wearing a deep-ocean, mesh-reinforced diving suit to go to the beach. If you go to the beach there is the possibility that you might go under, and that in the depths of the ocean a great white might come after you. Yes, it is possible but also highly improbable.

With Ruby we don’t even have to commit to create a permanent record of the operation performed in Listing BSB-1, we can get our work done directly in the Interactive Ruby Shell (IRB).

>> File.open('read_me.txt', 'r').each do |line| 
?>   puts line
>> end
Flexibility
Power
Simplicity
Beauty
=> #<file:read_me.txt>
>>

Listing BSB-2

As seen in Listing BSB-2, Ruby provides a File class, which has an open method that takes the name of the file and a file mode (in this case ‘r’ for read-only). So far so good, next is where the Ruby Way kicks in! The result of the call to open returns a File which mixes-in the Enumerable module, which gives it an iterator via the each method. The each method expects a block, the code wrap in the “do” and “end”. We can think of a block as an anonymous function and one of their basic usages of a block is loop abstraction. As opposed to Java, with Ruby we don’t need to peer into the internals of what we are iterating over, instead we provide a function with a known interface to the owner of the collection we want to iterate over. The result is less code and better encapsulation!

But Ruby was designed by a programmer and is therefore for programmers. Methods that would make sense to be included in the core of the language usually are! Therefore it is no surprise that the File class also has a “readlines” method, which returns an array and what do you know! Ruby knows how to print an Array:

>> puts File.readlines('read_me.txt')
Flexibility
Power
Simplicity
Beauty
=> nil
>>

Listing BSB-3

As a fairly accomplish Java developer I can tell you without reservation that Ruby is the most productive environment I’ve ever develop applications in.

JRuby

JRuby is pure Java implementation of Ruby that started in the early 2000s. JRuby aims to bring the beauty and simplicity of Ruby to the Java world and bring the Java ecosystem to the Ruby world. This means that you gain the best of both worlds; From a Java application you get to use Ruby applications and libraries and from a Ruby application you get access to Java Libraries (in a Ruby-eske way).

JRuby also runs significantly faster than most other Ruby implementations (if you don't count VM startup), it supports native threads, Unicode and can be fully compiled ahead of time or just in time.

JRuby also bridges the gap left by Java the language. Dave Thomas once described Java as “the safety scissor of the programming world”. Following that analogy, Ruby is then a samurai sword and via JRuby it brings powerful features to the Java world: Blocks, Closures, Open Classes, Meta-programming and Duck-typing all crucial ingredients when building a Domain Specific Language.

Calling Java From JRuby

In order to enable Java features in a Ruby program we need to add the ‘java’ module to our program. We can also include specific Java packages and group them under a Ruby module (modules are like un-instatiable, namespaced code containers; they can contain classes, methods, constants and free-flowing code)

require 'java'

module Java3D
  include_package "javax.media.j3d"
  include_package "com.sun.j3d.utils.geometry"
  include_package "com.sun.j3d.utils.universe"
end

module Swing
  include_package "java.awt"
  include_package "javax.swing"
end

module Math
  include_package "java.lang.Math"
end

Listing BSB-4

Requiring the Java module gives your Ruby code access to the bundled Java libraries. We used three modules to include and namespace specific Java packages: Java3D; containing multiple Java3D packages, Swing; containing the root AWT and Swing packages and Math; exposing the java.lang.Math class.

We these powerful Java libraries in place we can now let Ruby loose and write some code to use the Java 3D API. The Ruby class ThreeDimensionalCube creates a rotating, multi-colored cube; the power of Java meets the ease of development of Ruby:

class ThreeDimensionalCube
  include Java3D
  include Swing
  include Math

  def self.create_canvas
    config = Java3D::SimpleUniverse.get_preferred_configuration
    canvas = Java3D::Canvas3D.new(config)

    scene = create_scene_graph
    scene.compile

    # SimpleUniverse is a Convenience Utility class
    universe = Java3D::SimpleUniverse.new(canvas)

    # This will move the ViewPlatform back a bit so the
    # objects in the scene can be viewed.
    universe.get_viewing_platform.set_nominal_viewing_transform
    universe.add_branch_graph(scene)

    canvas
  end

  def self.create_scene_graph
    # Create the root of the branch graph
    obj_root = Java3D::BranchGroup.new

    # rotate object has composited transformation matrix
    rotate = Java3D::Transform3D.new
    temp_rotate = Java3D::Transform3D.new

    rotate.rot_x(PI / 4.0)
    temp_rotate.rot_y(PI / 5.0)
    rotate.mul(temp_rotate)

    obj_rotate = Java3D::TransformGroup.new(rotate)

    # Create the transform group node and initialize it to the
    # identity.  Enable the TRANSFORM_WRITE capability so that
    # our behavior code can modify it at runtime.  Add it to the
    # root of the subgraph.
    obj_spin = Java3D::TransformGroup.new
    obj_spin.set_capability(Java3D::TransformGroup::ALLOW_TRANSFORM_WRITE)

    obj_root.add_child(obj_rotate)
    obj_rotate.add_child(obj_spin)

    # Create a simple shape leaf node, add it to the scene graph.
    # ColorCube is a Convenience Utility class
    obj_spin.add_child(Java3D::ColorCube.new(0.4))

    # Create a new Behavior object that will perform the desired
    # operation on the specified transform object and add it into
    # the scene graph.
    y_axis = Java3D::Transform3D.new
    rotation_alpha = Java3D::Alpha.new(-1, 4000)

    rotator =
      Java3D::RotationInterpolator.new(rotation_alpha, obj_spin, y_axis,
      0.0, PI * 2.0)

    # a bounding sphere specifies a region a behavior is active
    # create a sphere centered at the origin with radius of 1
    bounds = Java3D::BoundingSphere.new
    rotator.set_scheduling_bounds(bounds)
    obj_spin.add_child(rotator)

    obj_root
  end

  def show
    frame = Swing::JFrame.new("3D Cube")
    frame.layout = Swing::BorderLayout.new
    frame.add("Center", ThreeDimensionalCube.create_canvas)
    frame.default_close_operation = Swing::JFrame::EXIT_ON_CLOSE
    frame.set_size(256, 256)
    frame.visible = true
  end

end

cube = ThreeDimensionalCube.new
cube.show

Listing BSB-5

We run the example (note that there is no need for a main method) with the last two lines in Listing BSB-5. First we instantiate a ThreeDimensionalCube object and then call the show method on said object. The result is shown in Figure BSB-1.

Bsb Jruby 01

Our Problem Domain: Dealing with XML

One of the pain areas of Java development has always been dealing with XML. We could attribute this to the overuse (or is it abuse) of XML in the Java world. But if you were unlucky enough to deal with the SAX and DOM API in Java you probably don’t have any hair left by now.

I was in the same boat, until I discovered XOM (XML Object Model) is one of the most versatile Java XML libraries in existence. XOM is a Tree-based API like DOM and JDom and it aims for correctness, simplicity and performance. In the Java world XOM provided the best of both worlds, fast parsing and a tree-model that allows navigation of the XML documents.

Using XOM is easy to create well-formed XML documents, for example Listing BSB-6 shows a “Hello World” example available with the XOM distribution.

import nu.xom.Document;
import nu.xom.Element;

/**
 *  * Hello World!
 */
public class HelloWorld {

  public static void main(String[] args) {
    Element root = new Element("root");    
    root.appendChild("Hello World!");
    Document doc = new Document(root);
    String result = doc.toXML();
    System.out.println(result);
  }
}  

Listing BSB-6

In listing BSB-7 shows another (more complex) example available from the XOM distribution. This example prints the headlines from an RSS feed.

import java.io.IOException;
import nu.xom.Builder;
import nu.xom.Element;
import nu.xom.Nodes;
import nu.xom.ParsingException;

public class RSSHeadlines extends MinimalNodeFactory {
    private boolean inTitle = false;
    private Nodes empty = new Nodes();

    public Element startMakingElement(String name, String namespace) {              
        if ("title".equals(name) ) {
            inTitle = true; 
        }
        return new Element(name, namespace);             
    }

    public Nodes makeText(String data) {        
        if (inTitle) System.out.print(data);
        return empty;      
    }

    public Nodes finishMakingElement(Element element) {
        if ("title".equals(element.getQualifiedName()) ) {
            System.out.println();
            inTitle = false;
        }
        return new Nodes(element);
    }

    public static void main(String[] args) {
        String url = 
               "http://bbc.co.uk/syndication/feeds/news/ukfs_news/world/rss091.xml";
        if (args.length > 0) {
          url = args[0];
        }
        
        try {
          Builder parser = new Builder(new RSSHeadlines());
          parser.build(url);
        }
        catch (ParsingException ex) {
          System.out.println(url + " is not well-formed.");
          System.out.println(ex.getMessage());
        }
        catch (IOException ex) { 
          System.out.println(
           "Due to an IOException, the parser could not read " + url
          ); 
        }
    }
}  

Listing BSB-7

If you ever built or parsed XML with any other Java API (other than JDOM or dom4J) you would be able to appreciate what XOM can do for you.

Close but not Cigar!

Although XOM does a great deal of heavy lifting for you, if you are Rubyist, Listings BSB-6 and BSB-7 feel like they reveal too much about how XOM works internally and hide the intent of the code with too much plumbing.

Ruby has an XML building library, created by Jim Weirich called XML Builder (or Builder for short). Builder is a Ruby DSL for constructing XML documents. For example, to recreate the “Hello World” example we could do write a simple Ruby program like that shown in Listing BSB-8

require 'rubygems'
require 'builder'

builder = Builder::XmlMarkup.new
builder.root "Hello World!"

puts builder.target!

Listing BSB-8

So what makes Builder a DSL? Note the call to the method “root”. As a Java developer your first reaction is “root must be a method of this builder object, right?” Wrong! Under the covers the call to the method root is being trap by a hook method called method_missing. Think of it as an event listener that is invoke in the event that an object is sent a message it cannot handle.

Internally, Builder is seeing that we are trying to invoke a non-existent method called “root” and instead adds an XML tag to the underlying document being created.

Builder is not as robust or correct when it comes to building XML when compared to XOM and it is nowhere near as fast. Builder, as clearly stated by its name, only “builds”.

Being the greedy developer that I am, I wanted the best of both worlds. How can I get Builder’s semantics for XML building mixed with the XOM’s ability to efficiently parse and navigate XML with XPath?

Excemel: A JRuby DSL for XML Manipulation

The answer to the previous question is a simple one: Use XOM but make it look like Builder. Enter Excemel! A simple JRuby DSL for XML manipulation. Excemel borrows Builder’s semantics for building an XML document backed by an underlying XOM model. Excemel also allows you to parse and manipulate an existing document and exposes XPath functionality in a simple way.

Let’s start with the simple Hello World example again, as shown in Listing BSB-9.

require 'excemel'

doc = Excemel::Document.new :root => "root"
doc.text! "Hello World!"

puts doc.to_pretty_xml

Listing BSB-9

Excemel uses XOM under the cover to build an XML document. It uses Ruby’s method_missing in the same fashion that Builder does as shown in Listing BSB-10.

require 'excemel'
 
xm = Excemel::Document.new :root => "html" # 
xm.head {                                  #   
  xm.title "History"                       #     
}                                          #                                
xm.body {                                  #   
  xm.comment! "HI"                         #     
  xm.h1 "Header"                           #     <h1>Header</h1>
  xm.p "paragraph"                         #     <p>paragraph</p>
}                                          #   

puts "<---- Plain XML ----->"
puts xm.to_pretty_xml

puts "\n<---- Use XPath to find the title ----->"
puts %[the title is #{xm.query("//title")}]

Listing BSB-10

But that’s where the similarities end. Excemel can do some things that Builder can’t, like reading XML directly from a file as shown in Listing BSB-11.

require 'excemel'

doc = Excemel::Document.new :file => "cd_catalog.xml"
puts doc.to_pretty_xml

Listing BSB-11

Listing BSB-12 shows the full power of a JRuby DSL. Remember the RSS headline extractor example shipped with XOM? With Excemel we can bring intent back to the forefront of our code. In about 1/3 of code of the original XOM version Excemel can more clearly perform the same task (with the same efficiency and accuracy, it is XOM under the covers!)

require 'excemel'

doc = Excemel::Document.new :url =>   
      "http://news.bbc.co.uk/rss/newsonline_uk_edition/world/rss.xml"
headlines = doc.query '//title'
headlines.each {|h| puts h }

Listing BSB-12

Conclusions

I hope that this short walk through the world of JRuby will open your eyes to the power of mixing a powerful dynamic language with a robust, fast and battle-tested Java library. Dynamic languages on the VM are paving the way of the future. Groovy and Ruby via JRuby can help us improve the current state of Java and make programming fun again!

References

About The Author

Brian Sam-Bodden is a developer, author and speaker that has spent over fifteen years building software in variety of languages. In the last four years his concentration has been on the Ruby programming language and the Ruby on Rails framework (although he still gets paid to do Java when no one is watching). Brian heads Integrallis , a Web Consultancy with offices in Columbus, Ohio and Scottsdale, Arizona.

Recommend Brian on Working With Rails


Items:   1 to 5 of 16   Next »