Rails 委托方法如何工作?

2024-03-19

阅读下面 jvans 的答案并再查看源代码几次后,我现在明白了:)。如果有人仍然想知道 Rails delegate 到底是如何工作的。 Rails 所做的就是在运行委托方法的文件/类中使用 (module_eval) 创建一个新方法。

例如:

  class A
    delegate :hello, :to => :b
  end

  class B
    def hello
     p hello
    end
  end

当调用 delegate 时,rails 会在 A 类中创建一个带有 (*args, &block) 的 hello 方法(技术上讲是在 A 类所写入的文件中),并且在该方法中,rails 所做的所有操作都是使用“:to”值(应该是类 A 中已经定义的对象或类)并将其分配给局部变量 _,然后只需调用该对象或类上传递参数的方法。

因此,为了让委托能够在不引发异常的情况下工作......以我们之前的示例为例。 A 的实例必须已经有一个引用类 B 的实例的实例变量。

  class A
    attr_accessor :b

    def b
      @b ||= B.new
    end

    delegate :hello, :to => :b
  end

  class B
    def hello
     p hello
    end
  end

这不是一个我已经知道的“如何在rails中使用委托方法”的问题。我想知道“委托”到底是如何委托方法的:D。在 Rails 4 源代码中,委托是在核心 Ruby Module 类中定义的,这使得它可以在所有 Rails 应用程序中作为类方法使用。

实际上我的第一个问题是 Ruby 的 Module 类是如何包含的?我的意思是每个 Ruby 类都有 > Object > Kernel > BasicObject 的祖先,并且 ruby​​ 中的任何模块都具有相同的祖先。那么当有人重新打开 Module 类时,ruby 到底是如何向所有 ruby​​ 类/模块添加方法的呢?

我的第二个问题是..我知道rails中的委托方法使用 module_eval 来做实际的委托,但我不太明白 module_eval 是如何工作的。

def delegate(*methods)
 options = methods.pop
 unless options.is_a?(Hash) && to = options[:to]
  raise ArgumentError, 'Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter).'
end

prefix, allow_nil = options.values_at(:prefix, :allow_nil)

if prefix == true && to =~ /^[^a-z_]/
  raise ArgumentError, 'Can only automatically set the delegation prefix when delegating to a method.'
end

method_prefix = \
  if prefix
    "#{prefix == true ? to : prefix}_"
  else
    ''
  end

file, line = caller.first.split(':', 2)
line = line.to_i

to = to.to_s
to = 'self.class' if to == 'class'

methods.each do |method|
  # Attribute writer methods only accept one argument. Makes sure []=
  # methods still accept two arguments.
  definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block'

  # The following generated methods call the target exactly once, storing
  # the returned value in a dummy variable.
  #
  # Reason is twofold: On one hand doing less calls is in general better.
  # On the other hand it could be that the target has side-effects,
  # whereas conceptually, from the user point of view, the delegator should
  # be doing one call.
  if allow_nil
    module_eval(<<-EOS, file, line - 3)
      def #{method_prefix}#{method}(#{definition})        # def customer_name(*args, &block)
        _ = #{to}                                         #   _ = client
        if !_.nil? || nil.respond_to?(:#{method})         #   if !_.nil? || nil.respond_to?(:name)
          _.#{method}(#{definition})                      #     _.name(*args, &block)
        end                                               #   end
      end                                                 # end
    EOS
  else
    exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")

    module_eval(<<-EOS, file, line - 2)
      def #{method_prefix}#{method}(#{definition})                                          # def customer_name(*args, &block)
        _ = #{to}                                                                           #   _ = client
        _.#{method}(#{definition})                                                          #   _.name(*args, &block)
      rescue NoMethodError => e                                                             # rescue NoMethodError => e
        if _.nil? && e.name == :#{method}                                                   #   if _.nil? && e.name == :name
          #{exception}                                                                      #     # add helpful message to the exception
        else                                                                                #   else
          raise                                                                             #     raise
        end                                                                                 #   end
      end                                                                                   # end
    EOS
  end
end

end


Ruby 不会在这里重新打开模块类。在 ruby​​ 中,类 Module 和类 Class 几乎相同。

    Class.instance_methods - Module.instance_methods #=> [:allocate, :new, :superclass]

主要区别在于您无法“新建”模块。 模块是 ruby​​ 的多重继承版本,所以当你这样做时:

 module A
 end
 module B
 end

 class C
   include A
   include B
 end

ruby 实际上在幕后创建了一种称为匿名类的东西。所以上面实际上相当于:

 class A
 end
 class B < A
 end
 class C < B
 end

这里的 module_eval 有点欺骗性。您正在查看的代码中没有任何内容涉及模块。 class_eval 和 module_eval 是同一件事,它们只是重新打开它们被调用的类,因此如果您想向类 C 添加方法,您可以这样做:

 C.class_eval do 
    def my_new_method
    end
  end

or

 C.module_eval do 
    def my_new_method
    end
  end

两者都相当于手动重新打开类并定义方法

  class C
   end
  class C
     def my_new_method
     end 
  end

因此,当他们在上面的源代码中调用 module_eval 时,他们只是重新打开当前的类,并动态定义您委托的方法

我认为这会更好地回答你的问题:

 Class.ancestors #=> [Module, Object, PP::ObjectMixin, Kernel, BasicObject]

由于 ruby​​ 中的所有内容都是类,因此方法查找链将遍历所有这些对象,直到找到它要查找的内容。通过重新操作模块,您可以为所有内容添加行为。这里的祖先链有点欺骗性,因为 BasicObject.class #=> Class 和 Module 位于 Class 的查找层次结构中,甚至 BasicObject 也继承了 repening 模块的行为。与类相比,在此处重新打开模块的优点是您现在可以从模块内以及类内调用此方法!非常酷,我自己在这里学到了一些东西。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Rails 委托方法如何工作? 的相关文章

  • Mountain Lion 的 RVM 问题

    我可以在运行 Mountain Lion 的 Mac 上安装任何 Ruby 版本 我已经确保 Brew Doctor 一切正常 当我运行 ie rvm intall 1 9 3 head 时 我得到以下信息 From git github
  • 检查 Float 是否等于 Ruby 中的整数值

    假设我有以下代码 x 0 8 y 1 0 最好的检查方法是什么y相当于Integer 目前我正在做 y to int y 这可行 但我觉得应该有更好的方法 You mod值为 1 并检查该值是否等于 0 if y 1 0
  • 从模块中获取类名

    如何从模块中获取该模块包含的类的类名 module ActMethods def some method attr names cls self class this doesn t work end end 如何进入 cls 变量 加载此
  • 如果不选择 Updated_at 属性,则无法更新 Rails 4 中的模型

    我正在从 Rails 3 2 13 升级到 4 并遇到更新模型的问题 我正在尝试仅更新模型上的单个属性 其中我只有 id 我还需要验证和回调 这在 Rails 3 中运行良好 但在 Rails 4 中遇到了麻烦 在 Rails 4 中 如果
  • CanCan load_and_authorize_resource 触发禁止属性

    我有一个使用强参数的标准 RESTful 控制器 class UsersController lt ApplicationController respond to html js def index users User all end
  • 如何使用多个 { 'not find' } 来干燥方法?

    我正在尝试优雅地处理以下错误的 JSON 其中Hash fetch似乎不是一个选择 使用 Hash fetch 优雅地处理错误的 JSON https stackoverflow com questions 25193627 handle
  • 将 gmaps4rails 与 https/SSL 结合使用

    我是 gmaps4rails 的忠实粉丝 它使向我们的网络应用程序添加地图变得非常容易 我现在遇到的唯一问题是 如果您在 https 页面上使用它 则会收到有关混合内容的安全警告 谷歌地图API确实支持https 所以我认为这只是将脚本引用
  • Xcode - 配置:错误:在 $PATH 中找不到可接受的 C 编译器

    从头开始重建 Mac 安装了 xcode 和 rvm 然后尝试安装 rubies 但它们都给了我 Error running configure prefix Users durrantm rvm rubies ruby 1 9 3 p12
  • 错误 - 迭代期间无法将新密钥添加到哈希中

    我是 ruby 新手 正在使用 gem 来访问英国卫报开放平台的一些数据 contentapi ruby 运行测试示例时 我不断收到错误 TestSearchContent RuntimeError can t add a new key
  • database.yml 的所有可能键是什么

    我刚刚发现reconnect true配置选项可以在database yml文件 还有哪些其他可能的配置选项 是否有所有选项的完整参考 已知的关键示例 default default adapter mysql2 encoding utf8
  • ActiveSupport::JSON 解码哈希丢失符号

    我正在尝试序列化和反序列化哈希 当散列被解除序列化时 密钥被解除符号化 例如不是更多 一 而是 一 从 Rails 控制台 gt gt h one gt 1 two gt two one gt 1 two gt two gt gt j Ac
  • 使用 FactoryGirl 测试简单的 STI

    我有一个类 它是其他一些专门处理该行为的类的基础 class Task lt ActiveRecord Base attr accessible type name command validates presence of type na
  • Application.css.scss 导致 Rails 4 出现问题

    所以我一直在学习 ruby on Rails 我决定在 myrubyblog 应用程序上的新帖子页面添加一些样式 一切正常 几天后 我决定将 posts scss 文件中的 scss 导入到 application css scss 中 然
  • C# 委托实例化与仅传递方法引用 [重复]

    这个问题在这里已经有答案了 我有一个简单的问题 与仅传递函数引用相比 实例化 C 委托有什么优势 我的意思是 Why do Thread t new Thread new ThreadStart SomeObject SomeMethod
  • RSpec 与 Shoulda?

    我是单元测试场景的新手 我现在只使用单元测试大约两个月了 当我在 Ruby 中进行单元测试时 我目前遵循 TDD 风格并使用 Test Unit TestCase 我还阅读了 RSpec 以及它如何遵循 BDD 方法 我还读过关于 Shou
  • 如何对 ruby​​ 的 URI.parse 方法进行猴子补丁

    一些流行的博客网站通常在其 URL 中使用方括号 但 ruby 的内置 URI parse 方法会阻塞它们 引发令人讨厌的异常 如下所示 http redmine ruby lang org issues show 1466 http re
  • 学习树顶

    我正在尝试自学 Ruby 的 Treetop 语法生成器 我发现 对于 最好的 文档来说 不仅文档非常稀疏 而且它的工作方式似乎并不像我希望的那样直观 从高层次上来说 我真的很喜欢比现场文档或视频更好的教程 如果有的话 在较低的层面上 这是
  • Rube.exe:Babun 中没有这样的文件或目录

    我正在尝试在 Babun 中安装 ruby gems 以便我可以安装 sass 和 compass 但不断收到错误 C RailsInstaller Ruby2 1 0 bin ruby exe No such file or direct
  • 表单提交按钮仅在重新加载后才有效

    我有一个构建表的索引页 我试图允许用户编辑表中的行 我试图以最基本的方式做到这一点 没有 javascript ajax 等 除非 Rails 提供了它 我的表在索引方法中显示良好 并且有一个表单作为表中的最后一行 可用于添加新行 新的形式
  • 如何在 Ruby on Rails 中访问控制器中的隐藏字段

    问题 如何访问隐藏字段值post id从文件view comments comment html erb并用在controllers dashboards controller rb 有 2 个控制器 仪表板和评论 并使用gem act a

随机推荐