ruby module_function vs y compris le module

Dans Ruby, je comprends que les fonctions du module peuvent être rendues disponibles sans mélange dans le module en utilisant module_function comme indiqué ici. Je peux voir comment cela est utile afin que vous puissiez utiliser la fonction sans mélanger dans le module.

 module MyModule def do_something puts "hello world" end module_function :do_something end 

Ma question est de savoir pourquoi vous voudriez peut-être définir la fonction de ces deux manières.

Pourquoi ne pas avoir

 def MyModule.do_something 

OU

 def do_something 

Dans quel type de cas serait-il utile d’avoir la fonction disponible pour être mélangée ou utilisée comme méthode statique?

Pensez à Enumerable .

C’est l’exemple parfait du moment où vous devez l’inclure dans un module. Si votre classe définit #each , vous obtenez beaucoup de bonnes choses en incluant simplement un module ( #map , #select , etc.). C’est le seul cas où j’utilise des modules comme mixins – lorsque le module fournit des fonctionnalités en termes de quelques méthodes, définies dans la classe dans laquelle vous incluez le module. Je peux affirmer que cela devrait être le seul cas en général.

Quant à la définition des méthodes “statiques”, une meilleure approche serait:

 module MyModule def self.do_something end end 

Vous n’avez pas vraiment besoin d’appeler #module_function . Je pense que c’est juste un truc étrange.

Vous pouvez même le faire:

 module MyModule extend self def do_something end end 

… mais cela ne fonctionnera pas bien si vous souhaitez également inclure le module quelque part. Je suggère de l’éviter jusqu’à ce que vous appreniez les subtilités de la métaprogrammation Ruby.

Enfin, si vous faites juste:

 def do_something end 

… il ne se terminera pas comme une fonction globale, mais comme une méthode privée sur Object (il n’y a pas de fonctions dans Ruby, juste des méthodes). Il y a deux inconvénients. Premièrement, vous n’avez pas d’espace de noms – si vous définissez une autre fonction du même nom, c’est celle qui sera évaluée plus tard. Deuxièmement, si vous avez des fonctionnalités implémentées en termes de #method_missing , le fait d’avoir une méthode privée dans Object #method_missing . Et finalement, le patching de singe Object est juste une affaire diabolique 🙂

MODIFIER:

module_function peut être utilisé de manière similaire à private :

 module Something def foo puts 'foo' end module_function def bar puts 'bar' end end 

De cette façon, vous pouvez appeler Something.bar , mais pas Something.foo . Si vous définissez d’autres méthodes après cet appel à module_function , elles seront également disponibles sans mélange.

Je ne l’aime pas pour deux raisons, cependant. Premièrement, les modules qui sont à la fois intégrés et dotés de méthodes “statiques” semblent un peu déroutants. Il pourrait y avoir des cas valides, mais ce ne sera pas souvent le cas. Comme je le disais, je préfère utiliser un module comme espace de noms ou le mélanger, mais pas les deux.

Deuxièmement, dans cet exemple, la bar serait également disponible pour les classes / modules qui se mélangent dans Something . Je ne suis pas sûr quand cela est souhaitable, puisque soit la méthode utilise self et il doit être mélangé, ou pas, et alors il n’a pas besoin d’être mélangé.

Je pense que l’utilisation de module_function sans passer le nom de la méthode est utilisée plus souvent qu’avec. Même chose pour private et protected .

C’est un bon moyen pour une bibliothèque Ruby d’offrir des fonctionnalités qui n’utilisent pas (beaucoup) l’état interne. Donc, si vous voulez (par exemple) offrir une fonction sin et ne voulez pas polluer l’espace de noms “global” ( Object ), vous pouvez le définir en tant que méthode de classe sous une constante ( Math ).

Toutefois, un développeur d’applications, qui souhaite écrire une application mathématique, peut avoir besoin de toutes les deux lignes. Si la méthode est également une méthode d’instance, elle peut simplement inclure le module Math (ou My::Awesome::Nested::Library ) et peut maintenant appeler directement sin (exemple stdlib).

Il s’agit vraiment de rendre une bibliothèque plus confortable pour ses utilisateurs. Ils peuvent choisir eux-mêmes s’ils veulent que les fonctionnalités de votre bibliothèque soient au premier plan.

Par ailleurs, vous pouvez obtenir une fonctionnalité similaire à module_function en utilisant: extend self (dans la première ligne du module). Pour moi, ça a l’air mieux et ça rend les choses un peu plus claires à comprendre.

Mise à jour: Plus d’informations de fond dans cet article de blog .

Si vous voulez regarder un exemple de travail, consultez le joyau chronique:

https://github.com/mojombo/chronic/blob/master/lib/chronic/handlers.rb

et Handlers est inclus dans la classe Parser ici:

https://github.com/mojombo/chronic/blob/master/lib/chronic/parser.rb

Il utilise module_function pour envoyer les méthodes de Handlers à des instances spécifiques de Handler en utilisant la méthode invoke de cette instance.