tag:blog.hasmanythrough.com,2006-02-27:/tag/activerecordhas_many :through - activerecord2012-01-20T11:03:07-08:00tag:blog.hasmanythrough.com,2006-02-27:Article/1402012-01-20T11:03:07-08:002012-01-20T17:33:06-08:00Modularized Association Methods in Rails 3.2Josh Susser<p>Happy Friday! It's Rails 3.2 day! The <a href="http://weblog.rubyonrails.org/2012/1/20/rails-3-2-0-faster-dev-mode-routing-explain-queries-tagged-logger-store">official release announcement</a> mentions a few of the big changes, but I'd like to take a moment to highlight a relatively <a href="https://github.com/rails/rails/pull/3636">small change</a> I was responsible for, one that I hope may make your life a little easier.</p>
<p>From the ActiveRecord <a href="https://github.com/rails/rails/blob/712b0b99a273c49fb4fad48ae61b4ce252ec0562/activerecord/CHANGELOG.md">CHANGELOG</a>:</p>
<pre><code>Generated association methods are created within a separate module to allow overriding and
composition using `super`. For a class named `MyModel`, the module is named
`MyModel::GeneratedFeatureMethods`. It is included into the model class immediately after
the `generated_attributes_methods` module defined in ActiveModel, so association methods
override attribute methods of the same name. *Josh Susser*
</code></pre>
<p>The point of this change is to allow more flexibility in working with associations in your model classes. When you define an association, ActiveRecord automagically generates some methods for you to work with the association. For example, a <code>has_many :patches</code> association generates the methods <code>patches</code> and <code>patches=</code> (and a few others).</p>
<p>Previously, those association methods were inserted directly into your model class. This change moves those methods into their own module which is then included in your model class. Your model gets the same methods through inheritance, but also gets to override those methods and still call them using <code>super</code>. Let's take a look at two ways this makes things easier for you.</p>
<p>Sometimes you want to replace the standard generated association methods. That's always been easy to do simply by defining new methods in your model class. The only wrinkle was that you had to make sure you defined your method <em>after</em> you set up the association, or calling <code>has_many</code> would overwrite your method, since last writer wins. That was usually not a problem, but sometimes plugins or other monkey patching extensions could add an association after your model's class was defined, which wouldn't give you a chance to add your method afterwards. With this change, you don't have to worry about those order dependencies anymore. Since those methods are generated in their own module, the order doesn't matter. This is a pretty small issue all told and I doubt it affected many people, but it's still worth mentioning.</p>
<p>The real reason for this change is being able to compose your own methods with the standard generated methods. Before this change, you'd have to use <code>alias_method_chain</code> or some other fancy footwork to layer your own logic on top of the standard association functionality. Either that or you'd have to somehow duplicate the standard behavior in your own method. Ick. Now you can compose methods using inheritance and <code>super</code>, the way Alan Kay intended you to. Here's the example from the docs:</p>
<pre><code>class Car < ActiveRecord::Base
belongs_to :owner
belongs_to :old_owner
def owner=(new_owner)
self.old_owner = self.owner
super
end
end
</code></pre>
<p>If you're familiar with ActiveRecord it's probably fairly obvious what's going on there, but I'll spell it out for the new kids. When you define the <code>belongs_to :owner</code> association, that generates a standard <code>owner=</code> method, and puts it in the module named <code>Car::GeneratedFeatureMethods</code>, which is the closest ancestor of class <code>Car</code>. If you're curious what this looks like, fire up the rails console and type <code>Car.ancestors</code> to see the class's inheritance chain. (Or use your own app and model, since that will be much easier than making up a new app just to see that one thing.)</p>
<p>In this Car class, you can see that changing owners keeps track of the old owner, so the new owner knows who to call when he can't figure out how to open the trunk. The generated <code>owner=</code> method does a fair amount of stuff including managing counter caches, running callbacks, setting inverse associations, etc. Skipping that could break a number of things, so after saving the old owner, you also want to run the generated method. Since it's in a module that Car inherits from, you only have to call <code>super</code> to get that to run. No muss, no fuss!</p>
<p>One more step towards simpler OOP in Rails! Thanks to my fellow Ruby Rogues <a href="http://about.avdi.org/">Avdi Grimm</a> and <a href="http://blog.grayproductions.net/">James Edward Gray II</a> for complaining about the old state of things enough to motivate me to finally go fix this.</p><p>Happy Friday! It's Rails 3.2 day! The <a href="http://weblog.rubyonrails.org/2012/1/20/rails-3-2-0-faster-dev-mode-routing-explain-queries-tagged-logger-store">official release announcement</a> mentions a few of the big changes, but I'd like to take a moment to highlight a relatively <a href="https://github.com/rails/rails/pull/3636">small change</a> I was responsible for, one that I hope may make your life a little easier.</p>
<p>From the ActiveRecord <a href="https://github.com/rails/rails/blob/712b0b99a273c49fb4fad48ae61b4ce252ec0562/activerecord/CHANGELOG.md">CHANGELOG</a>:</p>
<pre><code>Generated association methods are created within a separate module to allow overriding and
composition using `super`. For a class named `MyModel`, the module is named
`MyModel::GeneratedFeatureMethods`. It is included into the model class immediately after
the `generated_attributes_methods` module defined in ActiveModel, so association methods
override attribute methods of the same name. *Josh Susser*
</code></pre>
<p>The point of this change is to allow more flexibility in working with associations in your model classes. When you define an association, ActiveRecord automagically generates some methods for you to work with the association. For example, a <code>has_many :patches</code> association generates the methods <code>patches</code> and <code>patches=</code> (and a few others).</p>
<p>Previously, those association methods were inserted directly into your model class. This change moves those methods into their own module which is then included in your model class. Your model gets the same methods through inheritance, but also gets to override those methods and still call them using <code>super</code>. Let's take a look at two ways this makes things easier for you.</p>
<p>Sometimes you want to replace the standard generated association methods. That's always been easy to do simply by defining new methods in your model class. The only wrinkle was that you had to make sure you defined your method <em>after</em> you set up the association, or calling <code>has_many</code> would overwrite your method, since last writer wins. That was usually not a problem, but sometimes plugins or other monkey patching extensions could add an association after your model's class was defined, which wouldn't give you a chance to add your method afterwards. With this change, you don't have to worry about those order dependencies anymore. Since those methods are generated in their own module, the order doesn't matter. This is a pretty small issue all told and I doubt it affected many people, but it's still worth mentioning.</p>
<p>The real reason for this change is being able to compose your own methods with the standard generated methods. Before this change, you'd have to use <code>alias_method_chain</code> or some other fancy footwork to layer your own logic on top of the standard association functionality. Either that or you'd have to somehow duplicate the standard behavior in your own method. Ick. Now you can compose methods using inheritance and <code>super</code>, the way Alan Kay intended you to. Here's the example from the docs:</p>
<pre><code>class Car < ActiveRecord::Base
belongs_to :owner
belongs_to :old_owner
def owner=(new_owner)
self.old_owner = self.owner
super
end
end
</code></pre>
<p>If you're familiar with ActiveRecord it's probably fairly obvious what's going on there, but I'll spell it out for the new kids. When you define the <code>belongs_to :owner</code> association, that generates a standard <code>owner=</code> method, and puts it in the module named <code>Car::GeneratedFeatureMethods</code>, which is the closest ancestor of class <code>Car</code>. If you're curious what this looks like, fire up the rails console and type <code>Car.ancestors</code> to see the class's inheritance chain. (Or use your own app and model, since that will be much easier than making up a new app just to see that one thing.)</p>
<p>In this Car class, you can see that changing owners keeps track of the old owner, so the new owner knows who to call when he can't figure out how to open the trunk. The generated <code>owner=</code> method does a fair amount of stuff including managing counter caches, running callbacks, setting inverse associations, etc. Skipping that could break a number of things, so after saving the old owner, you also want to run the generated method. Since it's in a module that Car inherits from, you only have to call <code>super</code> to get that to run. No muss, no fuss!</p>
<p>One more step towards simpler OOP in Rails! Thanks to my fellow Ruby Rogues <a href="http://about.avdi.org/">Avdi Grimm</a> and <a href="http://blog.grayproductions.net/">James Edward Gray II</a> for complaining about the old state of things enough to motivate me to finally go fix this.</p>tag:blog.hasmanythrough.com,2006-02-27:Article/1322011-06-01T22:45:25-07:002011-06-01T22:45:25-07:00Limitless Strings for PostgreSQLJosh Susser<p>We all love ActiveRecord migrations and the sexy DSL for declaring fields. OK, I don't know if you do, but I sure do.
But life isn't perfect (don't get me started), and there's various details that make using the migration DSL a bit of
a pain.</p>
<p>The one thing that has annoyed me for ages is how string (VARCHAR) fields are handled for PostgreSQL. While the standard for SQL
requires you specify a limit for the length, PostgreSQL is more flexible. From the PostgreSQL docs at <a href="http://www.postgresql.org/docs/current/static/datatype-character.html">http://www.postgresql.org/docs/current/static/datatype-character.html</a>:</p>
<blockquote>
<p>If character varying is used without length specifier, the type accepts strings of any size. The latter is a PostgreSQL extension.</p>
</blockquote>
<p>Unless your string field needs a limited length for semantics (like a SSN or ZIP code), it's better not to specify
a limit for VARCHAR fields.</p>
<p>The problem is that ActiveRecord follows the SQL standard and insists on a limit of 255 if none is specified.
So if you define a field thusly:</p>
<pre><code>create_table "users" do |t|
t.string "name"
end
</code></pre>
<p>That is equivalent to</p>
<pre><code>CREATE TABLE users (
name character varying(255)
);
</code></pre>
<p>OK, you think, I know what to do. I'll specify a limit of nil!</p>
<pre><code>create_table "users" do |t|
t.string "name", :limit => nil
end
</code></pre>
<p>But no, that doesn't work. A nil limit is ignored and the default 255 is used anyway. There's a tiny patch for that I
might pursue, but in the mean time there is a pretty simple workaround.</p>
<p>Drop these lines into application.rb (assuming you are on Rails 3.0 or greater) within the Application class definition:</p>
<pre><code>initializer "postgresql.no_default_string_limit" do
ActiveSupport.on_load(:active_record) do
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:string].delete(:limit)
end
end
</code></pre>
<p>That removes the default limit for string fields. Problem solved. And that's even nicer than saying <code>:limit => nil</code> anyway.</p>
<p>And also, this is pretty cool. I got to use one of those fancy ActiveSupport initializer hooks. Let me explain in case you haven't
used one before. What that's doing is waiting until the <code>active_record</code> library is done loading, then running some code to
act on the initialized library. That ensures that the default value is removed after it's been created, but before anyone gets
a chance to use it.</p><p>We all love ActiveRecord migrations and the sexy DSL for declaring fields. OK, I don't know if you do, but I sure do.
But life isn't perfect (don't get me started), and there's various details that make using the migration DSL a bit of
a pain.</p>
<p>The one thing that has annoyed me for ages is how string (VARCHAR) fields are handled for PostgreSQL. While the standard for SQL
requires you specify a limit for the length, PostgreSQL is more flexible. From the PostgreSQL docs at <a href="http://www.postgresql.org/docs/current/static/datatype-character.html">http://www.postgresql.org/docs/current/static/datatype-character.html</a>:</p>
<blockquote>
<p>If character varying is used without length specifier, the type accepts strings of any size. The latter is a PostgreSQL extension.</p>
</blockquote>
<p>Unless your string field needs a limited length for semantics (like a SSN or ZIP code), it's better not to specify
a limit for VARCHAR fields.</p>
<p>The problem is that ActiveRecord follows the SQL standard and insists on a limit of 255 if none is specified.
So if you define a field thusly:</p>
<pre><code>create_table "users" do |t|
t.string "name"
end
</code></pre>
<p>That is equivalent to</p>
<pre><code>CREATE TABLE users (
name character varying(255)
);
</code></pre>
<p>OK, you think, I know what to do. I'll specify a limit of nil!</p>
<pre><code>create_table "users" do |t|
t.string "name", :limit => nil
end
</code></pre>
<p>But no, that doesn't work. A nil limit is ignored and the default 255 is used anyway. There's a tiny patch for that I
might pursue, but in the mean time there is a pretty simple workaround.</p>
<p>Drop these lines into application.rb (assuming you are on Rails 3.0 or greater) within the Application class definition:</p>
<pre><code>initializer "postgresql.no_default_string_limit" do
ActiveSupport.on_load(:active_record) do
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::NATIVE_DATABASE_TYPES[:string].delete(:limit)
end
end
</code></pre>
<p>That removes the default limit for string fields. Problem solved. And that's even nicer than saying <code>:limit => nil</code> anyway.</p>
<p>And also, this is pretty cool. I got to use one of those fancy ActiveSupport initializer hooks. Let me explain in case you haven't
used one before. What that's doing is waiting until the <code>active_record</code> library is done loading, then running some code to
act on the initialized library. That ensures that the default value is removed after it's been created, but before anyone gets
a chance to use it.</p>tag:blog.hasmanythrough.com,2006-02-27:Article/1142008-06-15T10:31:11-07:002008-06-15T10:31:11-07:00An extra special caseJosh Susser<p>A couple of months ago I ran into a weird issue in my current Rails project that made no sense at all. All we did was add a <code>lock_version</code> field to a model to enable optimistic locking and suddenly things started breaking in a big way. After a bit of digging we found it was because ActiveRecord wasn't properly quoting a table name when updating a record with optimistic locking. I submitted a patch for that issue (so it's fixed in Rails 2.1), but lately I've seen a few similar bugs having to do with table name quoting in various circumstances. The amusing thing to me is that all of these bugs have one thing in common: they were uncovered by creating a model named <em>Reference</em>.</p>
<p>At first I thought this was a pretty big coincidence, but after just a moment's thought it seemed pretty obvious. ActiveRecord pluralizes model names to form conventional table names, and <em>references</em> is a <a href="http://dev.mysql.com/doc/mysqld-version-reference/en/mysqld-version-reference-reservedwords-5-1.html">reserved keyword in MYSQL</a>. I guess Reference is a word that makes a good model name, especially if you're building a big data graph and can't think of a more specific relationship name, and it's about the only noun that pluralizes into a reserved keyword that anyone would ever use. In our case, we could have done a rename refactoring to change the model class name to CharacterReference. Instead we used an override and changed the table name to <code>t_references</code>, since that seemed like the least effort for a temporary workaround until the fix got released with Rails 2.1.</p>
<p>All these various issues with table name quoting are indeed bugs in ActiveRecord and should be reported and fixed. (There's also a major reworking of the internals of ActiveRecord in progress that should deal with virtually all of these issues in one fell swoop.) But in the mean time, you might want to avoid using model names that generate SQL reserved words, or just override the table name to something else.</p><p>A couple of months ago I ran into a weird issue in my current Rails project that made no sense at all. All we did was add a <code>lock_version</code> field to a model to enable optimistic locking and suddenly things started breaking in a big way. After a bit of digging we found it was because ActiveRecord wasn't properly quoting a table name when updating a record with optimistic locking. I submitted a patch for that issue (so it's fixed in Rails 2.1), but lately I've seen a few similar bugs having to do with table name quoting in various circumstances. The amusing thing to me is that all of these bugs have one thing in common: they were uncovered by creating a model named <em>Reference</em>.</p>
<p>At first I thought this was a pretty big coincidence, but after just a moment's thought it seemed pretty obvious. ActiveRecord pluralizes model names to form conventional table names, and <em>references</em> is a <a href="http://dev.mysql.com/doc/mysqld-version-reference/en/mysqld-version-reference-reservedwords-5-1.html">reserved keyword in MYSQL</a>. I guess Reference is a word that makes a good model name, especially if you're building a big data graph and can't think of a more specific relationship name, and it's about the only noun that pluralizes into a reserved keyword that anyone would ever use. In our case, we could have done a rename refactoring to change the model class name to CharacterReference. Instead we used an override and changed the table name to <code>t_references</code>, since that seemed like the least effort for a temporary workaround until the fix got released with Rails 2.1.</p>
<p>All these various issues with table name quoting are indeed bugs in ActiveRecord and should be reported and fixed. (There's also a major reworking of the internals of ActiveRecord in progress that should deal with virtually all of these issues in one fell swoop.) But in the mean time, you might want to avoid using model names that generate SQL reserved words, or just override the table name to something else.</p>tag:blog.hasmanythrough.com,2006-02-27:Article/1092008-05-06T08:17:09-07:002008-05-06T13:53:00-07:00A simple alternative to namespaced modelsJosh Susser<p>A project I'm working on now is up to 57 model classes and is still growing. That's a lot of classes - welcome to domain modeling. In my opinion, the number of classes is a fair tradeoff that keeps each class simple enough to understand. In some ways it moves complexity out of the model class internals into the inheritance hierarchy, which is an important part of object-oriented design. I've worked on projects with many more model classes than that too. (Financial applications seem to require a lot of classes to model the complicated workflow and permission systems.)</p>
<p>The place where it starts to get hard to manage is when I look at the file system and see so many files in one directory. My brain usually starts to overload when I see more than a dozen or so classes in a directory. My first inclination is to throw some related class files into a subdirectory. The problem is that the standard way to do that in Rails is to put those models classes in a namespace (module). Rails used to have big problems with namespaced models, mainly with the dependency auto-loading code that finds class files based on the model class name. Most of those problems have been fixed, but there are still some usability issues with namespaced models.</p><p>A project I'm working on now is up to 57 model classes and is still growing. That's a lot of classes - welcome to domain modeling. In my opinion, the number of classes is a fair tradeoff that keeps each class simple enough to understand. In some ways it moves complexity out of the model class internals into the inheritance hierarchy, which is an important part of object-oriented design. I've worked on projects with many more model classes than that too. (Financial applications seem to require a lot of classes to model the complicated workflow and permission systems.)</p>
<p>The place where it starts to get hard to manage is when I look at the file system and see so many files in one directory. My brain usually starts to overload when I see more than a dozen or so classes in a directory. My first inclination is to throw some related class files into a subdirectory. The problem is that the standard way to do that in Rails is to put those models classes in a namespace (module). Rails used to have big problems with namespaced models, mainly with the dependency auto-loading code that finds class files based on the model class name. Most of those problems have been fixed, but there are still some usability issues with namespaced models.</p>
<p>The first problem, and the biggest one, is <em>foxy fixtures</em>. I like foxy fixtures a lot and the feature makes fixtures much easier to work with. But namespaced models just don't work with foxy fixtures. To get them to sort of work, you have to insert calls to Fixtures.identify anywhere you'd use a normal association. You also can't use the fixture helpers in your tests, so you have to do an explicit find, again using Fixtures.identify. It's pretty ugly, and it's actually worse than it used to be with pre-foxy fixtures.</p>
<p>The other problem is setting up associations. You have to use the <code>:class_name</code> option to tell the association how to find the model class. Again, it's a bit ugly, but at least it works well once you tell the association which class to use.</p>
<p>There's also an issue with STI and polymorphic class names being saved un-namespaced, but it looks like that's been fixed on edge now.</p>
<p>So what's a developer to do? The workaround is really quite simple. <em>Don't use namespaces.</em></p>
<p>By default, Rails looks for models only in the app/models directory, and in subdirectories of app/models for namespaced models. But if you just want to put the model class files in a subdirectory, all you have to do is add that subdirectory to the load paths. If you do that, you don't need to put the model class in a namespace.</p>
<p>In your environment.rb file you'll find a commented out line with an example of setting <code>config.load_paths</code> (in the initializer run block). Uncomment and adjust that line, or if it's not there, just add the following line.</p>
<pre><code>config.load_paths += %W( #{RAILS_ROOT}/app/models/role #{RAILS_ROOT}/app/models/user )
</code></pre>
<p>That incantation adds the role and user subdirectories to the load paths collection, letting Rails find the various role and user class files. No namespacing required, and it works with or without inheritance.</p>
<p><strong>UPDATE:</strong> It's true, there are no original ideas. Boy meets girl, boy loses girl, boy gets girl back. Man against nature, man against man, man against himself. Same thing for blog posts. Chris Wanstrath blogged this up last year in <a href="http://errtheblog.com/posts/3-organize-your-models">a post on errtheblog</a>. Still, good ideas like good stories are worth a retelling now and then...</p>tag:blog.hasmanythrough.com,2006-02-27:Article/952007-10-29T00:11:00-07:002008-01-24T00:19:36-08:00Book Review: Pro Active RecordJosh Susser<p>I've been wanting to start doing some book reviews for a while, so here goes. The folks at Apress have been kind enough to send me review copies of a couple books, so I'm going to start with one of them. First up, <em>Pro Active Record</em>.</p>
<p>Title: <a href="http://apress.com/book/view/1590598474">Pro Active Record: Databases with Ruby and Rails</a><br/>
Authors: Kevin Marshall, Chad Pytel, Jon Yurek<br/>
Publisher: <a href="http://apress.com/">Apress</a></p>
<p>My first thought at seeing this book was, "Hey, a book all about ActiveRecord! Cool!" It's nice to finally see a volume dedicated to ActiveRecord, since it's my favorite part of Rails. However, this book ends up being something of a mixed bag. There's a lot of good information in it, and it does a decent job of covering all the basics. There are even some excellent parts here and there. But the book also has some problems, so it's hard for me to give it an unequivocal recommendation. Nevertheless, it does have value and fills a needed spot, so it may be what you're looking for.</p><p>I've been wanting to start doing some book reviews for a while, so here goes. The folks at Apress have been kind enough to send me review copies of a couple books, so I'm going to start with one of them. First up, <em>Pro Active Record</em>.</p>
<p>Title: <a href="http://apress.com/book/view/1590598474">Pro Active Record: Databases with Ruby and Rails</a><br/>
Authors: Kevin Marshall, Chad Pytel, Jon Yurek<br/>
Publisher: <a href="http://apress.com/">Apress</a></p>
<p>My first thought at seeing this book was, "Hey, a book all about ActiveRecord! Cool!" It's nice to finally see a volume dedicated to ActiveRecord, since it's my favorite part of Rails. However, this book ends up being something of a mixed bag. There's a lot of good information in it, and it does a decent job of covering all the basics. There are even some excellent parts here and there. But the book also has some problems, so it's hard for me to give it an unequivocal recommendation. Nevertheless, it does have value and fills a needed spot, so it may be what you're looking for.</p>
<h2>The Good</h2>
<p>Let's start by taking a look at what the book provides. As I said, it covers all the basics.</p>
<p><em>Pro Active Record</em> starts with a good introduction to ActiveRecord, going over a bit of the history, how to install it, and shows a small example program. The best part of this chapter is the section that shows all the configuration settings for the different database adapters. It's nice to see all that in one place.</p>
<p>Chapter 2 digs into the foundation of ActiveRecord and how it maps objects to SQL. It covers CRUD operations, transactions, and locking. I like how it is full of examples that show an AR API call and the resulting SQL. </p>
<p>Chapter 3 is entitled Setting Up Your Database, but it's mainly about using migrations. It does however start with a good summary of the main ActiveRecord conventions for table naming and structure. It would be even better to see a summary of all the magic fields in one place (e.g. features like counter cache and optimistic locking).</p>
<p>Chapter 4 is where things start to get interesting. This chapter gets into the meat and potatoes (or granola and tofu for us Californians) of ActiveRecord. The three main topics are Callbacks (what I call the life-cycle features), Associations, and Validations. This is where you start to get a feel for what using ActiveRecord is like. Every major feature comes with some code examples, which is a good thing, though as contrived examples go some of them are pretty uninspired. Probably the biggest weakness in terms of content is that the section on associations doesn't show more than the most basic use cases. ActiveRecord associations are a rich topic and could easily cover a whole chapter, if not several. But for a beginner's book on ActiveRecord, this is probably enough. Though not seeing any mention of association extensions made me sad.</p>
<p>Chapter 5 is like the whole book in microcosm. Some good stuff, some stupid stuff, but not a waste of time. It covers some "bonus features" of ActiveRecord. The section on observers was an interesting start, but the topic deserves more than just one page. On the other hand, there are thirteen whole pages wasted covering the List, Tree and Nested Set features that everyone has known for ages were slated to be pulled from ActiveRecord (and were just extracted and turned into plugins). The section on aggregations is adequate, but the examples obfuscate more than they reveal. And yet, the chapter recovers and even shines with one of the best discussions of metaprogramming in Ruby and ActiveRecord that I've yet to read. Really, I'd almost say it's worth it to get this book just for the Extending Active Record section of this chapter. The thing that makes it work is how they show a problem, then an evolving series of solutions that do similar things but with more sophistication as they progress. At each step there is a discussion of the advantages and drawbacks of each approach. If the whole book was up to this standard, it would be a no-brainer to recommend it to everyone.</p>
<p>Chapter 6 introduces ActiveRecord testing and debugging. It includes a rather light discussion of writing tests and the basics of using fixtures. Most of the testing section covers the assertion methods in <code>Test::Unit</code>. While this book's focus isn't testing, it's a shame that there weren't at least a few good examples or some deeper how-to discussion. The world really needs a good book on testing Rails (so get off your butts, guys - you know who you are!). There follows a section that lists and describes the ActiveRecord exception classes. Finally there's a section on debugging tips, which is mainly about using logging. I was disappointed the chapter didn't include any mention of using irb (or the Rails console) for experimenting with code or debugging, and that there was no mention of using the Ruby debugger.</p>
<p>Chapter 7 addresses how to work with legacy databases. This is one of the trickiest things to do with ActiveRecord, because you don't get to go with the flow and have to do a lot more configuration than normal. The first part of the chapter describes the various configuration methods ActiveRecord provides to override its default conventions, and it's great to see them listed in one place like that. Then there is a slow dive into the bowels of ActiveRecord, covering <code>find_by_sql</code> and some of the low-level database APIs. There's also a section on import/export of your favorite interchange formats (xml, yaml, csv). What is missing is any discussion of how to map typical un-Rails-ish database usage patterns into ActiveRecord. For example, how to deal with non-integer primary keys, composite keys, foreign key constraints, enums, etc. That would have made this chapter a real winner.</p>
<p>Chapter 8 is the catch-all for everything that didn't fit in the other chapters. It has a section on reading the ActiveRecord source code that provides a roadmap for finding your way around. There's a decent discussion of various alternatives to ActiveRecord (other Ruby ORM packages), though it misses some recent additions to the field like Sequel and DataMapper. The FAQ section is pretty good, and provided a number of answers that were missing from previous chapters. One answer even includes some code from my own blog (thanks for giving the proper attribution!).</p>
<p>Finally, there is an Appendix that documents the core ActiveRecord API.</p>
<p>Looking back, that's a pretty good chunk of information on ActiveRecord, and probably enough to get anyone started. It's also very readable (though all the anecdotes about Kevin got to be a bit much). I thought the organization was good and followed a logical progression.</p>
<h2>The Bad (and The Ugly, because you expected it)</h2>
<p>Unfortunately, for all the book provides, there are enough problems with it that I have to wonder if it missed out on some much-needed editing.</p>
<p>To begin, this book doesn't say anywhere what version of ActiveRecord (or Rails) it is documenting. It makes me wonder if the authors were all on the same page here, because while most places seem to be working with Rails 1.2, some look like they are talking about Rails 2.0 code. Since Rails 2.0 is being released any day now, that's an important issue. There have been a lot of changes to ActiveRecord since Rails 1.2, and readers may be pretty confused about the applicability of the information. If nothing else, this book needs an update to get it in sync with Rails 2.0.</p>
<p>Next off, the book doesn't come with any downloadable example code. I've reached the point where I think that is a basic requirement for a software book. I think it's important to provide code that you can play with, or can use to copy/paste into your own code. It's also possible to provide a lot more code in the example projects, since putting all that code in the book is usually too much. And maybe most of all, having real code from which you grab excerpts for the book means that you can have confidence the code actually works. The code examples in <em>Pro Active Record</em> were obviously written in a word processor and never executed, since many of them wouldn't work correctly or even compile. I've messed up doing that in my own blog postings enough that I've learned that lesson.</p>
<p>One of the problems that affects the code samples disproportionately is that the book is riddled with typos and capitalization errors. It looks like Word's auto-capitalization messed a lot of stuff up badly, especially the example code. Unfortunately, when you're showing source code for a language that is case-sensitive, what might otherwise be a minor annoyance can become a significant issue. This is the sort of thing that is so easily prevented that I don't understand how it can happen.</p>
<p>More seriously though, there are some glaring factual errors. For example, Chapter 2 states that <code>ActiveRecord#update_attributes</code> doesn't run validations on the model. But it's a totally common use case to use <code>update_attributes</code> like so:</p>
<pre><code>def update
@user = User.find(params[:id])
if @user.update_attributes(params[:user])
redirect_to user_path(@user)
else
render :action => "edit"
end
end
</code></pre>
<p>That's even how it's done in the standard scaffolding now. If <code>update_attributes</code> didn't run validations, that code wouldn't work. Now, the <code>update_attribute</code> (note the singular) method does <em>not</em> run validations, but it is used quite differently from the plural form method. I could see how a casual reader of the ActiveRecord API might get confused, but not someone who had ever written a typical CRUD Rails application. Oddly enough, they get it right in the API summary appendix. Ah, the joys of collaborative authorship.</p>
<p>The book also states that there is a standard <code>db:bootstrap</code> rake task in Rails, which doesn't exist even in Rails 2.0 (though Rick Olson and others have rolled their own). It says you can explicitly delete object instances in Ruby, something that is impossible in a garbage collected language like Ruby. And it says the :id attribute is optional in <code>has_and_belongs_to_many</code>, where it will in reality break certain things if you include it. They also claim that it doesn't matter which ActiveRecord class you call <code>find_by_sql</code> on, when in actuality the class determines what kind of model object is returned from the call. Those are just some examples I caught; there are plenty of others.</p>
<p>This may all sound nit-picky, but I think if you can't have confidence in the accuracy of a reference book, then you can't truly rely on any of the information in it. Fortunately for the authors, none of these detail errors are major problems and they could be corrected with a good technical edit of the book. Perhaps they can be corrected in the ebook without waiting for a second printing</p>
<h2>Bottom Line</h2>
<p>If you're looking to get started with ActiveRecord, <em>Pro Active Record</em> is a pretty good place to start. It has everything you need to get your bearings and start your journey. Be advised, however, that it's not going to take you much past the start of that journey. And at least with the current edition, you'll need to double check the directions along the way.</p>
<h2>Reviewing the Review</h2>
<p>Since this was my first book review here, I'd like to get some feedback. What did you all think of it? Useful? Too critical? Too short? Too long? Not enough pie?</p>
<p>I'm intending to do more of these (maybe one every month or two), so tell me what works and what doesn't.</p>