using model methods for repetitive finds

i am still fairly new to rails and am trying to write methods in the
model for finds that i will need to use over and over.

in my Task model i have:

def self.most_recent_tasks
  find(:all, :order => 'updated_at DESC', :limit => 10)
end

this works great when i try something like Task.most_recent_tasks, but
is there a way to rewrite it so that i can use this method on a
collection of tasks too? i don't know if i need to write a separate
method to be able to do something like:

josh.tasks.most_recent_tasks

or if there is a way to write one that works for both.

After playing around with it a little more I am using this:

def self.most_recent(tasks = self)
  tasks.find(:all, :order => 'updated_at DESC', :limit => 10)
end

now I can use something like:

Task.most_recent()

AND

Task.most_recent(josh.tasks)

although this works, i would really like to hear from other developers
and see if this would be the recommended way of doing something like
this.

See the association extensions section of the API near the top of this
page. Should let you do exactly what you're wanting.
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

Something like..

Class Josh < ActiveRecord::Base
    has_many :tasks do
       def most_recent(n)
          Find.tasks(:all, :order => 'updated_at DESC', :limit => n)
       end
    end
end

josh.tasks.most_recent(42) => array of 42 most recently updated tasks.

Josh wrote:

Hello Josh,

i am still fairly new to rails and am trying to write methods in the
model for finds that i will need to use over and over.

in my Task model i have:

def self.most_recent_tasks
  find(:all, :order => 'updated_at DESC', :limit => 10)
end

this works great when i try something like Task.most_recent_tasks, but
is there a way to rewrite it so that i can use this method on a
collection of tasks too? i don't know if i need to write a separate
method to be able to do something like:

josh.tasks.most_recent_tasks

or if there is a way to write one that works for both.

First solution : you can add another association to your model
like this.

class Person < AR::B
  has_many :tasks
  has_many :most_recent_tasks, :class_name => 'Task',
                   order => 'updated_at DESC', :limit => 10
end

So you could have josh.tasks and josh.most_recent_tasks

Second solution, you can use association extension, that
is, you define additional methods in a block for your has_many
association :

class Person < AR::B
  has_many :tasks do
    def most_recent_tasks(n = 10)
      find :all, :order => 'updated_at DESC', :limit => n
    end
  end
end

josh.tasks.most_recent_tasks
or josh.tasks.most_recent_tasks(20)

Note that you can put your definition in a module :

class Person < AR::B
  has_many :tasks, :extend => Stuff
end

and most_recent_tasks is in module Stuff.

   -- Jean-François.

Thanks for the replies.

Is there any specific reason why it would be better to keep this in the
User model instead of just having it in my Task model?

One of the reasons I was trying to do it this way was so I could just
put:

"Task.most_recent" and find ALL of the most recent tasks and then still
use the same method for individual users and put
"Task.most_recent(josh.tasks)"

I see how the methods you have posted would help a lot if I was only
finding a collection of tasks relating to the user, but with mine, i
could pass any collection i wanted or just not give it any paramters to
search the entire database.

Hi again Josh,

Is there any specific reason why it would be better to keep this in the
User model instead of just having it in my Task model?

One of the reasons I was trying to do it this way was so I could just
put:

"Task.most_recent" and find ALL of the most recent tasks
and then still use the same method for individual users and put
"Task.most_recent(josh.tasks)"

but your definition :

def self.most_recent(tasks = self)
tasks.find(:all, :order => 'updated_at DESC', :limit => 10)
end

works but is quite ugly, in the sense it's not the Ruby way.

If you want to share your AR query, put it in a module, then mix
it in the classes you want :

module Stuff
  def most_recent(n = 10)
    find :all, :order => 'updated_at DESC', :limit => n
  end
end

class User < AR::B
   class << self
     include Stuff
   end

  has_many :tasks, :extend => Stuff
end

class Task
  class << self
    include Stuff
  end
end

class Comment < AR::B
  class << self
    include Stuff
  end
end

Then you can do Comment.most_recent, josh.tasks.most_recent
Task.most_recent, Task.most_recent(20)

Modules are the way to make it DRY.

I see how the methods you have posted would help a lot
if I was only finding a collection of tasks relating to the user,
but with mine, i could pass any collection i wanted or just
not give it any paramters to search the entire database.

and with your method, sth like Task.most_recent(Comment)
would return the last recent comments, but since Comment model
is not necessarily related to Task model, this is quite ugly for me !

Regards,

    -- Jean-François.

beautiful! that's exactly what i'm looking for! thanks

ok i'm sorry for this one, but where is the best place to put modules
so that i can do this?

This module is model-related so app/models is OK for me.

   -- Jean-François.