首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在Ruby中创建Expando对象

在Ruby中创建Expando对象
EN

Stack Overflow用户
提问于 2011-01-16 11:31:29
回答 2查看 570关注 0票数 4

有没有更好的方法来编写这个Expando类?它的编写方式不起作用。我使用的是Ruby 1.8.7

引用自https://gist.github.com/300462/3fdf51800768f2c7089a53726384350c890bc7c3的起始代码

代码语言:javascript
复制
class Expando
    def method_missing(method_id, *arguments)
        if match = method_id.id2name.match(/(\w*)(\s*)(=)(\s*)(\.*)/)
              puts match[1].to_sym # think this was supposed to be commented 
              self.class.class_eval{ attr_accessor match[1].to_sym } 
              instance_variable_set("#{match[1]}", match[5])
        else
              super.method_missing(method_id, *arguments)
        end  
    end    
end

person = Expando.new 
person.name = "Michael"
person.surname = "Erasmus"
person.age = 29 
EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2011-01-16 11:40:17

编写它的最简单方法是根本不编写它!:)请参阅标准库中包含的OpenStruct类:

代码语言:javascript
复制
require 'ostruct'

record = OpenStruct.new
record.name    = "John Smith"
record.age     = 70
record.pension = 300

但是,如果我要写它,我会这样做:

代码语言:javascript
复制
# Access properties via methods or Hash notation
class Expando
  def initialize
    @properties = {}
  end
  def method_missing( name, *args )
    name = name.to_s
    if name[-1] == ?=
      @properties[name[0..-2]] = args.first
    else
      @properties[name]
    end
  end
  def []( key )
    @properties[key]
  end
  def []=( key,val )
    @properties[key] = val
  end
end

person = Expando.new
person.name = "Michael"
person['surname'] = "Erasmus"
puts "#{person['name']} #{person.surname}"
#=> Michael Erasmus
票数 9
EN

Stack Overflow用户

发布于 2011-01-16 12:02:33

如果您只是想获得一个可以正常工作的Expando,请改用OpenStruct。但是,如果您这样做是为了教育价值,那么让我们来修复这些bug。

method_missing的参数

当您调用person.name = "Michael"时,这将转换为对person.method_missing(:name=, "Michael")的调用,因此您不需要使用正则表达式提取参数。您指定的值是一个单独的参数。因此,

代码语言:javascript
复制
if method_id.to_s[-1,1] == "="     #the last character, as a string
   name=method_id.to_s[0...-1]     #everything except the last character
                                   #as a string
   #We'll come back to that class_eval line in a minute
   #We'll come back to the instance_variable_set line in a minute as well.
else
   super.method_missing(method_id, *arguments)
end

instance_variable_set

实例变量名称都以@字符开头。这不仅仅是语法上的糖,它实际上是名称的一部分。因此,您需要使用以下行来设置实例变量:

代码语言:javascript
复制
instance_variable_set("@#{name}", arguments[0])

(还要注意我们是如何从arguments数组中提取赋值的)

class_eval

self.classExpando类作为一个整体引用。如果你在它上面定义了一个attr_accessor,那么每个扩展都会有一个该属性的访问器。我不认为这是你想要的。

相反,您需要在class << self块(这是self的单例类或特征类)中执行此操作。这是在self的特征类中操作的。

所以我们会执行

代码语言:javascript
复制
class << self; attr_accessor name.to_sym ; end

但是,变量name实际上并不能在其中访问,所以我们需要首先挑选出单例类,然后运行class_eval。要做到这一点,一种常见的方法是使用它自己的方法eigenclass,因此我们定义

代码语言:javascript
复制
  def eigenclass
    class << self; self; end
  end

然后调用self.eigenclass.class_eval { attr_accessor name.to_sym } )

解决方案

将所有这些结合在一起,最终的解决方案是

代码语言:javascript
复制
class Expando
  def eigenclass
    class << self; self; end
  end

  def method_missing(method_id, *arguments)
    if method_id.to_s[-1,1] == "=" 
      name=method_id.to_s[0...-1]
      eigenclass.class_eval{ attr_accessor name.to_sym }
      instance_variable_set("@#{name}", arguments[0])
    else
      super.method_missing(method_id, *arguments)
    end      
  end    
end
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4703642

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档