Trellis Tutorial #3 - Creating a Stateless Component
Posted by Brian Sam-Bodden
Creating a Stateless Component
A component-driven framework would be pretty pointless if you couldn’t create your own components. Component creation is how developers can encapsulate a complex and/or repetitive piece of functionality and provide a clear API to access that functionality.
In Trellis, components wrap both logic and presentation in a small manageable package. A Trellis component provides a template to be rendered and logic that can be used to respond to external events.
In the first installment of the Trellis tutorial series we got to use some of the simple components made available through the core library. The core components provide very basic functionality expected in a component-driven web framework. But things only get interesting when you get to build your own components. Whether they are general purpose, domain specific or application specific components, a well-crafted set of components can truly improve the understanding of an application, ease maintenance and improve testability.
In this tutorial we’ll walk through the development of a simple stateless component to display images using the Flickr API. This example is included in the Trellis distribution under examples/flickr
Project Setup
Setting up a Trellis project is quite simple. We’ll start with the simple directory structure shown in Figure 1. I’ve named the root directory of the application ‘flickr’. The ruby file flickr.rb will contain our application and the file spec/flickr_spec.rb will contain our RSpec specifications.

Figure 1 - Flickr Application Directory Structure
Test Driven Development with Trellis with RSpec
In this tutorial we’ll work in a more realistic way that in previous tutorials. We’ll developed the Flickr example in a test-driven/test-first fashion. We’ll start with a bare bones skeleton for a Trellis application, as shown next (flickr.rb):
require 'rubygems' require 'trellis' include Trellis module Flickr class FlickrApp < Application end end
RSpec
For our TDD efforts we’ll use RSpec, “the original Behavior Driven Development framework for Ruby”. With RSpec you create “specifications” which are tests that describe the behavior of the system under certain conditions. We will try to follow the red-green-refactor mantra as close as possible:
- Writing a Test that describes a particular behavior of interest in the system.
- Running the Test (Red): Since the behavior hasn’t been implemented the test will fail.
- Implement: Writing enough code to pass the test
- Re-Running the Test (Green): With the implementation in place the test should now pass
- Refactor: With a basic foundation of how to fulfill the behavior and a test to prove the implementation we can now “refactor without fear”.
In the /spec directory we’ll add simple RSpec spec as follows (flickr_spec.rb):
require 'rubygems'
require 'spec'
require 'rack'
require 'rack/test'
require File.dirname(__FILE__) + '/../flickr.rb'
describe Flickr::FlickrApp do
include Rack::Test::Methods
def app
Flickr::FlickrApp.new
end
it "should have a home page declared" do
Flickr::FlickrApp.homepage == :home
end
end
Let’s talk about the spec we’ve just created. At the top, besides requiring rubygems, we need the RSpec library (spec), Rack (rack) and Rack::Test (rack/test). We will also need to make the file under test, flickr.rb available.
Rack::Test enables you to test a Rack-based application without having a running server. Rack::Test mocks HTTP requests and provides a DSL to test against the response.
The describe block, “describes” an scenario. In RSpec an scenario consists of one or more examples. Each example describes a discrete behavior of the class under test.
In the scenario above we have only one example so far which states that our application should have a home page named “home”. With the bare bones application code and the spec in place we can now run it to get our TDD going.

Figure 2 - Failed Spec (no home page)
As expected the test fails. We got to the “red” stage. To pass the test and get to “green” we can simply declare the home page as shown next:
module Flickr
class FlickrApp < Application
home :home
end
end
Running the tests again shows that we’ve accomplish what we set out to do in this simple example.

Figure 3 - Passed Spec (after adding a home page)
Testing with Rack::Test
With an understanding of the basic mechanics of TDD we can now move onto fleshing out the rest of the application. Our next example will make use of Rack::Test to make sure that our application returns an HTTP status code of 200 (OK). Previously we provided the spec with an #app method which returned an instance of the Flickr application. The #app method is required by Rack::Test and it enables the use of a simple DSL to interact with the application in a mocked HTTP server environment. Rack::Test provides a way to issue a mock HTTP request to the application, inspect the response, follow redirects and maintain cookies across requests.
it "should return an OK HTTP status" do get "/" last_response.status.should be(200) end
This test requests the root url of the application, which should redirect to a home page and then checks that the last response has a status of 200.

Figure 4 - Failed Spec (no OK response)
To pass this next example we need to provide a bare bones implementation of the Home page. The Home page provides a simple html template as shown next:
class Home < Page
template %[
<html xml:lang="en" lang="en"
xmlns:trellis="http://trellisframework.org/schema/trellis_1_0_0.xsd"
xmlns="http://www.w3.org/1999/xhtml">
<body>
Some Interesting Pictures...
</body>
</html>
], :format => :html
end
Let’s run the spec again and confirm that we get a 200 response.

Figure 5 - Passed Spec (after barebones home page impl)
Running what we have so far
At this point we do have enough of the application in place so that we can also run it and visually inspect the progress so far.
To be able to launch the application self-contained we need an instance of the application on which we can invoke the #start method. Add the two following lines at the end of the Flick module in flickr.rb:
web_app = FlickrApp.new web_app.start 3006 if __FILE__ == $PROGRAM_NAME
To launch the application run the flickr.rb ruby script like:
ruby flickr.rb

Figure 6 - Launching the Flickr Trellis App
Open a browser and point it to http://localhost:3006 and you should see:

Figure 7 - Bare bones Flickr Trellis App
The Flickr Interestingness Component
The Flickr Interestingess Trellis Component will use the Flickr API to make a call to the “interestiness” XML-RPC web service method which returns one or more random images from Flick that have been deemed “interesting” (warning to the easily offended).
We’ll start with a simple test to get the component to render something (anything really!). Our basic test will simply expect to find an html DIV with and id attribute of “flickr_viewer” and with text content equals to “Hello”:
it "should render the flickr component" do get '/' last_response.body.should include(%[Hello]) end

Figure 8 - Failed Component Test
To pass this test we can provide the bare bones implementation of a Trellis stateless component. Inside the Flickr module in flickr.rb we declare the FlickrInterestingness component which descends from Trellis::Component:
class FlickrInterestingness < Component
tag_name "flickr_interestingness"
render do |tag|
%[Hello]
end
end
A Trellis component has a tag_name, which by default is just the name of the component underscored and lower-cased. To provide the template or content for the component the Trellis::Component class provides the #render method which takes a block that expects a “tag” object as a parameter. We’ll go into more detail about the “tag” soon.
Next we’ll use the component in the page template via the tag, prefixed with the namespace ‘trellis’:
class Home < Page
template %[
<html xml:lang="en" lang="en"
xmlns:trellis="http://trellisframework.org/schema/trellis_1_0_0.xsd"
xmlns="http://www.w3.org/1999/xhtml">
<body>
Some Interesting Pictures...
<trellis:flickr_interestingness/>
</body>
</html>
], :format => :html
end
The return value of the render method is expected to be a string value that will be rendered on the page using the component. In our simple case we simply hardcoded a return value with the expected HTML snippet.

Figure 9 - Passed Render Component Test
As you can see a Trellis stateless component is simply a way to produce content; a tag that it is available to a Trellis template.
Using the Web Service
Now that we have a component that can render content we can now enhance the implementation to return something meaningful from the Flickr API. Our goal is to retrieve one or more images and render them to the page as:
<div id="flickr_viewer">
<div id="flickr_image"><img src="image_1.jpg" alt="This is image one"/></div>
</div>
The first thing we need to do is hit the web service and examine the resulting XML. The snippet of code below can accomplish this (I obtained an API key just for this example):
require 'xmlrpc/client'
flickruri = 'http://api.flickr.com/services/xmlrpc/'
server = XMLRPC::Client.new2(flickruri)
flickrkey = '15b43fbd25e10d51e8533d32bf7e1d1a'
details = {:api_key => flickrkey, :per_page => 1}
xml = server.call("flickr.interestingness.getList", details)
Running the code above in irb shows the resulting XML:
"\n\n\t \n\t\t\t\t"\n
From the XML document above we can see that it consists of a <photos> element wrapping one or more <photo> elements. To build an image URL we need to take the “farm”, “server”, “id”, “secret” and “format” in a string such as:
http://farm#{farm}.static.flickr.com/#{server}/#{id}_#{secret}_#{format}.jpg
To parse the XML we can use Nokogiri. The following snippet shows the bit of code we need to parse the values we need to build the image URL:
require 'nokogiri'
...
doc = Nokogiri::XML(xml)
doc.xpath("//photos/photo").each do |element|
server = element["server"]
id = element["id"]
secret = element["secret"]
title = element["title"]
farm = element["farm"]
image_url = "http://farm#{farm}.static.flickr.com/#{server}/#{id}_#{secret}_#{format}.jpg"
...
end
Building the HTML content
Finally after we get the values from the web service we can build the XML snippet using something like Builder. The code below shows how to do so:
require 'builder'
builder = Builder::XmlMarkup.new
builder.div(:id => "flickr_viewer") {
builder.div(:id => "flickr_image") {
builder.img(:src => image_url, :alt => title)
}
}
Passing parameters to the component
One of the main benefits of using components is that we can encapsulate reusable logic. But to be truly reusable we need to have the ability to parameterize that logic. A Trellis component can take parameters passed via the tag attributes. For example, let’s say that we wanted to pass an attribute called ‘foo’:
<trellis:flickr_interestingness foo="bar"/>
To access the attribute from within the render method we use the tag object passed to the block. The tag object represent the context of the component and it gives you access to a hash of attributes aptly named ‘attr’. For example, to retrieve the value of the ‘foo’ attribute in the #render method we use the tag object as follows:
render do |tag| foo = tag.attr['foo'] ... end
Our goal for the Interestingness component is to be able to pass the format of the pictures that we want to use as well as how many pictures to display. Inspecting the Flickr API interestingness call we see the values that need to be passed to the ‘format’ parameter:
- s small square 75x75
- t thumbnail, 100 on longest side
- m small, 240 on longest side
- - medium, 500 on longest side
- b large, 1024 on longest side (only exists for very large original images)
- o original image, either a jpg, gif or png, depending on source format
The ‘per_page’ parameter determines how many images to retrieve from the service. Since we want the component to show different images when refreshing the browser we’ll retrieve 10x the number of images requested and then randomly grab ‘x’ images.
TDDing the Flickr Interestingness Component
Now that we have determined at a high level what we need. Let’s write a couple of examples to have a working initial implementation of the component. First, a little refactoring of the existing examples is in order. Since we are going to be changing the render method of our component, the previous example “should render the flickr component” will obviously fail after the changes. By changing the test just to check for the opening div of the component we will still test that the component is actually rendering (whether the contents of the div tag are correct will be tested in separate examples)
it "should render the flickr component" do get '/' last_response.body.should include(%[<div id="flickr_viewer">]) end
Test for default number of images and default format
The test below will test that when using the component without passing any parameters it will render 3 images in the default format (’s’ or 75x75 pixels)
it "should render the default number of images in the default format" do
get '/'
body = Nokogiri::HTML(last_response.body)
images = body.xpath("//div[@id='test1']//div[@id='flickr_image']/img")
images.should have(3).images
images.each { |image| image['src'].should match(/\Ahttp.*_s.jpg\Z/) }
end
The test will retrieve the home page and then grab the ‘img’ elements contained in a div with the id of ‘flickr_image’ nested inside a div with an id of ‘test1’. Notice that we are using a named div to be able to differentiate the content of the different tests we will be performing.
After we have gathered those img elements with Nokogiri we will test that there are 3 images (the default) and that each one of those images is in the ‘small’ format (signified by the ‘_s’ in the file name)

Figure 10 - Failed Non-Parameterized Component Test
Let’s implement the first version of the ‘real’ Flickr Interestingness component. Using the code that we experimented with before to create a basic non-parameterized initial version of the component:
class FlickrInterestingness < Component
render do |tag|
format = 's'
displayed = 3
per_page = displayed * 10 # grab 10x more images
flickruri = 'http://api.flickr.com/services/xmlrpc/'
server = XMLRPC::Client.new2(flickruri)
flickrkey = '15b43fbd25e10d51e8533d32bf7e1d1a'
details = {:api_key => flickrkey, :per_page => per_page}
xml = server.call("flickr.interestingness.getList", details)
doc = Nokogiri::XML(xml)
builder = Builder::XmlMarkup.new
chosen = (0..per_page-1).to_a.sort_by{rand}[0..displayed-1]
index = 0
doc.xpath("//photos/photo").each do |element|
if chosen.include?(index)
server = element["server"]
id = element["id"]
secret = element["secret"]
title = element["title"]
farm = element["farm"]
image_url = "http://farm#{farm}.static.flickr.com/#{server}/#{id}_#{secret}_#{format}.jpg"
builder.div(:id => "flickr_viewer") {
builder.div(:id => "flickr_image") {
builder.img(:src => image_url, :alt => title)
}
}
end
index = index + 1
end
builder.target!
end
end
We also need to modify the Home Page template to include the ‘test1’ div element as follows:
<h1>Some Interesting Pictures...</h1> <h2>Default</h2> <div id='test1'> <trellis:flickr_interestingness/> </div>
Running the tests again reveals that our component is behaving as expected:

Figure 11 - Passed Non-Parameterized Component Test
We can also visually inspect the progress by launching the application:

Figure 12 - First working version of the Flickr Interestingness Component
Test for a specific number of images in the requested format
With a working component in place we can now add a test for the component’s ability to take parameters:
it "should render a request number of images in the requested format" do
get '/'
body = Nokogiri::HTML(last_response.body)
images = body.xpath("//div[@id='test2']//div[@id='flickr_image']/img")
images.should have(2).images
images.each { |image| image['src'].should match(/\Ahttp.*_t.jpg\Z/) }
end
Let’s also add the html for the test to the Home Page Template:
<h2>Parameterized</h2> <div id='test2'> <trellis:flickr_interestingness per_page='2' format='t' /> </div>
As you can see in this test we are asking for 2 images in the ‘t’ format (t thumbnail, 100 on longest side)
Let’s run the test and see it fail:

Figure 13 - Failed Parameterized Component Test
Let now add the simple changes to make the component be able to take parameters:
class FlickrInterestingness < Component
render do |tag|
format = tag.attr['format'] || 's'
displayed = tag.attr['per_page'] || '3'
displayed = displayed.to_i
per_page = displayed * 10 # grab 10x more images
...
We use the tag object ‘attr’ hash to retrieve the value for the ‘format’ and the ‘per_page’ parameters.

Figure 14 - Passed Parameterized Component Test
Yay! We can now visually inspect the page and see what we’ve got:

Figure 15 - Final version of the Flickr Interestingness Component
Conclusions
In this tutorial we’ve seen how complex logic can be encapsulated and parameterized in a component and we also learned how to develop a Trellis application in a Test-Driven Fashion (TDD). The component we’ve built; the Flickr Interestingness component wraps the complexity of retrieving XML from a web service, parsing the XML and building custom content. That is the type of complex logic that you do not want in your pages, specially if it is to be used more than once. The component we’ve created is still a simple stateless component. In future installments we will tackle the creation of stateful components.

