Задний план:
У меня есть модуль, который объявляет ряд методов экземпляра
module UsefulThings
def get_file; ...
def delete_file; ...
def format_text(x); ...
end
И я хочу вызвать некоторые из этих методов из класса. Как вы обычно делаете это в ruby, так:
class UsefulWorker
include UsefulThings
def do_work
format_text("abc")
...
end
end
проблема
include UsefulThings
приносит все методы из UsefulThings
. В этом случае я только хочу format_text
и явно не хочу get_file
и delete_file
.
Я вижу несколько возможных решений этого:
- Каким-то образом вызвать метод непосредственно в модуле, не включая его в любом месте
- Я не знаю, как / если это можно сделать. (Отсюда и этот вопрос)
- Каким-то образом включить
Usefulthings
и только привнести некоторые из его методов- Я также не знаю, как / если это можно сделать
- Создайте прокси-класс, включите
UsefulThings
в него, затем делегируйтеformat_text
этому экземпляру прокси- Это бы сработало, но анонимные прокси-классы - это хак. Тьфу.
- Разделите модуль на 2 или более меньших модуля
- Это также сработало бы и, вероятно, является лучшим решением, которое я могу придумать, но я бы предпочел его избегать, так как в конечном итоге я получу распространение десятков и десятков модулей - управление этим будет обременительным.
Почему в одном модуле много несвязанных функций? Это ApplicationHelper
приложение из рельсов, которое наша команда де-факто определила как место для захоронения чего-либо, что недостаточно специфично, чтобы принадлежать где-либо еще. В основном автономные служебные методы, которые используются везде. Я мог бы разбить его на отдельных помощников, но их было бы 30, все с одним методом каждый ... это кажется непродуктивным
Module#included
обратный вызов для запускаinclude
другого.format_text
Метод может быть перемещен в собственный модуль это, так как кажется , чтобы быть полезным на его собственный. Это сделало бы управление немного менее обременительным.module UT; def add1; self+1; end; def add2; self+2; end; end
и вы хотите использовать,add1
но неadd2
в классеFixnum
. Как это поможет иметь функции модуля для этого? Я что-то упускаю?Ответы:
Если метод модуля превращается в функцию модуля, вы можете просто вызвать его из Mods, как если бы он был объявлен как
Подход module_function, приведенный ниже, позволит избежать взлома любых классов, которые включают все моды.
Тем не менее, мне любопытно, почему набор несвязанных функций изначально содержится в одном модуле?
Отредактировано, чтобы показать, что включает в себя еще работу, если
public :foo
вызывается послеmodule_function :foo
источник
module_function
превращает метод в приватный, что может нарушить другой код - в противном случае это будет принятый ответFiles.truncate
и a,Strings.truncate
и я хочу явно использовать оба в одном классе. Создание нового класса / экземпляра каждый раз, когда мне нужен определенный метод, или модификация оригинала - не очень хороший подход, хотя я не разработчик Ruby.Я думаю, что самый короткий способ сделать простой одноразовый вызов (без изменения существующих модулей или создания новых) будет следующим:
источник
Object.new.extend(UsefulThings).get_file
Другой способ сделать это, если вы «владеете» модулем - это использовать
module_function
.источник
ModuleName.method :method_name
чтобы получить объект метода и вызвать его черезmethod_obj.call
. В противном случае мне пришлось бы привязать метод к экземпляру исходного объекта, что невозможно, если исходный объект является модулем. В ответ на Ориона Эдвардса,module_function
делает оригинальный метод экземпляра частным. ruby-doc.org/core/classes/Module.html#M001642def self.a; puts 'aaay'; end
Если вы хотите вызывать эти методы без включения модуля в другой класс, вам нужно определить их как методы модуля:
и тогда вы можете позвонить им с
или
Но в любом случае я бы порекомендовал поместить только связанные методы в один модуль или в один класс. Если у вас есть проблема с тем, что вы хотите включить только один метод из модуля, то это звучит как неприятный запах кода, и это не очень хороший стиль Ruby для объединения не связанных между собой методов.
источник
Чтобы вызвать метод экземпляра модуля без включения модуля (и без создания промежуточных объектов):
источник
format_text
предполагает существование другого метода, предоставляемого модулем, который (как правило) не будет присутствовать.Во-первых, я бы рекомендовал разбить модуль на полезные вещи, которые вам нужны. Но вы всегда можете создать класс, расширяющий его для вашего вызова:
источник
О. В случае, если вы всегда хотите вызывать их «квалифицированным», автономным способом (UsefulThings.get_file), то просто сделайте их статическими, как указали другие,
Б. Если вы все еще хотите сохранить миксин-подход в тех же случаях, а также однократный автономный вызов, вы можете иметь однострочный модуль, который расширяется вместе с миксином:
Так что оба работают тогда:
ИМХО это чище, чем
module_function
для каждого отдельного метода - на случай, если захотите их все.источник
extend self
это распространенная идиомаКак я понимаю вопрос, вы хотите смешать некоторые методы экземпляра модуля в класс.
Давайте начнем с рассмотрения того, как работает Module # include . Предположим, у нас есть модуль,
UsefulThings
который содержит два метода экземпляра:и
Fixnum
include
этот модуль:Мы видим, что:
Вы ожидали
UsefulThings#add3
переопределитьFixnum#add3
, чтобы это1.add3
вернулось4
? Учти это:Когда класс
include
является модулем, модуль становится суперклассом класса. Таким образом, из-за того, как работает наследование, отправкаadd3
экземпляруFixnum
вызоветFixnum#add3
вызов, возвращениеdog
.Теперь давайте добавим метод
:add2
кUsefulThings
:Теперь мы хотим,
Fixnum
чтобыinclude
только методыadd1
иadd3
. При этом мы ожидаем получить те же результаты, что и выше.Предположим, как указано выше, мы выполняем:
Какой результат? Нежелательный метод
:add2
добавляетсяFixnum
,:add1
добавляется и, по причинам, которые я объяснил выше,:add3
не добавляется. Так что все, что нам нужно сделать, этоundef
:add2
. Мы можем сделать это с помощью простого вспомогательного метода:который мы вызываем так:
Затем:
какой результат мы хотим.
источник
Не уверен, если кому-то все еще это нужно через 10 лет, но я решил это с помощью eigenclass.
источник
После почти 9 лет вот общее решение:
Неудачный трюк, который вам нужно применить, - это включение модуля после определения методов. В качестве альтернативы вы также можете включить его после того, как контекст определен как
ModuleIncluded.send(:include, CreateModuleFunctions)
.Или вы можете использовать его через само отражение .
источник