2013-01-14 6 views
27

Gibt es in Ruby eine Möglichkeit für eine Klasse zu wissen, wie viele Instanzen davon existieren und kann sie aufgelistet werden?Wie listet ich alle Objekte auf, die in Ruby aus einer Klasse erstellt wurden?

Hier eine Beispielklasse:

class Project 

    attr_accessor :name, :tasks 

    def initialize(options) 
    @name = options[:name] 
    @tasks = options[:tasks] 
    end 

    def self.all 
    # return listing of project objects 
    end 

    def self.count 
      # return a count of existing projects 
    end 


end 

Jetzt schaffe ich Projekt Objekte dieser Klasse:

options1 = { 
    name: 'Building house', 
    priority: 2, 
    tasks: [] 
} 

options2 = { 
    name: 'Getting a loan from the Bank', 
    priority: 3, 
    tasks: [] 
} 

@project1 = Project.new(options1) 
@project2 = Project.new(options2) 

Was Ich mag würde, ist Klassenmethoden wie Project.all und Project.count haben eine Liste zurückzukehren und zählen Sie aktuelle Projekte.

Wie mache ich das?

Antwort

40

Sie können hierfür das Modul ObjectSpace verwenden, insbesondere die Methode each_object.

ObjectSpace.each_object(Project).count 

Der Vollständigkeit halber ist hier, wie Sie diese in Ihrer Klasse (Hut Spitze zu Sawa) verwenden würde

class Project 
    # ... 

    def self.all 
    ObjectSpace.each_object(self).to_a 
    end 

    def self.count 
    all.count 
    end 
end 
+0

Müssen Sie in der Klasse "ObjectSpace" einschließen, damit dies funktioniert? – onebree

+2

@HunterStevens nein, wir mischen das Modul nicht in unsere Klasse ein, rufen nur eine Methode darauf –

+0

** WARNUNG **: Diese Lösung kann es leicht machen, sich in den Fuß zu schießen. Wenn Sie keinen Verweis auf Ihre Objekte behalten (zum Beispiel, wenn Sie 'Project.new' durchführen, ohne das Ergebnis etwas zuzuweisen), werden sie zu einem gewissen Zeitpunkt unbemerkt gesammelt und' ObjectSpace.each_object' hört natürlich auf, sie zu melden. Die Verwendung von '@@ instances = []' statt wie in der Antwort von rohit89 löst dieses Problem, indem ein Verweis auf diese Objekte beibehalten wird. – vmarquet

5

Eine Möglichkeit besteht darin, bei der Erstellung neuer Instanzen den Überblick zu behalten.

class Project 

    @@count = 0 
    @@instances = [] 

    def initialize(options) 
      @@count += 1 
      @@instances << self 
    end 

    def self.all 
     @@instances.inspect 
    end 

    def self.count 
     @@count 
    end 

end 

Wenn Sie ObjectSpace verwenden, seine dann

def self.count 
    ObjectSpace.each_object(self).count 
end 

def self.all 
    ObjectSpace.each_object(self).to_a 
end 
+0

Dies ist, was ich tun würde. Es funktioniert in allen Ruby-Implementierungen mit Sicherheit und kann bei Bedarf für verschiedene Zwecke erweitert werden. –

2

dies Vielleicht arbeiten:

class Project 
    class << self; attr_accessor :instances; end 

    attr_accessor :name, :tasks 

    def initialize(options) 
    @name = options[:name] 
    @tasks = options[:tasks] 

    self.class.instances ||= Array.new 
    self.class.instances << self 
    end 

    def self.all 
    # return listing of project objects 
    instances ? instances.dup : [] 
    end 

    def self.count 
    # return a count of existing projects 
    instances ? instances.count : 0 
    end 

    def destroy 
    self.class.instances.delete(self) 
    end 
end 

Aber Sie werden diese Objekte manuell zerstören müssen. Möglicherweise kann eine andere Lösung basierend auf dem ObjectSpace-Modul erstellt werden.

+0

Ich mag das, aber kann es einige eingebaute Reflektion geben - Gibt es das nicht in Rubin? Ich habe keine Ahnung, wie das ObjectSpace-Modul verwendet wird. Beispiel würde wirklich helfen –

+1

Gut. Mit ObjectSpace können Sie mit Garbage Collector interagieren. Das ist etwas, was ich in meinem Code nicht zu tun versuche. Sie können mit 'ObjectSpace.each_object (Project) .to_a' experimentieren, aber ich kann Ihnen nicht mehr helfen. – yattering

+0

einen bestimmten Grund, warum dies vermieden werden sollte? –

4
class Project 
    def self.all; ObjectSpace.each_object(self).to_a end 
    def self.count; all.length end 
end