Showing posts with label responds_to. Show all posts
Showing posts with label responds_to. Show all posts

Tuesday, March 10, 2009

Duck Typing

I learnt how to program in Ruby, and as we all know it's a very flexible language. Recently I've been doing a lot of stuff in C# on windows, and a lot more stuff in Objective-C on the iPhone. Something finally clicked for me, when I was using these languages, and that was Duck Typing.

Ruby, unlike the C style languages, couldn't care less about what type of object it is dealing with. Now before you start, I will repeat, it couldn't care less about what type of object it is dealing with.

What Ruby cares about is if object responds to the method you're sending it. In Ruby there is even a respond_to? method to assert this. i.e.

1.respond_to?(:to_s) #=> true
1.respond_to?(:foo_bar) #=> false

So you can have two objects, of totally differing types, and instead of asserting type like you might in say C#, you can instead check that the object responds to a method. The great thing about this, is that if it doesn't and you want it to, you can use Ruby's meta programming to dynamically inject that method into that object. For instance

unless 1.respond_to?(:foo_bar)<br />   1.class.class_eval do<br />      def foo_bar<br />         self * 100<br />      end<br />   end<br />end

1.foo_bar #=> 100
2.foo_bar #=> 200
30.foo_bar #=> 30000

12.2.foo_bar
#=> NoMethodError: undefined method `foo_bar' for 12.2:Float

So we've used class_eval to dynamically inject the method foo_bar in the class of 1(Fixnum), and so now all instances of Fixnum will respond to that method. We've never even checked it's type, we don't care, all we know is that we want it to respond to the method foo_bar and now it does. Obviously our Float class doesn't have this method hence the reason that 12.2 has raised the undefined method error. Any guesses on how to make this work :D, yeah that's right, class_eval and respond_to? will do it, just like we did with Fixnum.

So in summary what is the point of Duck Typing? Why not just check the class, and respond with what we as coders know it can and cannot do(or think we know it can do)?

Well, seriously why do you care what class it is? When what you really wanted to know is, whether it's going to respond to a method? Sometimes class and type is important, and obviously those features are there and should be used when class is important, but sometimes you just need to stop and ask yourself.

Do I can about type or do I just want to know if I can execute a method on this object?


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.