Ruby for Newbies Méthodes manquantes

Ruby est l’un des langages les plus utilisés sur le Web. Nous organisons ici une session sur Nettuts + qui vous présentera Ruby, ainsi que les formidables cadres et outils associés au développement de Ruby. Dans cet épisode, nous allons voir comment les objets Ruby traitent avec des méthodes qui n'existent pas.


Didacticiel vidéo?


Un problème (et une solution)

Disons que vous travaillez avec un objet Ruby. Et disons aussi que vous n'êtes pas tout à fait familier avec cet objet. Et disons aussi que vous appelez une méthode qui n'existe pas sur l'objet.

o = Object.new o.some_method # NoMethodError: méthode non définie 'une_méthode' pour #

Ceci est moins que souhaitable, donc Ruby a un moyen génial de nous permettre de nous en sortir. Regarde ça:

class OurClass def method_missing (nom_methode) met "il n'y a pas de méthode appelée '# nom_methode'" "end end = = NotreClasse.new o.some_method # => il n'y a pas de méthode appelée" some_method "

Nous pouvons créer une méthode appelée method_missing dans notre classe. Si l'objet sur lequel nous appelons la méthode n'a pas la méthode (et n'hérite pas de la méthode d'une autre classe ou d'un autre module), Ruby nous donnera une chance supplémentaire de faire quelque chose d'utile: si la classe a un method_missing méthode, nous vous donnerons les informations sur la méthode cal method_missing et laissez-le trier le désordre.

Eh bien, c'est génial! nous ne recevons plus de message d'erreur.


Une meilleure utilisation

Mais arrêtez-vous et réfléchissez-y une seconde. Tout d'abord: non, nous ne recevons plus de message d'erreur, mais nous n'obtenons rien d'utile. Il est difficile de dire ce qui serait utile dans ce cas, car le nom de la méthode ne suggère rien. Deuxièmement, c'est assez puissant, car cela vous permet de passer n'importe quelle méthode à un objet et d'obtenir un résultat intelligent..

Faisons quelque chose qui a plus de sens; Commencez par ceci:

class TutsSite attr_accessor: name,: tutoriels def initialize name = "", tuts = [] @name = name @tutorials = tuts end def get_tuts_about_javascript @ tutorials.select do | tut | tut [: tags] .include? "javascript" end end def get_tuts_by_jeffrey_way @ tutorials.select faire | tut | tut [: author] == "Jeffrey Way" fin fin fin

Ici vous voyez une petite classe pour un site tutoriel. Lors de la création d'un nouvel objet de site Web, nous lui transmettons un nom et un tableau de tutoriels. Nous nous attendons à ce que les tutoriels soient des hachages sous la forme suivante:

titre: "Certains titres", auteur: "l'auteur", tags: ["tableau", "de", "tags"] # Ruby 1.9 # OU : titre => "Certains titres",: auteur => " l'auteur ",: tags => [" array "," of "," tags "] # Ruby 1.8

Nous attendons des symboles comme clés. remarquez que si vous n'utilisez pas Ruby 1.9, vous devrez utiliser le format du bas pour vos hashes (les deux fonctionnent en 1.9)

Ensuite, nous avons deux fonctions d’aide qui nous permettent d’obtenir uniquement le tutoriel contenant une balise JavaScript ou uniquement les tutoriels de Jeffrey Way. Ceux-ci sont utiles pour filtrer les tutoriels? mais ils ne nous donnent pas trop d'options. Bien sûr, nous pourrions faire des méthodes nommées get_tuts_with_tag et get_tuts_by_author qui prennent des paramètres avec la balise ou le nom de l'auteur. Cependant, nous allons suivre un chemin différent: method_missing.

Comme nous l'avons vu, method_missing obtient le nom de la méthode tentée en tant que paramètre. Ce que je n'ai pas mentionné, c'est que c'est un symbole. De plus, les paramètres transmis à la méthode et au bloc (le cas échéant) sont également disponibles. Notez que les paramètres sont transmis en tant que paramètres individuels à method_missing, donc la convention habituelle est d'utiliser l'opérateur splat pour tous les rassembler dans un tableau:

def method_missing nom, * args, & block end

Donc, puisque nous pouvons obtenir le nom de la méthode qui a été tentée, nous pouvons analyser ce nom et en faire quelque chose d'intelligent. Par exemple, si l'utilisateur appelle quelque chose comme ceci:

nettuts.get_tuts_by_jeffrey_way nettuts.get_tuts_about_html nettuts.get_tuts_about_canvas_by_rob_hawkes nettuts.get_tuts_by_jeremy_mcpeak_about_asp_net

Alors, passons à cela; éliminez ces méthodes précédentes et remplacez-le par ceci:

def method_missing nom, * argument, & block tuts = @ tutorials.dup nom = nom.à_.downcase if (md = / ^ get_tuts_ (by_ | à propos de _) (\ w *?) ((_ by_ | _à propos de _) (\ w *) )? $ /. correspond le nom) si md [1] == 'by_' tuts.select! | tut | tut [: auteur] .downcase == md [2] .gsub ("_", "") tuts.select! | tut | tut [: tags] .include? md [5] .gsub ("_", "") si md [4] == '_about_' elsif md [1] == 'about_' tuts.select! | tut | tut [: tags] .include? md [2] .gsub ("_", "") tuts.select! | tut | tut [: auteur] .downcase == md [5] .gsub ("_", "") si md [4] == '_by_' end else tuts = "Cet objet ne prend pas en charge l'objet" #  name '"end tuts end

Ne vous inquiétez pas, nous allons passer à travers tout cela maintenant. Nous commençons par dupliquer le @ tutoriels tableau; chaque objet Ruby a un dup méthode qui le copie; si nous ne faisions pas cela et que nous venions de dire tuts = @tutorial-nous travaillerions avec le tableau d'origine, ce que nous ne voulons pas faire; nous voulons préserver ce tableau tel quel. Ensuite, nous filtrerons les hashs du tutoriel que nous ne voulons pas.

Nous devons également obtenir le nom de la méthode; depuis qu'il est passé à method_missing en tant que symbole, nous le convertissons en chaîne avec to_s puis assurez-vous qu'il est en minuscule avec downcase.

Maintenant, nous devons vérifier que la méthode correspond au format que nous voulons. après tout, il est possible que quelqu'un puisse passer autre chose à la méthode. Alors, analysons ce nom de méthode. Si cela correspond, nous allons travailler sur la magie; sinon, nous retournons un message d'erreur par défaut:

 if (md = /^get_tuts_(by_|about_)(\w*?)((_by_|_about_)(\w*))?$/.match name) #coming else tuts = "Cet objet ne prend pas en charge le méthode '# name' "end

Cela semble plutôt décourageant, mais vous devriez le comprendre: en gros, nous cherchons? Get_tuts_? suivi de? by_? ou? à propos de? ensuite, nous avons un nom d'auteur ou une balise, suivi de? _by_? ou? environ? et un auteur ou une étiquette. Si cela correspond, nous stockons le MatchData objet dans Maryland; sinon, nous aurons néant retour; dans ce cas, nous allons définir tuts au message d'erreur. Nous faisons cela pour que de toute façon, nous puissions revenir tuts.

Donc, les expressions régulières correspond, nous aurons un MatchData objet. Si le nom de la méthode utilisée était get_tuts_by_andrew_burgess_about_html, Voici les indices que vous avez:

0. get_tuts_by_andrew_burgess_about_html 1. by_ 2. andrew_burgess 3. _about_html 4. _about_ 5. html

Je remarque que si l’un des groupes facultatifs n’est pas renseigné, son index a la valeur néant.

Donc, les données que nous voulons sont aux indices 2 et 5; N'oubliez pas que nous ne pouvons obtenir qu'une balise, qu'un auteur, ou les deux (dans l'un ou l'autre ordre). Nous devons ensuite filtrer les tuts qui ne correspondent pas à nos critères. Nous pouvons le faire avec le tableau sélectionner méthode. Il passe chaque élément à un bloc, un par un. Si le bloc revient vrai, l'article est conservé; si ça revient faux, l'élément est jeté hors du tableau. Commençons par ceci:

si md [1] == 'by_' tuts.select! | tut | tut [: auteur] .downcase == md [2] .gsub ("_", "") tuts.select! | tut | tut [: tags] .include? md [5] .gsub ("_", "") si md [4] == '_about_'

Si md [1] est? by_?, nous savons que l'auteur est venu en premier. Par conséquent, à l'intérieur du bloc de la première sélectionner appelez, nous obtenons le tut nom de l'auteur de hash (downcase et le comparer à md [2]. J'utilise la méthode de substitution globale-gsub-pour remplacer tous les traits de soulignement par un seul espace. Si la comparaison des chaînes est vraie, l'élément est conservé. sinon ce n'est pas. Dans la seconde sélectionner appel, nous vérifions la balise (stockée dans md [5]) dans le tut [: tags] tableau. Le tableau comprendre? la méthode retournera vrai si l'élément est dans le tableau. Remarquez le modificateur à la fin de cette ligne: nous ne le faisons que si le quatrième indice est la chaîne??.

Notez que nous utilisons réellement le tableau sélectionner méthode: nous utilisons sélectionner! (avec un coup / point d'exclamation). Cela ne renvoie pas un nouveau tableau contenant uniquement les éléments sélectionnés. cela fonctionne avec le réel tuts tableau en mémoire.

Maintenant que vous comprenez cela, vous ne devriez pas avoir de problème avec les lignes suivantes:

elsif md [1] == 'about_' tuts.select! | tut | tut [: tags] .include? md [2] .gsub ("_", "") tuts.select! | tut | tut [: auteur] .downcase == md [5] .gsub ("_", "") si md [4] == '_by_' end

Ces lignes font la même chose que ci-dessus, mais elles concernent les noms de méthodes dans la situation inverse: tag first, facultatif author second.

A la fin de la méthode, on retourne tuts; c'est soit le tableau filtré, soit le message d'erreur.

Maintenant, testons ceci:

tuts = [title: "Comment passer d'une image de N & B à une couleur avec une toile", auteur: "Jeffrey Way", tags: ["javascript", "canvas"], titre: "Node.js étape par étape : Application de blogging ", auteur:" Christopher Roach ", tags: [" javascript "," node "], titre:" Les 30 sélecteurs CSS que vous devez mémoriser ", auteur:" Jeffrey Way ", tags: [" css "," sélecteurs "], titre:" Responsive Web Design: Guide visuel ", auteur:" Andrew Gormley ", tags: [" html "," responsive design "]], titre:" Développement Web à partir de Scratch : Basic Layout ", auteur:" Jeffrey Way ", tags: [" html "], titre:" Protéger une application CodeIgniter contre CSRF ", auteur:" Ian Murray ", tags: [" php "," codeigniter " ], title: "Gérer les tâches cron avec PHP", auteur: "Nikola Malich", tags: ["php", "tâches cron"]] nettuts = TutsSite.new "Nettuts +", tuts p nettuts.get_tuts_by_ian_murray # [: title => "Protéger une application CodeIgniter contre CSRF",: author => "Ian Murray",: tags => ["php", "codeigniter"]] nettuts.get_tuts_about_html # [: ti tle => "Conception Web réactive: un guide visuel",: author => "Andrew Gormley",: tags => ["html", "responsive design"], : title => "Développement Web à partir de Scratch: Base Layout ",: author =>" Jeffrey Way ",: tags => [" html "]] nettuts.get_tuts_by_jeffrey_way_about_canvas # [: title =>" Comment passer d'une image à partir de N & B à colorier avec du canevas ",: author => "Jeffrey Way",: tags => ["javascript", "canvas"]], p nettuts.get_tuts_about_php_by_nikola_malich # [: title => "Gérer les tâches cron avec PHP",: author => "Nikola Malich", : tags => ["php", "cron jobs"]] nettuts.submit_an_article # Cet objet ne prend pas en charge la méthode 'submit_an_article' "

je suis p-extraire les résultats de ces méthodes, de sorte que vous puissiez l'exécuter dans un fichier ruby ​​sur la ligne de commande.


Un avertissement

Je devrais mentionner que, bien que ce soit assez cool, ce n'est pas nécessairement le bon usage de method_missing. C’est là principalement pour vous protéger des erreurs. Cependant, la convention n’est pas mauvaise: elle est largement utilisée dans les ActiveRecord classes qui sont une grande partie de Ruby on Rails.


Un bonus

Vous ne saviez probablement pas qu’il existait une fonctionnalité similaire en JavaScript: c’est la __noSuchMethod__ méthode sur les objets. Autant que je sache, cela n’est supporté que par FireFox, mais c’est une idée intéressante. J'ai réécrit l'exemple ci-dessus en JavaScript et vous pouvez le vérifier à ce JSBin.


Conclusion

C'est un tour pour aujourd'hui! J'ai des affaires intéressantes avec Ruby dans ma manche et je viens bientôt te chercher. Gardez un œil sur Nettuts +, et si vous voulez quelque chose de spécifique, faites-le-moi savoir dans les commentaires.!