将变量传递给Ruby中动态生成的类

时间:2016-04-14 15:09:24

标签: ruby-on-rails ruby

我试图通过编写动态生成的类方法来干我的代码。

我想要的是有多个错误类,我有以下代码

红宝石

class ChatPolicy::Error < StandardError
ERROR_CLASSES = [
    { class_name: 'UserBlacklisted', message: 'Message 1' },
    { class_name: 'UserSuspended', message: 'Message 2' },
    { class_name: 'UserNotEligibleToRent', message: 'Message 3' },
    { class_name: 'MembershipTierNotAllowed', message: 'Message 4' }
]

ERROR_CLASSES.each do |cls|
    Object.const_set(cls[:class_name], Class.new {
        attr_reader :object

        def initialize(object)
            @object = object
            @message = cls[:message]
        end
    })
end
end

但是,由于在Class.new {}块中,变量无法传入。我无法初始化消息变量。我想知道如何实现这一目标?

2 个答案:

答案 0 :(得分:2)

使用def定义方法时,不能从方法定义中的外部作用域引用局部变量,即cls方法中的initialize变量。

可以从块内部引用这样的局部变量,你可以使用块来创建一个define_method的方法。

因此,在您的示例中,您可以通过更改行

来使其工作
def initialize(object)

define_method(:initialize) do |object|

既然方法体是一个块,你可以参考体内的cls

答案 1 :(得分:1)

  

但是,由于在Class.new {}块中,无法传入变量。

这不是关于“变量无法传入”,这是关于“在一个块内,接收器不同而且cls局部变量无法解析。”有两种可能的方法来实现目标:

- 直接从可见内容中查找消息:

@message = ChatPolicy::Error::ERROR_CLASSES.detect do |hash|
  hash[:class_name] == self.class.name
end[:message]

- 或使用class_eval

ERROR_CLASSES.each do |cls|
    Object.const_set(cls[:class_name], class_eval %Q|
      Class.new {
        attr_reader :object

        def initialize(object)
            @object = object
            @message = '#{cls[:message]}' # ⇐ HERE !!!
        end
    }|)
end

UPD @matt的方法更好;我留下这个答案仅用于历史目的。