Thursday, April 24, 2008

Rending your XML as HTML with XSLT in Rails

I had a need to convert XML services that my rails app was providing into customizable html widgets tht could then be in-jested by some sort of server side includes(like NGINX's SSI). So I needed to use XSLT.

When the W3C wrote the XML specification they also developed the Extensible Stylesheet Language for Transformations. XSLT provides a powerful, flexible language for transforming XML documents into something else. In this case, we're going to use it to create HTML documents.

So to quickly look at how we're rendering xml currently.


def show
@event = Event.find(params[:id])
render :xml => @event
end

OK so we are simply rendering only xml back from this request. Now of course we can react to any registered mime-type we want, obviously HTML is one of those. So we do something like....

def show
@event = Event.find(params[:id])
responds_to do |format|
format.html
format.xml { render :xml => @event }
end
end

So, I'm going to assume that you've seen this responds to block before and you know that it's just going to render the show.html.erb template in our views, when html is requested.

Installing Ruby's XSLT Library


I'm on Mac OS-X 10.5.2 and haven't tested this install on anything else, but it's pretty simple really, unfortunately it's not a gem, but hey, it's not that hard

First you'll need to download the library, you can get it from here

Unzip that guy and in your terminal navigate to that directory, then execute the following commands(You'll need Make installed so ensure you've XCode installed on OS-X, or some sort of GCC/Make):
ruby extconf.rb
make
make test
make doc
sudo make install

Fire up irb and ensure you return true from require 'xml/xslt' if you get a false, the library hasn't installed properly, you can probably find help at http://greg.rubyfr.net/pub/packages/ruby-xslt/files/README.html

Getting Rails to render:


Firstly, include your new friend in environment.rb
require 'xml/xslt'

In my application controller I've added a private method

def xslt(_xml, _xslt)
xlt = XML::XSLT.new
xlt.xml = render_to_string :xml => _xml
xlt.xsl = _xslt
xlt.serve
end

Now in your controller method(I'll use the example from above)

def show
@event = Event.find(params[:id])
respond_to do |format|
format.html { render :inline => xslt(@event, "#{RAILS_ROOT}/xslt_template/show.xslt") }
format.xml { render :xml => @event }
end
end

Create an XSLT Style


You're of course going to need XSLT styles to apply to pages.
I'll start with the XML I'm using. It's just rendered by the show method from above:

<event>
<created-at type="datetime">2008-04-23T18:36:28+10:00</created-at>
<date type="date">2008-04-23</date>
<id type="integer">1</id>
<location>Camperdown Park</location>
<title>Cardboard Tube Fighting</title>
<updated-at type="datetime">2008-04-23T18:36:28+10:00</updated-at>
</event>

And this is the XSLT template I'll use to style this.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="html" />

<xsl:template match="/">
<xsl:apply-templates select="event/location" />
<xsl:apply-templates select="event/title" />
</xsl:template>

<xsl:template match="title">
<h4>
<xsl:value-of select="." />
</h4>
</xsl:template><br />
<xsl:template match="location">
<h1>
<xsl:value-of select="." />
</h1>
</xsl:template>
</xsl:stylesheet>

Smile like a bandit!


terminal: script/server
Navigate to the path of your method and voila you've just applied an XSLT style to your XML stream dynamically.

Next is for me to roll this into a Rail templating language... But that's another post.

Thursday, April 17, 2008

Programmers aren't creative

I used to be a photographer, and recently I've been selling some of my gear to new photographic hopefuls. Of course the inevitable questions arises about what I do now?
I'm a programmer I respond.
The usual response is:
Oh, that's not a very creative field to go to. I mean you used to be a photographer.

Well I'm going to put this question out there to the world. Is programming not creative? Really stop and think about it.
As someone who has come from a creative field to a "non-creative" field, I can tell you that in my opinion programming is very creative. Programmers are constantly asked to produce a completely new thing, from nothing at all. To create a program. Photographers are generally asked to capture something there in front of them. Now I'm not here to start the whole photography isn't creative debate I feel there is a degree of creativity in photography, but why does programming seem to have this non-creative air about it.
Is it Bill Gates fault? Or all the other a-typical nerd looking boys made infamous in the '80s for the development of Windows etc. How long will it take for people to put together the iPod/iMac/iPhone with programmers? All those cool funky user interfaces, those problems are solved by programmers... Are they not creative solutions?

So we come to the question, what is creativity?

the ability to transcend traditional ideas, rules, patterns, relationships, or the like, and to create meaningful new ideas, forms, methods, interpretations, etc.; originality, progressiveness, or imagination

I don't know about you, but that pretty well sums up about 8/10ths of my job as a programmer.

Anyone else feel that their industry is unnecessarily brandished with the non-creative iron?

Wednesday, April 16, 2008

Mocking and Stubbing in rSpec

I program in Ruby. A lot. I've recently found myself writing applications to provide simple web services to flash applications. We've pretty well standardized on using JSON as our preferred format at Snepo, so I've found it useless attempting to build and delivery anything without using some sort of TDD.

Welcome rSpec to my world, and it adds in a level of communication between myself and the involved parties about what a service should do, that I'm not sure any other framework does. For instance, we can begin specifying behaviors that the controller should return.
I might have a service that finds me all users who've been active in the last 24 hours. So I'd specify, like follows.


describe "Users Controller", "find_recent action" do
it "should find all users who've been active in the last 24 hours" do
c, r = request("/users/find_recent/24")
c.body.should eql(["some json array of elements"])
end
end

But! Here is our first problem with outside in development, we don't have a any methods in a User model yet! Now we could go off and build some, but this behavior might change as we move forward and in any case, we should test those methods with their own tests, this is testing the controller class after all. So why not try to get as much of this working how we want now. Well we can.

Hello Mocks and Stubs!

Mocks and Stubs confused me for a long time. The official definition in rSpec says: Mock objects are imitation objects that give you declarative control over their behaviour in the course of the execution of an example

I found this really confusing so I'll try to explain it, in my own words.
A mock allows you to send a message to an object(including a class, a class is an object after all) that does not yet exist( that is the message/method does not yet exist ). The rSpec framework looks out for the method that your test to call this object, and then returns what you specified in your test. If it is never called it considers that the test has failed. That is, a mock is a test in itself, a stub is not. A stub simply substitutes itself for the real object. So to flesh out the above example with a mock.

describe "Users Controller", "find_recent action" do
it "should find all users who've been active in the last 24 hours" do
@users = mock("A list of users")
User.should_receive(:find_all).with(24).and_return(@users)
c, r = request("/users/find_recent/24")
c.body.should eql(@users.to_json)
end
end

What we are doing here is creating a new mock object(@users) so we have something to return from our mock of the static method find_all. That happens in the line below, we tell rspec that it should_receive a message (:find_all) for a class called User, and when it does receive that message to return the mock object that we created above for the controller to keep on using it.
If we just wanted to stub that, i.e. if we didn't care if it got called, but if it does we'd better return something, we'd use: User.stub!(:find_all).and_return(@users).

There is one small misnomer in that whole mocking stubbing framework in rSpec, and that is @users = mock("A list of users"). This is not a mock, if it's not used rSpec doesn't care, there is no expectation placed on this object, to me this should be something like stub_object, in any case, that's a little gottcha albeit a linguistic one.
Now you see, you can get to it from the outside in this time and you can get all the model method's that you'll need sorted before your write a scrap of code in your models.
Oh and just so you know, your controller method might look something like this( in Merb ).

def find_recent
users = User.find_recent(params[:duration].to_i)
render_text users.to_json
end