【问题标题】:Method For Making Methods: Easy Ruby Metaprogramming制作方法的方法:简单的 Ruby 元编程
【发布时间】:2010-05-17 21:08:02
【问题描述】:

我在视图助手中有很多这样的方法

  def background
    "#e9eaec"
  end
  def footer_link_color
    "#836448"
  end

我希望将这些方法暴露给视图,但我希望助手更简洁一些。例如,将散列转换为方法(或其他东西)的最佳方法是什么?

【问题讨论】:

    标签: ruby metaprogramming


    【解决方案1】:
    module MyHelper
      {:background => "#e9eaec", :footer_link_color => "#836448"}.each do |k,v|
        define_method(k) {v}
      end
    end
    

    虽然我不认为用这种简洁性来换取第一种方法的可读性一定是个好主意。

    如果你想概括一下,可以在 Module 类中添加如下方法:

    class Module
      def methods_from_hash(hash)
        hash.each do |k,v|
          define_method(k) {v}
        end
      end
    end
    

    然后在你的助手中调用methods_from_hash(:background => ...)

    【讨论】:

    • 实际上,如果您使用散列变量和模块类的猴子补丁会更干净。我挺喜欢它的。谢谢。
    • 我在下面的“答案”中重新混合了你的答案......虽然我需要找出在原始问题的上下文中哪个版本更好(帮助者)。
    【解决方案2】:

    如果您在命名空间中创建常量,那么您可以轻松地为这些常量创建访问器:

    class Foo
    
      module Values
        FOO = 1
        BAR = 'bar'
        BAZ = :baz
      end
      include Values
    
      Values.constants.each do |name|
        define_method(name.downcase) do
          Values.const_get(name)
        end
      end
    
    end
    
    foo = Foo.new
    p foo.foo    # => 1
    p foo.bar    # => "bar"
    p foo.baz    # => :baz
    

    include Values 将常量混合到 Foo 中以方便 Foo 自己的方法。这种模式不需要工作。

    【讨论】:

    • Ruby 知道它们的大小写是常量,还是大小写只是一种约定?
    • @yar,Ruby 知道我们正在定义一个常量,因为标识符以大写字母开头。我不知道它如何跟踪什么是常数,什么不是。
    • 这是一个关于 Ruby 的讽刺可悲事实:它有很多是用非 Ruby 编写的,除非你想深入了解下面的 C,否则很难了解所有这些内容。谢谢你的回答。
    • @yar:你说它就像是 ruby​​ 的一个坏特性。实际上,在大多数语言中,您不需要知道底层是如何处理的。 C 中的 const 是如何处理的?在 C++ 中?爪哇?你知道通过声明它 const 它是一个常量。但我不需要知道更多。同样在 ruby​​ 中:声明一个大写的变量,并将其视为常量。但是,如果这就是您所说的,那么周围有大量的文档。
    • @nathanvda,这是对 Ruby 的负面评论,尽管在这种情况下可能不是。正如您所说,在 Java 中 static final 是一种语言功能,因此不是用 Java 编写的。所以在这种情况下你是对的。
    【解决方案3】:

    实际上,ruby 有一个叫做OpenStruct 的东西,当你想要散列但又不想像散列一样使用它时,它非常棒并且非常有用。

    require 'ostruct'
    
    colors = OpenStruct.new({:background => "0x00FF00", :color => "0xFF00FF"})
    
    p colors.background #=> "0x00FF00"
    

    【讨论】:

    • 感谢 vava,感谢 sepp2k 对我的回答(下方某处)的评论,我昨天了解到了这一点。
    【解决方案4】:

    这是我对 sepp2k 答案的混音。它有点面向对象,甚至在irb 中也可以使用。不确定是修补Object 还是Hash

    class Hash
      def keys_to_methods()
        each do |k,v|
          self.class.send(:define_method, k,  Proc.new {v});
        end
        length
      end
    end
    

    测试代码

    hash = {:color_one=>"black", :color_two=>"green"}
    hash.keys_to_methods
    has.color_one # returns black
    

    OpenStruct:再次感谢 sepp2k!我不知道this 存在。

    这是另一个使用 method_missing 的版本

    class Hash
      def method_missing(method_id)
        key = method_id.id2name
        if has_key?(key)
          return self[key]
        elsif has_key?(key.to_sym)
          return self[key.to_sym]
        else
          super.method_missing(method_id)
        end
      end
    end
    
    hash = {:color_one=>"black", :color_two=>"green"}
    hash.color_one
    

    我确信我可以让代码更紧凑(如果我知道怎么做的话)。

    【讨论】:

    • 修补对象没有任何意义,因为这只会对至少具有每个方法的对象起作用(因此至少是可枚举的)。另请注意,如果您想要的只是一个哈希,您可以通过 hash.key 而不是 hash[key] 获取键的值,您可以只使用 OpenStruct(或带有 method_missing 的猴子补丁哈希)。
    • 谢谢@sepp2k,我用method_missing实现了,但我不知道如何优雅地处理作为符号或字符串的键。
    猜你喜欢
    • 1970-01-01
    • 2012-03-18
    • 2015-06-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-17
    • 2011-10-08
    • 1970-01-01
    相关资源
    最近更新 更多