【问题标题】:How to set hash instance's value like this in Ruby?如何在 Ruby 中像这样设置哈希实例的值?
【发布时间】:2013-02-17 07:52:53
【问题描述】:

我有两个类A和B,它们都有一些选项,通常我会使用一个Hash来存储选项,例如@options[:name]='xxxx';现在我想用元编程重构它,

class A
    set_option :name, "james"
    set_option :address, "some street"

    def hello
        puts @options[:name]
        puts @options[:address]
    end
end


class B
    set_option :age, 18

    def greeting
        put @options[:age]
    end
end 

这里我想使用set_option 将键值对设置为一个哈希实例@options,我该怎么做?

此外,我想将解决方案包装到一个单独的模块中。

更新:

首先谢谢,你所有的答案对我来说都很有价值,让我更清楚,现在我意识到我想要的东西不太正确,所以如果我提出这样的问题呢?

  • 使用一种方法option 替换@options 怎么样?
  • A 类和B 类都可以为同一个键设置不同的选项,所以可能有些答案'@@options 不起作用?我希望不同的类可以有不同的哈希实例。

``` 像这样:

class A
    set_option :name, "james"
    set_option :provider, 'twitter'

    def hello
        puts option[:name]
    end
end


class B
    set_option :name, "not james"

    def greeting
        put option[:name]
    end
end 

经过深思熟虑,我认为我真正想要的是不同类的不同选项哈希实例,而不是类的实例。

这是我想要的,它可以工作。

module HasOptions
  def self.included(cls)
    cls.class_eval do
      def self.set_option(key, value)
        options[key] = value
      end

      def self.options
        @options ||= {}
      end

      def options
        self.class.options
      end
    end
  end
end


class Baz
  include HasOptions
  set_option :name, "bad"

  def greeting
        puts options[:name]
  end
end

class Foo
  include HasOptions
  set_option :name, "foo"

  def greeting
        puts options[:name]
  end
end

感谢您的所有帮助。

【问题讨论】:

    标签: ruby metaprogramming


    【解决方案1】:

    使用实例变量,就像你试图做的那样,是行不通的。但是,您可以使用这样的方法:

    module HasOptions
      def self.included(cls)
        cls.class_eval do
          def self.set_option(key, value)
            (@@options ||= {})[key] = value
          end
        end
      end
    
      def options
        @options ||= @@options.dup
      end
    end
    

    此实现允许您设置每个实例的选项,而不会覆盖所有实例的通用选项。

    【讨论】:

      【解决方案2】:

      看起来你有点搞砸了。让我试着列出你想解决的问题:

      • 将选项存储在类变量 Hash 的实例中(我猜对了吗?- 为您的所有类实例提供一个哈希);
      • 使用漂亮的符号来设置选项。

      以上所有内容实际上都不需要元编程。要满足第一个条件,您只需找到所有类的第一个共同祖先,然后 [monkey] 使用您的 @@options var 和 set_option 方法修补它。如果你没有决定为你的所有类提供你自己的超类,让我们修补它的祖先(例如Kernel 模块):

      module Kernel
        def set_option name, value
          (@@options ||= {})[name.to_sym] = value
        end
      end
      

      现在任何类都可以包含set_option“指令”以将选项放入共享选项哈希中(考虑使用实例变量@options而不是@@options来使选项特定于实例,或使用Alex D 的解决方案。)

      如果您想为选项设置使用花哨的花哨语法(带有默认值、原位检查器、褶边和奢侈品),您将在此处使用DSLthis article 中的第一个示例展示了如何使用 DSL 实现纯 set_option。进一步的调整只受您的想象力的限制。

      【讨论】:

      • 感谢@mudasobwa,对于您列出的问题项,第一个不是我想要的,我想要不同类的不同哈希实例。
      • @donnior 然后在 cmets 中建议的任何解决方案中简单地摆脱双 @ 以支持 @options :-)
      【解决方案3】:

      如果你想要初始化实例选项,你应该使用实例方法来做。

      module OptionSetter
        def set_option(key, value)
          @options[key] = value
        end
      end
      
      class Base
        def initialize(options = {})
          @options = {}
          options.each do |key, value|
            set_option key, value
          end
        end
      end
      
      class A < Base
        include OptionSetter
      
        def hello
          puts @options[:name]
          puts @options[:address]
        end
      end
      
      class B < Base
        include OptionSetter
      
        def greeting
          puts @options[:age]
        end
      end
      
      A.new(name: "james", address: "some street").hello
      B.new(age: 18).greeting
      

      在 ruby​​ 中更常见的方法是使用 attr_accessor's

      class Base
        attr_accessor :options
      
        def initialize(options = {})
          @options = options
        end
      end
      
      class A < Base
        def hello
          puts @options[:name]
          puts @options[:address]
        end
      end
      
      class B < Base
        def greeting
          puts @options[:age]
        end
      end
      
      A.new(name: "james", address: "some street").hello
      B.new(age: 18).greeting
      
      # another approach
      james = A.new
      james.options[:name] = "james"
      james.options[:address] = "some street"
      james.hello
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-09-03
        • 2016-01-31
        • 2021-02-05
        • 1970-01-01
        • 2017-05-29
        • 2019-04-30
        • 2012-07-15
        • 1970-01-01
        相关资源
        最近更新 更多