define_method - ruby module_function vs módulo incluido
ruby define_method (3)
Es una buena manera para que una biblioteca de Ruby ofrezca una funcionalidad que no use (mucho) estado interno. Por lo tanto, si (por ejemplo) quiere ofrecer una función sin
y no desea contaminar el espacio de nombres " Object
" global, puede definirlo como método de clase bajo una constante ( Math
).
Sin embargo, un desarrollador de aplicaciones, que quiera escribir una aplicación matemática, podría necesitar el sin
cada dos líneas. Si el método también es un método de instancia, solo puede incluir el módulo Math
(o My::Awesome::Nested::Library
) y ahora puede llamar directamente a sin
(ejemplo de stdlib).
Se trata realmente de hacer una biblioteca más cómoda para sus usuarios. Pueden elegirse ellos mismos, si quieren la funcionalidad de su biblioteca en el nivel superior.
Por cierto, puedes lograr una funcionalidad similar como module_function
usando: extend self
(en la primera línea del módulo). En mi opinión, se ve mejor y hace las cosas un poco más claras de entender.
Actualización: Más información de fondo en este artículo del blog .
En ruby, entiendo que las funciones del módulo pueden estar disponibles sin mezclar en el módulo usando module_function
como se muestra aquí. Puedo ver cómo esto es útil para que pueda usar la función sin mezclar en el módulo.
module MyModule
def do_something
puts "hello world"
end
module_function :do_something
end
Mi pregunta es, sin embargo, por qué es posible que desee tener la función definida de ambas maneras.
¿Por qué no solo tener
def MyModule.do_something
O
def do_something
¿En qué tipo de casos sería útil tener la función disponible para ser mezclada, o para ser utilizada como un método estático?
Piense en Enumerable .
Este es el ejemplo perfecto de cuándo necesitas incluirlo en un módulo. Si su clase define #each
, obtendrá mucha bondad simplemente al incluir un módulo ( #map
, #select
, etc.). Este es el único caso cuando uso módulos como mixins, cuando el módulo proporciona funcionalidad en términos de algunos métodos, definidos en la clase en la que se incluye el módulo. Puedo argumentar que este debería ser el único caso en general.
En cuanto a la definición de métodos "estáticos", un mejor enfoque sería:
module MyModule
def self.do_something
end
end
Realmente no necesitas llamar a #module_function
. Creo que son solo cosas extrañas del legado.
Incluso puedes hacer esto:
module MyModule
extend self
def do_something
end
end
... pero no funcionará bien si también desea incluir el módulo en algún lugar. Sugiero evitarlo hasta que aprendas las sutilezas de la metaprogramación de Ruby.
Finalmente, si solo haces:
def do_something
end
... no terminará como una función global, sino como un método privado en Object
(no hay funciones en Ruby, solo métodos). Hay dos desventajas. Primero, no tiene espacios de nombres; si define otra función con el mismo nombre, la que se evalúa más adelante es la que obtiene. En segundo lugar, si tiene una funcionalidad implementada en términos de #method_missing
, tener un método privado en Object
lo #method_missing
. Y, por último, el parche del mono es solo un mal negocio :)
EDITAR:
module_function
puede usarse de una manera similar a la private
:
module Something
def foo
puts ''foo''
end
module_function
def bar
puts ''bar''
end
end
De esa manera, puedes llamar a Something.bar
, pero no a Something.foo
. Si define otros métodos después de esta llamada a module_function
, también estarán disponibles sin mezclarse.
Sin embargo, no me gusta por dos razones. Primero, los módulos que están mezclados y tienen métodos "estáticos" suenan un poco dudosos. Puede haber casos válidos, pero no será tan a menudo. Como dije, prefiero usar un módulo como espacio de nombres o mezclarlo, pero no ambos.
Segundo, en este ejemplo, la bar
también estaría disponible para las clases / módulos que se mezclan en Something
. No estoy seguro de cuándo es deseable, ya que el método se usa a self
y debe mezclarse, o no, y entonces no es necesario mezclarlo.
Creo que usar module_function
sin pasar el nombre del método se usa con más frecuencia que con. Lo mismo ocurre con los private
y protected
.
Si quieres ver un ejemplo práctico, mira la gema crónica:
https://github.com/mojombo/chronic/blob/master/lib/chronic/handlers.rb
y Handlers está siendo incluido en la clase Parser aquí:
https://github.com/mojombo/chronic/blob/master/lib/chronic/parser.rb
Está utilizando module_function para enviar los métodos de Handlers a instancias específicas de Handler utilizando el método de invocación de esa instancia.