1

I would like:

module MyLog

  def log
    unless @log
      @log = Logger.new(log_path)
      @log.formatter =  proc do |severity, datetime, progname, msg|
        "#{datetime} #{msg}\n"
      end
    end
    @log
  end

end

To be reused between other classes like this:

Class A
  def self.log_path; 'log/a.log' end
  def log_path; 'log/a.log' end
  include MyLog
  extend  MyLog

  def some_method
    log.debug 'some thing'
  end

  def self.some_class_method
    log.debug 'in a class method'
  end

end

Is there a shorter way than those four lines at start of class A?

Another thing

I would like to log by batches:

def expire
  expired_ids = []
  failed_ids  = []

  all.each do |event|
    if event.expire # saves record
      expired_ids << event.id
    else
      failed_ids << event.id
    end
  end

  log.debug "These ids were expired: #{ expired_ids }"
  log.debug "These ids failed to expire: #{ failed_ids }"
end

Is there a way I can do this cleanly? Separating logging from method logic?

4

3 回答 3

6

这是我最近遇到像你这样的问题时一直在做的事情:

class A
  class << self
    include MyLog

    def log_path
      'log/a.log'
    end
  end

  delegate :log_path, :log, to: "self.class"
end

否则,选择最佳的编程方法取决于您的情况、您要重用脚本的频率、在其生命周期内必须重构多少次等等。但无论如何,请不要试图节省代码数,下次自己阅读代码时尽量省去麻烦。

至于您的第二个问题,您面临的主要困境是,您是否值得费心引入 Events 类:

class Events < Array
  def foobar
    each_with_object [[], []] do |event, (expired_ids, failed_ids)| 
      ( event.expire ? expired_ids : failed_ids ) << event.id
    end
  end
end

然后到时候:

def expire
  expired_ids, failed_ids = Events[ all ].foobar
  log.debug "These ids were expired: #{ expired_ids }"
  log.debug "These ids failed to expire: #{ failed_ids }"
end
于 2013-08-07T07:06:06.267 回答
3

您可以使用该included钩子自动定义类方法和实例方法:

module MyLog

  def self.included(base)
    base.extend(Methods)
    base.send(:include, Methods)
  end

  module Methods
    def log_path; 'log/a.log' end

    def log
      unless @log
        @log = Logger.new(log_path)
        @log.formatter =  proc do |severity, datetime, progname, msg|
          "#{datetime} #{msg}\n"
        end
      end
      @log
    end
  end

end

像这样,一旦包含模块,类和实例方法都将自动定义。

对于第二个问题,使用partition并返回值,然后记录它们:

def expire
  all.partition(&:expire)
end

然后,在您调用expire的地方,您可以记录返回值:

def call_something
  expired, failed = expire
  log.debug "These ids were expired: #{expired.map(&:id)}"
  log.debug "These ids failed to expire: #{failed.map(&:id)}"
end
于 2013-08-07T07:26:08.380 回答
2

如果你的日志路径总是应该是log/<lower_case_class>.log,你可以在你的模块中实现它,你应该没问题。当模块的方法被执行时, self 仍然是调用该方法的对象,所以你可以说 self.class 并得到 A 而不是你的模块名称。

查看这个问题的公认答案,了解如何将方法添加到您的类以及该类的对象

为什么模块的“自我”方法不能成为类的单例方法?

您可以使用 Enumerable#partition 来分解您的“全部”(您实际上并没有在您发布的代码中说明它的来源)

expired, failed = all.partition(&:expire)
于 2013-08-07T05:44:38.527 回答