Ruby on Rails: Test Model Domain Changes 0

Posted by luca
on Monday, April 28
You know how tests are fundamental for a well-developed project, for this reason we should create step-by-step a net of test cases. Of course we also should be able to change rapidly our tests as the same we do with our code. Ruby on Rails is a great framework, because its shortcuts, the wide usage of DSL etc.. All this stuff can save a lot of time, but what about tests? Are we really able to follow our code?

Create and Destroy

ActiveSupport provides few useful tools to improve our test, I really appreciate assert_difference and assert_no_difference. Basically, this two methods accepts as arguments a code chunk (as string) and a block. When the test run, it binds the block first, then it assert if the changements caused by the block call are the same expected by first argument.
def test_should_be_created
  assert_difference 'Person.count' do
    create_person
  end
end
We are testing a Person creation, we pass as first argument 'Person.count', and the code that should correctly save the person. If the model will be saved, a new record should exists into the database table. At this moment assert_difference evaluates the first argument, and assert if there are differences in the Person count.
def test_should_be_destroyed
  assert_difference 'Person.count', -1 do
    destroy_person
  end
end
This example is just a bit different, we are also passing a Fixnum as argument. This because we want assert another difference from the default one, which is +1. So, if the model will be correctly saved, we will have a negative difference, of one, into the Person count.
def test_should_require_first_name_on_create
  assert_no_difference 'Person.count' do
    create_person
    assert person.errors.on(:first_name)
  end
end
The third example uses assert_no_difference, to test aganist model validations. ActiveRecord, by default, prevents the creation of a model if a validation doesn't pass. In this case our model requires first_name as mandatory attribute, but unfortunately it's nil, so the creation fails and the brand new record will be not created.

Update

As you can see, those two methods are very useful for test creation and destruction of models, but totally missing the goal of the update. In fact, the update process of a record, doesn't produces numerical differences. I created two methods to supply this lack.
def assert_updated(model, message = nil, &block) 
  yield
  assert_not_equal model.attributes, model.reload.attributes, message
end
def assert_not_updated(model, message = nil, &block)
  yield
  assert_equal model.attributes, model.reload.attributes, message
end
Just add them to your test/test_helper.rb, and they will be available in all your test cases.
def test_should_update
  assert_updated person do
    update_person
  end
end
First, you should notice that the first argument it isn't a string but an ActiveRecord. The behaviour of this method is similar to the previous I illustrated, it first bind the block, then assert if the attributes of the model are different. It internally uses ActiveRecord::Base#attributes which returns an hash of model attributes, then assert the differences with Ruby's assert_not_equal.
def test_should_require_first_name_on_update
  assert_not_updated person do
    update_person
    assert person.errors.on(:first_name)
  end
end
Similarly to all other examples, it first performs the block, call then assert there are no changes in the model attributes.

Conclusion

Those methods should provide a rapid way to write and mantain your test cases. If you enjoyed this post, feel free to recommend me on Working With Rails.

Bit.Fall 1

Posted by luca
on Saturday, April 05

Bit.Fall by Julius Popp.

Make your elements draggables and resizeables with Resizeable.js 0

Posted by luca
on Wednesday, March 19

Hello, I'm still alive and I want to share a useful JavaScript class: Resizeable.js. It allows to make draggable and resizeable your DOM elements!

How it works?

new Resizeable('element');

Your element has now two sensible areas: the border area and the central area. If you click on the central area and move the mouse around, keeping the left button pressed, your element will be dragged. If you are on the border area, and perform the same gesture, your element will be resized.

The default size of the border area is 6px, but you can customize it via the options passed to the constructor:

new Resizeable('element', {top:12, right:12, bottom:12, left:12});

Try it now!

Try it now on the demo page.

Credits and License

Resizeable.js is based on Thomas Fakes homonym class and on Thomas Fuchs Scriptaculous, this means it depends on Prototype(1.6.0+) and Scriptaculous (1.8.0+) itself.

Resizeable.js is Free-Software, distributed under the MIT License.

Download

Resizeable.js (md5: aae8256ab0eff3972a03ed67e238e623).

Ruby: XML Parsing With SAX 3

Posted by luca
on Wednesday, January 30

SAX is an event-driven parser for XML.

It sequentially reads the xml and generates special events. So, if you want to use SAX, you should implement the code to handle them. It's quite different from the DOM model, where the whole xml is parsed and loaded in an tree.
As you can see, the first approach is more difficult than the DOM one. Why we should use it? Depends.
If you want to extract certain informations from a big file, probably you should choose a SAX implementation, in this way you can avoid the initial DOM loading overhead.

The Ruby XML Library

The Ruby core library has a built-in XML parser (both DOM and SAX) called REXML, but it's terribly slow, it's highly advisable to use libxml. It's a binding to the popular library from Gnome and it was released as gem.

The Ruby Implementation

In first instance we need an handler, to deal with the SAX events.

class Handler
  def method_missing(method_name, *attributes, &block)
  end
end

Libxml generates several events and it expects to find certain methods into the class assigned ad handler. With method_missing we simply avoid any exception.

A More Useful Example

We try to extract the most recent headlines of a blog.

Download the feed:

curl http://feeds.feedburner.com/LucaGuidi >> luca.xml

Now we need our custom SAX parser:

require 'rubygems'
require 'xml/libxml'
require 'handler'

class SaxParser
  def initialize(xml)
    @parser = XML::SaxParser.new
    @parser.string = xml
    @parser.callbacks = Handler.new
  end

  def parse
    @parser.parse
    @parser.callbacks.elements
  end
end

We have just wrapped the SAX parser from libxml and we have registered our first class as callback handler.

Now we are going to improve the handler to recognize and save the post titles:

class Handler
  attr_accessor :elements

  def initialize
    @elements = []
  end

  def on_start_element(element, attributes)
    @print = true if element == 'title'
  end

  def on_characters(characters = '')
    @elements << characters if @print
  end

  def on_end_element(element)
    @print = false
  end

  # Handle all missing methods of the SAX events chain.
  # You can implement or omit one or many of those methods, without any raising Exception.
  # 
  # The complete chain is:
  #   on_start_document
  #   on_processing_instruction(instruction, arguments)
  #   on_start_element(element, attributes)
  #   on_characters(characters = '')
  #   on_end_element(element)
  #   on_end_document
  def method_missing(method_name, *attributes, &block)
  end
  end

When the handler is instantiated we create an internal array to store our results, then when we find and title element we set on true the print flag. When it's true we can store the data into elements, then we set on false on the ending handler of the element.

Usage

We create a trivial script:

#!/usr/bin/env ruby
require 'sax_parser'

xml = open(ARGV[0], 'r').collect { |l| l }.join
puts SaxParser.new(xml).parse

From the shell:

./parse luca.xml

Conclusion

SAX is less elegant and easy than DOM, but could be very useful in certain cases.

Click To Globalize: High Resolution Video Tutorial 3

Posted by luca
on Saturday, January 26

Click To Globalize: Getting Started [High Resolution]

The video tutorial for Click to Globalize is now available in high resolution: Click To Globalize: Getting Started.

Hands On My Brand New iPod Touch 2

Posted by luca
on Friday, January 18

My iPod Touch

Yesterday i got a 16Gb iPod Touch at the Rome Apple Store: it's a really, really amazing gadget. I love it!

MondoPOP 0

Posted by luca
on Saturday, January 05

mondopop logo

MondoPOP is a permanent expo of Pop toys, located in Rome.

I've been there a month ago, and I was fascinated about this new wave of artists, writers and crews. I think it's a great think, to support this kind of projects: people who transform their passion in a work.

Good luck, guys!!

Ruby: How To Avoid A respond_to? Call 0

Posted by luca
on Wednesday, January 02

I'm writing this post as contribution to the Campagna Anti-IF (Anti-IF Campaign).

Problem

I'm developing an internal Rails plugin for widgets, it provides a class called Widget (really unconventional :-P), and each widget should inherit from it.
The actual implementation provides a callback called before_render, that allows to add some logic to a widget, if implemented it's called before the widget rendering.

Ruby doesn't have abstract methods, so I have to check if the subclass has the implementation of mentioned method:

# Rendering code..
before_render if respond_to? :before_render
# ...

As you can see it's an inefficient and inelegant way to render the widget, cause we always check if the method was implemented, and because I have introduced an if statement.

Solution

I added to Widget an empty before_render method: if the method it wasn't implemented into the subclass the rendering code will be safely called.
Here the new code:

def before_render
end

# Rendering code..
before_render
# ...

Rails: How To Create Custom Validations 0

Posted by luca
on Friday, December 21

Often our model objects leaning toward to be confused or noisy, due to validations DSLs. Imagine a class Answer, with an attribute, that should be exactly a string representation of a boolean. Ok, I know it's an odd example, but: it's trivial enough to make this example clear, and.. It happened to me to deal with this situation. :-P

class Answer < ActiveRecord::Base
  validates_inclusion_of :value, :in => %w( true false ),
                         :message => "Should be exactly true or false."
end

Now, we try to clean-up a bit this code.
First, create a file named validations.rb into lib, then copy and paste this code:

module ActiveRecord
  module Validations
    module ClassMethods
      @@boolean_values = %w( true false )
      @@validates_boolean_msg = "Should be exactly #{@@boolean_values.join(' or ')}."

      # Check if the value is a boolean: <tt>true</tt> or <tt>false</tt>.
      def validates_boolean(*attr_names)
        configuration = { :message   => @@validates_boolean_msg,
                          :in        => @@boolean_values }

       configuration.update(attr_names.pop) if attr_names.last.is_a?(Hash)
       validates_inclusion_of attr_names, configuration
      end
    end
  end
end

Then we are going to add the following line at the end of environment.rb
require 'validations'

Let's clean the code:

class Answer < ActiveRecord::Base
  validates_boolean :value
end

Is it better? Maybe.. ;-)

Rome JavaDay 07: Slides 0

Posted by luca
on Wednesday, December 19

JRuby and DSLs

The Rome JavaDay 07 staff has created a Slideshare group.

I have already uploaded my presentation about: JRuby and DSLs.

Subsonica in Rome 1

Posted by luca
on Wednesday, December 19

Me & Boosta

I met Subsonica during a dj-set in Rome.

You can find my shots here.

Migration Complete 0

Posted by luca
on Wednesday, December 19

I migrated this blog to Joyent, and now I'm using Mephisto.

Migration complete!!

Migration In Progress 0

Posted by luca
on Sunday, December 16

I'm going to migrate this blog on a new server, I apologize for eventual inconveniences.

Click To Globalize: Rails 2.0 Ready 5

Posted by luca
on Friday, December 14

I have finished to work on Click To Globalize, to made it Rails 2.0 compatible.

What's Changed?

All and nothing: from the user point of view, the plugin has the same behaviors of the previous version. My recent activity was a refactoring, now it:

  • Works with CSRF Killer
  • Works with Prototype 1.6.0.1 and Scriptaculous 1.8.0.1
  • Works with rewritten version of Scriptaculous Ajax.InPlaceEditor
  • Works with new Prototype events handling
  • Uses new Prototype's Element#addMethods and Function#wrap to add methods and AOP
  • Uses Protoype Hash#get, instead of square brackets
  • Uses .html.erb as helper, instead of .rhtml
  • Has a more clean installation/disinstallation process
  • Has DRYed up tests
  • Hasn't prototype.js into the packaging

How To Use It?

Rails 2.0

$ ./script/plugin install http://dev.23labs.net/svn/rails/plugins/click_to_globalize/trunk

Rails 1.2.x
$ ./script/plugin install http://dev.23labs.net/svn/rails/plugins/click_to_globalize/branches/for-1.2.x

For a detailed guide, howtos, snippets, video-tutorials and other infos, please visit the Click To Globalize page.

Click To Globalize: Repository Changes 2

Posted by luca
on Sunday, December 09

As I previously announced, I'm working on Rails 2.0 compatibility for Click To Globalize.

I decided to move the stuff for Rails 1.2.x to for-1.2.x branch and continue the porting into trunk.

How To Install

Rails 2.0

$ ./script/plugin install http://dev.23labs.net/svn/rails/plugins/click_to_globalize/trunk

Rails 1.2.x
$ ./script/plugin install http://dev.23labs.net/svn/rails/plugins/click_to_globalize/branches/for-1.2.x