Fonctionnalités cachées de Ruby

160

Poursuivant 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 noyau Ruby, sans aucun élément Ruby on Rails.

Voir également:

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

Je vous remercie

squadette
la source
devrait être wiki communautaire
SilentGhost

Réponses:

80

De Ruby 1.9 Proc # === est un alias de l'appel Proc #, ce qui signifie que les objets Proc peuvent être utilisés dans des instructions case comme ceci:

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
Farrel
la source
1
J'ai en fait écrit un joyau à un moment donné pour faire cela, mais mon code était (a) en désordre et (b) lent. Je suis très heureux que la fonctionnalité en ait fait le noyau.
James A. Rosen
76

Peter Cooper a une bonne liste de trucs Ruby. Peut-être que mon préféré est de permettre à la fois des articles uniques et des collections d'être énumérés. (Autrement dit, traitez un objet non-collection comme une collection contenant uniquement cet objet.) Cela ressemble à ceci:

[*items].each do |item|
  # ...
end
James A. Rosen
la source
38
Une forme plus explicite (et donc plus agréable) de ceci est Array (items) .each
mislav
Si itemsest une chaîne, vous n'avez pas à la mettre entre [*…]. String.each n'itère pas les caractères comme certains peuvent s'y attendre. Il revient simplement au bloc.
mxcl
À quoi cela servirait-il jamais? Juste curieux.
Ed S.
1
@Ed: c'est bien si vous écrivez une méthode et que vous voulez permettre à l'utilisateur de la méthode de passer une liste de varargs ou un tableau.
James A. Rosen
64

Je ne sais pas à quel point cela est caché, mais je l'ai trouvé utile lorsque j'ai besoin de créer un hachage à partir d'un tableau unidimensionnel:

fruit = ["apple","red","banana","yellow"]
=> ["apple", "red", "banana", "yellow"]

Hash[*fruit]    
=> {"apple"=>"red", "banana"=>"yellow"}
astronautisme
la source
Notez que Hash[ [["apple","red"], ["banana","yellow"] ]produit le même résultat.
Marc-André Lafortune
54

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

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

D'autres exemples incluent:

a, b, c = *('A'..'Z')

Job = Struct.new(:name, :occupation)
tom = Job.new("Tom", "Developer")
name, occupation = *tom
tomafro
la source
13
Incidemment, pour les curieux, cela fonctionne en appelant implicitement to_a sur la cible du splat.
Bob Aman
1
Si vous n'êtes pas intéressé par le match, vous pouvez l'avoir text, number = *"text 555".match(/regexp/)[1..-1].
Andrew Grimm
text, number = "Something 981".scan(/([A-z]*) ([0-9]*)/).flatten.map{|m| Integer(m) rescue m}
Jonas Elfström
7
Les deux bons trucs, mais il doit y avoir un moment où c'est trop de magie, non?!
tomafro
1
@Andrew, avez-vous pensé que ce match peut renvoyer nul? nil n'a pas de méthode []
Alexey
52

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

1.upto(100) do |i|
  puts i if (i == 3)..(i == 15)
end
Konstantin Haase
la source
11
C'est vrai ... quelqu'un va devoir m'expliquer celui-ci. Cela fonctionne, mais je ne comprends pas pourquoi.
Bob Aman
12
L'opérateur de bascule est un if complet. Son état passe à vrai dès i == 3et passe à faux après i != 3 et i == 15. Similaire à une bascule: en.wikipedia.org/wiki/Flip-flop_%28electronics%29
Konstantin Haase
1
Je n'appellerais pas exactement cela une fonctionnalité cachée, tellement ennuyeuse. Je me souviens de la première fois que je l'ai découvert dans #Ruby sur Freenode, il y a des années; J'ai utilisé pratiquement toutes les fonctionnalités de Ruby à un moment donné, sauf celle-ci.
ELLIOTTCABLE
1
Je n'appellerais pas ça un ennui, c'est juste quelque chose que vous n'avez pas utilisé. Je l'utilise et il peut bien réduire le code, surtout lorsque je saisis des blocs de lignes dans des fichiers en fonction de certains critères.
the Tin Man
49

Une des choses intéressantes à propos de ruby ​​est que vous pouvez appeler des méthodes et exécuter du code dans des endroits où d'autres langages seraient désapprouvés, comme dans les définitions de méthode ou de classe.

Par exemple, pour créer une classe qui a une superclasse inconnue jusqu'à l'exécution, c'est-à-dire qu'elle est aléatoire, vous pouvez faire ce qui suit:

class RandomSubclass < [Array, Hash, String, Fixnum, Float, TrueClass].sample

end

RandomSubclass.superclass # could output one of 6 different classes.

Cela utilise la Array#sampleméthode 1.9 (en 1.8.7 uniquement, voir Array#choice), et l'exemple est assez artificiel mais vous pouvez voir la puissance ici.

Un autre exemple intéressant est la possibilité de mettre des valeurs de paramètres par défaut qui ne sont pas fixes (comme le demandent souvent d'autres langues):

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, pas au moment de l'appel. Ainsi, une fois qu'une superclasse a été choisie, elle reste cette superclasse pour le reste du programme.

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

bjeanes
la source
2
Remarque: Array # rand est fourni par ActiveSupport que vous pouvez utiliser en dehors de Rails aussi facilement querequire 'activesupport'
rfunduk
Le choix du numéro de tableau est en 1.8.7
Josh Lee
24
Le choix du # tableau est 1.8.7 uniquement ! Ne l'utilisez pas, il est parti en 1.9 et disparaîtra en 1.8.8. Utilisez #sample
Marc-André Lafortune
python: class DictList ([dict, list] [random.randint (0,1)]): pass
Anurag Uniyal
def do_something_at (something, at = lambda {Time.now}) at.call #now attribuer dynamiquement l'heure de fin
Jack Kinsella
47

Une autre petite fonctionnalité - convertir un Fixnumen 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
tomafro
la source
1
Et pour être complet, String#to_s(base)peut être utilisé pour reconvertir en un entier; "1001001100101100000001011010010".to_i(2), "499602d2".to_i(16)etc. tous renvoient l'original Fixnum.
Huw Walters
40

Hashes avec des 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.

August Lilleaas
la source
1
Ouais vrai. Le hachage Ruby peut accepter l'opérateur '<<' s'il existe déjà une valeur par défaut assignée avec '=' (ne se soucie pas même si c'est une affectation vide) sinon le hachage n'acceptera pas '<<'. CMIIW
mhd
39

Téléchargez la source et le problème de Ruby 1.9 make golf, puis 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.cpour des choses plus soignées qui se cachent.

manveru
la source
38

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 acceptant n-1. Ici, il est combiné avec le conseil Proc # === 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
Farrel
la source
35

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 l'expression de valeur renvoyée sur le côté droit si la variable n'est pas définie. Ceci n'est pas explicitement documenté, mais bien connu.

Cependant, ce &&=n'est pas si largement connu.

string &&= string + "suffix"

est équivalent à

if string
  string = string + "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.

EmFi
la source
2
Plus précisément, string &&= string + "suffix" équivaut à string = string && string + "suffix". Cela &&et ||retourner leur deuxième argument est discuté dans le PickAx, p. 154 (Partie I - Facettes de Ruby, expressions, exécution conditionnelle).
Richard Michael
29

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)
hoyhoy
la source
C'est, apparemment, un "ordre de grandeur plus lent" que l'utilisation d'un bloc. igvita.com/2008/07/08/6-optimization-tips-for-ruby-mri
Charles Roper
Je viens de l'essayer et j'ai trouvé qu'il n'y avait pas de différence significative entre les deux. Je ne sais pas d'où vient ce truc "d'ordre de grandeur". (Utilisation de Ruby 1.8.7)
Matt Grande
1
Faire cela en dehors de Rails est également pratique et peut être fait require 'activesupport'car c'est en fait de là que viennent la plupart de ces assistants.
rfunduk
8
cela était lent à cause de l'implémentation de active_support, c'est-à-dire qu'il acceptait plusieurs arguments afin que vous puissiez faire de la merde cool comme (1..10) .inject &: *, mais le cas d'utilisation principal était souvent d'appeler une méthode sur chaque membre d'un collection par exemple% w (le renard brun rapide) .map &: upcase. à partir de 1.8.7, il est rubis de base et les performances sont raisonnables.
Steve Graham
4
@thenduks: Et cela peut être fait sans l'aide d'actifsupport dans ruby ​​1.8.7 et 1.9.
Andrew Grimm
28

Un dernier - dans ruby, vous pouvez utiliser n'importe quel caractère que vous souhaitez délimiter des chaînes. Prenez le code suivant:

message = "My message"
contrived_example = "<div id=\"contrived\">#{message}</div>"

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

contrived_example = %{<div id="contrived-example">#{message}</div>}
contrived_example = %[<div id="contrived-example">#{message}</div>]

En plus d'éviter d'avoir à échapper des délimiteurs, vous pouvez utiliser ces délimiteurs pour des chaînes multilignes plus agréables:

sql = %{
    SELECT strings 
    FROM complicated_table
    WHERE complicated_condition = '1'
}
tomafro
la source
19
pas n'importe quel personnage, mais c'est quand même assez cool. Il fonctionne également avec d'autres littéraux:% () /% {} /% [] /% <> /% || % r () /% r {} /% r [] /% r <> /% r || % w () /% w {} /% w [] /% w <> /% w ||
Bo Jeanes
Il y a aussi la syntaxe ci-dessous: << BLOCK ... BLOCK, que j'aime utiliser pour des choses comme les instructions SQL multilignes, etc.
Martin T.
26

Je trouve que l'utilisation de la commande define_method pour générer dynamiquement des méthodes est assez intéressante et moins 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 dynamiquement les méthodes "press1" à "press9". Plutôt que de taper les 10 méthodes qui contiennent essentiellement le même code, la commande define method est utilisée pour générer ces méthodes à la volée si nécessaire.

commentaires
la source
4
Le seul problème avec define_method est qu'il ne permet pas de passer des blocs comme paramètres dans ruby ​​1.8. Consultez ce billet de blog pour une solution de contournement.
Andrew Grimm
26

Utilisez un objet Range comme une liste paresseuse infinie:

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/

horseyguy
la source
Le lazy_select dans l'article lié est très soigné.
Joseph Weissman
C'est vraiment génial. J'aime la façon dont Infinity est un flottant, de cette façon quand j'ai essayé ceci: (-Inf..Inf) .take (4) il a soulevé un (logiquement cohérent) ne peut pas itérer à partir d'une erreur de flottement. : D
zachaysan
23

module_function

Les méthodes de module qui sont déclarées comme module_function créeront des copies d'elles-mêmes en tant que méthodes d'instance privée 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 #<C:0x1261a00>

Si vous utilisez module_function sans aucun argument, alors toutes les méthodes de module qui viennent après l'instruction module_function deviendront automatiquement module_functions elles-mêmes.

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!'
newtonapple
la source
4
Si vous souhaitez simplement déclarer des méthodes privées dans des modules, utilisez simplement le mot clé private. En plus de rendre la méthode privée dans les classes qui incluent le module, module_function copie la méthode dans l'instance de module. Dans la plupart des cas, ce n'est pas ce que vous voulez.
tomafro
Je sais que vous pouvez simplement utiliser privé. Mais c'est une question sur les fonctionnalités cachées de Ruby. Et, je pense que la plupart des gens n'ont jamais entendu parler de module_function (moi y compris) jusqu'à ce qu'ils le voient dans la documentation et commencent à jouer avec.
newtonapple
Une alternative à l'utilisation module_function(2ème façon) est d'utiliser simplement extend self(ce qui est plutôt sympa: D)
J -_- L
23

Injecter court, comme tel:

Somme de la plage:

(1..10).inject(:+)
=> 55
user130730
la source
2
Il convient de noter que vous avez besoin de Ruby 1.9 ou de Rails avec Ruby 1.8 pour que cela fonctionne.
mxcl
1
@Max Howell: ou require 'backports':-)
Marc-André Lafortune
1
N'est-ce pas un double de la réponse de hoyhoy?
Andrew Grimm
21

Attention: cet objet a été élu n ° 1 du hack le plus horrible de 2008 , donc à utiliser avec précaution. En fait, évitez-le comme la peste, mais c'est très certainement du rubis caché.

Superators ajoutent de nouveaux opérateurs à Ruby

Vous avez toujours voulu un opérateur de prise de contact super secret pour une opération unique dans votre code? Vous aimez jouer au golf codé? Essayez des 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 à part l' admirer.

Capitaine Hammer
la source
19

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

Vous pouvez facilement prendre deux tableaux de longueur égale et les transformer en hachage 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 faire ça aussi:

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

Ce qui donne le même résultat, mais le splat et l'aplatissement sont totalement inutiles - peut-être qu'ils ne l'étaient pas dans le passé?)

Jordan Running
la source
3
Cela n'a en effet pas été documenté pendant longtemps (voir redmine.ruby-lang.org/issues/show/1385 ). Notez que cette nouvelle forme est nouvelle pour Ruby 1.8.7
Marc-André Lafortune
19

Hashs auto-vivifiants en Ruby

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 très pratique.

Trevoke
la source
1
Je l'envelopperais dans un module pour avoir le même sentiment que l'init de hachage natif:module InfHash; def self.new; Hash.new {|h,k| h[k] = Hash.new(&h.default_proc)}; end; end
asaaki
16

Destructuration d'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 d'un tableau imbriqué de n'importe quelle profondeur.

horseyguy
la source
15

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 voudrez peut-être aussi regarder const_set/const_get/const_defined?pour que votre nouvelle classe soit correctement enregistrée, afin qu'elle inspectimprime un nom au lieu d'un numéro.

Ce n'est pas quelque chose dont vous avez besoin tous les jours, mais tout à fait pratique lorsque vous le faites.

Juste amoureux
la source
1
MyClass = Class.new Array do; def hi; 'hi'; end; endsemble être équivalent à class MyClass < Array; def hi; 'hi'; end; end.
yfeldblum
1
Probablement plus vrai que je ne l'avais pensé. Il semble même que vous puissiez hériter d'une variable plutôt que d'une seule constante. Cependant, la version sucrée (deuxième) ne semble pas fonctionner si vous devez construire le nom de la classe au moment de l'exécution. (Baring eval, bien sûr.)
Justin Love
Cette technique est assez bien décrite dans le livre Metaprogramming Ruby .
Paul Pladijs
13

créer un tableau de nombres consécutifs:

x = [*0..5]

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

horseyguy
la source
Oui, mais ce n'est pas aussi court et doux;)
horseyguy
2
la concision est objective, la lisibilité est affaire de goût et d'expérience
Alexey
L' *opérateur splat ( ) appelle de to_atoute façon.
Matheus Moreira
13

Une grande partie de la magie que vous voyez dans Rubyland a à voir avec la métaprogrammation, qui consiste simplement à écrire du code qui écrit du code pour vous. Ruby attr_accessor, attr_readeret attr_writersont tous metaprogramming simples dans la mesure où ils créent deux méthodes dans une ligne, suivant un modèle standard. Rails fait beaucoup de métaprogrammation avec ses méthodes de gestion des relations comme has_oneet belongs_to.

Mais c'est assez simple de créer vos propres astuces de métaprogrammation en utilisant class_evalpour exécuter du code écrit dynamiquement.

L'exemple suivant permet à un objet wrapper de transmettre certaines méthodes à un objet 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.forwardsprend des symboles pour les noms des méthodes et les stocke dans le methodstableau. Ensuite, pour chacun de ceux donnés, nous utilisonsdefine_method pour créer une nouvelle méthode dont le travail est d'envoyer le message, y compris tous les arguments et blocs.

Une excellente ressource pour les problèmes de métaprogrammation est Why the Lucky Stiff "Seeing Metaprogramming Clearly" .

TALlama
la source
Je souhaite plonger tête la première dans la métaprogrammation en rubis. Pourriez-vous fournir quelques références pour commencer (autres que le lien donné)? Les livres feront aussi l'affaire. Merci.
Chirantan
La série de vidéocasts de PragProg "The Ruby Object Model and Metaprogramming" est une bonne introduction à la métaprogrammation utilisant ruby: pragprog.com/screencasts/v-dtrubyom/…
caffo
@Chirantan, jetez un œil à Metaprogramming Ruby .
Paul Pladijs le
12

utilisez tout ce qui répond à ===(obj)des comparaisons de cas:

case foo
when /baz/
  do_something_with_the_string_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 tous être utilisés.

Merci à Farrel pour le rappel d' Proc#callêtre aliasé comme Proc#===dans Ruby 1.9.

James A. Rosen
la source
11

Le binaire "ruby" (au moins les IRM) supporte un grand nombre de commutateurs qui ont rendu les one-liners perl très populaires.

Les plus importants:

  • -n Configure une boucle externe avec juste "gets" - qui fonctionne comme par magie avec un nom de fichier donné ou STDIN, en définissant chaque ligne de lecture dans $ _
  • -p Similaire à -n mais avec un puts 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 à l'entrée
  • -e Exécute un morceau de code
  • -c Vérifier le code source
  • -w Avec 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 à chercher sur Google "ruby one-liners" et "perl one-liners" pour des tonnes d'exemples pratiques et utilisables supplémentaires. Il vous permet essentiellement d'utiliser ruby ​​comme un remplacement assez puissant de awk et sed.

minaguib
la source
10

La méthode send () est une méthode à usage général qui peut être utilisée sur n'importe quelle classe ou objet de Ruby. S'il n'est pas remplacé, send () accepte une chaîne et appelle le nom de la méthode dont il est passé. 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 une manière amusante et dynamique 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

commentaires
la source
Cela semble être un excellent moyen d'ouvrir une faille de sécurité.
mP.
4
J'utiliserais des symboles dans la mesure du possible.
Reto
9

Tromper une classe ou un module en disant qu'il a exigé quelque chose qu'il n'a pas vraiment requis:

$" << "something"

Ceci est utile par exemple lorsque vous demandez 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, Backgroundrb's 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")
olégueret
la source
Est-ce que cela résout les problèmes où le gem A nécessite foo-1.0.0 et le gem B nécessite foo-1.0.1?
Andrew Grimm
Non parce que le code de "quelque chose" ne sera pas disponible: cela simule seulement que "quelque chose" est nécessaire, mais il ne le nécessite pas vraiment. $ "est un tableau contenant les noms de modules chargés par require (il est utilisé par require pour empêcher le chargement des modules deux fois). Donc, si vous l'utilisez pour tromper les gemmes, cela produira un plantage lorsque les gemmes essaieront d'utiliser le" quelque chose "réel. code, car il n’existera pas. Vous pouvez à la place forcer la codification d’une version concrète d’un gem (par exemple, foo-1.0.0), au lieu de la dernière: docs.rubygems.org/read/chapter/4#page71
olegueret
9

Fixnum#to_s(base)peut être vraiment utile dans certains cas. Un tel cas est la génération de jetons uniques aléatoires (pseudo) 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"
malade malade
la source
9

Définir une méthode qui accepte n'importe quel nombre de paramètres et les ignore tous

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

La hellométhode ci-dessus n'a besoin que d'être affichée à puts "hello"l'écran et d'appeler super- mais comme la superclasse hellodéfinit les paramètres, elle doit également le faire - mais comme elle n'a pas réellement besoin d'utiliser les paramètres elle-même, elle n'a pas besoin de leur donner un nom.

horseyguy
la source