代码之家  ›  专栏  ›  技术社区  ›  Kimmo Lehto

如何在匿名类定义中命名常量?

  •  2
  • Kimmo Lehto  · 技术社区  · 6 年前

    通过创建匿名类时 Class.new ,它们似乎没有自己的常量命名空间:

    klass1 = Class.new do
      FOO = "foo"
    end
    
    klass2 = Class.new do
      FOO = "bar"
    end
    

    warning: already initialized constant FOO 看起来是对的:

    > klass1.const_get(:FOO)
    "bar"
    > klass2.const_get(:FOO)
    "bar"
    > FOO
    "bar"
    

    我将在一个简单的DSL中使用这种方法来为应用程序定义插件,如下所示:

    class App
      class AddonBase
    
        attr_reader :session
    
        def initialize(session)
          @session = session
        end
      end
    
      def self.addons
        @addons ||= {}
      end
    
      def self.addon(name, &block)
        addons[name] = Class.new(AddonBase, &block)
      end
    end
    

    对于简单的附加组件来说,这很好,但是如果定义常量,它们将在 Object:: 而不是变成 addons[name]::CONSTANT

    App.addon "addon1" do
    
      PATH="/var/run/foo"
    
      def execute
        File.touch(PATH)
      end
    end
    
    App.addon "addon2" do
    
      PATH="/etc/app/config"
    
      def execute
        File.unlink(PATH)
      end
    end
    
    # warning: already initialized constant PATH
    

    常量可以是任何东西,附加组件甚至可以定义自己的实用程序子类,因此它不仅仅是替换 PATH 有功能的。

    有办法解决这个问题吗?

    3 回复  |  直到 6 年前
        1
  •  2
  •   Aleksei Matiushkin    6 年前

    创建时 一堂课一堂课。新的,他们似乎没有自己的 命名空间 对于常量

    当然,根据匿名这个词的定义。比较以下两个片段:

    class C1; puts "|#{name}|"; end
    #⇒ |C1|
    C2 = Class.new { puts "|#{name}|" }
    #⇒ ||
    

    除非指定给常量,否则类没有名称,因此内部定义的所有常量都转到 Object 命名空间。也就是说,这里的警告实际上是指错误和 Object::FOO = "bar" Object::FOO = "foo" 不变。

    也就是说,在这种情况下不能使用常量。改用类级实例变量,或者手动构造唯一的常量名(我建议避免污染 对象 不过,用一堆不相关的常量初始化。)

        2
  •  2
  •   Stefan    6 年前

    通过创建匿名类时 Class.new ,它们似乎没有自己的常量命名空间

    是的,你可以用 const_set

    klass1 = Class.new do
      const_set(:FOO, 'foo')
    end
    
    klass2 = Class.new do
      const_set(:FOO, 'bar')
    end
    
    klass1::FOO #=> "foo"
    klass2::FOO #=> "bar"
    

    或通过 self::

    klass1 = Class.new do
      self::FOO = 'foo'
    end
    
    klass2 = Class.new do
      self::FOO = 'bar'
    end
    
    klass1::FOO #=> "foo"
    klass2::FOO #=> "bar"
    
        3
  •  1
  •   chumakoff    6 年前

    实际上,问题是如何使用包含常量定义的proc定义类。正如已经说过的那样,这是不可能的,因为proc得到类的值,这不允许定义常量。

    我建议另一种方法。你能用模块而不是过程来定义新的插件,把一个模块混合到一个类中吗?

    module AddonModule
      FOO = "foo"
    end
    
    klass = Class.new
    klass.include AddonModule
    
    klass::FOO # => "foo"
    

    DSL中的用法:

    def self.addon(name, addon_module)
      addon = Class.new(AddonBase)
      addon.include addon_module
    
      addons[name] = addon
    end