Wie verwandle ich eine Zeichenfolge in einen Klassennamen, aber nur, wenn diese Klasse bereits vorhanden ist?
Wenn Amber bereits eine Klasse ist, kann ich von einem String zur Klasse gelangen über:
Object.const_get("Amber")
oder (in Schienen)
"Amber".constantize
Aber beides wird scheitern, NameError: uninitialized constant Amber
wenn Amber noch keine Klasse ist.
Mein erster Gedanke ist, die defined?
Methode zu verwenden, aber sie unterscheidet nicht zwischen bereits existierenden und nicht existierenden Klassen:
>> defined?("Object".constantize)
=> "method"
>> defined?("AClassNameThatCouldNotPossiblyExist".constantize)
=> "method"
Wie teste ich, ob eine Zeichenfolge eine Klasse benennt, bevor ich versuche, sie zu konvertieren? (Okay, wie wäre es mit einem begin
/ rescue
Block, um NameError-Fehler abzufangen? Zu hässlich? Ich stimme zu ...)
ruby
ruby-on-rails-3
class
defined
furchtloser Dummkopf
quelle
quelle
defined?
Im Beispiel wird genau das getan, was es tun soll: Es wird überprüft, ob dieconstantize
Methode für ein String-Objekt definiert ist. Es ist egal, ob die Zeichenfolge "Object" oder "AClassNameThatCouldNotPossiblyExist" enthält.Antworten:
Wie wäre es
const_defined?
?Denken Sie daran, dass in Rails das automatische Laden im Entwicklungsmodus erfolgt. Daher kann es beim Testen schwierig sein:
>> Object.const_defined?('Account') => false >> Account => Account(id: integer, username: string, google_api_key: string, created_at: datetime, updated_at: datetime, is_active: boolean, randomize_search_results: boolean, contact_url: string, hide_featured_results: boolean, paginate_search_results: boolean) >> Object.const_defined?('Account') => true
quelle
In Schienen ist es wirklich einfach:
amber = "Amber".constantize rescue nil if amber # nil result in false # your code here end
quelle
rescue
war hilfreich, weil manchmal Konstanten entladen werden können und die Überprüfung mitconst_defined?
falsch ist.Inspiriert von der obigen Antwort von @ ctcherry, ist hier eine 'sichere Klassenmethode send', bei der
class_name
es sich um eine Zeichenfolge handelt. Wennclass_name
keine Klasse benannt wird, wird null zurückgegeben.def class_send(class_name, method, *args) Object.const_defined?(class_name) ? Object.const_get(class_name).send(method, *args) : nil end
Eine noch sicherere Version, die
method
nur aufruft , wenn sie daraufclass_name
reagiert:def class_send(class_name, method, *args) return nil unless Object.const_defined?(class_name) c = Object.const_get(class_name) c.respond_to?(method) ? c.send(method, *args) : nil end
quelle
Es scheint, dass alle Antworten, die die
Object.const_defined?
Methode verwenden, fehlerhaft sind. Wenn die betreffende Klasse aufgrund des verzögerten Ladens noch nicht geladen wurde, schlägt die Zusicherung fehl. Der einzige Weg, dies definitiv zu erreichen, ist folgender:validate :adapter_exists def adapter_exists # cannot use const_defined because of lazy loading it seems Object.const_get("Irs::#{adapter_name}") rescue NameError => e errors.add(:adapter_name, 'does not have an IrsAdapter') end
quelle
Ich habe einen Validator erstellt, um zu testen, ob eine Zeichenfolge ein gültiger Klassenname (oder eine durch Kommas getrennte Liste gültiger Klassennamen) ist:
class ClassValidator < ActiveModel::EachValidator def validate_each(record,attribute,value) unless value.split(',').map { |s| s.strip.constantize.is_a?(Class) rescue false }.all? record.errors.add attribute, 'must be a valid Ruby class name (comma-separated list allowed)' end end end
quelle
Ein anderer Ansatz, falls Sie die Klasse auch erhalten möchten. Gibt null zurück, wenn die Klasse nicht definiert ist, sodass Sie keine Ausnahme abfangen müssen.
class String def to_class(class_name) begin class_name = class_name.classify (optional bonus feature if using Rails) Object.const_get(class_name) rescue # swallow as we want to return nil end end end > 'Article'.to_class class Article > 'NoSuchThing'.to_class nil # use it to check if defined > puts 'Hello yes this is class' if 'Article'.to_class Hello yes this is class
quelle