3 Stimmen

Zugriff auf den Namespace einer Klasse innerhalb eines Moduls

Ich arbeite an einem Modul, das unter anderem der Klasse, in die man es einbindet, eine allgemeine "Finder"-Funktionalität hinzufügen wird. Das Problem: Aus Gründen der Bequemlichkeit und der Ästhetik möchte ich einige Funktionen hinzufügen außerhalb der Klasse, im gleichen Geltungsbereich wie die Klasse selbst.

Zum Beispiel:

class User
  include MyMagicMixin
end

# Should automagically enable:

User.name('Bob')   # Returns first user named Bob
Users.name('Bob')  # Returns ALL users named Bob 
User(5)            # Returns the user with an ID of 5
Users              # Returns all users

Ich kann die Funktionen ausführen innerhalb diese Methoden, kein Problem. Und Fall 1 ( User.name('Bob') ) ist einfach. Die Fälle 2-4 erfordern jedoch die Möglichkeit, neue Klassen und Methoden außerhalb der User . Die Module.included Methode ermöglicht mir den Zugriff auf die Klasse, aber nicht auf den sie enthaltenden Bereich. Es gibt keine einfache Methode vom Typ "parent", die ich bei Class oder Module sehen kann. (Für Namespace, ich meine, nicht Superklasse noch verschachtelte Module).

Der beste Weg, den ich mir vorstellen kann, um dies zu tun ist mit einigen String-Parsing auf die Klasse der #name um den Namespace zu verlassen und die Zeichenkette wieder in eine Konstante zu verwandeln. Aber das scheint ungeschickt, und da dies Ruby ist, habe ich das Gefühl, dass es einen eleganteren Weg geben sollte.

Hat jemand eine Idee? Oder bin ich einfach zu schlau für mein eigenes Wohl?

3voto

jrallison Punkte 609

Ich würde dazu neigen, zu clever zu sein.

Selbst wenn es eine elegante Lösung gäbe, erscheint es doch recht merkwürdig, ein Modul einzubauen innerhalb eine Klasse, die Klassen erzeugt außerhalb die Klasse.

2voto

Jörg W Mittag Punkte 349574

Das ist ein Problem, das manchmal auf den Mailinglisten auftaucht. Es ist auch ein Problem, das in Rails auftritt. Die Lösung ist, wie Sie bereits vermutet haben, im Grunde Regexp munging.

Es gibt jedoch noch ein grundsätzlicheres Problem: In Ruby haben Klassen keinen Namen! Eine Klasse ist einfach ein Objekt wie jedes andere. Sie können sie einer Instanzvariablen, einer lokalen Variablen, einer globalen Variablen, einer Konstanten oder auch gar nichts zuweisen überhaupt . Die Module#name Methode ist im Grunde nur eine einfache Methode, die wie folgt funktioniert: Sie durchsucht die Liste der definierten Konstanten, bis sie eine findet, die auf den Empfänger zeigt. Wenn sie eine findet, gibt sie die erste Konstante zurück, die sie finden kann, andernfalls gibt sie nil .

Es gibt hier also zwei Fehlerarten:

a = Class.new
a.name # => nil
B = a
B.name # => "B"
A = B
A.name # => "B"
  • eine Klasse kann überhaupt keinen Namen haben
  • eine Klasse kann mehr als einen Namen haben, aber Module#name gibt nur den ersten gefundenen Wert zurück

Wenn nun jemand versucht, die As um eine Liste von A s, werden sie ziemlich überrascht sein, wenn sie feststellen, dass es diese Methode nicht gibt, sondern dass sie die Bs um das gleiche Ergebnis zu erzielen.

Diese tut in der Realität passieren. In MacRuby, zum Beispiel String.name gibt zurück. NSMutableString , Hash.name gibt zurück. NSMutableDictionary y Object.name gibt zurück. NSObject . Der Grund dafür ist, dass MacRuby die Ruby-Laufzeit und die Objective-C-Laufzeit in einem integriert. Da die Semantik eines Objective-C-veränderbaren Strings identisch mit der eines Ruby-Strings ist, besteht die gesamte Implementierung der Ruby-String-Klasse im Wesentlichen aus einer einzigen Zeile: String = NSMutableString . Und da MacRuby auf Objective-C aufsetzt, bedeutet dies, dass Objective-C zuerst beginnt, was bedeutet, dass NSMutableString wird zuerst in die Symboltabelle eingefügt, d.h. es wird gefunden zunächst durch Module#name .

1voto

James A. Rosen Punkte 62226

In Ihrem Beispiel, User ist einfach eine Konstante, die auf eine Class Objekt. Sie können leicht erstellen eine andere konstanter Zeiger, wenn MyMagicMixin ist enthalten:

module MyMagicMixin
  class <<self
    def self.included(klass)
      base.extend MyMagicMixin::ClassMethods
      create_pluralized_alias(klass)
    end

    private

    def create_pluralized_alias(klass)
      fq_name = klass.to_s
      class_name = fq_name.demodulize
      including_module = fq_name.sub(Regexp.new("::#{class_name}$", ''))
      including_module = including_module.blank? ? Object : including_module.constantize
      including_module.const_set class_name.pluralize, klass
    end
  end

  module ClassMethods
    # cass methods here
  end
end

Das beantwortet natürlich nicht die Frage, ob Sie sollte so etwas zu tun.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X