Antecedentes:
Tengo un módulo que declara varios métodos de instancia
module UsefulThings
def get_file; ...
def delete_file; ...
def format_text(x); ...
end
Y quiero llamar a algunos de estos métodos desde una clase. La forma en que normalmente haces esto en ruby es así:
class UsefulWorker
include UsefulThings
def do_work
format_text("abc")
...
end
end
Problema
include UsefulThings
trae todos los métodos de UsefulThings
. En este caso solo quiero format_text
y explícitamente no quiero get_file
y delete_file
.
Puedo ver varias soluciones posibles para esto:
- Invoca de alguna manera el método directamente en el módulo sin incluirlo en ningún lado
- No sé cómo / si esto se puede hacer. (De ahí esta pregunta)
- De alguna manera incluye
Usefulthings
y solo trae algunos de sus métodos- Tampoco sé cómo / si esto se puede hacer
- Crear una clase proxy, incluir
UsefulThings
y delegueformat_text
a esa instancia de proxy- Esto funcionaría, pero las clases proxy anónimas son un truco. Yuck
- Divida el módulo en 2 o más módulos más pequeños.
- Esto también funcionaría, y es probablemente la mejor solución que se me ocurre, pero preferiría evitarlo, ya que terminaría con una proliferación de docenas y docenas de módulos; gestionar esto sería una carga
¿Por qué hay muchas funciones no relacionadas en un solo módulo? Es ApplicationHelper
de una aplicación de rieles, que nuestro equipo ha decidido de facto como el vertedero de cualquier cosa que no sea lo suficientemente específica como para pertenecer a otro lugar. En su mayoría, métodos de utilidad independientes que se utilizan en todas partes. Podría dividirlo en ayudantes separados, pero habría 30 de ellos, todos con 1 método cada uno ... esto parece improductivo
Module#included
devolución de llamada para activar unoinclude
del otro. Elformat_text
método podría trasladarse a su propio módulo, ya que parece ser útil por sí mismo. Esto haría que la gestión sea un poco menos onerosa.module UT; def add1; self+1; end; def add2; self+2; end; end
y quiere usaradd1
pero noadd2
en claseFixnum
. ¿Cómo ayudaría tener funciones de módulo para eso? ¿Me estoy perdiendo de algo?Respuestas:
Si un método en un módulo se convierte en una función de módulo, simplemente puede cancelarlo de Mods como si hubiera sido declarado como
El enfoque de module_function a continuación evitará romper cualquier clase que incluya todos los Mods.
Sin embargo, tengo curiosidad por qué un conjunto de funciones no relacionadas están todas contenidas dentro del mismo módulo en primer lugar.
Editado para mostrar que incluye trabajo si
public :foo
se llama despuésmodule_function :foo
fuente
module_function
convierte el método en uno privado, lo que rompería otro código; de lo contrario, esta sería la respuesta aceptadaFiles.truncate
y aStrings.truncate
y quiero usar ambos en la misma clase, explícitamente. Crear una nueva clase / instancia cada vez que necesito un método específico o modificar el original no es un buen enfoque, aunque no soy un desarrollador de Ruby.Creo que la forma más corta de hacer una llamada única desechable (sin alterar los módulos existentes o crear otros nuevos) sería la siguiente:
fuente
Object.new.extend(UsefulThings).get_file
Otra forma de hacerlo si "posee" el módulo es utilizarlo
module_function
.fuente
ModuleName.method :method_name
para obtener un objeto de método y llamarlo a través demethod_obj.call
. De lo contrario, tendría que vincular el método a una instancia del objeto original, lo que no es posible si el objeto original es un Módulo. En respuesta a Orion Edwards,module_function
hace que el método de instancia original sea privado. ruby-doc.org/core/classes/Module.html#M001642def self.a; puts 'aaay'; end
Si desea llamar a estos métodos sin incluir el módulo en otra clase, debe definirlos como métodos de módulo:
y luego puedes llamarlos con
o
Pero de todos modos, recomendaría que coloque solo métodos relacionados en un módulo o en una clase. Si tiene el problema de que desea incluir solo un método del módulo, entonces suena como un mal olor de código y no es un buen estilo Ruby juntar métodos no relacionados.
fuente
Para invocar un método de instancia de módulo sin incluir el módulo (y sin crear objetos intermedios):
fuente
format_text
supone la existencia de otro método proporcionado por el módulo, que (generalmente) no estará presente.En primer lugar, recomendaría dividir el módulo en las cosas útiles que necesita. Pero siempre puede crear una clase que la extienda para su invocación:
fuente
R. En caso de que usted quiera llamarlos siempre de manera independiente y "calificada" (UsefulThings.get_file), simplemente hágalos estáticos como otros señalaron,
B. Si aún desea mantener el enfoque de mezcla en los mismos casos, así como la invocación independiente única, puede tener un módulo de una sola línea que se extienda con la mezcla:
Entonces ambos funcionan entonces:
En mi humilde opinión es más limpio que
module_function
para todos los métodos, en caso de querer todos.fuente
extend self
Es un idioma común.Según tengo entendido la pregunta, desea mezclar algunos de los métodos de instancia de un módulo en una clase.
Comencemos considerando cómo funciona el Módulo # incluir . Supongamos que tenemos un módulo
UsefulThings
que contiene dos métodos de instancia:y
Fixnum
include
s ese módulo:Vemos eso:
¿Esperaba
UsefulThings#add3
anularFixnum#add3
, para que eso1.add3
volviera4
? Considera esto:Cuando la clase
include
es el módulo, el módulo se convierte en la superclase de la clase. Entonces, debido a cómo funciona la herencia, el envíoadd3
a una instancia deFixnum
haráFixnum#add3
que se invoque, regresandodog
.Ahora agreguemos un método
:add2
paraUsefulThings
:Ahora queremos
Fixnum
queinclude
sólo los métodosadd1
yadd3
. Al hacerlo, esperamos obtener los mismos resultados que anteriormente.Supongamos que, como arriba, ejecutamos:
Cual es el resultado? Se
:add2
agrega el método no deseadoFixnum
,:add1
se agrega y, por las razones que expliqué anteriormente,:add3
no se agrega. Entonces todo lo que tenemos que hacer esundef
:add2
. Podemos hacerlo con un método auxiliar simple:que invocamos así:
Luego:
cual es el resultado que queremos
fuente
No estoy seguro si alguien aún lo necesita después de 10 años, pero lo resolví usando eigenclass.
fuente
Después de casi 9 años, aquí hay una solución genérica:
El desafortunado truco que debe aplicar es incluir el módulo después de que se hayan definido los métodos. Alternativamente, también puede incluirlo después de que el contexto se defina como
ModuleIncluded.send(:include, CreateModuleFunctions)
.O puede usarlo a través de la gema reflection_utils .
fuente