Using a class method with or without named scopes

Posted by Brian in Howto, Rails (August 17th, 2009)

Here’s a quick one, building off of the previous post on Geokit and Named Scopes.

If you have defined a class method you want to run which operates on a collection of records, you can share it with named scopes.

class Project < ActiveRecord::Base
  validates_presence_of :name
  named_scope :completed, :conditions => {:completed => true}

  # returns array of names for projects
  def self.names
    scope = self.scoped({})
    scope.all.collect{|record| record.name}
  end
end

This allows you to do

@project_names = Project.names

Or

@project_names = Project.completed.names

If you return the scope, you can continue chaining. If you don’t return the scope, then you can’t. This comes in handy though for creating a method that returns other objects than the object you’re working with. Here’s a completely contrived example:

Say you want to find the actual hours you’ve spent on all your completed projects:

class Project < ActiveRecord::Base
  named_scope :completed, :conditions => {:completed => true}
  
  def self.tasks
    tasks = []
    self.scoped({}).all.each do |project|
      tasks += (project.tasks)
    end
    tasks
  end
  
  def self.actual_hours
    hours = 0
    tasks.each do |task|
      hours += task.hours
    end
    hours
  end

Grab the value like this:

actual_hours = Project.completed.actual_hours

Now, of course there are other ways to do this, and this particular method will result in a lot of queries. If you were doing this hours counting in a real app, you’d be better off using SQL. This example illustrates the point though. Now go build something more awesome.

Leave a reply

:mrgreen: :neutral: :twisted: :shock: :smile: :???: :cool: :evil: :grin: :oops: :razz: :roll: :wink: :cry: :eek: :lol: :mad: :sad: