Tło:
Mam moduł, który deklaruje kilka metod instancji
module UsefulThings
def get_file; ...
def delete_file; ...
def format_text(x); ...
end
I chcę wywołać niektóre z tych metod z poziomu klasy. Sposób, w jaki zwykle robisz to w Ruby, wygląda tak:
class UsefulWorker
include UsefulThings
def do_work
format_text("abc")
...
end
end
Problem
include UsefulThings
wprowadza wszystkie metody z UsefulThings
. W tym przypadku chcę tylko format_text
i wyraźnie nie chcę get_file
i delete_file
.
Widzę kilka możliwych rozwiązań tego problemu:
- W jakiś sposób wywołuje metodę bezpośrednio w module, nie włączając jej nigdzie
- Nie wiem, jak / czy można to zrobić. (Stąd to pytanie)
- W jakiś sposób włączaj
Usefulthings
i wprowadzaj tylko niektóre z jego metod- Nie wiem też, jak / czy można to zrobić
- Utwórz klasę proxy, dołącz ją
UsefulThings
, a następnie delegujformat_text
do tej instancji proxy- To by zadziałało, ale anonimowe klasy proxy to hack. Fuj.
- Podziel moduł na 2 lub więcej mniejszych modułów
- To też by zadziałało i jest prawdopodobnie najlepszym rozwiązaniem, jakie przychodzi mi do głowy, ale wolałbym tego uniknąć, ponieważ skończyłoby się to mnożeniem dziesiątek modułów - zarządzanie tym byłoby uciążliwe
Dlaczego w jednym module jest wiele niezwiązanych ze sobą funkcji? Pochodzi ApplicationHelper
z aplikacji railsowej, którą nasz zespół de facto zdecydował jako wysypisko wszystkiego, co nie jest na tyle specyficzne, by należało do innego miejsca. W większości samodzielne metody narzędziowe, które są używane wszędzie. Mógłbym podzielić to na oddzielnych pomocników, ale byłoby ich 30, wszyscy z 1 metodą każda ... wydaje się to nieproduktywne
Module#included
wywołania zwrotnego do wyzwalaniainclude
drugiego.format_text
Sposób można przenieść do niego własnego modułu, ponieważ wydaje się być przydatna na swój własny. Dzięki temu zarządzanie byłoby mniej uciążliwe.module UT; def add1; self+1; end; def add2; self+2; end; end
i chcesz używać,add1
ale nieadd2
w klasieFixnum
. Jak pomogłoby posiadanie do tego funkcji modułu? Czy coś mi brakuje?Odpowiedzi:
Jeśli metoda na module zostanie zamieniona na funkcję modułu, możesz po prostu wywołać ją z Modów, tak jakby była zadeklarowana jako
Poniższe podejście module_function pozwoli uniknąć zerwania klas, które zawierają wszystkie mody.
Jestem jednak ciekawy, dlaczego zestaw niepowiązanych funkcji znajduje się w tym samym module w pierwszej kolejności?
Edytowano, aby pokazać, że nadal działa, jeśli
public :foo
zostanie wywołana pomodule_function :foo
źródło
module_function
zamienia metodę w prywatną, która złamałaby inny kod - w przeciwnym razie byłaby to akceptowana odpowiedźFiles.truncate
i aStrings.truncate
i chcę jawnie użyć obu w tej samej klasie. Tworzenie nowej klasy / instancji za każdym razem, gdy potrzebuję określonej metody lub modyfikowanie oryginału, nie jest dobrym podejściem, chociaż nie jestem programistą Rubiego.Myślę, że najkrótszym sposobem zrobienia po prostu wyrzucenia pojedynczego połączenia (bez zmiany istniejących modułów lub tworzenia nowych) byłby następujący:
źródło
Object.new.extend(UsefulThings).get_file
Innym sposobem, aby to zrobić, jeśli posiadasz moduł, jest użycie
module_function
.źródło
ModuleName.method :method_name
aby pobrać obiekt metody i wywołać go za pośrednictwemmethod_obj.call
. W przeciwnym razie musiałbym powiązać metodę z wystąpieniem oryginalnego obiektu, co nie jest możliwe, jeśli oryginalny obiekt to Module. W odpowiedzi na Orion Edwardsmodule_function
czyni oryginalną metodę instancji prywatną. ruby-doc.org/core/classes/Module.html#M001642def self.a; puts 'aaay'; end
Jeśli chcesz wywołać te metody bez włączania modułu do innej klasy, musisz zdefiniować je jako metody modułu:
a potem możesz do nich zadzwonić
lub
W każdym razie zalecałbym umieszczenie tylko pokrewnych metod w jednym module lub w jednej klasie. Jeśli masz problem polegający na tym, że chcesz dołączyć tylko jedną metodę z modułu, to brzmi to jak nieprzyjemny zapach kodu i nie jest dobrym stylem Ruby łączenie niepowiązanych metod razem.
źródło
Aby wywołać metodę instancji modułu bez dołączania modułu (i bez tworzenia obiektów pośredniczących):
źródło
format_text
zakłada istnienie innej metody dostarczonej przez moduł, która (ogólnie) nie będzie obecna.Po pierwsze, polecam rozbicie modułu na przydatne rzeczy, których potrzebujesz. Ale zawsze możesz utworzyć klasę rozszerzającą ją dla twojego wywołania:
źródło
O. W przypadku, gdy chcesz zawsze wywoływać je w „kwalifikowany”, samodzielny sposób (UsefulThings.get_file), a następnie po prostu nadać im statyczny charakter, jak wskazywali inni,
B.Jeśli nadal chcesz zachować podejście miksowania w tych samych przypadkach, a także jednorazowe, samodzielne wywołanie, możesz mieć moduł jednowierszowy, który rozszerza się o mikser:
Więc oba działają:
IMHO jest czystszy niż w
module_function
przypadku każdej metody - na wypadek, gdybyś chciał je wszystkie.źródło
extend self
to powszechny idiom.Jak rozumiem pytanie, chcesz połączyć niektóre metody instancji modułu w klasę.
Zacznijmy od rozważenia, jak działa moduł # include . Załóżmy, że mamy moduł
UsefulThings
zawierający dwie metody instancji:i
Fixnum
include
ten moduł:Widzimy to:
Czy spodziewałeś
UsefulThings#add3
się nadpisaniaFixnum#add3
, więc1.add3
to wróci4
? Rozważ to:Kiedy klasa
include
jest modułem, moduł staje się nadklasą klasy. Tak więc, ze względu na sposób działania dziedziczenia, wysłanieadd3
do instancjiFixnum
will spowodujeFixnum#add3
wywołanie i zwróceniedog
.Teraz dodajmy metodę
:add2
doUsefulThings
:Teraz chcą
Fixnum
sięinclude
tylko metodyadd1
iadd3
. Robiąc to, spodziewamy się uzyskać takie same wyniki, jak powyżej.Załóżmy, jak powyżej, wykonujemy:
Jaki jest wynik? Niepożądana metoda
:add2
jest dodawanaFixnum
,:add1
dodawana iz powodów, które wyjaśniłem powyżej,:add3
nie jest dodawana. Więc wszystko, co musimy zrobić, toundef
:add2
. Możemy to zrobić prostą metodą pomocniczą:które wywołujemy w ten sposób:
Następnie:
który jest wynikiem, jakiego oczekujemy.
źródło
Nie jestem pewien, czy ktoś nadal go potrzebuje po 10 latach, ale rozwiązałem go za pomocą własnej klasy.
źródło
Po prawie 9 latach oto ogólne rozwiązanie:
Niefortunna sztuczka, którą musisz zastosować, to dołączenie modułu po zdefiniowaniu metod. Alternatywnie możesz również dołączyć go po zdefiniowaniu kontekstu jako
ModuleIncluded.send(:include, CreateModuleFunctions)
.Lub możesz go użyć za pośrednictwem klejnotu reflection_utils .
źródło