objetos manejo lista instanciar herencia clases clase ruby

manejo - Busca a todos los descendientes de una clase en Ruby



objetos y clases en ruby (15)

(Rails <= 3.0) Alternativamente, podría usar ActiveSupport :: DescendantsTracker para realizar la escritura. De la fuente:

Este módulo proporciona una implementación interna para rastrear descendientes, que es más rápido que iterar a través de ObjectSpace.

Dado que se modulariza muy bien, puede simplemente ''seleccionar'' ese módulo en particular para su aplicación Ruby.

Puedo ascender fácilmente la jerarquía de clases en Ruby:

String.ancestors # [String, Enumerable, Comparable, Object, Kernel] Enumerable.ancestors # [Enumerable] Comparable.ancestors # [Comparable] Object.ancestors # [Object, Kernel] Kernel.ancestors # [Kernel]

¿Hay alguna forma de descender también de la jerarquía? Me gustaría hacer esto

Animal.descendants # [Dog, Cat, Human, ...] Dog.descendants # [Labrador, GreatDane, Airedale, ...] Enumerable.descendants # [String, Array, ...]

pero no parece haber un método de descendants .

(Esta pregunta surge porque quiero encontrar todos los modelos en una aplicación Rails que descienden de una clase base y listarlos; tengo un controlador que puede funcionar con cualquier modelo y me gustaría poder agregar nuevos modelos sin tener que modificar el controlador.)


Alternativamente (actualizado para ruby ​​1.9+):

ObjectSpace.each_object(YourRootClass.singleton_class)

Manera compatible con Ruby 1.8:

ObjectSpace.each_object(class<<YourRootClass;self;end)

Tenga en cuenta que esto no funcionará para los módulos. Además, YourRootClass se incluirá en la respuesta. Puede usar Array # - u otra forma de eliminarlo.


Aquí hay un ejemplo:

class Parent def self.descendants ObjectSpace.each_object(Class).select { |klass| klass < self } end end class Child < Parent end class GrandChild < Child end puts Parent.descendants puts Child.descendants

pone Parent.descendants te da:

GrandChild Child

pone Child.descendants te da:

GrandChild


Aunque el uso de ObjectSpace funciona, el método de clase heredado parece ser más adecuado aquí . Documentación de Ruby heredada (subclase)

El espacio de objetos es esencialmente una forma de acceder a cualquier cosa y a todo lo que está usando actualmente la memoria asignada, por lo que iterar sobre cada uno de sus elementos para comprobar si es una subclase de la clase Animal no es ideal.

En el siguiente código, el método heredado de clase Animal implementa una devolución de llamada que agregará cualquier subclase recién creada a su matriz de descendientes.

class Animal def self.inherited(subclass) @descendants = [] @descendants << subclass end def self.descendants puts @descendants end end


Este método devolverá un hash multidimensional de todos los descendientes de un Objeto.

def descendants_mapper(klass) klass.subclasses.reduce({}){ |memo, subclass| memo[subclass] = descendants_mapper(subclass); memo } end { MasterClass => descendants_mapper(MasterClass) }


Puede require ''active_support/core_ext'' y usar el método de descendants . Mira el documento y pruébalo en IRB o haz palanca. Se puede usar sin rieles.


Rails proporciona un método de subclases para cada objeto, pero no está bien documentado, y no sé dónde está definido. Devuelve una matriz de nombres de clase como cadenas.


Reemplaza el método de clase llamado inherited . A este método se le pasará la subclase cuando se crea y se puede seguir.


Ruby Facets tiene descendientes de clase #,

require ''facets/class/descendants''

También es compatible con un parámetro de distancia generacional.


Sé que estás preguntando cómo hacer esto en herencia, pero puedes lograrlo directamente en Ruby separando los nombres de la clase ( Class o Module )

module DarthVader module DarkForce end BlowUpDeathStar = Class.new(StandardError) class Luck end class Lea end end DarthVader.constants # => [:DarkForce, :BlowUpDeathStar, :Luck, :Lea] DarthVader .constants .map { |class_symbol| DarthVader.const_get(class_symbol) } .select { |c| !c.ancestors.include?(StandardError) && c.class != Module } # => [DarthVader::Luck, DarthVader::Lea]

Es mucho más rápido de esta manera que la comparación con todas las clases en ObjectSpace como proponen otras soluciones.

Si realmente necesita esto en una herencia, puede hacer algo como esto:

class DarthVader def self.descendants DarthVader .constants .map { |class_symbol| DarthVader.const_get(class_symbol) } end class Luck < DarthVader # ... end class Lea < DarthVader # ... end def force ''May the Force be with you'' end end

puntos de referencia aquí: http://www.eq8.eu/blogs/13-ruby-ancestors-descendants-and-other-annoying-relatives

actualizar

al final todo lo que tienes que hacer es esto

class DarthVader def self.inherited(klass) @descendants ||= [] @descendants << klass end def self.descendants @descendants || [] end end class Foo < DarthVader end DarthVader.descendants #=> [Foo]

gracias @saturnflyer por sugerencia


Si tiene acceso al código antes de cargar cualquier subclase, puede usar el método inherited .

Si no lo hace (que no es un caso pero puede ser útil para cualquier persona que encuentre esta publicación) puede escribir:

x = {} ObjectSpace.each_object(Class) do |klass| x[klass.superclass] ||= [] x[klass.superclass].push klass end x[String]

Lo siento si me perdí la sintaxis, pero la idea debería ser clara (no tengo acceso a ruby ​​en este momento).


Si usa Rails> = 3, tiene dos opciones en su lugar. Utilice apidock.com/rails/Class/descendants si desea más de una profundidad de nivel de clases de niños, o use .subclasses para el primer nivel de clases de niños.

Ejemplo:

class Animal end class Mammal < Animal end class Dog < Mammal end class Fish < Animal end Animal.subclasses #=> [Mammal, Fish] Animal.descendants #=> [Dog, Mammal, Fish]


Una versión simple que proporciona una matriz de todos los descendientes de una clase:

def descendants(klass) all_classes = klass.subclasses (all_classes + all_classes.map { |c| descendants(c) }.reject(&:empty?)).flatten end


Usar la gema descenddants_tracker puede ayudar. El siguiente ejemplo se copia del documento de la joya:

class Foo extend DescendantsTracker end class Bar < Foo end Foo.descendants # => [Bar]

Esta gema es utilizada por la popular gema virtus , así que creo que es bastante sólida.


Ruby 1.9 (o 1.8.7) con ingeniosos iteradores encadenados:

#!/usr/bin/env ruby1.9 class Class def descendants ObjectSpace.each_object(::Class).select {|klass| klass < self } end end

Ruby pre-1.8.7:

#!/usr/bin/env ruby class Class def descendants result = [] ObjectSpace.each_object(::Class) {|klass| result << klass if klass < self } result end end

Úselo así:

#!/usr/bin/env ruby p Animal.descendants