Wie instanziiere ich eine Klasse aus einer Namenszeichenfolge in Rails?

77

Wie können wir eine Klasse aus ihrer Namenszeichenfolge in Ruby-on-Rails instanziieren?

Zum Beispiel haben wir den Namen in der Datenbank in einem Format wie "ClassName" oder "my_super_class_name".

Wie können wir daraus ein Objekt erstellen?

Lösung:

Habe selbst danach gesucht, aber nicht gefunden, also hier ist es. Ruby-on-Rails-API-Methode

name = "ClassName"
instance = name.constantize.new  

Es kann sogar nicht formatiert werden, wir können die Zeichenfolgenmethode .classify verwenden

name = "my_super_class"
instance = name.classify.constantize.new

Natürlich ist dies vielleicht nicht sehr "Rails Way", aber es löst seinen Zweck.

Vjatseslav Gedrovits
quelle
4
Nur zu Ihrer Information, Konstante ist eine ActiveSupport-Komfortmethode, die dies tut, Object.const_getund Classify ist eine ActiveSupport-Methode, die versucht, eine Zeichenfolge in eine Standardklassenformatierung umzuwandeln. Was Sie tun, ist identisch mit Evgineys Antwort, mit einigen zusätzlichen Überprüfungen. Während das Konstantisieren wahrscheinlich eine bessere Lösung ist (weil es Sanity Checks durchführt), hilft es, die von Ihnen verwendeten Tools zu verstehen.
Quandrum
Vielen Dank dafür, um ehrlich zu sein, habe nicht überprüft, was es im Handbuch tut.
Vjatseslav Gedrovits

Antworten:

77
klass = Object.const_get "ClassName"

über Klassenmethoden

class KlassExample
    def self.klass_method
        puts "Hello World from Class method"
    end
end
klass = Object.const_get "KlassExample"
klass.klass_method

irb(main):061:0> klass.klass_method
Hello World from Class method
Eugene Rourke
quelle
Damit können Sie nicht auf Klassenmethoden zugreifen. Meine obige Lösung funktioniert korrekt mit Klassenmethoden.
Vjatseslav Gedrovits
Loading development environment (Rails 3.2.9) irb(main):001:0> name = "PartnerGateway" => "PartnerGateway" irb(main):002:0> klass = name.constantize.new => #<PartnerGateway id: nil, name: nil, partner_id: nil, gateway: nil, changed_by_id: nil, deleted_at: nil, created_at: nil, updated_at: nil> irb(main):003:0> klass.name => nil
Vjatseslav Gedrovits
irb(main):005:0> klass2 = Object.const_get name => PartnerGateway(id: integer, name: string, partner_id: integer, gateway: string, changed_by_id: integer, deleted_at: datetime, created_at: datetime, updated_at: datetime) irb(main):006:0> klass2.name => "PartnerGateway" irb(main):008:0> klass2.id NoMethodError: undefined method id 'für # <Klasse: 0xac3e4c0> `
Vjatseslav Gedrovits
Mit meiner Methode können Sie beides tun. "KlassExample" .constantize.self_method_name und klass = "KlassExample" .constantize.new klass.normal_method
Vjatseslav Gedrovits
Ja ... es beweist nichts, was Sie offensichtlich nicht als neu bezeichnen. Verwenden Sie "Object.const_get". Versuchen Sie es mit klass2.new.name
Eugene Rourke
40

Andere suchen möglicherweise auch nach einer Alternative, die keinen Fehler auslöst, wenn die Klasse nicht gefunden wird. safe_constantizeist genau das.

class MyClass
end

"my_class".classify.safe_constantize.new #  #<MyClass:0x007fec3a96b8a0>
"omg_evil".classify.safe_constantize.new #  nil 
Maninalift
quelle
8

Sie können einfach eine Zeichenfolge konvertieren und eine Klasse daraus initialisieren, indem Sie:

klass_name = "Module::ClassName"
klass_name.constantize

So initialisieren Sie ein neues Objekt:

klass_name.constantize.new

Ich hoffe, dass sich dies als hilfreich herausstellt. Vielen Dank!

Pushp Raj Saurabh
quelle
Benutze das nicht! Es ist ein sehr verletzlicher Ansatz: praetorian.com/blog/ruby-unsafe-reflection-vulnerabilities
TiSer
@TiSer Können Sie bitte erklären, wie anfällig es ist, solange ich keine Zeichenfolge aus meinen Anforderungsparametern konstant halte?
Pushp Raj Saurabh
Danke für den Link zum Blog. Das ist sehr hilfreich. Solange wir die Konstantisierung verwenden, ohne Anforderungsparameter an sie zu übergeben und sie in eine private Funktion einzuschließen, sehe ich keine Bedrohung.
Pushp Raj Saurabh
Diese Zeile in dem von Ihnen freigegebenen Blog ist äußerst wichtig, wenn Sie mit Reflexionen arbeiten: Reflexion wird verwendet, um die Art eines Programms zur Laufzeit zu ändern, und sollte nicht mit Zeichenfolgen aus nicht vertrauenswürdigen Quellen verwendet werden.
Pushp Raj Saurabh
1
In Ihrem direkten Beispiel - ja, aber ich weiß nicht, wo Sie eine solche Objekttransformation von einer einzelnen ganzen Zeichenfolge zu einer Klasse in der realen Welt durchführen möchten.
TiSer
5

Ich bin überrascht, dass niemand in seinen Antworten über Sicherheit und Hacking nachdenkt. Die Instanziierung einer beliebigen Zeichenfolge, die wahrscheinlich sogar indirekt aus Benutzereingaben stammt, erfordert Probleme und Hacking. Wir alle sollten / müssen auf der Whitelist stehen, es sei denn, wir sind sicher, dass die Zeichenfolge vollständig kontrolliert und überwacht wird

def class_for(name)
  {
    "foo" => Foo,
    "bar" => Bar,
  }[name] || raise UnknownClass
end

class_for(name_wherever_this_came_from).create!(params_somehow)

Wie Sie die entsprechenden Parameter willkürlich kennen würden, ohne eine Whitelist zu haben, wäre eine Herausforderung, aber Sie haben die Idee.

RubesMN
quelle