Hintergrund:
Ich habe ein Modul, das eine Reihe von Instanzmethoden deklariert
module UsefulThings
def get_file; ...
def delete_file; ...
def format_text(x); ...
end
Und ich möchte einige dieser Methoden innerhalb einer Klasse aufrufen. Wie Sie dies normalerweise in Rubin tun, ist wie folgt:
class UsefulWorker
include UsefulThings
def do_work
format_text("abc")
...
end
end
Problem
include UsefulThings
bringt alle Methoden aus UsefulThings
. In diesem Fall will ich nur format_text
und will explizit nicht get_file
und delete_file
.
Ich kann mehrere mögliche Lösungen dafür sehen:
- Rufen Sie die Methode irgendwie direkt auf dem Modul auf, ohne sie irgendwo einzuschließen
- Ich weiß nicht, wie / ob das geht. (Daher diese Frage)
- Irgendwie einschließen
Usefulthings
und nur einige seiner Methoden einbringen- Ich weiß auch nicht, wie / ob das geht
- Erstellen Sie eine Proxy-Klasse, schließen Sie
UsefulThings
diese ein und delegieren Sie sieformat_text
an diese Proxy-Instanz- Dies würde funktionieren, aber anonyme Proxy-Klassen sind ein Hack. Yuck.
- Teilen Sie das Modul in 2 oder mehr kleinere Module auf
- Dies würde auch funktionieren und ist wahrscheinlich die beste Lösung, die ich mir vorstellen kann, aber ich würde es vorziehen, sie zu vermeiden, da sich am Ende Dutzende und Dutzende von Modulen ansammeln würden - dies zu verwalten wäre mühsam
Warum gibt es in einem Modul viele unabhängige Funktionen? Es stammt ApplicationHelper
aus einer Rails-App, die unser Team de facto als Müllhalde für alles ausgewählt hat, was nicht spezifisch genug ist, um irgendwohin zu gehören. Meist eigenständige Dienstprogrammmethoden, die überall verwendet werden. Ich könnte es in separate Helfer aufteilen, aber es würden 30 von ihnen sein, alle mit jeweils einer Methode ... das scheint unproduktiv
Module#included
Rückruf verwenden , um eineninclude
der anderen auszulösen . Dieformat_text
Methode könnte in ein eigenes Modul verschoben werden, da sie für sich genommen nützlich zu sein scheint. Dies würde das Management etwas weniger belasten.module UT; def add1; self+1; end; def add2; self+2; end; end
und möchten verwenden,add1
aber nichtadd2
im UnterrichtFixnum
. Wie würde es helfen, Modulfunktionen dafür zu haben? Vermisse ich etwasAntworten:
Wenn eine Methode auf einem Modul in eine Modulfunktion umgewandelt wird, können Sie sie einfach von Mods abrufen, als ob sie als deklariert worden wäre
Mit dem folgenden Ansatz für module_function wird vermieden, dass Klassen beschädigt werden, die alle Mods enthalten.
Ich bin jedoch neugierig, warum eine Reihe von nicht verwandten Funktionen überhaupt in demselben Modul enthalten sind.
Bearbeitet, um zu zeigen, dass auch noch funktioniert, wenn danach
public :foo
aufgerufen wirdmodule_function :foo
quelle
module_function
Nebenbei bemerkt , verwandelt die Methode in eine private, die anderen Code brechen würde - sonst wäre dies die akzeptierte AntwortFiles.truncate
und ein gibtStrings.truncate
und ich beide explizit in derselben Klasse verwenden möchte. Das Erstellen einer neuen Klasse / Instanz jedes Mal, wenn ich eine bestimmte Methode benötige oder das Original ändere, ist kein guter Ansatz, obwohl ich kein Ruby-Entwickler bin.Ich denke, der kürzeste Weg, einen einzelnen Anruf einfach wegzuwerfen (ohne vorhandene Module zu ändern oder neue zu erstellen), wäre folgender:
quelle
Object.new.extend(UsefulThings).get_file
Eine andere Möglichkeit, dies zu tun, wenn Sie das Modul "besitzen", ist die Verwendung
module_function
.quelle
ModuleName.method :method_name
, um ein Methodenobjekt zu erhalten und es über aufzurufenmethod_obj.call
. Andernfalls müsste ich die Methode an eine Instanz des ursprünglichen Objekts binden, was nicht möglich ist, wenn das ursprüngliche Objekt ein Modul ist. Als Reaktion auf Orion Edwards wirdmodule_function
die ursprüngliche Instanzmethode privat. ruby-doc.org/core/classes/Module.html#M001642def self.a; puts 'aaay'; end
Wenn Sie diese Methoden aufrufen möchten, ohne das Modul in eine andere Klasse aufzunehmen, müssen Sie sie als Modulmethoden definieren:
und dann können Sie sie mit anrufen
oder
Trotzdem würde ich empfehlen, dass Sie nur verwandte Methoden in ein Modul oder in eine Klasse einfügen. Wenn Sie das Problem haben, dass Sie nur eine Methode aus dem Modul einschließen möchten, klingt dies nach einem schlechten Codegeruch und es ist kein guter Ruby-Stil, nicht verwandte Methoden zusammenzustellen.
quelle
So rufen Sie eine Modulinstanzmethode auf, ohne das Modul einzuschließen (und ohne Zwischenobjekte zu erstellen):
quelle
format_text
die Existenz einer anderen vom Modul bereitgestellten Methode, die (im Allgemeinen) nicht vorhanden ist.Erstens würde ich empfehlen, das Modul in die nützlichen Dinge aufzuteilen, die Sie benötigen. Sie können jedoch jederzeit eine Klasse erstellen, die diese für Ihren Aufruf erweitert:
quelle
A. Wenn Sie sie immer "qualifiziert" und eigenständig aufrufen möchten (UsefulThings.get_file), machen Sie sie einfach statisch, wie andere betonten.
B. Wenn Sie immer noch den mixin Ansatz in gleichen Fällen behalten wollen, als auch das einmaligen Standalone - Aufruf können Sie haben ein Einzeiler - Modul , das sich selbst mit dem mixin:
Also funktioniert beides dann:
IMHO ist es sauberer als
module_function
für jede einzelne Methode - falls Sie alle wollen.quelle
extend self
ist eine gängige Redewendung.Nach meinem Verständnis möchten Sie einige Instanzmethoden eines Moduls in eine Klasse mischen.
Lassen Sie uns zunächst überlegen, wie Modul # include funktioniert. Angenommen, wir haben ein Modul
UsefulThings
, das zwei Instanzmethoden enthält:und
Fixnum
include
s das Modul:Wir sehen das:
Hatten Sie damit gerechnet, es
UsefulThings#add3
zu überschreibenFixnum#add3
, damit1.add3
es zurückkehren würde4
? Bedenken Sie:Wenn die Klasse
include
das Modul ist, wird das Modul zur Oberklasse der Klasse. Aufgrund der Funktionsweise der Vererbung wird das Sendenadd3
an eine Instanz vonFixnum
dazu führenFixnum#add3
, dass aufgerufen wird und zurückgegeben wirddog
.Fügen wir nun eine Methode hinzu
:add2
zuUsefulThings
:Wir wollen nun
Fixnum
aninclude
nur die Methodenadd1
undadd3
. In diesem Fall erwarten wir die gleichen Ergebnisse wie oben.Angenommen, wir führen wie oben Folgendes aus:
Was ist das Ergebnis? Die unerwünschte Methode
:add2
wird hinzugefügtFixnum
,:add1
hinzugefügt und aus den oben erläuterten Gründen:add3
nicht hinzugefügt. Wir müssen also nur noch tunundef
:add2
. Wir können das mit einer einfachen Hilfsmethode tun:was wir so aufrufen:
Dann:
Welches ist das Ergebnis, das wir wollen.
quelle
Ich bin mir nicht sicher, ob jemand es nach 10 Jahren noch braucht, aber ich habe es mit der Eigenklasse gelöst.
quelle
Nach fast 9 Jahren ist hier eine generische Lösung:
Der unglückliche Trick, den Sie anwenden müssen, besteht darin, das Modul einzuschließen, nachdem die Methoden definiert wurden. Alternativ können Sie es auch einschließen, nachdem der Kontext als definiert wurde
ModuleIncluded.send(:include, CreateModuleFunctions)
.Oder Sie können es über die Reflection_utils verwenden Juwel .
quelle