Blog
User blogs
| Romain GEORGES |
Tag cloud
POST #62 : const_missing, autoload V2, convention plutot que configuration
une autre façon de régler les pbm de require, convention/configuration
je viens de tomber sur un post de Noah Gibbs sur Rubyflow [[http://www.rubyflow.com/items/8222-no-more-requires-autoload-classes]] et je trouve le concept intéressant, et utilisé intelligemment, (j'ai juste un peu enrobé l'affaire):
On peu régler les pbms de require une bonne fois pour toutes en faisant une surcharge de const_missing sur l'Object Ruby.
tel que :
1 # test_const_missing.rb
2 class Object
3 def self.const_missing(c)
4 require "./plugin"
5 Plugin
6 end
7 end
8
9 Plugin.new.print_test
Et le code du plugin :
1 # plugin.rb
2 class Plugin
3 def print_test
4 puts "test!"
5 end
6 end
On peu généraliser, respectant le paradigme très Ruby, "Convention plutôt que configuration" :
de l'usage du snake_case (MonModuleGenial => mon_module_genial.rb), on crée ou ajoute dans sa/ses lib(s) utilitaire(s) :
1 # utils/to_underscore.rb
2 def to_underscore(string)
3 string.gsub(/::/, '/').
4 gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
5 gsub(/([a-z\d])([A-Z])/,'\1_\2').
6 tr("-", "_").
7 downcase
8 end
puis (je referai un post sur un solution complète d'init/utils avec recherche dans un Gem):
1 require "utils/to_underscore"
2 # utils/override_object.rb
3 class Object
4 def self.const_missing(c)
5 if c =~ /^Plugin.*$/ then
6 plug_file = to_underscore(c.to_s).sub("_",'s/')
7 require plug_file
8 Object.const_get(c)
9 puts "Loading plugin : #{c.to_s}" if $VERBOSE
10 else
11 STDERR.puts "Missing constant: #{c.inspect}!"
12 end
13 end
14 end
le code d'un plugin :
1 # plugins/mon_module_genial.rb
2 class PluginMonModuleGenial
3 def print_test
4 puts "test!"
5 end
6 end
- dans le code de l'appli
1 PluginMonModuleGenial.new.print_test
POST #58 : Autoload => qui se traduit par chargement différé ...
Autoload en Ruby ne veut pas dire chargement automatique
autoload¶
En regardant micon (post précédant), je suis tomber sur un verbe Ruby que je ne connaissais pas!
On peut faire du Ruby depuis 10 ans et être passé à côté de ça ? et bien oui !
autoload qui d'ailleur est un faux ami,
Vous voyez le AUTOLOAD (démoniaque) de Perl, et bien "ça na rien à voir!"(c) zj, ic, prae(s) et ... ça me rajeunit pas tout ça...
il ne s'execute pas comme un pre-hook au require d'un fichier, mais il fait autre chose, "c'est comme Gif-sur-Yvette, c'est différent..." (c) les mêmes, (spéciale dédicace pour Ic, je suis nostalgique moi en ce moment....)
Explication¶
En fait le autoload Ruby, c'est bien ! surtout quand on code modulaire et que potentiellement certains modules ne sont pas exploités ou rarement mais qu'on veut pas se faire _ pendant l'initialisation de l'application, je m'explique par l'exemple :
Exemple :¶
$ vi mon_module_optionel.rb --
1 2 module MonModule 3 4 def MonModule::init 5 puts 'initialisation' 6 # init code 7 end 8 9 # methods ... 10 def self.test 11 #nop 12 end 13 14 end 15 16 MonModule::init 17
test via irb¶
Avec require¶
irb(main):001:0> require 'mon_module_optionel' initialisation => true
Avec autoload¶
irb(main):001:0> autoload :MonModule, 'mon_module_optionel' => nil irb(main):002:0> include MonModule initialisation => Object
Observation¶
On voit bien que le load réel (interpretation) est différé, mais le compilateur n'hurle pas de NameError, car il "s'attend" a trouver un namespace (module,classe) avec comme nom la Constante fournit à autoload.
en gros on enregistre le symbole du namespace :MonModule est on y l'associe à un wrapper qui va faire le require qui va charger le code, détachant le wrapper au profis du module ou de la classe voulue. c'est beau !
Dans l'exemple j'ai fait un include, mais MonModule.test aurait fournit le même résultat, jouer avec un classe aussi en faisant un new.
Also available in: Atom