【问题标题】:How to alias a class method within a module?如何给模块中的类方法起别名?
【发布时间】:2012-10-06 17:29:36
【问题描述】:

我正在使用 Ruby v1.9.2 和 Ruby on Rails v3.2.2 gem。我有以下模块

module MyModule
  extend ActiveSupport::Concern

  included do
    def self.my_method(arg1, arg2)
      ...
    end
  end
end

我想为类方法命名为my_method。所以,我陈述了以下(不工作)代码:

module MyModule
  extend ActiveSupport::Concern

  included do
    def self.my_method(arg1, arg2)
      ...
    end

    # Note: the following code doesn't work (it raises "NameError: undefined
    # local variable or method `new_name' for #<Class:0x00000101412b00>").
    def self.alias_class_method(new_name, old_name)
      class << self
        alias_method new_name, old_name
      end
    end

    alias_class_method :my_new_method, :my_method
  end
end

换句话说,我想以某种方式扩展Module 类,以便在MyModule 中添加一个alias_class_method 方法。但是,我想让它在我所有的 Ruby on Rails 应用程序中工作并且可用。

  1. 我应该把与Module 类的Ruby 核心扩展相关的文件放在哪里?也许在 Ruby on Rails lib 目录中?
  2. 我应该如何在核心扩展文件中正确“扩展”Module 类?
  3. 这是正确的方法吗?也就是说,例如,我应该“扩展”另一个类(Object、BasicObject、Kernel、...)而不是Module?或者,我应该完全避免实施上述核心扩展吗?

但是,更重要的是,是否有一个 Ruby 功能可以让我完成我想要完成的工作,这样我就不必扩展它的类?

【问题讨论】:

    标签: ruby-on-rails ruby metaprogramming core extending


    【解决方案1】:

    您可以使用define_singleton_method 将旧方法包装在一个新名称下,如下所示:

    module MyModule
      def alias_class_method(new_name, old_name)
        define_singleton_method(new_name) { old_name }
      end
    end
    
    class MyClass
      def my_method
        puts "my method"
      end
    end
    
    MyClass.extend(MyModule)
    MyClass.alias_class_method(:my_new_method, :my_method)
    MyClass.my_new_method     # => "my method"
    

    回答您的评论,您不必手动扩展每个类。 define_singleton_method 在 Object 类中实现。所以你可以简单地扩展Object 类,所以每个类都应该有可用的方法......

    Object.extend(MyModule)
    

    把它放在你的 Rails 应用程序的初始化程序中,你应该很高兴......

    【讨论】:

    • 我还没有测试你的代码,但是通过使用它我必须扩展我需要运行alias_class_method 方法的所有类/模块。是不是效率低下(因为我可以用 Ruby 元编程做同样的事情)而不是 DRYed?
    • 我对您的方法的“价值”-“努力”仍有一些疑问:我可以/应该使用“公开课”方法来完成我想做的事情吗? 为什么不呢?
    • 好吧,我不能真正回答这个问题,因为你没有说你到底需要它做什么......如果你需要“公开课”方法来完成你必须做的事情,那么恐怕不会有任何其他方式,因为简单的“alias_method”仅适用于实例方法。但对我来说问题是:为什么需要给类级别的方法起别名?哪个特例?我总是采用最窄的方法,这意味着如果我需要在整个类树中工作的某些行为使用顶级类,如果整个树中有 2 个子树,则使用 2 个子树的基类...跨度>
    • +1 因为在我尝试了许多谷歌搜索示例(可能已经过时?)之后,这至少对我有用。 3.times{ puts "thanks!" } (ruby-2.0.0-p195)
    • 有谁知道如何以链式方式进行这项工作? (à la 流行的 Rails 方法alias_method_chain)如果您只是定义一个新方法来调用旧方法,然后重新定义旧方法,由于后期绑定,您将调用您的新方法。编辑:是这个吗? stackoverflow.com/questions/13408136/…
    【解决方案2】:

    我在这个网站上找到了答案:http://engineering.lonelyplanet.com/2012/12/09/monitoring-our-applications-ruby-methods/

    解决方案是将class_eval 与块一起使用。这样就可以使用封闭范围内的变量。

    module Alias
    
      def trigger
        @trigger = true
      end
    
      def method_added(name)
        if @trigger
          @trigger = false
          with_x = "#{name}_with_x"
          without_x = "#{name}_without_x"
          define_method(with_x) do
            "#{send(without_x)} with x"
          end
          alias_method without_x, name
          alias_method name, with_x
        end
      end
    
      def singleton_method_added(name)
        if @trigger
          @trigger = false
          with_x = "#{name}_with_x"
          without_x = "#{name}_without_x"
          define_singleton_method(with_x) do
            "singleton #{send(without_x)} with x"
          end
          singleton_class.class_eval do
            alias_method without_x, name
            alias_method name, with_x
          end
        end
      end
    
    end
    
    class TestAlias
    
      extend Alias
    
      trigger
      def self.foo
        'foo'
      end
    
      trigger
      def bar
        'bar'
      end
    
    end
    
    TestAlias.foo # => 'singleton foo with x'
    TestAlias.new.bar # => 'bar with x'
    

    如果您没有singleton_class,那么您可能应该升级您的Ruby 版本。如果这不可能,您可以这样做:

    class Object
      def singleton_class
        class << self
          self
        end
      end
    end
    

    【讨论】:

      【解决方案3】:

      接受的答案令人困惑并且不起作用。

      class Module
        def alias_class_method(new_name, old_name)
          define_singleton_method(new_name, singleton_method(old_name))
        end
      end
      
      module MyModule
        def self.my_method
          'my method'
        end
      end
      
      MyModule.alias_class_method(:my_new_method, :my_method)
      MyModule.my_new_method # => "my_method"
      

      【讨论】:

        猜你喜欢
        • 2012-10-07
        • 1970-01-01
        • 1970-01-01
        • 2017-02-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多