Skip to main content

Getting on with Ruby on Rails

I'm just about to deploy the first internal application written in Ruby on Rails for Framestore. This will hopefully mark a nice change in my group towards using frameworks such as RoR and Django for database-driven web applications.

The application in this case is the primary archiving tool for the company's long-term storage of assets. The code is a mixture of Perl for the scanning and writing data to DTF2 or SAIT tapes, and a web interface for browsing and searching on the data, and generating requests for the Data Operations team on behalf of the various artists and producers working on internal projects.

This web interface was previously written in PHP, in a relatively nice way, but utilised no frameworks or modules other than the Pear MySQL class. It was also rapidly growing out of control, as the database grew in size, and the complexity of queries was growing with the functionality of the application.

Since we had the luxury of a ground-up rewrite of the Perl code over the past month or so, I've instigated a schema change to bring the database in line with ActiveRecord's expectations, which has made a huge difference to the ease in which I can sketch out the Rails code. The only odd problem I had was related to tables which logically needed no primary key, as they represented the 'zero-or-one' part of a 'one-to-zero-or-one' table mapping. In my case, a file may be part of a sequence, but it may not, so there are file entries, along with optional sequence entries.

Within the SequenceEntry model, I had to set the primary key to be the same as the foreign key, as ActiveRecord demands some sort of unique way of distinguishing records.

  class SequenceEntry < ActiveRecord::Base
      belongs_to ::file_entry
      set_primary_key :file_entry_id
  end

One of the more annoying aspects of ActiveRecord I can't seem to get away from is the double SQL query you seem to need when you're looking at pagination of complex queries. I have a block which looks like:

file_count = FileEntry.count_by_sql("SELECT COUNT(*) FROM file_entries
                 LEFT JOIN directory_entries ON directory_entries.id =
                           file_entries.directory_entry_id
                 LEFT JOIN runs ON directory_entries.run_id = runs.id
                 LEFT JOIN tapes ON runs.tape_id = tapes.id
                 WHERE filename LIKE '#{search_pattern}'
                 AND tapes.barcode = '#{barcode}'")

which returns the number of entries I will be fetching, and then the subsequent find() query, but since each query takes of the order of 20 seconds, it's making the user wait double the amount of time that should be necessary.

The only other thing I'm finding a little difficult is where to place the code that cleans up form input variables before they are passed to the find() statements. It may either be a case of using a helper object, or possibly the prepend_before_filter, but the DRY principle has got me seeking an elegant cross-method solution.