Fonctions cachées de Ruby

En continuant le meme “Fonctionnalités cachées de …”, partageons les fonctionnalités moins connues mais utiles du langage de programmation Ruby.

Essayez de limiter cette discussion avec le kernel Ruby, sans aucun truc Ruby on Rails.

Voir également:

  • Fonctionnalités cachées de C #
  • Fonctionnalités cachées de Java
  • Caractéristiques cachées de JavaScript
  • Fonctionnalités cachées de Ruby on Rails
  • Fonctionnalités cachées de Python

(S’il vous plaît, une seule fonctionnalité cachée par réponse.)

Je vous remercie

From Ruby 1.9 Proc # === est un alias de Proc # call, ce qui signifie que les objects Proc peuvent être utilisés dans des cas comme ceux-ci:

def multiple_of(factor) Proc.new{|product| product.modulo(factor).zero?} end case number when multiple_of(3) puts "Multiple of 3" when multiple_of(7) puts "Multiple of 7" end 

Peter Cooper a une bonne liste de trucs Ruby. Peut-être mon préféré lui permet-il d’énumérer à la fois les objects uniques et les collections. (C’est-à-dire, traiter un object non-collection comme une collection contenant uniquement cet object.) Il ressemble à ceci:

 [*items].each do |item| # ... end 

Je ne sais pas à quel point c’est caché, mais je l’ai trouvé utile lorsque vous devez créer un Hash à partir d’un tableau à une dimension:

 fruit = ["apple","red","banana","yellow"] => ["apple", "red", "banana", "yellow"] Hash[*fruit] => {"apple"=>"red", "banana"=>"yellow"} 

Une astuce que j’aime est d’utiliser l’extenseur splat ( * ) sur des objects autres que des tableaux. Voici un exemple de correspondance d’une expression régulière:

 match, text, number = *"Something 981".match(/([Az]*) ([0-9]*)/) 

Autres exemples:

 a, b, c = *('A'..'Z') Job = Struct.new(:name, :occupation) tom = Job.new("Tom", "Developer") name, occupation = *tom 

Wow, personne n’a mentionné l’opérateur de la bascule:

 1.upto(100) do |i| puts i if (i == 3)..(i == 15) end 

Une des choses intéressantes à propos de ruby ​​est que vous pouvez appeler des méthodes et exécuter du code là où d’autres langages ne le seraient pas, comme dans les définitions de méthodes ou de classes.

Par exemple, pour créer une classe ayant une superclasse inconnue jusqu’à l’exécution, c’est-à-dire aléatoire, vous pouvez procéder comme suit:

 class RandomSubclass < [Array, Hash, String, Fixnum, Float, TrueClass].sample end RandomSubclass.superclass # could output one of 6 different classes. 

Ceci utilise la méthode d' Array#sample 1.9 Array#sample (dans 1.8.7 uniquement, voir Array#choice ), et l'exemple est assez artificiel mais vous pouvez voir la puissance ici.

Un autre bon exemple est la possibilité de mettre des valeurs de parameters par défaut qui ne sont pas fixes (comme d'autres langages le demandent souvent):

 def do_something_at(something, at = Time.now) # ... end 

Bien sûr, le problème avec le premier exemple est qu’il est évalué au moment de la définition et non lors de l’appel. Ainsi, une fois qu'une super-classe a été choisie, elle rest cette super-classe pour le rest du programme.

Cependant, dans le deuxième exemple, chaque fois que vous appelez do_something_at , la variable at sera l'heure à laquelle la méthode a été appelée (enfin, très très proche).

Une autre caractéristique minuscule – convertir un Fixnum dans n’importe quelle base jusqu’à 36:

 >> 1234567890.to_s(2) => "1001001100101100000001011010010" >> 1234567890.to_s(8) => "11145401322" >> 1234567890.to_s(16) => "499602d2" >> 1234567890.to_s(24) => "6b1230i" >> 1234567890.to_s(36) => "kf12oi" 

Et comme Huw Walters l’a commenté, la conversion dans l’autre sens est tout aussi simple:

 >> "kf12oi".to_i(36) => 1234567890 

Hash avec les valeurs par défaut! Un tableau dans ce cas.

 parties = Hash.new {|hash, key| hash[key] = [] } parties["Summer party"] # => [] parties["Summer party"] << "Joe" parties["Other party"] << "Jane" 

Très utile en métaprogrammation.

Téléchargez la source Ruby 1.9, et lancez le make golf , vous pouvez faire des choses comme ceci:

 make golf ./goruby -e 'h' # => Hello, world! ./goruby -e 'p St' # => StandardError ./goruby -e 'p 1.tf' # => 1.0 ./goruby19 -e 'p Fil.exp(".")' "/home/manveru/pkgbuilds/ruby-svn/src/trunk" 

Lisez le golf_prelude.c pour plus de choses golf_prelude.c cachées.

Un autre ajout amusant dans la fonctionnalité 1.9 Proc est Proc # curry qui vous permet de transformer un Proc acceptant n arguments en un seul acceptant n-1. Ici, il est combiné avec le Proc # === tip que j’ai mentionné ci-dessus:

 it_is_day_of_week = lambda{ |day_of_week, date| date.wday == day_of_week } it_is_saturday = it_is_day_of_week.curry[6] it_is_sunday = it_is_day_of_week.curry[0] case Time.now when it_is_saturday puts "Saturday!" when it_is_sunday puts "Sunday!" else puts "Not the weekend" end 

Opérateurs booléens sur des valeurs non booléennes.

&& et ||

Les deux renvoient la valeur de la dernière expression évaluée.

C’est pourquoi le ||= mettra à jour la variable avec la valeur renvoyée expression du côté droit si la variable n’est pas définie. Ce n’est pas explicitement documenté, mais la connaissance commune.

Cependant, le &&= n’est pas très connu.

 ssortingng &&= ssortingng + "suffix" 

est équivalent à

 if ssortingng ssortingng = ssortingng + "suffix" end 

C’est très pratique pour les opérations destructives qui ne devraient pas se poursuivre si la variable n’est pas définie.

La fonction Symbol # to_proc fournie par Rails est vraiment cool.

Au lieu de

 Employee.collect { |emp| emp.name } 

Tu peux écrire:

 Employee.collect(&:name) 

Un dernier: dans Ruby, vous pouvez utiliser n’importe quel caractère pour délimiter des chaînes. Prenez le code suivant:

 message = "My message" consortingved_example = "
#{message}
"

Si vous ne voulez pas échapper les guillemets dans la chaîne, vous pouvez simplement utiliser un séparateur différent:

 consortingved_example = %{
#{message}
} consortingved_example = %[
#{message}
]

En plus de ne pas avoir à échapper à des délimiteurs, vous pouvez utiliser ces délimiteurs pour créer des chaînes multilignes plus agréables:

 sql = %{ SELECT ssortingngs FROM complicated_table WHERE complicated_condition = '1' } 

Je trouve que l’utilisation de la commande define_method pour générer dynamicment des méthodes est assez intéressante et peu connue. Par exemple:

 ((0..9).each do |n| define_method "press_#{n}" do @number = @number.to_i * 10 + n end end 

Le code ci-dessus utilise la commande “define_method” pour créer dynamicment les méthodes “press1” à “press9”. Plutôt que de taper les 10 méthodes qui contiennent déjà le même code, la commande define method est utilisée pour générer ces méthodes à la volée si nécessaire.

Utilisez un object Range comme liste de fin indéfinie:

 Inf = 1.0 / 0 (1..Inf).take(5) #=> [1, 2, 3, 4, 5] 

Plus d’infos ici: http://banisterfiend.wordpress.com/2009/10/02/wtf-infinite-ranges-in-ruby/

fonction_module

Les méthodes de module déclarées en tant que module_function créent des copies d’elles-mêmes en tant que méthodes d’instance privées dans la classe qui inclut le module:

 module M def not! 'not!' end module_function :not! end class C include M def fun not! end end M.not! # => 'not! C.new.fun # => 'not!' C.new.not! # => NoMethodError: private method `not!' called for # 

Si vous utilisez module_function sans aucun argument, alors toute méthode de module fournie après l’instruction module_function deviendra automatiquement module_functions.

 module M module_function def not! 'not!' end def yea! 'yea!' end end class C include M def fun not! + ' ' + yea! end end M.not! # => 'not!' M.yea! # => 'yea!' C.new.fun # => 'not! yea!' 

Injecter court, comme tel:

Somme de gamme:

 (1..10).inject(:+) => 55 

Attention: cet article a été élu n ° 1 des Hack les plus horribles de 2008 , donc utilisez-le avec précaution. En fait, évitez-le comme la peste, mais c’est très certainement Hidden Ruby.

Les superateurs ajoutent de nouveaux opérateurs à Ruby

Vous avez toujours besoin d’un opérateur de négociation ultra-secret pour une opération unique dans votre code? Comme jouer au golf de code? Essayez les opérateurs comme – ~ + ~ – ou <--- Ce dernier est utilisé dans les exemples pour inverser l'ordre d'un élément.

Je n’ai rien à voir avec le projet Superators au-delà de l’admirer.

Je suis en retard à la fête, mais:

Vous pouvez facilement prendre deux tableaux de même longueur et les transformer en un hash avec un tableau fournissant les clés et l’autre les valeurs:

 a = [:x, :y, :z] b = [123, 456, 789] Hash[a.zip(b)] # => { :x => 123, :y => 456, :z => 789 } 

(Cela fonctionne car Array # zip “zippe” les valeurs des deux tableaux:

 a.zip(b) # => [[:x, 123], [:y, 456], [:z, 789]] 

Et Hash [] peut prendre un tel tableau. J’ai vu des gens le faire aussi:

 Hash[*a.zip(b).flatten] # unnecessary! 

Qui donne le même résultat, mais le splat et l’aplatissement ne sont absolument pas nécessaires – peut-être qu’ils n’étaient pas dans le passé?)

Hash auto-vivifiants en rbuy

 def cnh # silly name "create nested hash" Hash.new {|h,k| h[k] = Hash.new(&h.default_proc)} end my_hash = cnh my_hash[1][2][3] = 4 my_hash # => { 1 => { 2 => { 3 =>4 } } } 

Cela peut être sacrément pratique.

Détruire un tableau

 (a, b), c, d = [ [:a, :b ], :c, [:d1, :d2] ] 

Où:

 a #=> :a b #=> :b c #=> :c d #=> [:d1, :d2] 

En utilisant cette technique, nous pouvons utiliser une affectation simple pour obtenir les valeurs exactes que nous voulons de la masortingce nestede de n’importe quelle profondeur.

Class.new()

Créez une nouvelle classe au moment de l’exécution. L’argument peut être une classe à dériver, et le bloc est le corps de la classe. Vous pourriez aussi vouloir regarder const_set/const_get/const_defined? pour que votre nouvelle classe soit correctement enregistrée, afin que inspect imprime un nom au lieu d’un nombre.

Pas quelque chose dont vous avez besoin tous les jours, mais très pratique quand vous le faites.

créer un tableau de nombres consécutifs:

 x = [*0..5] 

définit x sur [0, 1, 2, 3, 4, 5]

Une grande partie de la magie que vous voyez dans Rubyland concerne la métaprogrammation, qui consiste simplement à écrire du code qui écrit du code pour vous. attr_accessor , attr_reader et attr_writer sont tous de simples métaprogrammations, en ce sens qu’ils créent deux méthodes sur une ligne, en suivant un modèle standard. Rails fait beaucoup de métaprogrammation avec ses méthodes de gestion de relations comme has_one et belongs_to .

Mais il est assez simple de créer vos propres astuces de métaprogrammation en utilisant class_eval pour exécuter du code écrit dynamicment.

L’exemple suivant permet à un object wrapper de transférer certaines méthodes à un object interne:

 class Wrapper attr_accessor :internal def self.forwards(*methods) methods.each do |method| define_method method do |*arguments, &block| internal.send method, *arguments, &block end end end forwards :to_i, :length, :split end w = Wrapper.new w.internal = "12 13 14" w.to_i # => 12 w.length # => 8 w.split('1') # => ["", "2 ", "3 ", "4"] 

La méthode Wrapper.forwards prend des symboles pour les noms de méthodes et les stocke dans le tableau des methods . Ensuite, pour chacun de ceux donnés, nous utilisons define_method pour créer une nouvelle méthode dont le travail consiste à envoyer le message, y compris tous les arguments et blocs.

Pourquoi consulter le métaprogrammation de façon claire par le Lucky Stiff ?

utiliser tout ce qui répond à ===(obj) pour les comparaisons de cas:

 case foo when /baz/ do_something_with_the_ssortingng_matching_baz when 12..15 do_something_with_the_integer_between_12_and_15 when lambda { |x| x % 5 == 0 } # only works in Ruby 1.9 or if you alias Proc#call as Proc#=== do_something_with_the_integer_that_is_a_multiple_of_5 when Bar do_something_with_the_instance_of_Bar when some_object do_something_with_the_thing_that_matches_some_object end 

Module (et donc Class ), Regexp , Date et bien d’autres classes définissent une méthode d’instance: === (autre), et peuvent toutes être utilisées.

Merci à Farrel pour le rappel de l’ Proc#call étant alias comme Proc#=== dans Ruby 1.9.

Le binary “ruby” (au moins l’IRM) prend en charge un grand nombre des commutateurs qui ont rendu les perl one-liners assez populaires.

Les plus importants:

  • -n Configure une boucle externe avec juste “get” – qui fonctionne comme par magie avec le nom de fichier donné ou STDIN, en définissant chaque ligne de lecture dans $ _
  • -p Similaire à -n mais avec une put automatique à la fin de chaque itération de boucle
  • -a Appel automatique à .split sur chaque ligne d’entrée, stocké dans $ F
  • -i Fichiers d’entrée d’édition sur place
  • -l Appel automatique à .chomp en entrée
  • -e Exécute un morceau de code
  • -c Vérifier le code source
  • -w Avec des avertissements

Quelques exemples:

 # Print each line with its number: ruby -ne 'print($., ": ", $_)' < /etc/irbrc # Print each line reversed: ruby -lne 'puts $_.reverse' < /etc/irbrc # Print the second column from an input CSV (dumb - no balanced quote support etc): ruby -F, -ane 'puts $F[1]' < /etc/irbrc # Print lines that contain "eat" ruby -ne 'puts $_ if /eat/i' < /etc/irbrc # Same as above: ruby -pe 'next unless /eat/i' < /etc/irbrc # Pass-through (like cat, but with possible line-end munging): ruby -p -e '' < /etc/irbrc # Uppercase all input: ruby -p -e '$_.upcase!' < /etc/irbrc # Same as above, but actually write to the input file, and make a backup first with extension .bak - Notice that inplace edit REQUIRES input files, not an input STDIN: ruby -i.bak -p -e '$_.upcase!' /etc/irbrc 

N'hésitez pas à google "ruby one-liners" et "perl one-liners" pour des exemples plus pratiques et utilisables. Il vous permet essentiellement d'utiliser Ruby comme un remplacement assez puissant pour awk et sed.

La méthode send () est une méthode à usage général qui peut être utilisée sur n’importe quelle classe ou object de Ruby. S’il n’est pas remplacé, send () accepte une chaîne et appelle le nom de la méthode dont il transmet la chaîne. Par exemple, si l’utilisateur clique sur le bouton “Clr”, la chaîne “press_clear” sera envoyée à la méthode send () et la méthode “press_clear” sera appelée. La méthode send () permet un moyen amusant et dynamic d’appeler des fonctions dans Ruby.

  %w(7 8 9 / 4 5 6 * 1 2 3 - 0 Clr = +).each do |btn| button btn, :width => 46, :height => 46 do method = case btn when /[0-9]/: 'press_'+btn when 'Clr': 'press_clear' when '=': 'press_equals' when '+': 'press_add' when '-': 'press_sub' when '*': 'press_times' when '/': 'press_div' end number.send(method) number_field.replace strong(number) end end 

Je parle plus de cette fonctionnalité dans Blogging Shoes: L’application Simple-Calc

Tromper une classe ou un module en lui disant qu’il a besoin de quelque chose dont il n’a pas vraiment besoin:

 $" << "something" 

Ceci est utile par exemple lorsque vous exigez A qui à son tour nécessite B mais nous n'avons pas besoin de B dans notre code (et A ne l'utilisera pas non plus via notre code):

Par exemple, bdrb_test_helper requires de bdrb_test_helper requires 'test/spec' , mais vous ne l’utilisez pas du tout, donc dans votre code:

 $" << "test/spec" require File.join(File.dirname(__FILE__) + "/../bdrb_test_helper") 

Fixnum#to_s(base) peut être très utile dans certains cas. Un tel cas génère des jetons uniques (pseudo) aléatoires en convertissant un nombre aléatoire en chaîne en utilisant la base de 36.

Jeton de longueur 8:

 rand(36**8).to_s(36) => "fmhpjfao" rand(36**8).to_s(36) => "gcer9ecu" rand(36**8).to_s(36) => "krpm0h9r" 

Jeton de longueur 6:

 rand(36**6).to_s(36) => "bvhl8d" rand(36**6).to_s(36) => "lb7tis" rand(36**6).to_s(36) => "ibwgeh" 

Définir une méthode qui accepte un nombre quelconque de parameters et les supprime tous

 def hello(*) super puts "hello!" end 

La méthode hello ci hello dessus n’a besoin que de mettre "hello" à l’écran et d’appeler super – mais comme la superclasse hello définit les parameters qu’elle doit également – mais comme elle n’a pas besoin d’utiliser les parameters elle-même – elle n’a pas pour leur donner un nom.