2 Stimmen

Wie kann man die Verschachtelung in Ruby dynamisch ändern?

Ich verwende Module als Namespaces und möchte sie dynamisch mit Klassen befüllen, z. B.:

module Module1
  # ...
end

module Module2
  # ...
end

[Module1, Module2].each do |the_module|
  the_module.module_eval do
    class ApiTest < ActiveSupport::TestCase
      # ...
    end
  end
end

module_eval würde funktionieren, außer dass es pas die Verschachtelung ändern und die äußere beibehalten. Infolgedessen werden die enthaltenen Konstanten nicht in den Modulen verschachtelt.

Die ursprüngliche Motivation besteht darin, die dieselbe Tests für verschiedene API-Implementierungen, die jeweils in einem eigenen Modul enthalten sind.

1voto

Andrew Marshall Punkte 92252

Sie müssen Folgendes verwenden const_set :

module Bar; end
module Baz; end

[Bar, Baz].each do |mod|
  mod.const_set("Foo", Class.new do
    def hello
      "Hello world!"
    end
  end)
end

Bar::Foo.new.hello  # => "Hello world!"
Baz::Foo.new.hello  # => "Hello world!"

Wenn die neue Klasse eine Oberklasse benötigt, können Sie diese als Parameter an Class.new .

1voto

Ryan LeCompte Punkte 4201

Hier eine weitere Möglichkeit (beachten Sie "selbst::")

module Module1
  # ...
end

module Module2
  # ...
end

[Module1, Module2].each do |the_module|
  the_module.module_eval do
    class self::ApiTest < ActiveSupport::TestCase
      # ...
    end
  end
end

0voto

farnoy Punkte 6868

Ich glaube, ich habe einen Weg gefunden, dies zu tun.

klass = Class.new(String)
klass.class_eval do
  def custom?; return true; end
end
Module1.module_exec do
 const_set :Custom, klass
end

Two::Custom.new.custom?
#=> true

Das erste Argument für Class::new wobei die SuperClass (geerbt von)

0voto

sawa Punkte 160498

Ich habe es nicht getestet, aber vielleicht so etwas?

[Module1, Module2].each do |the_module|
  the_module.const_set("ApiTest", Class.new(ActiveSupport::TestCase) do
    # ...
  end)
end

0voto

Steve Jorgensen Punkte 10505
# Get ourselves a clean, top-level binding.
def main_binding
  binding
end

module ModuleUtils
  module ModuleMethods ; end
  self.extend ModuleMethods

  module ModuleMethods
    # Get a binding with a Module.nesting list that contains the
    # given module and all of its containing modules as described
    # by its fully qualified name in inner-to-outer order.
    def module_path_binding(mod)
      raise ArgumentError.raise(
        "Can't determine path nesting for a module with a blank name"
      ) if mod.name.to_s.empty?
      m, b = nil, main_binding
      mod.name.split('::').each do |part|
        m, b =
        eval(
          "[ #{part} , #{part}.module_eval('binding') ]",
          b
        )
      end
      raise "Module found at name path not same as specified module" unless m == mod
      b
    end
  end
end

Sie können dann Code in jedem gewünschten verschachtelten Modulkontext ausführen, indem Sie ModuleUtils.module_path_binding(<some-module>) um eine Bindung zu erhalten, die Sie als zweites Argument an eval() übergeben können.

Siehe mehr unter https://gist.github.com/2051705 .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X