Archive for the ‘Ruby’ Category

One-on-One with ActiveRecord One-to-One

{Wednesday, February 1st, 2006}

This is the first in a series of entries exploring ActiveRecord associations through tests. I start with the simplest theoretical form – a one-to-one.

The MySQL schema driving the tests follows.

create table parents (
    id                   int not null auto_increment,
    created_on    datetime not null,
    updated_on   datetime not null,
    lock_version int default 0,
    name             varchar(15) not null,
    primary key (id)
) type=InnoDB;
create table children (
    id                   int not null auto_increment,
    parent_id       int not null,
    created_on    datetime not null,
    updated_on   datetime not null,
    lock_version int default 0,
    name             varchar(15) not null,
    constraint fk_children_parent foreign key (parent_id) references parents(id),
    primary key (id)
) type=InnoDB;

It’s a simple child -> parent relationship, the tables are named accordingly and conform to ActiveRecord’s pluralization demands. I’ve included the columns created_on, updated_on and lock_version mostly to exercise the features that automatically populate these values, but I occasionally put these columns to use in the tests.

The object representation of these tables, the Parent and Child classes, follow.

class Parent < ActiveRecord::Base
    has_one :child, :dependent => true
    has_one :youngest_child, :class_name => “Child”, :order => “created_on DESC”
end
class Child < ActiveRecord::Base
    belongs_to :parent
    belongs_to :mother, :class_name => “Parent”, :foreign_key => “parent_id”, :conditions => “name = ‘Mother’”
end

I’ve included simple uses of the class methods for forming the association (has_one and belongs_to) as well as the odder order clause filtering a parents one-to-many view of its children to a one-to-one and the conditions clause that filters a child’s view of a parent.

In total that’s a staggering 8 lines for a fair amount of features.

Let’s fire up some tests and see what’s happening under the hood.

As a sanity check let’s try and save a Parent.

    def test_create_successfully_persists_parent
        parent = Parent.create(:name => "parent1")
        assert(!parent.new_record?)
        assert_not_nil(parent.id)
        assert_not_nil(Parent.find(parent.id))
    end

Now how about a Child?

    def test_create_without_parent_causes_foreign_key_constraint
        assert_raise(ActiveRecord::StatementInvalid) { Child.create(:name => "child2") }
    end

OK, so the child needs a parent. For those of you with Agile Web Development with Rails this goes against the grain of what’s recommended on page 230, but that approach would work if the FK to Order was nullable (although that makes no conceptual sense).

So lets try building (unpersisted) a Child and assigning that directly to a created (persisted) Parent.

    def test_new_child_assigned_to_parents_child_persists_association
        parent = Parent.create(:name => "parent3")
        parent.child = Child.new(:name => "child3")
        assert(!parent.child.new_record?)
        assert_not_nil(parent.child.id)
        found_parent = Parent.find(parent.id)
        assert_equal(parent.child, found_parent.child)
    end

I find it unusual that assigning an unpersisted Child to a persisted Parent immediately persists the Child. It feels like ‘persistence by touch’ - but as we’ll see shortly persisted objects don’t always persist other objects they touch.

Lets reverse things around - what happens if a created Child persists a new/built Parent?

    def test_assigning_new_parent_to_created_child_does_not_persist_association
        old_parent = Parent.create(:name => "parent4")
        child = Child.create(:name => "child4", :parent => old_parent)
        # To create a child we need an existing parent.
        child.parent = Parent.new(:name => "new_parent4")
        assert(child.parent.new_record?)
        assert_nil(child.parent.id)
        assert_equal(old_parent, Child.find(child.id).parent)
    end

This is a rather unconventional way to attempt to establish the relationship, so ActiveRecord doesn’t support it. It is more logical that the parent exist before the child. In this way, ActiveRecord encourages you to work parent->child when navigating your objects.

Lets now look at the pseudo one-to-one mapping, the order clause.

    def test_parent_allows_multiple_children_and_youngest_is_as_expected
        parent = Parent.create(:name => "parent5")
        oldest_child = Child.create(:name => "many_child_1", :parent => parent)
        sleep(1)
        youngest_child = Child.create(:name => "many_child_2", :parent => parent)
        assert_equal(parent, oldest_child.parent)
        assert_equal(parent, youngest_child.parent)
        # That's not one-to-one!
        assert_equal(youngest_child, parent.youngest_child)
    end

So it’s not pure one-to-one, but virtual one-to-one relationships of this type can prove handy.

Now let’s try the conditions clause.

    def test_conditions_clause_returns_parent_if_parent_matches_condition
        parent = Parent.create(:name => "Mother")
        child = Child.create(:name => "child6", :parent => parent)
        assert_equal(parent, child.mother)
    end

That ends our tour of one-to-one associations.

Before I sign-off, while writing these tests I stumbled across some unusual results usually brought about by newbie syntactic errors. Here’s an example:

    def test_child_reload_is_forced_if_the_argument_is_not_a_boolean
        parent = Parent.create(:name => "parent7")
        parent.child = Child.new(:name => "child7")
        child = parent.child
        assert_equal(parent.child(child), parent.child(true))
        # Both of these methods reload the child.  Ouch.
    end

OK, so occasionally it would be nice to use a static language ;-)

For the ActiveRecord

{Thursday, January 26th, 2006}

True to the mantra of Rails, ActiveRecord makes persistence staggering simple. I still raise an eyebrow to persistence being this simple:

class User < ActiveRecord::Base
end

Yep, that’s all that’s needed to map to a table named users. ActiveRecord instantly generates a heap of methods and a few fields in the class facilitating CRUD operations with the table.

While I’ll defer from presenting the essentials of ActiveRecord (there’s plenty of material out there) here’s a teaser; fields will be automatically generated in the class representing each of the tables columns and methods like find_by_name_and_role (aka instant finders) will also be added, presenting queries in DSL form.

While simplicity is beauty, Java developers cringe at the prospect of using an Active Record approach to persistence, mixing the concerns of persistence with the domain model. Their accustomed to the Data Mapper approach employed by prominent Java ORM tools. Their mantra is all about a clean separation of layers to facilitate testability, maintainability, comprehension and to a lesser degree task separation. From their point of view, if you use that User object in a context where persistence is not needed you’ve got that extra persistence baggage hanging around.

Java developers sink deeper into their seats when they learn how prominent class methods (aka static methods) are in ActiveRecord. Out the window go pluggability and testability in comes a tight domain language (not so bad) and so called ‘unit tests’ that integrate with the database (not so good).

So me being one of those Java developers passionate about clean layer separations and anal enough to blast colleagues for incorrect use of the term ‘unit test’ – sure, I’ve shaken my head and cringed a tad. But for pure simplicity, hats off to the ActiveRecord team.

I guess it’s a contrast in two style of elegance; simplicity vs. conventional OO design. It’s interesting to note that latter is often considered an art taking years to grasp, while the former perhaps requires much less of a mental investment. But in saying that, part of the simplicity of ActiveRecord and Rails is that some of the OO design decisions are made for you. The layers are already clearly defined (although the persistence and domain layers are blurred), the tools already chosen.

But shifting the focus back to ActiveRecord, do I really care about that blurring if persistence of my domain model is a key concern of my application? Heck no. I can still test my domain logic without persisting a thing. I don’t have to configure a mapping layer, I just stick to ActiveRecord’s numerous naming conventions and it does that for me. The benefits outweigh the merit of conventional OO wisdom.

While I’ve been learning the finer details of ActiveRecord and appreciating its simplicity, I’ve been occasionally confused by a feature or design decision made by the ActiveRecord team. Top of the pops are the features added to classes involved in one-to-one, one-to-many and many-to-many associations.

So in my next few blogs I’ll demonstrate these features through tests, something that would have been a handy accompaniment to the explanations of these associations in Agile Web Development with Rails.

Note: For those after a detailed comparison of ActiveRecord and Hibernate, the Hibernate vs. Rails Showdown posted almost a year ago on TheServerSide quickly sketches out the differences.