名前を使用してRubyのクラスを動的に定義するにはどうすればよいですか?
私はクラスを動的に作成する方法を知っていますなし
dynamic_class = Class.new do
def method1
end
end
ただし、クラス名を指定することはできません。クラスを動的に作成したいwith名前。
ここに私がやりたいことの例がありますが、もちろん実際には機能しません。
(クラスのインスタンスではなく、クラス定義を作成していることに注意してください)
class TestEval
def method1
puts "name: #{self.name}"
end
end
class_name = "TestEval"
dummy = eval("#{class_name}")
puts "dummy: #{dummy}"
dynamic_name = "TestEval2"
class_string = """
class #{dynamic_name}
def method1
end
end
"""
dummy2 = eval(class_string)
puts "dummy2: #{dummy2}" # doesn't work
実際の出力:
dummy: TestEval
dummy2:
望ましい出力:
dummy: TestEval
dummy2: TestEval2
================================================== ====
回答:sepp2kの方法を使用した完全に動的なソリューション
dynamic_name = "TestEval2"
Object.const_set(dynamic_name, Class.new) # If inheriting, use Class.new( superclass )
dummy2 = eval("#{dynamic_name}")
puts "dummy2: #{dummy2}"
クラスの名前は、単にそれを参照する最初の定数の名前です。
つまりmyclass = Class.new
を実行してからMyClass = myclass
を実行すると、クラスの名前はMyClass
になります。ただし、実行時までクラスの名前がわからない場合は、MyClass =
を実行できません。
したがって、代わりにModule#const_set
を使用して、constの値を動的に設定できます。例:
dynamic_name = "ClassName"
Object.const_set(dynamic_name, Class.new { def method1() 42 end })
ClassName.new.method1 #=> 42
私もこれをいじっています。私の場合、ActiveRecord :: Baseの拡張機能をテストしようとしていました。クラスを動的に作成できるようにする必要がありました。アクティブレコードはクラス名に基づいてテーブルを検索するため、そのクラスを匿名にすることはできませんでした。
これがあなたのケースに役立つかどうかはわかりませんが、ここに私が思いついたものがあります:
test_model_class = Class.new(ActiveRecord::Base) do
def self.name
'TestModel'
end
attr_accessor :foo, :bar
end
ActiveRecordに関する限り、self.nameを定義するだけで十分です。これは、クラスが匿名にできないすべての場合に実際に機能すると推測しています。
(sep2kの答えを読んだばかりで、彼の方がいいと思っています。とにかくここに置いておきます。)
これは本当に古い質問であり、他のRubyistがこのためにコミュニティから遠ざけるかもしれませんが、人気のあるJavaプロジェクトを=でラップする非常に薄いラッパーgemの作成に取り組んでいますRubyクラス。@ sepp2kの答えに基づいて、1つのプロジェクトで何度も何度もやらなければならなかったので、いくつかのヘルパーメソッドを作成しました。オブジェクトやカーネルなどのトップレベルの名前空間。
module Redbeam
# helper method to create thin class wrappers easily within the given namespace
#
# @param parent_klass [Class] parent class of the klasses
# @param klasses [Array[String, Class]] 2D array of [class, superclass]
# where each class is a String name of the class to create and superclass
# is the class the new class will inherit from
def self.create_klasses(parent_klass, klasses)
parent_klass.instance_eval do
klasses.each do |klass, superklass|
parent_klass.const_set klass, Class.new(superklass)
end
end
end
# helper method to create thin module wrappers easily within the given namespace
#
# @param parent_klass [Class] parent class of the modules
# @param modules [Array[String, Module]] 2D array of [module, supermodule]
# where each module is a String name of the module to create and supermodule
# is the module the new module will extend
def self.create_modules(parent_klass, modules)
parent_klass.instance_eval do
modules.each do |new_module, supermodule|
parent_klass.const_set new_module, Module.new { extend supermodule }
end
end
end
end
これらのメソッドを使用するには(これはJRubyであることに注意してください):
module Redbeam::Options
Redbeam.create_klasses(self, [
['PipelineOptionsFactory', org.Apache.beam.sdk.options.PipelineOptionsFactory]
])
Redbeam.create_modules(self, [
['PipelineOptions', org.Apache.beam.sdk.options.PipelineOptions]
])
end
なぜ??
これにより、Javaプロジェクトを使用するJRuby gemを作成することができ、必要に応じてオープンソースコミュニティとこれらのクラスを装飾することができます。私のgemは非常に薄いラッパーなので、他のモジュールを拡張するために多くのサブクラスとモジュールを作成する必要がありました。
J.D. Powerで言うように、「これは謝罪による開発です。ごめんなさい」。
次のコードはどうですか:
dynamic_name = "TestEval2"
class_string = """
class #{dynamic_name}
def method1
end
end
"""
eval(class_string)
dummy2 = Object.const_get(dynamic_name)
puts "dummy2: #{dummy2}"
Evalは、ランタイムPCオブジェクトを返しません。少なくとも私のPCでは返されません。 Object.const_getを使用して、Classオブジェクトを取得します。