views:

79

answers:

2

I've add a custom sort to an ActiveRecord model by defining a method like this:

class MyClass < ActiveRecord::Base
  belongs_to :parent_model #this would be the many in a has_many relationship

  def <=>(other)
    self.att <=> other.att
  end
end

Suffice it to say, the logic actually being employed in the comparison is a bit more complex than this example, and is not something that can be accomplished with SQL.

Because this class is only used as nested fields in an encompassing model, there doesn't seem to be a straight-forward place to sort the result set. In the controller, :my_class is a part of the include for eager loading, but I don't do anything else with the object array until the view (when I do form.fields_for :my_class). What I really want is to be able to do the equivalent of default_scope using my logical sort, but I don't think that's possible. Right now, my only option seems to be adding an extra line in the controller just to do the sort on this result, but that doesn't seem like it's the right thing to do.

Is there something more elegant I'm missing here?

A: 

The hands down best way to do this is to set up your database for SQL sorting and give an :order option in your scopes/finds. Doing the sort in the controller is going to be very costly once the number of objects loaded passes a certain threshold. Especially if you're trying to limit the number of records listed.

If the sort really is more complicated than sort this column than that one. You should consider adding a sort key column to the database. You've clearly worked out some method of ordering things, usually that involves comparing two values derived from the state of the object. Just store that value in the database in the new sort_key column with a before_create/save callback and order results by that.

Task Example:

Ordered by priority, but records where due date is a Friday, are listed before all others. Uses a sort_key column

class Task < ActiveRecord::Base
  def calculate_sort_key
    key = due_date.wday == 5 ? "0" : "1"
    key += prioirty.to_s
    self.sort_key = key.to_i
  end

  before_save :calculate_sort_key
  default_scope :order => "sort_key"
end
EmFi
A: 

I haven't been able to test if this would work, but you could try an anonymous association module, like this:

class ParentModel < ActiveRecord::Base
  has_many :my_class do
    # Sort the result set...  
  end
end
John Topley
This method would be much easier and DRY than sorting in the controller. But it has the same problems of sorting in the controller. Namely computation time/memory usage on large sets, and complications w.r.t. limiting the number of rows selected. Then again it's not likely to be a problem in certain relationships.
EmFi
I did try several variations of this, which, given the circumstances of the particular situation, is more what I was looking for. But, I couldn't make the leap of how to execute the block by default. I haven't worked with anonymous association block enough to really understand -- beyond the obvious-- how they work, I suppose. Thank you, though, for the suggestion.