翻译 Ruby 元编程在野外

chenge · 2016年08月11日 · 最后由 msg7086 回复于 2016年08月12日 · 5026 次阅读

之前你可能读到 Ruby 元编程,也许你的一些项目中使用它,但一些最流行的开源项目是如何利用这个特性?

答案在这篇文章中!

Rails 示例

Rails 大量使用元编程,这是一个开始的好地方。

例如,当您想要检查当前环境,在你的 Rails 应用程序,你做些什么:

Rails.env.production?

但什么是 env 并且这是如何工作的呢?答案是 StringInquirer 类,String 这个类的一个子类。这个类使用 method_missing,让你可以调用 env.production?而不是 env == production.

代码是这样的:

def method_missing(method_name, *arguments)
  if method_name[-1] == '?'
    self == method_name[0..-2]
  else
    super
  end
end

这就是说:“如果是以问号结尾的方法名称就做比较,否则继续祖先链”。

原始代码可在这里找到。

Sinatra 委托

如果您以前使用过 Sinatra 你可能知道有两种方法来定义你的路由:通过使用 get / post 这种独立于任何类以外的直接方法,或通过定义一个类继承自 Sinatra::Application.

Sinatra 的 DSL(领域特定语言) 方法定义在 Sinatra::Application 内,所以你如何在这个类以外使用呢?

嗯,这里有两个事情:元编程和模块的扩展。

Sinatra 定义了一个 Sinatra::Delegator 模块,在 base.rb,用于将方法调用委托给一个 target。默认情况下目标设置为 Sinatra::Application。

这是一个简化版的定义于 Sinatra::Delegator 的 delegate 类方法:

def self.delegate(*methods)
  methods.each do |method_name|
   define_method(method_name) do |*args, &block|
     Delegator.target.send(method_name, *args, &block)
   end
  end
end

delegate :get, :patch, :put, :post, :delete

这段代码创建了一组方法 ( define_method),将传递方法调用到 Sinatra::Application(Delegator.target 的默认值).

然后 main 对象被扩展,使用 Sinatra::Delegator 定义的方法,这使得 Sinatra 的 DSL 可用 Sinatra::Application 类。

值得注意的是,Ruby 有两个用于方法委托的内置类,如果你需要他们:Delegator & Forwardable.

Paperclip Gem

Paperclip 是 gem,帮助您的应用程序来处理文件上传。文件加在 ActiveRecord 模型。has_attached_file 方法定义附加文件。

例如:

class User
  has_attached_file :avatar, :styles => { :normal => "100x100#" }
end

该方法定义在 paperclip.rb,它使用元编程在模型定义方法。该方法可用于访问文件附件 (Paperclip::Attachment 类的一个实例)。例如,如果附加文件 :avatar, Paperclip 将定义一个 avatar 方法在模型上。

这是 define_instance_getter 方法负责:

# @name  => The name of the attachment
# @klass => The ActiveRecord model where this method is being defined

def define_instance_getter
  name    = @name
  options = @options

  @klass.send :define_method, @name do |*args|
    ivar = "@attachment_#{name}"
    attachment = instance_variable_get(ivar)

    if attachment.nil?
      attachment = Attachment.new(name, self, options)
      instance_variable_set(ivar, attachment)
    end
  end
end

从这段代码中我们可以看到,对象存储在附件 @attachment_#{name}实例变量。在我们的例子中 @attachment_avatar.

然后检查这个附件已经存在,如果它不存在,它创建一个新的 Attachment 对象和设置实例变量。

这是原始源代码.

结论

如您所见,元编程是一个非常强大和灵活的技术,但当你想要使用的时候请记得这句话:“强大意味着更大的责任”。

如果你喜欢这篇文章,不要忘了订阅我的新闻:)

原英文链接

首发链接

看了标题好奇什么叫“在野外”于是点了一下原文😱

:doge: 《野战 Ruby 元编程》

需要 登录 后方可回复, 如果你还没有账号请 注册新账号