如何在Ruby模块中动态定义Object方法?

时间:2011-10-23 19:07:00

标签: ruby

我有一个案例,我正在尝试利用Padrino使用的数据库配置文件为我正在编写的自定义工作程序,而无需加载Padrino环境,也无需修改现有的数据库配置。简而言之,我想编写我的代码来使用现有的数据库配置代码,而不是修改现有的配置。

有问题的数据库配置文件如下所示:

# This is just here for right now so I can test
# to see if logger is set to anything in DB config
puts "Logger in DB config: #{logger}"

ActiveRecord::Base.configurations[:development] = {
  :adapter => 'sqlite3',
  :database => Padrino.root('db', "app_development.db") }
ActiveRecord::Base.logger = logger
ActiveRecord::Base.include_root_in_json = true
ActiveRecord::Base.store_full_sti_class = true
ActiveSupport.use_standard_json_time_format = true
ActiveSupport.escape_html_entities_in_json = false
ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[Padrino.env])

我的自定义工作人员如下所示:

ROOT = File.expand_path(File.dirname(__FILE__))
$LOAD_PATH.unshift(ROOT)

require 'active_record'
require 'logger'

# Mock Padrino module with methods used by database config file
module Padrino
  def self.root(*args)
    return File.expand_path(File.join("#{ROOT}/..", *args))
end

  def self.env
    return :development
  end
end

module Worker
  class << self
    attr_accessor :logger

    def init
      @logger       = Logger.new(STDOUT)
      @logger.level = Logger::DEBUG

      require "#{ROOT}/../config/database"
    end
  end
end

Worker.init

当我在我的工作人员中需要数据库配置文件时,出现以下错误:

/home/user/devel/app/config/database.rb:3:in `<top (required)>': undefined local variable or method `logger' for main:Object (NameError)
from /home/user/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:55:in `require'
from /home/user/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:55:in `require'
from worker.rb:26:in `init'
from worker.rb:31:in `<main>'

鉴于此,我将Worker#init函数修改为以下内容:

def init
  @logger       = Logger.new(STDOUT)
  @logger.level = Logger::DEBUG

  Object.send(:define_method, :logger, lambda { @logger })

  puts "Logger in Worker module: #{logger}"

  require "#{ROOT}/../config/database"
end

此更改导致以下输出:

Logger in Worker module: #<Logger:0x9505088>
Logger in DB config:

我认为这意味着@logger不在数据库配置文件的范围内,即使我在Object上定义记录器方法时使用lambda。

奇怪的是,如果我从Object.send函数中提取Worker#init代码行,而是在我调用Worker.init之前调用它,如下所示,我得到以下结果。< / p>

Object.send(:define_method, :logger, lambda { Worker.logger })
Worker.init

结果

Logger in Worker module: #<Logger:0x81bc8b4>
Logger in DB config: #<Logger:0x81bc8b4>

有人可以向我解释为什么如果我在Object.send函数中进行Worker#init调用它不会像在工作模块外部那样工作吗?

1 个答案:

答案 0 :(得分:0)

在第一个示例中,您尝试使用未定义的变量'logger',正如您可以从错误消息中看到的那样。尝试以这种方式使用记录器功能:

ActiveRecord::Base.logger = Logger.new(STDOUT)