Dynamically defined classes incorrectly distribute data - an error or an encoding error?

I am trying to set up a system by which I can generate a series of similar Ruby classes that differ by an integer parameter, which I store in the class variable of the corresponding class - something similar to C ++ templates.

However, referencing (hence creating) the new version of the template class overwrites the saved parameters in previous versions, and I cannot understand why.

Here is a minimal example

class Object
  def self.const_missing(name)
    if name =~ /^Templ(\d+)$/
      return make_templ $1.to_i
    else
      raise NameError.new("uninitialised constant #{name}")
    end
  end

private
  def make_templ(base)
    # Make sure we don't define twice
    if Object.const_defined? "Templ#{base}"
      return Object.const_get "Templ#{base}"
    else
      # Define a stub class
      Object.class_eval "class Templ#{base}; end"

      # Open the class and define the actual things we need.
      Object.const_get("Templ#{base}").class_exec(base) do |in_base|        
        @@base = in_base

        def initialize
          puts "Inited with base == #{@@base}"
        end
      end

      Object.const_get("Templ#{base}")
    end
  end
end

irb(main):002:0> Templ1.new
Inited with base == 1
=> #<Templ1:0x26c11c8>
irb(main):003:0> Templ2.new
Inited with base == 2
=> #<Templ2:0x20a8370>
irb(main):004:0> Templ1.new
Inited with base == 2
=> #<Templ1:0x261d908>

Did I find an error in my Ruby (ruby 1.9.2p290 (2011-07-09) [i386-mingw32]), or did I just code something wrong?

+5
source share
2 answers

@@base Object, Object TemplX var. , Module # class_variable_set class_variable_get, .

: , make_templ self.const_missing, , Object . eval (string), . NameError, const_missing, , - - .

class Object
  def self.const_missing(name)
    if name =~ /^Templ(\d+)$/
      return make_templ $1.to_i
    end
    super
  end

private
  def self.make_templ(base)
    klass_name = "Templ#{base}"
    unless const_defined? klass_name
      klass = Class.new(Object) do
        class_variable_set :@@base, base
        def initialize
          puts "Inited with base == #{self.class.class_variable_get(:@@base)}"
        end
      end
      const_set klass_name, klass    
    end

    const_get klass_name
  end
end

. . , @@base, , , , . : Fowler, RailsTips

+1

@Casper , . . eval :


EDIT: @dbenhur, .

class Object
  def self.const_missing(name)
    name =~ /^Templ(\d+)$/ ? make_templ($1.to_i) : super
  end

private
  def self.make_templ(base)
    klass_name = "Templ#{base}"
    if const_defined? klass_name
      const_get klass_name
    else
      klass = Class.new(Object) do
        class << self
          attr_accessor :base
        end
        self.base = base
        def initialize
          puts "Inited with base == #{self.class.base}"
        end
      end
      const_set klass_name, klass    
    end
  end
end

puts Templ1.new.class.base
# => Inited with base == 1
# => 1
puts Templ2.new.class.base
# => Inited with base == 2
# => 2
puts Templ1.new.class.base
# => Inited with base == 1
# => 1
+1

All Articles