Thursday, October 25, 2007

Terminate batch job (Y/N)? Y

If you happen to use Ruby/Rails on Windows then you've probably got annoyed at least a few times by the "Terminate batch job" prompt you get when typing Control-C at an irb or mongrel session. Even though the program already terminated after a Control-C, the prompt asks anyway and you are forced to give an answer. That's because those Ruby programs are wrapped inside batch files, which have that behaviour hard-coded into cmd.exe.

It's possible to patch cmd.exe, but if your only problem is with some Ruby scripts, you can simply get rid of the batch files and replace then by pure Ruby scripts.

In the case of irb rename bin\irb.bat to bin\irb.rb and remove the lines from the beginning of the file until goto endofruby. For mongrel_rails delete bin\mongrel_rails.cmd and rename bin\mongrel_rails to bin\mongrel_rails.rb. The steps are similar for other files. Sometimes the Ruby script is coded directly inside the batch file. Other times the batch file just invokes Ruby with another file without an extension. But be sure .RB is appended to the PATHEXT environment variable.

The solution is so simple and yet I took months to finally put it in practice...

Wednesday, October 3, 2007

Persisting variables between partial calls

In Rails, there are times when you need to preserve the value of a variable across partial renderings. For example, you want a partial to render a row of a table with numbered rows. How do you do that?

Store the value in an instance variable:

<tr>
  <td><%= @row_id = (@row_id || 0) + 1 %></td>
</tr>

The variable will be preserved while the page is being rendered, in an instance of a dynamically created class, subclassed from ActionView::Base (as of Rails 1.1.6 that happened in ActionController::Base.initialize_template_class).

The same variable will be available inside the corresponding helper, since the helper is included as a module of that dynamically created class. So code like the one shown above may be moved to a helper.

Use the trick wisely, though! A variable seen by different files is almost a global variable.

Friday, June 22, 2007

Mocha stubs with multiple parameters/result sets

I've just decided to give a try to some mock framework for Ruby. Actually a mock framework, since I never used one seriously before. Among other options (FlexMock, RSpec and ruby-mock) I came across Mocha, which looked like a rather good framework. It's not trivial to decide on the best one because, aside from ruby-mock, which is pretty old, all of them perform equally well (or bad) in the opinions I found on the Web.

My immediate need was for a quick way to define stubs. The class I wanted to test would call some methods in another object which needed a specific behaviour. One method of that other object needed to provide the class under test with results which depended upon the parameters passed in. A pretty common scenario, I believe. However, the documentation does not state how to stub a method like that, when no particular order of calls is required. (It says you can set an expectation on the order parameters will be passed in and results returned, but that's beyond my needs.)

Fortunately, the Mocha::Mock::stubs method may be invoked multiple times with the same method name, as I discovered trying it out. For each expectation returned you can set different parameters and return values, hence simulating an object that acts based on its parameters, without tying it to any particular order. Here's a test-case which creates a mock for a calculator object:

require 'mocha'

class CalculatorTestCase < Test::Unit::TestCase
  
  def test_add
    calculator = mock
    calculator.stubs(:add).with(1, 2).returns(3)
    calculator.stubs(:add).with(3, 7).returns(10)
    
    assert_equal(3, calculator.add(1, 2))
    assert_equal(10, calculator.add(3, 7))
  end
  
end

I still need to better explore the world of a mock framework, but am already feeling ashamed of not having tried it before. I'm sure from now on I'll write a lot less code inside test-case files.

Wednesday, June 20, 2007

Inside a class method "self" points to the class!

I often find this kind of construct in Ruby source files (Ruby's stdlib included):

class Parser

  # ...

  def Parser.parse_file(filename)
    parser = Parser.new(File.open(filename, 'rb'))
    parser.parse
  end

end

However, when you are inside a class method, the self variable already points to the class of the method. Thus, prepending the call to new with the class' name is unnecessary repetition. Moreover, although either self or the class name is still required before the name of a class method definition, I find the former form a better option. In the event of a refactoring involving a change in the class name, you'd be less prone to mistakes.

So, the code above could have been written as:

class Parser

  # ...

  def self.parse_file(filename)
    parser = new(File.open(filename, 'rb'))
    parser.parse
  end
  
end

There's a small caveat regarding inheritance, though. In the first construct, parse_file will always create an instance of Parser, while the second will create an instance of the class specified in the call to parse_file. That might be desirable or not.

Tuesday, June 19, 2007

E-mail address leaked from RubyCorner to spammers?

A few minutes ago I received a spam message at my Inbox. A snippet follows:

Subject: Information
From: INFINITY LOTTERY <infinitylottery@yahoo.de>
To: Administrator <"#{'q'.succ}ubycorner@romulo.e#{2+2}ward.com">

INFINITY LOTTERY PROMOTIONS THE NETHERLANDS
FROM:THE DESK OF THE PRESIDENT
INFINITY LOTTERY PRIMOTION/PRIZE AWARD DEPT
Ref. Number:WRH/12726/PZP
Batch Number:29/064701/904HM

ATTENTION.

Sir/ Ma/ Miss,

We are pleased to inform you of the result of the winners of the Infinity
Lottery Promotions
programs held on 14th June,2007.Your e-mail address attached to ticket
number 085---2356789--789
with serial number 8567--46 drew lucky numbers 9- -01-23455-34 which
consequently won in the
category A.You have therefore been approved for a lump sum pay of US
$1.200,000.00(one million two hundred
thousand United States Dollars)in cash credited to file Ref.
Number:WRH/12726/PZP.This is from a total
cash prize of US $16.800,000.00usd international winners in this category.
CONGRATULATIONS!!!

(...)

Most interesting is the address to which the message was sent (mangled here by me), a unique one I generated on June 3, 2007 using E4ward to sign up for RubyCorner. I didn't give it to anyone else. That leaves only two possibilities:

  1. It somehow leaked from RubyCorner. I don't see anyway to get the e-mail of other users or set a specific option on my profile regarding visibility. Thus, if not intentionally leaked, it was stolen.
  2. The spammer used a dictionary attack against the sub-domain romulo.e4ward.com. That seems unlikely. What kind of idiot would perform such an onerous attack to find addresses among no more than a few dozens which are known to be from an e-mail forwarding service?

To be really sure I need to generate a random address and sign up again for RubyCorner. Meanwhile, if you also suspect the address you have registered at RubyCorner was abused, please, let me know.

Wednesday, June 13, 2007

Creating an object from the class name

How do you create an object in Ruby given its class name as a symbol or string?

It turns out that that's not as obvious as using send to call methods in an already existing object. You need to use the Kernel.const_get method to convert your symbol or string into a class reference (in Ruby class names are constants):

def create_object(klass)
  Kernel.const_get(klass).new
end

my_hash = create_object(:Hash)

There's no good reason to use that trick in such a simplistic example, but it could be really helpful if you were reading the class names from a file or when out of the class' scope (perhaps violating encapsulation!).

Friday, June 1, 2007

Quick mapping between XML and Ruby objects

Often you'll find yourself reading an XML file and writing boilerplate code with REXML.

I was in the middle of a tool that'd generate Ruby code from an XML Schema when I started feeling the pain of working directly with REXML. From that point it was only a matter of hours to come up with this simple and elegant solution:

require 'rexml/document'

class XMLDef
  attr_accessor :text

  def self.xml_attribute(attr_name, xml_elem)
    attr_accessor attr_name

    @attributes = Hash.new unless defined?(@attributes)
    @attributes[attr_name] = xml_elem
  end
  
  def self.xml_array(array_name, xml_elem, xml_class)
    attr_reader array_name
    
    @arrays = Hash.new unless defined?(@arrays)
    @arrays[array_name] = [xml_elem, xml_class]
  end
  
  def self.xml_object(object_ref_name, xml_elem, xml_class)
    attr_reader object_ref_name

    @objects = Hash.new unless defined?(@objects)
    @objects[object_ref_name] = [xml_elem, xml_class]
  end
  
  def self.load_element(elem)
    result = new
    
    result.text = elem.text
    
    if @attributes
      @attributes.each do |k, v|
        result.send(k.to_s + '=', elem.attributes[v])
      end
    end
    
    if @arrays
      @arrays.each do |k, v|
        result.instance_variable_set('@' + k.to_s, Array.new)
        
        elem.each_element(v[0]) do |x|
          result.send(k).send('<<', v[1].load_element(x))
        end
      end
    end
    
    if @objects
      @objects.each do |k, v|
        if x = elem.elements[v[0]]
          result.instance_variable_set('@' + k.to_s, v[1].load_element(x))
        end
      end
    end
    
    return result
  end
  
  def self.load_xml(xml)
    return load_element(REXML::Document.new(xml).root)
  end
end

To create a mapping you simply inherit from XMLDef. xml_attribute, xml_array and xml_object create the accessors. load_element recursively reads the XML into the objects you define. load_xml is the method you call on the root of your mapping to read the actual XML.

For example, suppose you want a mapping for the following XML:

<planetSystem name='Solar System'>
  <dimensions diameter='100000 AU' mass='2e30 kg'/>
  <planet name='Earth' lifeFriendly='1'>
    <dimensions diameter='12745 km' mass='5.97e24 kg'/>
    <description>The planet were we live</description>
  </planet>
  <planet name='Mars' lifeFriendly='0'>
    <dimensions diameter='6805 km' mass='6.42e23 kg'/>
  </planet>
  <planet name='Jupiter'>
    <description>Biggest planet in the solar system</description>
  </planet>
</planetSystem>

You'll write:
class Description < XMLDef
end

class Dimensions < XMLDef
  xml_attribute :diameter, "diameter"
  xml_attribute :mass, "mass"
end

class Planet < XMLDef
  xml_attribute :name, "name"
  xml_attribute :life_friendly, "lifeFriendly"
  xml_object :dimensions, "dimensions", Dimensions
  xml_object :description, "description", Description
end

class PlanetSystem < XMLDef
  xml_attribute :name, "name"
  xml_array :planets, "planet", Planet
  xml_object :dimensions, "dimensions", Dimensions
end

I don't think you could write anything more obvious. The annotations make the code pretty straightforward to understand and maintain. I was coding a lot of test-cases for my XML Schema reader. The mapper rendered them completely useless.

This is how you'd read the XML above:

x = PlanetSystem.load_xml(File.open('planets.xml'))

puts x.name
puts "Diameter: " + x.dimensions.diameter                     
puts "Mass: " + x.dimensions.mass

puts ""

p = x.planets[0]
puts p.name + ': ' + p.description.text
puts "Life friendly: " + p.life_friendly
puts "Diameter: " + p.dimensions.diameter
puts "Mass: " + p.dimensions.mass

That's not hard to extend the class to save data back to XML or create a new one from scratch.

Today, after spending a few hours coding the class above, I found ROXML, which follows the same idea as mine. Some of the similarities are interesting, by the way. ROXML also has the methods xml_attribute and xml_object. And an example included in the source code references the Pickaxe book, which I did in the test-cases for XMLDef. I guess the author also had the book over the table when thinking out an example.

Friday, May 11, 2007

Ruby and the Master File Table

If you already investigated Ruby's installation directory, you may have noticed the huge amount of files there. I've just installed Ruby 1.8.6 and Rails 1.2.3. The disk contains now 26,978 more files and 4,142 more folders.

A medium sized Windows installation has around 40,000 files and folders, meaning Ruby accounts for almost 50% of all Master File Table record entries when it is installed in an NTFS partition. The first obvious effect is that a search on the root of the drive must examine twice the number of files. The other, not so obvious, is that the MFT will fragment, because of the large number of small files.

Moreover, small files (typically 1,500 bytes or less) are stored directly inside the MFT. From those 26,978 files 20,660 are 1,500 bytes or smaller (not to say about the directories, whose sizes I don't know how to calculate, although I bet most are considered small), i.e. most of Ruby's files are inside the MFT.

If you don't want Ruby to be a big degradation factor for your hard disk performance, install it on a separate partition, which you can simply reformat latter if the MFT gets heavily fragmented.

Thursday, May 3, 2007

Prime number calculator

Do you remember back in your first classes in university when you was taught how to calculate a series of prime numbers in C or Pascal? Do you remember those tens of lines of code?

You need a lot less in Ruby:

def primes(up_to)
  prev = []
  (2..up_to).select do |x|
    max_p = Math.sqrt(x).truncate
    if !prev.find { |y| y <= max_p ? x % y == 0 : break }
      prev << x
    end
  end
end

Let's try it:

irb> primes(10)
=> [2, 3, 5, 7]
irb> primes(100)
=> [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97]
irb>

The method goes beyond naive approaches, testing each number only with previously found prime numbers that are not greater than its square root. On my machine it computes the primes up to 100,000 in under 3 seconds.

Wednesday, May 2, 2007

Initializing a lookup table in Rails

You're writing a migration script for a Rails application which will create a "Priorities" table with columns for name and position. What if you want to initialize it with well known values? Here's the Ruby way:

create_table :priorities do |t|
  t.column :name, :string
  t.column :position, :integer
end

say_with_time "Initializing priorities..." do
  ["low", "normal", "high"].each_with_index do |elem, i|
    Priority.create(:name => elem, :position => 1 + i)
  end
end