2013-02-17 46 views
2

我有两个类A和B,他们都有一些选项,通常我会用一个哈希来存储选项,如@options[:name]='xxxx';现在我想重构使用元编程它,如何在Ruby中像这样设置哈希实例的值?

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 

感谢您的帮助。

回答

5

正如你试图做的那样使用一个实例变量是行不通的。但是,你可以使用的方法是这样的:

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 

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

1

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

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

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 
2

它看起来像你搞乱的东西一点点。让我试着列出要解决的问题:

  • 存储选项类的一个实例变量Hash(为什么我得到了它正确的酮哈希你的类的所有实例?);
  • 使用漂亮的表示法来设置选项。

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

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

现在,任何一个分类可以包括set_option“指令”把选项共享选项如果你想拥有的选项设置花花哨的语法(使用默认值,就地跳棋,ruches和豪华哈希(考虑使用实例变量@options,而不是@@options使特定实例的选项,或使用亚历克斯·D的解决方案。)

商品),您在这里使用DSLthis article中的第一个示例显示了如何使用DSL实现纯粹的set_option。进一步的调整只限于你的想象力。

+0

感谢@mudasobwa,对于您列出的问题项目,第一个不是我想要的,我想为不同的类使用不同的哈希实例。 – donnior 2013-02-18 03:36:35

+0

@donnior然后在评论中建议的任何解决方案中简单地摆脱双“@”,转而选择“@ options”.-) – mudasobwa 2013-02-18 06:15:04