Brian Sam-Bodden's complete blog can be found at: http://www.integrallis.com/blog/
Monday, June 11, 2012
These notes are meant to answer some basic questions about using Stripe for billing, and also to answer some questions about implementation which are not covered in the Stripe documentation and forums. This is not a comprehensive overview by any means. If you find errors, please let me know and I will fix them. The examples will use the stripe-ruby gem (v1.7.0). A list of basic operations can be found in Stripe's documentation.
Stripe recommends downloading the gem over SSL from the mirror at https://code.stripe.com.
sudo gem install --source https://code.stripe.com stripe
Here is a list of resources which I have found useful:
- Railscasts episode 288
- Stripe sample checkout form
- https://answers.stripe.com/
- https://stripe.com/docs/testing
Creating Subscription Plans
Plans can be created through the Stripe Dashboard or the API. Since plans will not change often, it’s easy to create them through the Dashboard and retrieve them from the application via the API.
Stripe::Plan.create(
:amount => 999,
:interval => 'month',
:name => 'My Basic Plan',
:currency => 'usd',
:id => 'basic'
)
Create Token with Stripe.js
Stripe provides a javascript library which sends encrypted credit card and cardholder data to Stripe’s servers from the browser. Card info is sent (along with the accounts public key) directly to Stripe and is never handled by host application.
// https://gist.github.com/1750368
Stripe.setPublishableKey('pk_YOUR_PUBLISHABLE_KEY');
Stripe.createToken({
number: $('.card-number').val(),
cvc: $('.card-cvc').val(),
exp_month: $('.card-expiry-month').val(),
exp_year: $('.card-expiry-year').val()
}, stripeResponseHandler);
The response includes the ID of the created card token if successful, and relevant error messages otherwise. The token represents a valid credit card stored by Stripe’s servers. Card tokens do not represent charges of any kind.
Token Object
A The host server can retrieve information about the card by retrieving the token referenced by the token ID. The response will contain basic card details including the last four digits of the card, instead of the entire number.
Stripe::Token.retrieve("tok_00000000000000")
Create Customer
The card token ID can then be submitted to the host server. Card tokens can be used to create a one-time charge or to create a customer with a subscription. The relationship between a customer and a plan is described by a subscription. Subscriptions can be updated or canceled through the customer object.
Stripe::Customer.create(
:description => "Customer for jim@example.com",
:plan => "basic",
:card => "tok_00000000000000" # obtained with Stripe.js
)
Many applications will offer a free plan. Stripe will allow plans to have an amount of 0 (zero). Customers can be subscribed to a free plan without providing credit card information. Subscribing free users to a free plan could make the upgrade process simpler, since a customer record will already exist. Providing a card token is optional when creating a customer with a subscription to a free account.
Stripe::Customer.create(
:description => "Customer for jim@example.com",
:plan => "my_free_plan_id"
)
Updating Customer Subscription
It’s likely that a host application will allow users to change to a different plan. In this case, the relevant customer object can be retrieved and updated. Updates can optionally be prorated.
c = Stripe::Customer.retrieve("cus_00000000000000")
c.update_subscription(:plan => "enterprise", :prorate => true)
Customers can only be updated from a free plan to a paid plan if a valid card token is provided with the request.
c = Stripe::Customer.retrieve("cus_00000000000000")
c.update_subscription(:plan => "pro", :card => "tok_00000000000000" ,:prorate => true)
Customers can be updated from a paid plan to a free plan without providing a card token. In this case Stripe will still have the user’s active card on file; so if the user switches back to a paid plan, it is optional to include a card token with the update request.
At this time, Stripe does not offer a way to remove an active card from a customer which is subscribed to a free plan.
Cancel Customer Subscription
When a user deletes their account or their customer record is no longer needed, the customer object can be retrieved and deleted.
c = Stripe::Customer.retrieve("cus_00000000000000")
c.delete
Webhook Authentication
It is critical to properly authenticate webhooks. There are two methods of authentication recommended by Stripe. The first (and simplest) method is to retrieve the event using the id parameter of the webhook request.
# in a Rails controller
def my_webhook_action
# this will raise InvalidRequestError (status 404) if the event does not exist
event = Stripe::Event.retrieve(params[:id])
# safe to use the returned event object
# ...
rescue Stripe::InvalidRequestError => e
logger.error "Unable to authenticate webhook request: #{e.message}"
# 401 Unauthorized response
end
The second method for authenticating webhooks is to use HTTP basic authentication. When setting the webhook url in the Stripe Dashboard, provide a username and password. See Railscasts episode 82 for details on implementing HTTP basic authentication in a Rails app.
Testing
If you are implementing your system in Ruby, I highly recommend the VCR gem. For a good overview of VCR and it capabilities, watch Railscasts episode 291 *.
It is important not to commit your secret api key to the repo. Use the filter_sensitive_data configuration option to redact your private key from the recordings.
VCR.configure do |c|
# ...
c.filter_sensitive_data('[secret_key]') { Stripe.api_key }
end
Appendix
* Please note that some of the configuration syntax has changed in newer versions of the VCR gem:
# VCR.config do |c| # outdated
VCR.configure do |c| # new
# ...
# c.stub_with :fakeweb # outdated
c.hook_into :fakeweb # new
end
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 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 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 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
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 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
- Spine: http://spinejs.com/
- jQuery: http://jquery.com
- jQuery UI: http://jqueryui.com
- Mustache Templates Plugin: https://github.com/janl/mustache.js
- Example Code:
- https://github.com/bsbodden/jquery-shopping-cart
- https://github.com/bsbodden/jquery-spine-shopping-cart
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 …
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!
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.
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!
