a work on process

Viewing posts tagged: ActiveRecord

Book Review: Pro ActiveRecord

17 October 2007 (9:53 am)

By James Stewart
Filed under: Book Reviews
Tagged: , , , , ,

Right at the start of Pro Active Record the authors address a possible problem some may have with it: that there’s not enough in Active Record to warrant a full book. They point out that the basics are well covered as sections elsewhere but that this is the first book to really dig into working with legacy schema and other ‘advanced’ uses. That’s fair enough, but after reading the book I am still left with the question of why, then, they dedicate the first half to covering ActiveRecord’s most basic concepts?

Judging from postings on the rails email list, there’s certainly a lot of confusion about ActiveRecord, associations, observers, how to work with legacy table names and primary keys, and so on. But in a book with a title prefix of “Pro” I was expecting to jump straight into the nitty gritty of topics like compound/composite primary keys and performance tuning, probably with some real world examples, and maybe with a serious exploration of AR’s internals. As it is, such topics only get a quick treatment in the final chapter (the compound/composite primary keys section is a paragraph referring users to http://compositekeys.rubyforge.org).

It’s almost always instructive reading other developers’ code and it would be unfair to claim that I didn’t spot a couple of tips that may prove useful, but they were passing things. And sometimes I found myself wondering what happened to the tech review process, particularly in the coverage of the has_one association, where not only is the variable naming confusing, but they seem to be calling the each method on a single ActiveRecord instance.

I’m left wondering what the audience is for this book. The title and blurbs suggest it’s pitched at people who want to go deeper into ActiveRecord than they have before, but the content is better suited for someone with some database experience who wants to pick up ActiveRecord to write some scripts. As it is, if you’ve worked with ActiveRecord before your time will be better spent writing plugins and exploring the internals for yourself, and if you’ve not you’ll get most of the same material from a decent Rails book and some time exploring.

Disclaimer: I was sent a copy of this book for review by the publisher. You can find it at apress, amazon US, amazon UK and all sorts of other places.

Recommend this post:

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

 

The Joy of Separation

28 March 2007 (3:58 pm)

By James Stewart
Filed under: Notes
Tagged: , , ,

The thing I probably enjoy most about working with Rails is that it makes it easier to put logic in the right place than not to. To get back in the swing of blogging after a light couple of weeks, I thought it might be useful to run through an example of how that’s worked out for me recently.

From time to time I’ve been working on an application that will contain a database of musicians. To be able to add new musicians quickly I’ve been using:

new_artist = Artist.find_or_create_by_name('My New Artist')

So far, that’s worked fine. But as any self-respecting music fan knows there are always variations in how artists are referred to. What do we do if we need to represent, say, Black Rebel Motorcycle Club, aka BRMC, aka B.R.M.C. Or, even worse, Guns-n-Roses, and their many variations.

Thankfully, there’s musicbrainz which can provide a list of aliases. So once I have a table of aliases linked to artists, I can simply add a quick convenience method to the Artist model and call:

new_artist = Artist.find_or_create_by_name_or_aliases('My New Artist')

It looks a little like:

def self.find_or_create_by_name_and_aliases(name)
  artist = Artist.find_by_name(name)
 
  if artist.nil?
    aliased = Alias.find_by_alias(name)
    artist = aliased.artist if aliased
  end
 
  artist ||= Artist.create(:name => name)
end

In reality, it’s a little more work than that as the alias data has to be imported, and for performance reasons it’s probably best not to hold up every insert of a new artist to give us time to hit musicbrainz. But it’s pretty easy to create the artist up front and then either trigger a backgroundrb task or leave the cleanup for a rake task. I added another method on my Artist model to be used when cleaning out duplicates. It runs through the relationships and reassigns them (in reality, I’d do a bit more testing to make sure I’m not creating duplicated relationships), creates the alias, and then gets rid of the duplicate:

def convert_to_alias(duplicate)
  duplicate.appearances.each do |appearance|
    appearance.update_attributes(:artist_id => self.id)
  end
 
  artist.aliases.create(:alias => duplicate.name)
  duplicate.destroy
end

So the domain remains neatly self-contained and the controllers or other interfaces need only know a little bit about it. The code is easy to test, and easy to work with. And it’s less work than doing it any other way. For a little more on that way of working I’d highly recommend Jamis Buck’s post Skinny Controller, Fat Model.

Recommend this post:

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

 

:select and :include in ActiveRecord queries

20 March 2007 (3:40 am)

By James Stewart
Filed under: Notes
Tagged: , ,

Along with Bill Eisenhauer, I’ve been digging into what it would take to fix the problem I found with GeoKit, that it wouldn’t support :include queries properly. The explanation has seen us going deep into the internals of ActiveRecord to discover, as others have before, that the :select parameter (which GeoKit uses to build its distance column in the query) and the :include parameter don’t play nicely together.

The reason for that is that Rails employs special methods to build the select parameters when there’s a :include in the query (ie. when you’re joining with other tables and eager-loading them) and currently ignores :select along the way. GeoKit would ordinarily produce SQL along the lines of:

SELECT *, 
	SQRT(
		POW(69.1*(42.962927-latitude), 2) +
		POW(42.0590342306803*(-85.637179-longitude), 2)
	) AS distance 
FROM bus_stops 
ORDER BY distance ASC LIMIT 1

but with a :include on a has_many relationship, we don’t get that distance column and so most of the queries will fail.

There are a couple of patches on the rails trac that seek to clean that up, but so far neither has been adopted. I’m sure the GeoKit developers would appreciate any input on workarounds.

Recommend this post:

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

 

I’ve been procrastinating on getting my first Ruby on Rails plugin (first anyone else might be interested in, at least) out the door, mainly due to some configuration problems with Subversion, but finally I have that all sorted out and the plugin ready to go.

loads_from_amazon is an ActiveRecord Mixin that works with the Ruby/Amazon library to let you populate an ActiveRecord object with the details of a product listed on amazon. In the README I use the following example:

class Book < ActiveRecord::Base
  acts_as_amazon
  maps_to_amazon_attribute :authorlist => 'authors', :combine => ';'
  maps_to_amazon_attribute :title => 'product_name'
  maps_to_amazon_attribute :isbn => 'asin'
  maps_to_amazon_attribute :publisher => 'manufacturer'
  maps_to_amazon_attribute :pubdate => 'release_date'
end
 
@book = Book.load_from_amazon(isbn) # Loads but does not save
@otherbook = Book.load_from_amazon!(other_isbn) # Loads data and saves to database

So feel free to grab it from svn (http://projects.jystewart.net/svn/rails/loads_from_amazon/trunk) and let me know what you think.

Recommend this post:

[Slashdot] [Digg] [Reddit] [del.icio.us] [Facebook] [Technorati] [Google] [StumbleUpon]

 
« Previous Page