Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails model class method for collection of objects

I'm having trouble writing class methods to use on collections of ActiveRecord objects. I've run into this issue twice in the last couple of hours, and it seems like a simple problem, so I know I'm missing something, but I haven't been able to find answers elsewhere.

Example:

class Order < ActiveRecord::Base

  belongs_to :customer

  scope :month, -> { where('order_date > ?', DateTime.now.beginning_of_month.utc) }

  def self.first_order_count
    map(&:first_for_customer?).count(true)
  end

  def first_for_customer?
    self == customer.orders.first
    # this self == bit seems awkward, but that's a separate question...
  end

end

If I call Order.month.first_order_count, I get NoMethodError: undefined method 'map' for #<Class:...

As far as I know, that's because map can't be called directly on Order, but needs an Enumerable object instead. If I call Order.year.map(&:first_for_customer?).count(true), I get the desired result.

What's the right way to write methods to use on a collection of ActiveRecord objects, but not on the class directly?

like image 283
elements Avatar asked Jul 01 '15 07:07

elements


People also ask

What are class methods in Rails?

Class Methods are the methods that are defined inside the class, public class methods can be accessed with the help of objects. The method is marked as private by default, when a method is defined outside of the class definition.

What is Activemodel?

Active Model is a library containing various modules used in developing classes that need some features present on Active Record.

What is the difference between scope and class method rails?

Scopes are just class methods. Internally Active Record converts a scope into a class method. "There is no difference between them" or “it is a matter of taste”.

Does Ruby have class methods?

There are two standard approaches for defining class method in Ruby. The first one is the “def self. method” (let's call it Style #1), and the second one is the “class << self” (let's call it Style #2). Both of them have pros and cons.


2 Answers

In your case, you can use a trick in this case.

def self.first_order_count
   all.map(&:first_for_customer?).count(true)
end

Will do the trick, without any other problems, this way if you concatenate this method on where clause you still get results from that where, this way you get what you need if you call this method directly on Order.

like image 145
Nermin Avatar answered Oct 05 '22 11:10

Nermin


ActiveRecord collections are usually manipulated using scopes, with the benefits of being able to chain them and let the database do the heavy lifting. If you must manage it in Ruby, you can start with all.

def self.first_order_count
  all.map(&:first_for_customer?).count(true)
end

What are you trying to achieve with your code though?

like image 26
fylooi Avatar answered Oct 05 '22 12:10

fylooi