Ruby:我可以在类方法中使用实例方法吗?

17

我有一个类,其中包含这个类方法:

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

同时在同一个类中我还有一个实例方法:

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

我在#format_date处遇到了"undefined method"错误。(我一开始尝试了不带self的方法)。你不能在同一个类的类方法中使用实例方法吗?


2
为什么format_date是一个实例方法?它没有使用任何实例的内容。 - tdgs
当然不行。为了调用实例方法,您需要该类的一个实例。 - KL-7
@tdgs 很好的观点。我将其设置为实例方法,因为我认为将其设置为类方法会暗示它是供公共使用的,而事实上它仅供内部使用。 - steve_gallagher
@KL-7 当然,你是对的。就像我其他评论中所述,我的意图只是有一个方法来"帮助"类方法在它的类中使用。也许将它变成类方法并不一定意味着它可以在类外部使用,或者也许我需要采取完全不同的策略。 - steve_gallagher
@steve_gallagher,你可以像这样将你的类方法设为私有。 - KL-7
感谢大家帮助我避免在工作中出丑。 - steve_gallagher
3个回答

30

简短回答是不行的,除非你有如下情况,否则无法在类方法中使用类的实例方法:

class A
  def instance_method
    # do stuff
  end

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

但就我所看到的来说,format_date并不需要成为一个实例方法。因此,可以像这样编写format_date:

def self.format_date(date)
   # do stuff
end

我有一个实例方法,因为它是从验证中调用的,还有一个类方法,可以调用实例方法,以保持DRY。 - Albert Català
然后做类似这样的事情 class A; def instance_method; self.class.class_method; end; def self.class_method; -- 做一些事情 -- ; end; end - tdgs

7

只需创建类方法

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

如果您需要实例方法,请将其委托给类方法。

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

我认为从类范围内调用实例方法不是一个好主意。


1
class.format_args?为什么要在关键字class上调用一个类方法? - Linuxios
2
这并不意味着在关键词 class 上调用它将产生任何东西,除了语法错误。你需要 self - Linuxios
现在我可以愉快地给你点赞,因为我喜欢你的回答。 - Linuxios

3
你可以使用 YourClassName.new.format_date(your_date),但我认为你应该重构你的代码 - 这个方法可能不属于实例。为什么不扩展 Date 类或将 format_date 作为你正在使用的类的类方法?
编辑:以下是关于你的代码需要考虑的几个方面:
  • 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
    

老天,这段话太长了,但它更优雅地实现了你想做的事情(尽管我会听取不同意见!)


我最初将它设置为类方法,但是我认为(可能错误地)类方法应该在类外部使用,而这个方法严格来说是一个内部方法。 - steve_gallagher
我认为那个想法是错误的!实例方法应该与该类的特定属性相关联。你的方法实际上与实例没有任何关系。我会把它转换回类方法。你还有一些重构代码的机会 - 我会编辑我的答案。 - Rob d'Apice
@steve_gallagher:你可以将类方法设为私有的。看一下这个gist - Linuxios
@steve_gallagher 我已经编辑了我的回答,并提供了一些其他的建议。 - Rob d'Apice
目前建议使用 first_or_initialize。 - Yuri Barbashov
如果Rails可以提供这样动态生成方法的功能,它一定是由黑魔法制成的! - Linuxios

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接