Rails service object returning 'No Method error'

1.9k views Asked by At

To begin: This is my first attempt at getting business logic out of the Model/controller space. Here is some initial logic I'm trying to abstract. The path is app/services/Date_calc.rb.

class Date_calc
  require User
  require Report

  def months(range)
    User.first.reports.order("report_month desc").limit(range).pluck(:report_month)
  end
end

In my application I have two models, User and Reports. User has_many Reports. The reports table has a field called report_month.

Calling Date_calc.months(6) in the Rails console returns: TypeError: no implicit conversion of Class into String.

My intended response was an array of dates, e.g. ["01/01/2013", "01/02/2013", "01/03/2013", ... ].

I'm not really sure what I'm doing wrong here.

2

There are 2 answers

2
msergeant On BEST ANSWER

In addition to the require lines lacking quote marks, you are calling an instance method on a class. You either need to instantiate the Date_calc like this

months = Date_calc.new.months(6)

or make it a class method like this

def self.months(range)...
3
Jordan Running On

The problem is that require expects a String (e.g. require "path_to_module") but you're giving it a Class:

require User
require Report

If this is running in Rails, then Rails will autoload both of these classes. You don't need to do require at all.

Edit: After you've removed those lines, Date_calc.months(6) is still going to give you a NoMethodError, because months is an instance method, but you're trying to call it as though it's a class method. You either need to call it on an instance of the Date_calc class, like this:

Date_calc.new.months(6)

Alternatively, you could define it as a class method by doing def self.months(range) instead of def months(range), which would allow you to call Date_calc.months(6). But you might want to think about whether or not Date_calc should actually be a class, or if it should be a module.

It should be a class if you want to have multiple instances of it, like you would want an instance of a User or instance of a Report. "User" and "Report" are both nouns, because the class represents a thing that we might want to have more than one of. Does the sentence "I want to create a Date_calc" make sense? How about the sentence "I want to create two Date_calcs"? If you said yes to both, then a class makes sense.

If, however, you just want something to put some related methods in, but that doesn't represent a thing, then you might want to use a module instead. In Rails, for example, the Rails object is not a class, it's a module. It doesn't make sense to say "I want to create a Rails," much less "I want to create two Rails." Rails is just a module used to group related methods together. If Date_calc fits that description, then it makes more sense as a module:

module Date_calc
  def self.months(range)
    # ...
  end
end

# and then...
Date_calc.months(6)

As you can see, we use def self.months instead of def months, because we want to be able to call the method on the module itself.