tutorial rails que ejemplos descargar curso caracteristicas ruby ruby-on-rails-3 class defined

que - ruby on rails tutorial



¿Cómo puedo verificar si una clase está definida? (6)

¿Qué tal const_defined? ?

Recuerde en Rails, hay carga automática en modo de desarrollo, por lo que puede ser complicado cuando lo está probando:

>> 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

¿Cómo convierto una cadena en un nombre de clase, pero solo si esa clase ya existe?

Si Amber ya es una clase, puedo pasar de una cadena a la clase a través de:

Object.const_get("Amber")

o (en Rails)

"Amber".constantize

Pero cualquiera de estos fallará con NameError: uninitialized constant Amber si Amber aún no es una clase.

Mi primer pensamiento es usar el defined? método, pero no discrimina entre las clases que ya existen y las que no:

>> defined?("Object".constantize) => "method" >> defined?("AClassNameThatCouldNotPossiblyExist".constantize) => "method"

Entonces, ¿cómo pruebo si una cadena nombra una clase antes de intentar convertirla? (Bien, ¿qué tal un bloque de begin / rescue para detectar errores de NameError? ¿Demasiado feo? Estoy de acuerdo ...)


Creé un validador para probar si una cadena es un nombre de clase válido (o una lista de nombres de clase válidos separados por comas):

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


En los rieles es realmente fácil:

amber = "Amber".constantize rescue nil if amber # nil result in false # your code here end


Inspirado por la respuesta de @ ctcherry anterior, aquí hay un ''envío de método de clase segura'', donde class_name es una cadena. Si class_name no nombra una clase, devuelve nil.

def class_send(class_name, method, *args) Object.const_defined?(class_name) ? Object.const_get(class_name).send(method, *args) : nil end

Una versión aún más segura que invoca el method solo si class_name responde a ella:

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


Otro enfoque, en caso de que quiera obtener la clase también. Devolverá nil si la clase no está definida, por lo que no tiene que atrapar una excepción.

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


Parecería que todas las respuestas usando el Object.const_defined? método son defectuosos. Si la clase en cuestión no se ha cargado todavía, debido a la carga diferida, la aserción fallará. La única forma de lograr esto definitivamente es así:

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