Ruby: Can I use instance methods inside a class method?
Asked Answered
E

3

17

I have a class that contains this class method:

def self.get_event_record(row, participant)
  event = Event.where(
      :participant_id   => participant.id,
      :event_type_code  => row[:event_type],
      :event_start_date => self.format_date(row[:event_start_date])
  ).first

  event = Event.new(
      :participant_id   => participant.id,
      :event_type_code  => row[:event_type],
      :event_start_date => self.format_date(row[:event_start_date])
  ) if event.blank?

  event
end

And I also have, in the same class, an instance method:

def format_date(date)
  parsed_date = date.split('/')

  # if month or day are single digit, make them double digit with a leading zero
  if parsed_date[0].split("").size == 1
    parsed_date[0].insert(0, '0')
  end
  if parsed_date[1].split("").size == 1
    parsed_date[1].insert(0, '0')
  end

  parsed_date[2].insert(0, '20')

  formatted_date = parsed_date.rotate(-1).join("-")
  formatted_date
end

I'm getting an 'undefined method' error for #format_date. (I tried it without the self in front, at first). Can you not use instance methods in class methods of the same class?

Ernestoernestus answered 28/6, 2012 at 13:3 Comment(6)
Why is format_date an instance method? It is not using anything from the instance.Connotative
Of course, you can't. In order to call an instance method you need an instance of your class.Bobbiebobbin
@Connotative Good point. I made it an instance method because I thought that making it a class method would imply it was for public use, when the case is it's only for internal useErnestoernestus
@Bobbiebobbin Of course, you're right. Like in my other comments, my intent was to just have a method that "helps" a class method in its class. Maybe making it a class method doesn't necessarily imply its for use outside the class, or maybe I need to take a different tact altogetherErnestoernestus
@steve_gallagher, you can make your class method private like that.Bobbiebobbin
Thanks to all for helping me not make an ass out of myself at work.Ernestoernestus
C
31

Short answer is no, you cannot use instance methods of a class inside a class method unless you have something like:

class A
  def instance_method
    # do stuff
  end

  def self.class_method
     a = A.new
     a.instance_method
  end
end

But as far as I can see, format_date does not have to be an instance method. So write format_date like

def self.format_date(date)
   # do stuff
end
Connotative answered 28/6, 2012 at 13:8 Comment(2)
I have a instance method because is called from a validation, and a class method than was fine to be able to call the instance method, to be DRYRodolforodolph
Then do something like this class A; def instance_method; self.class.class_method; end; def self.class_method; -- do stuff -- ; end; endConnotative
H
7

Just create class method

def self.format_date (..)
  ...
end

And if u need instance method, delegate it to class method

def format_date *args
  self.class.format_date *args
end

And i don't think that it is good idea to call instance methods from class scope

Hyperphagia answered 28/6, 2012 at 13:7 Comment(3)
class.format_args? Why are you calling a class method on the jeyword class?Darya
That doesn't mean that calling it on the keyword class will do anything but have a syntax error. You need self.Darya
Now I can happily give you +1, cause I like your answer.Darya
K
3

You could do YourClassName.new.format_date(your_date), although I think it's pretty clear you should be restructuring your code - this method probably doesn't belong on an instance. Why don't you extend the Date Class, or make format_date a class method on the class you are using?

EDIT: Here are a few other things to think about with your code:

  • Your whole format_date method goes to a lot of lengths to manipulate dates as strings. Why not use Ruby's Date Class? Using Date.parse or Date.strptime or even "01/01/2001".to_date might be useful depending on your locale
  • Consider extending the String class for your method, if you really need to make your own method:

    class String
      def to_friendly_formatted_date
        Date.strptime(self, "%d/%m/%y")
      end
    end
    "01/08/09".to_friendly_formated_date
    
  • Your class method is crying our for the find_or_initialize_by helper methods:

    self.get_event_record(row, participant)
      find_or_initialize_by_participant_id_and_event_type_code_and_event_start_date(:participant_id => participant.id, :event_type_code => row[:event_type_code], :event_start_date => row[:event_start_date].to_friendly_formatted_date)
    end
    

By god it's long, but it achieves what you're trying to do more elegantly (although I'm open to argument!)

Karoline answered 28/6, 2012 at 13:6 Comment(6)
I originally had it as a class method, but, I was thinking (possibly erroneously) that class methods are meant to be used outside of the class, while this method is strictly an internal method.Ernestoernestus
I think that thought was erroneous! Instance methods should relate to the specific attributes of an instance of that class. Your method really has nothing to do with an instance at all. I'd turn it back into a class method. You have a few opportunities to re-factor your code, too - I'll edit my answer.Pantaloons
@steve_gallagher: You can make class methods private. Take a look at this gist.Darya
@Ernestoernestus Have edited my answer with a few other tips.Pantaloons
first_or_initialize is recommended atmHyperphagia
Rails really must be made of black magic if it can give you dynamically generated methods like that!Darya

© 2022 - 2024 — McMap. All rights reserved.