रूबी में एक वर्ग के सभी वंशज देखें


144

मैं आसानी से रूबी में वर्ग पदानुक्रम को चढ़ सकता हूं:

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

क्या पदानुक्रम में भी उतरने का कोई तरीका है? मैं यह करना चाहूंगा

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

लेकिन वहाँ एक descendantsविधि नहीं लगती है ।

(यह सवाल इसलिए उठता है क्योंकि मैं एक रेल एप्लिकेशन में सभी मॉडलों को ढूंढना चाहता हूं जो एक बेस क्लास से उतरते हैं और उन्हें सूचीबद्ध करते हैं; मेरे पास एक नियंत्रक है जो ऐसे किसी भी मॉडल के साथ काम कर सकता है और मैं नए मॉडल जोड़ना चाहूंगा नियंत्रक को संशोधित किए बिना।)

जवाबों:


146

यहाँ एक उदाहरण है:

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

डालता है। माता-पिता आपको देते हैं:

GrandChild
Child

बाल डालता है। वंशज आपको देता है:

GrandChild

1
यह महान काम करता है, धन्यवाद! मुझे लगता है कि अगर आप मिलीसेकंड दाढ़ी बनाने की कोशिश कर रहे हैं तो हर वर्ग का दौरा बहुत धीमा हो सकता है, लेकिन यह मेरे लिए पूरी तरह से तेज़ है।
डगलस गिलहरी

1
singleton_classइसके बजाय Classइसे और तेज़ करें ( apidock.com/rails/Class/descendants पर स्रोत देखें )
brauliobo

21
सावधान रहें यदि आपके पास ऐसी स्थिति हो सकती है जहां कक्षा अभी तक मेमोरी में लोड नहीं हुई है, ObjectSpaceतो यह नहीं होगा।
एडमंड ली

मैं यह काम कैसे कर सकता हूं ? Objectऔर BasicObject, यह जानने के लिए उत्सुक हैं कि वे क्या दिखाते हैं
अमोल पुजारी

@AmolPujari p ObjectSpace.each_object(Class)सभी कक्षाओं का प्रिंट आउट लेगा। आप selfविधि में कोड की पंक्ति में इसके नाम के प्रतिस्थापन द्वारा इच्छित किसी भी वर्ग के वंशज भी प्राप्त कर सकते हैं ।
BobRodes

62

यदि आप रेल> = 3 का उपयोग करते हैं, तो आपके पास दो विकल्प हैं। उपयोग करें .descendantsयदि आप बच्चों की कक्षाओं की एक से अधिक स्तर की गहराई चाहते हैं, या .subclassesबाल वर्गों के पहले स्तर के लिए उपयोग करें ।

उदाहरण:

class Animal
end

class Mammal < Animal
end

class Dog < Mammal
end

class Fish < Animal
end

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

6
ध्यान दें कि विकास में, यदि आपके पास उत्सुक लोडिंग बंद है, तो ये विधियाँ केवल कक्षाओं को लौटाएंगी यदि उन्हें लोड किया गया है (अर्थात यदि उन्हें पहले से चल रहे सर्वर द्वारा संदर्भित किया गया है)।
Stephen.hanson

1
@ stephen.hanson यहाँ सही परिणाम की गारंटी देने का सबसे सुरक्षित तरीका क्या है?
क्रिस एडवर्ड्स

26

रूबी 1.9 (या 1.8.7) निफ्टी जंजीर पुनरावृत्तियों के साथ:

#!/usr/bin/env ruby1.9

class Class
  def descendants
    ObjectSpace.each_object(::Class).select {|klass| klass < self }
  end
end

रूबी पूर्व 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

इसका उपयोग ऐसे करें:

#!/usr/bin/env ruby

p Animal.descendants

3
यह मॉड्यूल के लिए भी काम करता है; कोड में "क्लास" के दोनों उदाहरणों को "मॉड्यूल" से बदलें।
कोरिंथे

2
अतिरिक्त सुरक्षा के लिए किसी को लिखना चाहिए ObjectSpace.each_object(::Class)- यह तब काम करता रहेगा जब आप एक YourModule :: क्लास डिफाइन्ड होते हैं।
रेने सारसो

19

विरासत में मिली कक्षा विधि को ओवरराइड करें । इस विधि को उपवर्ग पास किया जाएगा जब यह बनाया जाता है जिसे आप ट्रैक कर सकते हैं।


मुझे यह भी पसंद है। विधि को ओवरराइड करने से थोड़ी घुसपैठ होती है, लेकिन यह वंशज विधि को थोड़ा अधिक कुशल बनाता है क्योंकि आपको हर वर्ग का दौरा नहीं करना पड़ता है।
डगलस गिलहरी

@ डगलस जबकि यह कम दखलंदाजी है, तो आपको शायद यह देखने के लिए प्रयोग करना होगा कि क्या यह आपकी आवश्यकताओं को पूरा करता है (यानी जब रेल नियंत्रक / मॉडल पदानुक्रम का निर्माण करता है?)।
जोश ली

यह विभिन्न गैर-एमआरआई रूबी कार्यान्वयन के लिए अधिक पोर्टेबल है, जिनमें से कुछ में ऑब्जेक्टस्पेस के उपयोग से गंभीर प्रदर्शन ओवरहेड है। रूबी में "ऑटो-पंजीकरण" पैटर्न लागू करने के लिए वर्ग # विरासत में मिला है।
जॉन व्हिटली

एक उदाहरण साझा करने के लिए देखभाल? चूंकि यह कक्षा स्तर है, इसलिए मुझे लगता है कि आपको प्रत्येक वर्ग को किसी प्रकार के वैश्विक चर में संग्रहीत करना होगा?
Noz

@Noz नहीं, केवल कक्षा पर एक उदाहरण चर। लेकिन तब वस्तुओं को जीसी द्वारा एकत्र नहीं किया जा सकता है।
Phrogz

13

वैकल्पिक रूप से (रूबी 1.9+ के लिए अद्यतन):

ObjectSpace.each_object(YourRootClass.singleton_class)

रूबी 1.8 संगत तरीका:

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

ध्यान दें कि यह मॉड्यूल के लिए काम नहीं करेगा। साथ ही, YourRootClass को उत्तर में शामिल किया जाएगा। आप इसे हटाने के लिए Array # - या किसी अन्य तरीके का उपयोग कर सकते हैं।


वह बहोत अच्छा था। क्या आप मुझे समझा सकते हैं कि यह कैसे काम करता है? मैंने इस्तेमाल कियाObjectSpace.each_object(class<<MyClass;self;end) {|it| puts it}
डेविड वेस्ट

1
रूबी 1.8 में, class<<some_obj;self;endकिसी ऑब्जेक्ट का सिंगलटन_क्लास लौटाता है। 1.9+ में आप इसके some_obj.singleton_classबजाय उपयोग कर सकते हैं (प्रतिबिंबित करने के लिए मेरा उत्तर अपडेट किया गया है)। प्रत्येक वस्तु अपने सिंगलटन_क्लास का एक उदाहरण है, जो कक्षाओं के लिए भी लागू होती है। चूँकि प्रत्येक_ओजेक्ट (SomeClass) SomeClass के सभी उदाहरण लौटाता है, और SomeClass, SomeClass.singleton_class का एक उदाहरण है, प्रत्येक_object (SomeClass.singleton_class) SomeClass और सभी उपवर्ग लौटाएगा।
एपिरोस

10

यद्यपि ऑब्जेक्टस्पेस कार्यों का उपयोग करते हुए, वंशानुगत वर्ग विधि विरासत में मिली (उपवर्ग) रूबी प्रलेखन के लिए बेहतर उपयुक्त लगती है

ऑब्जेक्टस्पेस अनिवार्य रूप से कुछ भी और सब कुछ एक्सेस करने का एक तरीका है जो वर्तमान में आवंटित मेमोरी का उपयोग कर रहा है, इसलिए यह जांचने के लिए कि अगर यह एनिमल क्लास का सब-आइडल आदर्श नहीं है, तो इसके हर एक तत्व पर पुनरावृत्ति करें।

नीचे दिए गए कोड में, विरासत में मिला पशु वर्ग विधि एक कॉलबैक को लागू करती है जो किसी भी नए बनाए गए उपवर्ग को अपने वंशज सरणी में जोड़ देगा।

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

  def self.descendants
    puts @descendants 
  end
end

6
`@desdesants = = []` अन्यथा आपको केवल अंतिम वंशज मिलेगा
एक्सल टेट्ज़ाल्फ़

4

मैं जानता हूँ कि आप कैसे विरासत में ऐसा करने के लिए कह रहे हैं, लेकिन आप नाम-रिक्ति वर्ग द्वारा रूबी में सीधे के साथ इस लक्ष्य को हासिल कर सकते हैं ( Classया 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]

यह इस तरह से बहुत तेजी से हर वर्ग की तुलना में ObjectSpaceअन्य समाधानों की तरह है।

यदि आपको गंभीरता से विरासत में इसकी आवश्यकता है तो आप कुछ इस तरह से कर सकते हैं:

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

यहाँ बेंचमार्क: http://www.eq8.eu/blogs/13-ruby-ancestors-descendants-and-other-annoying-relatives

अपडेट करें

अंत में आपको बस यही करना है

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

  def self.descendants
    @descendants || []
  end
end

class Foo < DarthVader
end

DarthVader.descendants #=> [Foo]

सुझाव के लिए @saturnflyer धन्यवाद


3

(रेल्स <= 3.0) वैकल्पिक रूप से आप ActiveSupport :: DescendantsTracker का उपयोग कर सकते हैं। स्रोत से:

यह मॉड्यूल वंशज को ट्रैक करने के लिए एक आंतरिक कार्यान्वयन प्रदान करता है जो ऑब्जेक्टस्पेस के माध्यम से चलने की तुलना में तेज़ है।

चूंकि यह अच्छी तरह से modularize है, आप अपने रूबी ऐप के लिए उस विशेष मॉड्यूल को केवल 'चेरी-पिक' कर सकते हैं।


2

रूबी पहलुओं में कक्षा # वंशज हैं,

require 'facets/class/descendants'

यह एक जनरेशनल डिस्टेंस पैरामीटर का भी समर्थन करता है।


2

एक साधारण संस्करण जो एक वर्ग के सभी वंशजों को एक सरणी देता है:

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

1
यह एक बेहतर उत्तर की तरह दिखता है। दुर्भाग्य से यह अभी भी कक्षाओं के आलसी-लोडिंग का शिकार है। लेकिन मुझे लगता है कि वे सब करते हैं।
डेव मोर्स

@DaveMorse मैंने फाइलों को सूचीबद्ध करना और स्थिरांक को स्वयं लोड करना समाप्त कर दिया है ताकि उन्हें वंशज के रूप में पंजीकृत किया जा सके (और फिर इस पूरी चीज़ को हटाकर: D)
डोरियन

1
ध्यान दें कि #subclassesरेल ActiveSupport से है।
एलेक्स डी

1

आप विधि का require 'active_support/core_ext'उपयोग कर सकते हैं descendantsडॉक्टर की जाँच करें , और इसे आईआरबी या प्राइ में एक शॉट दें। रेल के बिना इस्तेमाल किया जा सकता है।


1
यदि आपको अपने जेमफाइल में सक्रिय समर्थन जोड़ना है, तो यह वास्तव में "रेल के बिना" नहीं है। यह सिर्फ आपके पसंद के रेल के टुकड़ों को चुन रहा है।
कालेब

1
यह एक दार्शनिक स्पर्शरेखा की तरह लगता है जो यहां विषय के लिए प्रासंगिक नहीं है, लेकिन मुझे लगता है कि रेल घटक का उपयोग करने का मतलब यह है कि using Railsएक समग्र अर्थ में है।
Thelostspore

0

रेल हर वस्तु के लिए एक उपवर्ग विधि प्रदान करती है, लेकिन यह अच्छी तरह से प्रलेखित नहीं है, और मुझे नहीं पता कि यह कहां परिभाषित है। यह स्ट्रिंग के रूप में वर्ग नामों की एक सरणी देता है।


0

वंशज_ट्रैकर रत्न का उपयोग करने से मदद मिल सकती है। निम्नलिखित उदाहरण मणि के डॉक्टर से कॉपी किया गया है:

class Foo
  extend DescendantsTracker
end

class Bar < Foo
end

Foo.descendants # => [Bar]

इस रत्न का उपयोग लोकप्रिय पुण्य रत्न द्वारा किया जाता है , इसलिए मुझे लगता है कि यह बहुत ठोस है।


0

यह विधि किसी ऑब्जेक्ट के सभी वंशजों के बहुआयामी हैश को लौटा देगी।

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

{ MasterClass => descendants_mapper(MasterClass) }

-1

यदि आपके पास किसी भी उपवर्ग को लोड करने से पहले कोड तक पहुंच है, तो आप विरासत में मिली विधि का उपयोग कर सकते हैं ।

यदि आप ऐसा नहीं करते हैं (जो कि मामला नहीं है, लेकिन यह किसी के लिए उपयोगी हो सकता है जिसने यह पोस्ट पाया है) तो आप बस लिख सकते हैं:

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

खेद है कि अगर मैं वाक्य रचना से चूक गया लेकिन विचार स्पष्ट होना चाहिए (इस समय मेरे पास माणिक की पहुंच नहीं है)।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.