Fundo:
Eu tenho um módulo que declara vários métodos de instância
module UsefulThings
def get_file; ...
def delete_file; ...
def format_text(x); ...
end
E eu quero chamar alguns desses métodos de dentro de uma classe. Como você normalmente faz isso em ruby é assim:
class UsefulWorker
include UsefulThings
def do_work
format_text("abc")
...
end
end
Problema
include UsefulThings
traz todos os métodos de UsefulThings
. Neste caso, eu só quero format_text
e explicitamente não quero get_file
e delete_file
.
Eu posso ver várias soluções possíveis para isso:
- De alguma forma, invoque o método diretamente no módulo sem incluí-lo em nenhum lugar
- Não sei como / se isso pode ser feito. (Daí esta pergunta)
- De alguma forma, inclua
Usefulthings
e apenas traga alguns métodos- Também não sei como / se isso pode ser feito
- Crie uma classe de proxy, inclua nela e,
UsefulThings
em seguida, delegueformat_text
para essa instância de proxy- Isso funcionaria, mas classes proxy anônimas são um hack. Que nojo.
- Divida o módulo em 2 ou mais módulos menores
- Isso também funcionaria, e é provavelmente a melhor solução em que consigo pensar, mas prefiro evitá-la, pois acabo com uma proliferação de dezenas e dezenas de módulos - gerenciar isso seria oneroso
Por que existem muitas funções não relacionadas em um único módulo? É ApplicationHelper
de um aplicativo de trilhos, que nossa equipe decidiu de fato como o depósito de lixo para qualquer coisa não específica o suficiente para pertencer a qualquer outro lugar. Métodos utilitários principalmente independentes que são usados em qualquer lugar. Eu poderia dividi-lo em ajudantes separados, mas haveria 30 deles, todos com 1 método cada ... isso parece improdutivo
Module#included
retorno de chamada para acionar uminclude
do outro. Oformat_text
método pode ser movido para seu próprio módulo, pois parece ser útil por si só. Isso tornaria a administração um pouco menos onerosa.module UT; def add1; self+1; end; def add2; self+2; end; end
e queira usar,add1
mas nãoadd2
na aulaFixnum
. Como ajudaria a ter funções de módulo para isso? Estou esquecendo de algo?Respostas:
Se um método em um módulo é transformado em uma função de módulo, você pode simplesmente chamá-lo de Mods como se tivesse sido declarado como
A abordagem module_function abaixo evitará quebrar todas as classes que incluem todos os Mods.
No entanto, estou curioso para saber por que um conjunto de funções não relacionadas está contido no mesmo módulo em primeiro lugar?
Editado para mostrar que ainda inclui trabalho se
public :foo
for chamado apósmodule_function :foo
fonte
module_function
transforma o método em um privado, que iria quebrar outro código - caso contrário this'd ser a resposta aceitaFiles.truncate
e umStrings.truncate
e eu quiser usar os dois na mesma classe, explicitamente. Criar uma nova classe / instância sempre que preciso de um método específico ou modificar o original não é uma abordagem agradável, embora eu não seja um desenvolvedor Ruby.Eu acho que a maneira mais curta de fazer uma única chamada descartável (sem alterar os módulos existentes ou criar novos) seria a seguinte:
fonte
Object.new.extend(UsefulThings).get_file
Outra maneira de fazer isso se você "possuir" o módulo é usar
module_function
.fonte
ModuleName.method :method_name
para obter um objeto de método e chamá-lo viamethod_obj.call
. Caso contrário, eu teria que vincular o método a uma instância do objeto original, o que não é possível se o objeto original for um módulo. Em resposta ao Orion Edwards,module_function
torna o método de instância original privado. ruby-doc.org/core/classes/Module.html#M001642def self.a; puts 'aaay'; end
Se você quiser chamar esses métodos sem incluir o módulo em outra classe, precisará defini-los como métodos do módulo:
e então você pode chamá-los com
ou
De qualquer forma, eu recomendaria que você colocasse apenas métodos relacionados em um módulo ou em uma classe. Se você tiver um problema que deseja incluir apenas um método do módulo, isso soa como um mau cheiro de código e não é bom estilo Ruby reunir métodos não relacionados.
fonte
Para chamar um método de instância do módulo sem incluir o módulo (e sem criar objetos intermediários):
fonte
format_text
assuma a existência de outro método fornecido pelo módulo, que (geralmente) não estará presente.Em primeiro lugar, eu recomendo dividir o módulo nas coisas úteis que você precisa. Mas você sempre pode criar uma classe estendendo isso para sua chamada:
fonte
R. Caso você queira sempre chamá-los de uma maneira "qualificada" e autônoma (UsefulThings.get_file), basta torná-los estáticos, conforme indicado por outros,
B. Se você ainda deseja manter a abordagem de mixin nos mesmos casos, bem como a invocação independente, você pode ter um módulo de uma linha que se estenda com o mixin:
Então, ambos funcionam então:
IMHO é mais limpo do que
module_function
para todos os métodos - caso deseje todos eles.fonte
extend self
é um idioma comum.Pelo que entendi a pergunta, você deseja misturar alguns dos métodos de instância de um módulo em uma classe.
Vamos começar considerando como o Módulo # include funciona. Suponha que tenhamos um módulo
UsefulThings
que contém dois métodos de instância:e
Fixnum
include
esse módulo:Nós vemos que:
Você esperava
UsefulThings#add3
substituirFixnum#add3
, para que1.add3
retornasse4
? Considere isto:Quando a classe
include
é o módulo, o módulo se torna a superclasse da classe. Portanto, por causa de como a herança funciona, o envioadd3
para uma instância deFixnum
fará comFixnum#add3
que seja invocado, retornandodog
.Agora vamos adicionar um método
:add2
paraUsefulThings
:Agora queremos
Fixnum
queinclude
apenas os métodosadd1
eadd3
. Com isso, esperamos obter os mesmos resultados acima.Suponha que, como acima, executamos:
Qual é o resultado? O método indesejado
:add2
é adicionado aFixnum
,:add1
é adicionado e, pelas razões que expliquei acima,:add3
não é adicionado. Então, tudo o que precisamos fazer éundef
:add2
. Podemos fazer isso com um método auxiliar simples:que invocamos assim:
Então:
qual é o resultado que queremos.
fonte
Não tenho certeza se alguém ainda precisa disso depois de 10 anos, mas eu resolvi usando a classe própria.
fonte
Depois de quase 9 anos, aqui está uma solução genérica:
O truque infeliz que você precisa aplicar é incluir o módulo após a definição dos métodos. Como alternativa, você também pode incluí-lo após o contexto ser definido como
ModuleIncluded.send(:include, CreateModuleFunctions)
.Ou você pode usá-lo através da gema reflection_utils .
fonte