MonkeyPatching Class Methods in Ruby/Rails

April 29, 2008 | code, rails, ruby

I ran into this problem while trying to MonkeyPatch some class methods into existing Rails classes.

Say you have the following Mooable module (as a rails plugin) that you want to dynamically inject into ActiveRecord::Base (or any ruby class for that matter).


# app/models/my_model.rb
class MyModel < ActiveRecord::Base
 ...
end

# plugins/mooable/lib/mooable.rb
module Mooable
  def moo
    return "Moo!"
  end
end

# plugins/mooable/init.irb
ActiveRecord::Base.send :include, Mooable

# test
MyModel.new.moo # => "Moo!"

But what if you wanted #moo to be a class method on ActiveRecord::Base? I was tempted to do the following:


#plugins/mooable/lib/mooable.rb
module Mooable
  def self.moo
   return "Moo!"
  end
end

# test
MyModel.moo # => NoMethodError: undefined method `moo' for MyModel:Class

As you can see, simply defining the module method as a class method is a no-go (I’m sure this is a major nuby mistake, but it’s always worth a try!)

The best way I have found so far to monkeypatch new class methods into rails classes with a plugin is to use the Object#extend method inside of the module’s self.included hook. The new class methods are defined within a sub-module Mooable::ClassMethods for easy inclusion.


# plugins/mooable/lib/mooable.rb
module Mooable

  def self.included(klass)
    klass.extend ClassMethods
  end

  module ClassMethods
      def moo
        return "Moo Static/Class Method"
      end
  end

  def moo
    return "Moo Instance Method!"
  end
end

# test
MyModel.moo # => "Moo Static/Class Method"
MyModel.new.moo # => "Moo Instance Method!"

This works like a charm.

Hemingway on Design Patterns

April 12, 2008 | opinion

I had a shared blog post from kottke.org in my google reader today. It’s an Ernest Hemingway quote on the author’s use of symbolism:

“No good book has ever been written that has in it symbols arrived at beforehand and stuck in,” says Hemingway. “That kind of symbol sticks out like raisins in raisin bread. Raisin bread is all right, but plain bread is better.” He opens two bottles of beer and continues: “I tried to make a real old man, a real boy, a real sea and a real fish and real sharks. But if I made them good and true enough they would mean many things. The hardest thing is to make something really true and sometimes truer than true.”

I often get a lot of flack for thinking and talking about design patterns. I think the main reason is because most people who learn about them, get excited and start wildly trying to apply them everywhere possible– which subverts the underlying principle of design patterns.

One of the most valuable lessons I’ve learned about design patterns is to find them within the context of the problem and not to let the problem be defined by the pattern. Don’t apply your favorite pattern to a problem just because you know or like it. This is what Hemingway is saying; applying a design pattern for design pattern’s sake “sticks out like raisins in raisin bread.”

Gotcha: HABTM Relationships Use Join Table Id As Model Id

April 10, 2008 | code, rails

Having problems with Has and Belongs To Many(habtm) relationships loading your models with the wrong Ids? I was, the story follows.

Admittedly, HABTM is a relationship in Rails that I use rarely. This little snag caught me off-guard last week, and the usual internet searches didn’t yield particularly fruitful results.

Say you have a simple HABTM relationship:


#Predator.rb
class Predator < ActiveRecord::Base
  has_and_belongs_to_many :prey
end

#Prey.rb
class Predator < ActiveRecord::Base
  has_and_belongs_to_many :predators
end

So you’ve added the relationship, now you go to add your join table as a new migration:


class CreatePreyPredators < ActiveRecord::Migration
  def self.up
    create_table :prey_predators do |t|
      t.integer :prey_id
      t.integer :predator_id
    end
  end

  def self.down
    drop_table :prey_predators
  end
end

Now, say you’ve added a few relationships in your database:


select * from prey_predators;
+---+--------------+--------------+
| id |   prey_id   | predator_id  |
+------------------+--------------+
|  1 |   218872420 | 368007386    |
+------------------+--------------+
1 row in set (0.00 sec)

BUT, when you look at your shiny new relationship in the console, you see this:


>> Prey.find(:first).predators
#<Predator id: 1, name: "test_predator" >

You can see that IRB is reporting the predator’s id as 1 and not 368007386. What you’re seeing isn’t the primary key of the predator record, but the primary key of the relationship record in the join table where predator was found.

It would appear that Rails gives any column named ‘id’ precedence when determining the id of the record– ignoring the foreign kesy in the join table. I tried specifying the :foreign_key and :associated_foreign_key on the has_and_belongs_to_many relationship, but that only appears to affect the query that selects the records, not the model’s “reconstitution” from the database.

Unless you want this behavior, just add :id => false to your migration and you’re woes will be gone:


class CreatePreyPredators < ActiveRecord::Migration
  def self.up
    create_table :prey_predators, :id => false do |t|
      t.integer :prey_id
      t.integer :predator_id
    end
  end

  def self.down
    drop_table :prey_predators
  end
end

Feed Url Changing

April 5, 2008 | site news

I’m going to start hosting my feed through feedburner. Please update your feed urls to: http://feeds.feedburner.com/Acts_as_martian

Arriving at a DSL for XPath Testing in Rails

February 17, 2008 | code, rails, ruby, testing, xml

Recently I was writing custom Xml exporting for some of our Rails models. I favor TDD over just shoot-from-the-hip development, so I had to write some tests. For testing the validity of the XML, I chose XPath for its simplicity.

As I added functionality (and thereby added tests) I noticed that my tests were becoming increasingly redundant, and that Test::Unit’s syntax was making my test files ugly and more importantly hard to read. As I started DRY-ing up the individual tests and trying to find better ways to share code and make the tests more succinct, I noticed that I was driving very close to creating my own mini-DSL for testing Xml with Xpath.

My Requirements for Xml Exporting

  1. A required element must always exist in the resulting Xml regardless of the model’s state
  2. An optional element:
    • Must exist when the model meets a certain state condition (i.e. the member is not null)
    • Must not exist when that condition is not met

A DSL That Meets My Testing Needs

As mentioned earlier, this DSL evolved from repeated refactoring my tests in order to make them more clear. Here is an example of an XPath-based Xml test:


xpath_tests_for @video_library do
  # ensure that the Xml always contains:
  #   <library><videos></videos</library>
  require '/library/videos'

  # ensure that when certain conditions are met, the Xml contains:
  #   <library><genres></genres></library>
  optional '/library/genres' do
    before :test_missing do |library|
      library.genres.clear
    end

    before :test_existing do |library|
      library.genres << Genre.new :name => "Comedy"
    end
  end
end

Here’s a run-down of the language elements in this mini-DSL:

xpath_tests_for @model
Describe tests for a given instance of a model

require <xpath>
Add a test requiring the xpath in the test-suite’s Xml blob

optional <xpath>
Add a test requiring the xpath in the Xml under certain conditions

before :test_missing
A block defining the conditions whereby the element must be missing in an optional test

before :test_existing
A block defining the conditions whereby the element must be existing in an optional test

How This DSL Emerged

As I mentioned at the start of this article, I didn’t approach this testing with a DSL in mind, the entire concept emerged from my testing. Here is an example of the emergent nature of this DSL. As seen in the earlier example, I can test all of my requirements, but here is a new requirement:

Since this is custom Xml generation for a specific target application, do not change the default behavior of ActiveRecord#to_xml

As you might have imagined the testing currently functions with the test runner calling ActiveRecord’s built-in to_xml method, and my Library model had code in it to generate the custom xml format.


# somewhere in the xpath test runner
xml_for_testing = @video_library.to_xml

# models/library.rb
def to_xml
  # code to render custom xml format
end

However, in order to meet the requirement, I had to change my model, moving my custom Xml generation from to_xml to some new method:


# models/library.rb
def generate_custom_xml
  # code to render custom xml format
end

Now I’m meeting the requirement, but the tests are broken because the test runner is now calling the wrong method for my tests

In order to accomodate this change, I updated my DSL to include a block for specifying how xml should be generated:


xpath_tests_for @video_library do
  generate_xml_as do |library|
    library.generate_custom_xml
  end

  require '/library/videos'
  ...
end

This way I my test runner can yield the data model to the generation block and the block can handle the specifics of using the model to generate xml.

Note: Because i’m doing “red/green” test-driven-development, I actually wrote the new test syntax first and the code later, but for clarity, wrote the article in the opposite order.

Final Notes

If I wasn’t practicing TDD, I probably would have never come up with a succinct mini-language for XPath-based testing. During the initial design of this feature, it would have seemed like an over-engineering. Who wants to put that much effort in testing anyway? However, since the code emerged as necessary from my test-driven-development, I was not only able to write some interesting testing code, but I also now have new plugin for performing XPath testing in other apps.

Not only is this a personal win for TDD in my book, but since this is my first DSL (thanks Ruby!) I learned a few things about development with a DSL. Most notably, since the DSL defines an ideal API, I rarely if ever changed the code in my tests. Most of the work I did to get this functional and optimized was in the test-runner and supporting classes. And all of that code was able to be written without changing my tests

XPath matching in Test::Unit

February 12, 2008 | code, rails, ruby, testing, xml

Inspired by an article on adding XPath matching to RSpec. I’m not using RSpec currently, and I also have a need for this functionality in my tests, so I decided to a helper for use in Test::Unit to achieve XPath matching in my tests.

Add a new helper method:
Add the following code to your test/test_helper.rb in order to create the assert_has_xpath method.


#test/test_helper.rb
require 'rexml/document'

# Asserts that the specified xpath matches
# at least once in the given document
def assert_has_xpath (xpath, doc)
  doc = doc.is_a?(REXML::Document) ? doc : REXML::Document.new(doc)
  match = REXML::XPath.match doc, xpath
  assert !match.empty?, "Missing xpath '#{xpath}' in document: #{doc}"
end

Optionally, you can add the above to a module and include it in your test_helper, or in your individual tests.

Use the helper in your tests:
Now, in your tests, you can do something like the following


#model.rb
def to_xml options = {}
  xml = options[:builder] ||= Builder::XmlMarkup.new(options)

  xml.toys{
    xml.toy "ball"
    xml.toy "iPhone"
  }
end

#model_test.rb
def test_create_xml_creates_toys
  model = Model.new
  xml = model.to_xml
  assert_has_xpath "/toys[toy='iPhone']", xml
end

And voila! You now have XPath testing in your Test::Unit tests.

How To Create A Mixin To Extend Core Types in Rails 2.x

January 21, 2008 | rails

Recently I wanted to create a mixin for String in my Rails 2.0 project that allowed me to DRY up pluralization given an arbitrary number. Here’s an example usage:


"mile".pluralize_for(trip.miles) #returns "mile" when trip.miles is one and "miles" otherwise.

The mixin code is simple:


# martian_extensions.rb
module MartianExtensions
  def pluralize_for(number)
    unless number == 1
      self.to_s.pluralize
    else
      self.to_s
    end
  end
end

The challenge came with finding a simple, configuration-free way to ensure that this mixin is loaded. Following the guidance of Jamis Buck in this (by now ancient) blog article, i eventually settled on his solution of creating my simple mixin as a full-fledged plugin.

In order to do this without adding any configuration, you can follow Rails’ automagic loading rules, namely, create an apt folder structure under your vendor/plugins directory:

    /vendor/plugins/martian_extensions/
    /vendor/plugins/martian_extensions/init.rb
    /vendor/plugins/martian_extensions/lib
    /vendor/plugins/martian_extensions/lib/martian_extensions.rb

You’ve already seen martian_extensions.rb, as for init.rb, that’s what does the work of ensuring String loads my module and includes pluralize_for:


#init.rb
String.send :include, MartianExtensions

Personally, I’ll keep my core extensions in this single plugin so that I can keep it portable between my applications. It seems pretty clean to me, but if there’s a better solution that you have found, please comment!

Disable Globalize Translate() to Reduce Log Output

January 8, 2008 | rails

If you’re using globalize to translate or localize your rails site you’ll have noticed that your log quickly fills up with messages like the following (especially in the devlopment environment):


  Globalize::Language Columns (0.000516)   SHOW FIELDS FROM globalize_languages
  Globalize::Language Load (0.000202)   SELECT * FROM globalize_languages WHERE (globalize_languages.`rfc_3066` = 'en-us') LIMIT 1
  Globalize::Language Load (0.000131)   SELECT * FROM globalize_languages WHERE (globalize_languages.`iso_639_1` = 'en') LIMIT 1
  Globalize::Country Columns (0.000383)   SHOW FIELDS FROM globalize_countries
  Globalize::Country Load (0.000152)   SELECT * FROM globalize_countries WHERE (globalize_countries.`code` = 'us') LIMIT 1

We do a lot of view text translation in our templates, a lot like the following:


<%= "Welcome to the best rails site ever".translate %>

And over time, you’ll end up with tons of .translate calls, each one causing extra log cruft.

If you’re developing a feature that doesn’t require your localization to be enabled, and you want to skim down the globalize log cruft from this type of translation, here’s a simple fix to softly disable the extensions to String

Edit vendor/plugins/globalize/lib/gloablize/localization/core_ext.rb and make it look like the following:


def translate(default = nil, arg = nil, namespace = nil)
  # Locale.translate(self, default, arg, namespace)
  self.to_s
end

You can switch the comment from line #2 to line#3 to re-enable translate easily.

Note: make sure to restart your server to ensure this takes effect.

What to do when you’re not sleeping…

December 14, 2007 | links

… read blog posts, or post about them.  Here are a few of my highlights in the ruby rails  software blog world for the week:

Hello, World

December 12, 2007 | rails

I am alive!

def hello_world

	puts "Boyakasha"

end