AccueilAccueil  PortailPortail  FAQFAQ  RechercherRechercher  MembresMembres  GroupesGroupes  S'enregistrerS'enregistrer  ConnexionConnexion  




Partagez | 
 

 un peu de métaprogrammation avec Ruby

Voir le sujet précédent Voir le sujet suivant Aller en bas 
AuteurMessage
Citadin Lv.7
Citadin Lv.7
avatar


Masculin Inscrit le : 07/09/2011
Messages : 186

MessageSujet: un peu de métaprogrammation avec Ruby    Mar 22 Mai 2012 - 14:23

un peu de métaprogrammation avec Ruby

Introduction
Cet article est disponnible ici : http://funkywork.blogspot.com/2011/12/metaprogrammation-et-ruby.html
et en PDF ici : http://nuki.music-all.be/tutos/meta.pdf

1 Avant-propos

La métaprogrammation est une manière de manipuler des données et structures décrivant eux même des applications/programmes.
Il s’agit d’une manière de programmer qui peut réduire considérablement la taille d’une portion de code ainsi que la complexité de l’utilisation d’une bibliothèque, d’un outil. La métaprogrammation fait partie d’un ensemble de technique requierant de la rigueure mais pouvant apporter énormément à une application.
J’ai choisi d’étudier ce concept autour du langage de programmation Ruby, qui est un langage que je connais relativement bien et qui admet nativement des
concepts de métaprogrammation.
Je ne suis pas ingénieur et je ne serai pas en mesure d’aborder tous les concepts de métaprogrammation, cependant, considérez que cet article peut être une introduction accessible pouvant amener à la création d’un Domain specific language.
L’objectif de cet article est donc de spécifier certaines choses du langages pour permettre au lecteur d’étendre Ruby pour plus de flexibilité dans la rédaction de code.


2 La métaprogrammation ?

Dans le cas de la programmation orienté objet, la métaprogrammation est une forme de traitement qui vise à modifier la déscription d’un objet et/ou ses comportemments. Cela repose sur plusieurs concepts tels que la réflexivité, l’introspection et d’autre. Cependant, dans cette rédaction, nous nous focaliserons sur des techniques de métaprogrammation sans pour autant rentrer dans une approche excessivement formelle et abstraite. Retenons donc la métaprogrammation va nous aider à faciliter la résolution de problèmes parfois ennuyeux de 1manière élégante. Dans cet article nous n’aborderons pas des méthodes spécifique à la métaprogrammation, mais nous évaluerons des spécifités du langage utilise (et parfois logique) pour la métaprogrammation.





Théorie

Le Ruby est un langage adapté à la métaprogrammation pour des raisons simples : son expressivité, son modèle objet respectant relativement bien les normes du paradigme orienté classes (bien qu’il soit possible de fonctionner par prototypage). D’ailleurs, en programmant en Ruby, vous avez été naturellement confrontés à de la métaprogrammation. Par exemple, la génération automatique d’accesseur et de mutateurs qui utilise de la métaprogrammation (dont je parlerai plus tard).


3 Le MonkeyPatching

le monkeypatching est une manière de modifier/étendre du code sans en modifier la source. C’est relativement courant dans la programmation dite dynamique et cela peut servir à modifier correctement une application/bibliothèque sans en modifier la source. Ce qui rend donc une restauration de données d’origine très facile. Le Monkeypatching de ruby prend son sens avec les possibilité d’aliaser les méthodes. Pour rappel, un alias est une manière de renommer quelque chose. Donc grace aux alias, il sera possible de modifier le comportemment d’une méthode sans modifier la classe dans laquelle elle se trouve physiquement. Voici un exemple avec la classe Array pour laquelle je vais modifier la méthode push (Ajoute un objet dans un tableau) de manière à ce qu’elle nous indique l’objet ajouté. (Je suis conscient que ce n’est pas très pratique mais il ne s’agit que d’un exemple).

Code:
class Array
  # Alias de la méthode push
  alias ancien_push push
  # Redéfinition de push
  def push object
    # Appel de l'ancienne méthode
    self.ancien_push object
    print "Ajout de #{object.to_s}"
  end
end

Comme vous pouvez le voir, il est très facile de modifier une méthode. Cependant, il est aussi possible de greffer de nouvelles méthodes à une classe déjà existante. Par exemple, ajoutons une méthode getfirst, cette méthode ne sert a rien mais elle nous retournera le premier objet contenu dans notre tableau :

Code:
class Array
  def getfirst
    return self[0]
  end
end

Ce qui nous amène a la conclusion que si une classe porte le même nom qu’une autre classe, elle se fusionneront. Cependant, sans alias, les méthodes seront écrasées. Donc a moins de ne modifier completement le traitement d’une méthode, évitons de réécrire du code inutilement et utilisons les alias.

3.1 Encore plus loin dans la création de patch’s
Nous avons vu qu’il était possible de greffer/modifier des méthodes aux classes. Il est aussi possible de greffer des méhodes à des objets. Par exemple, admettons que j’ai une variable qui contienne une chaine de caractère et que je veuille pouvoir utiliser sur cette variable une méthode qui me retournerait un booléen pour savoir si la première lettre de ma chaine est bien C. Ce genre de méthode ne serait utile que pour une seule variable et il serait dommage de modifier intégralement la classe String pour si peu.

Code:
test_string = "Ce que je suis, un Chat"
def test_string.verification
  return (self =~ /^C/) != nil
end


J’en conviens que cette méthode n’est pas vraiment utile, mais comme vous pouvez le voir, il est facile de greffer certaines composantes uniquement à certaines instances et non à toute une classe.

3.2 Une arme à double tranchant
Bien que vu sous cet aspect, le monkeypatching semble une solution agréable de modification de code et donc, par extension, de confort d’utilisation, je trouve (et ça n’engage que moi) que le monkeypatching pose aussi un réel problème de raisonnement du code. Bien qu’il soit très agréable de pouvoir jongler avec les méthodes du langage, la modification abusive entraîne rend souvent un code plus complexe a relire que si des classes spécifiques avaient été définies. Cependant, ce n’est qu’une opinion.

3.3 Conclusion
Le monkeypatching nous amène naturellement à l’affirmation que les classes sont ouvertes. Il est donc possible de les modifier à la volée. Ce qui amène donc à recommander une forme d’éducation de la part des développeurs pour éviter que cet excès de liberté n’amène à la déterioration d’un noyau de code. Je vous laisse analyser cette exemple pour comprendre cette mise en garde.

Code:
class Fixnum
  def +(obj)
    self * obj
  end
end

Bien que cet exemple démontre une certaine stupidité de la part du développeur, il serait possible d’imaginer des exemples plus perfide et moins facilement détectable.



4 Un peu plus sur les classes

Un concept utile à la métaprogrammation est qu’en Ruby, les noms de classes sont des constantes. Il est donc possible d’instancier nos objets de cette manière :

Code:
class Some
  def initialize
    @attribut = 10
  end
end
classe = Some
test = classe.new

Mais ce n’est pas tout à propos des classes. En Ruby, une classe est définie quand le programme est lancé et non à la compilation (car le langage est interprêté). Entre les blocks méthodes, il est aussi possible d’exécuter du code. Par exemple, imaginons une méthode changeant de comportemment en fonction de son contexte :

Code:
$in_debug = true
class Some
  if $in_debug
    def test
      return "méthode en debug"
    end
  else
    def test
      return "méthode pas en debug"
    end
  end
end
t = Some.new
print t.test

Cependant, sachez que cette exécution est effectuée à l'exécution du code et donc, même en changeant la valeur de la variable globale, la classe a été définie comme étant en debugmode.
Il est donc possible de définir les accesseur et les mutateurs au moyen d'une boucle. Je montre cet exemple uniquement pour donner des pistes vers la création d'un Je me confonds en excuses... (Domain specific language), cependant, je vous déconseille fortemment d'utiliser une boucle pour définir les accesseurs/mutateurs. Il ne s'agit, ici, que d'une simple expérience.

Code:
class Some
  for i in 0..3
    attr_accessor "arg#{i}".to_sym
  end
  def initialize
    @arg0 = 2
    @arg1 =9
    @arg2 =18
    @arg3 =27
  end
end
test = Some.new
print test.arg2

la notion importante de cet exemple est que les classes sont définies à l'exécution et qu'il est donc possible de prendre beaucoup de raccourcis syntaxique.

4.1 Les classes sont des objets
En ruby (et dans d'autres langages de programmation orientés objets), une classe est avant tout une instance d'un objet Class. Il est donc tout a fait possible d'accéder au constructeur de Class (et de définir une classe en lui passant un block en paramètre). Cependant, cela va beaucoup plus loin.
Rappellons nous qu'il est possible d'exécuter des actions entre des méthodes en Ruby. C'est d'ailleurs sur ce principe que reposent les attr_accessor, attr_reader,attr_writter qui sont des méthodes qui définissent un comportemment sur l'instance de Classe et non sur la future instance de notre classe.

4.2 Utiliser les module
Bien que le comportemment primaire des modules soit de faire office d'espace nom, il est possible de faire ce qu'on appelle des Mixins. Il s'agit d'utiliser l'inclusion d'un module pour partager ses méthodes avec une classe. Par exemple:

Code:
module AModule
  def sayHello
    print "Hello guy's, i'm #{@prenom} #{@nom}"
  end
end
class Michael
  include AModule
  def initialize
    @prenom = "Michael"
    @nom = "Spawn"
  end
end
mick = Michael.new
mick.sayHello

Dans cet exemple, le module utilise les attributs de la classe et l'appelle de la méthode sayHello fonctionne parfaitement.
Si vous avez déjà souvent utilisé Ruby, vous savez qu'il est possible de définir des méthodes de classes de cette manière:

Code:
class Some
  class << self
    def test x, y
      return x + y
    end
  end
  # Ou bien
  def self.test2 x , y
    return x * y
  end
end

En utilisant la théorie des mixins, il est possible de relier des méthodes à un contexte statique, donc en tant que méthode de classe (et non d'instance). Voici un exemple:

Code:
module AModule
  def somme x, y
    return x + y
  end
end
class Some
  extend AModule
end
print Some.somme 9, 10

Comme on peut le voir dans l'exemple, il est donc possible d'étendre des méthodes au singleton d'une classe. Il faut évidemment prendre ces exemples dans des contextes pertinents, comme par exemple la réalisation d'une bibliothèque réutilisable. Tout ces petits exemples nous amènent petit à petit à la dernière partie de ce cours.



Pratique

Nous allons maintenant mettre en pratique les cas que nous avons soulevés précédemment sous forme de petits exemples rapides.

5 Construire une classe dynamiquement
Une classe est une instance d’un objet Class, il est donc possible d’accèder à son constructeur.

Code:

uneClasse = Class.new do
  attr_accessor :argument1
  attr_accessor :argument2
  def initialize args1, args2
    @argument1, @argument2 = args1, args2
  end
end
test = uneClasse.new 1, 2
print test.argument2

Cet exemple assez naïf n'est ici que pour montrer qu'il est possible de créer des classes dynamiques au même titre que n'importe quelle objet.


6 Vers un premier Je me confonds en excuses...

Grâce aux concepts étudiés précédemment, nous allons pouvoir nous lancer progressivement dans la création d'un petit Je me confonds en excuses... pour faciliter l'utilisation de nos propres outils.
L'exemple fourni est très simple (et très inutile), il montre comment utiliser de manière intuitive, une méthode pour multiplier un argument par une valeur fournie.

Code:
module AR
  def bind_test *args
    return @test if args.length == 0
    @test = args[0]
  end
end
class Some
  extend AR
  bind_test 99
  attr_accessor :arg1
  def initialize arg1
    @arg1 = arg1 * (self.class).bind_test
  end
end
test = Some.new(9)
print test.arg1

Dans le cas d'une construction de librairie, le module AR n'est pas visible (enfin, pas situé dans la même portion de code), il est donc possible de simuler un véritable petit Je me confonds en excuses... comme par exemple dans la portion de code suivante:

Code:
class Weapons < Tables
  set_table_name :weapons
  set_table_fields :id, :cost, :stats
  set_default_value :default_data
end



Conclusion

J'achève ici cette sommaire présentation de certains concepts liés à la métaprogrammation. Il s'agit de méthodes qui peuvent être très amusantes à rédiger. Cependant, il ne s'agit pas d'une approche très orientée "concepts précis" mais plutôt d'une explication générale sur certains concepts relatifs à Ruby pour la construction orienté métaprogrammation. Ce genre de techniques sont généralement utilisées pour le développement de librairies. Je doute qu'il soit réellement nécéssaire de déployer ce genre de méthode dans des petites applications.
Je vous remercie d'avoir lu mon article.
Michaël.

_________________
http://fa.gri.im/~mspawn/
Revenir en haut Aller en bas
Voir le profil de l'utilisateur http://fa.gri.im/~mspawn/
Illusionniste Lv.12
Illusionniste Lv.12
avatar


Masculin Age : 27
Inscrit le : 14/02/2010
Messages : 796

MessageSujet: Re: un peu de métaprogrammation avec Ruby    Jeu 31 Mai 2012 - 20:05

Cet article a un intérêt plus grand que celui sur le monkeypatching pour les lecteurs de ce forum, vu que tu utilises des bouts de codes en ruby pour exemples.

Ceci dit, ta définition de la métaprogrammation est pas super claire. Okay, c'est lié au concept qui est assez flou lui même, mais si tu donnais un exemple de "méthode qui modifie une méthode", en pseudo langage "objet voiture attribut roue", ça permettrait de mieux capter.
Ensuite, faut vraiment que tu te places au niveau du lecteur : explique ce que font tous les bouts de codes que tu écris ici. Sinon, tu prêches aux convaincus, tu apprends à ceux qui connaissent déjà.


_________________


Merci à Kouett pour :
Spoiler:
 
Revenir en haut Aller en bas
Voir le profil de l'utilisateur
Nomimange'twa ♪
Nomimange'twa ♪
avatar


Masculin Age : 26
Inscrit le : 02/02/2010
Messages : 4689

MessageSujet: Re: un peu de métaprogrammation avec Ruby    Sam 25 Mai 2013 - 12:22

Comme Tiro l'a dit, c'est pas super simple à comprendre, mais ça explique un concept intéressant.
Même si ça n'est pas accessible à tous, ça n'a plus rien à faire dans les soumissions de tutos. Je déplace.

Merci du partage S4s', même si je dis ça avec 1 an de retard... (et que je te donne tes 5 points professeur avec 1 an de retard aussi donc).

_________________

Revenir en haut Aller en bas
Voir le profil de l'utilisateur http://imagin.jeunforum.com/portal.htm
Contenu sponsorisé




MessageSujet: Re: un peu de métaprogrammation avec Ruby    

Revenir en haut Aller en bas
 

un peu de métaprogrammation avec Ruby

Voir le sujet précédent Voir le sujet suivant Revenir en haut 
Page 1 sur 1

Permission de ce forum:Vous ne pouvez pas répondre aux sujets dans ce forum
RPG Maker VX :: Entraide :: Tutoriels :: Astuces de maker-
Créer un forum | © phpBB | Forum gratuit d'entraide | Signaler un abus | Forum gratuit