Une des choses que j’aime dans Ruby, c’est que c’est surtout un langage très lisible (excellent pour le code auto-documenté).
Cependant, inspiré par cette question: Ruby Code a expliqué Et la description de la façon dont fonctionne ||=
dans Ruby, je pensais aux idiomes de Ruby que je n’utilise pas, car franchement, je ne les ai pas entièrement .
Ma question est donc, semblable à l'exemple de la question citée en référence, quels sont les idiomes de Ruby communs, mais non évidents, dois-je connaître pour être un programmeur Ruby véritablement compétent?
Au fait, de la question référencée
a ||= b
est équivalent à
if a == nil || a == false
a = b
end
(Merci à Ian Terrell pour la correction)
Edit: Il s'avère que ce point n'est pas totalement controversé. L'expansion correcte est en fait
(a || (a = (b)))
Voir ces liens pour pourquoi:
Merci à Jörg W Mittag pour l'avoir signalé.
La clause magic if qui permet au même fichier de servir de bibliothèque ou de script:
if __FILE__ == $0
# this library may be run as a standalone script
end
Emballage et déballage des matrices:
# put the first two words in a and b and the rest in arr
a,b,*arr = *%w{a dog was following me, but then he decided to chase bob}
# this holds for method definitions to
def catall(first, *rest)
rest.map { |Word| first + Word }
end
catall( 'franken', 'stein', 'berry', 'sense' ) #=> [ 'frankenstein', 'frankenberry', 'frankensense' ]
Le sucre syntatical pour les hachages en tant qu'arguments de méthode
this(:is => :the, :same => :as)
this({:is => :the, :same => :as})
Initialisateurs de hachage:
# this
animals = Hash.new { [] }
animals[:dogs] << :Scooby
animals[:dogs] << :Scrappy
animals[:dogs] << :DynoMutt
animals[:squirrels] << :Rocket
animals[:squirrels] << :Secret
animals #=> {}
# is not the same as this
animals = Hash.new { |_animals, type| _animals[type] = [] }
animals[:dogs] << :Scooby
animals[:dogs] << :Scrappy
animals[:dogs] << :DynoMutt
animals[:squirrels] << :Rocket
animals[:squirrels] << :Secret
animals #=> {:squirrels=>[:Rocket, :Secret], :dogs=>[:Scooby, :Scrappy, :DynoMutt]}
syntaxe de métaclasse
x = Array.new
y = Array.new
class << x
# this acts like a class definition, but only applies to x
def custom_method
:pow
end
end
x.custom_method #=> :pow
y.custom_method # raises NoMethodError
variables d'instance de classe
class Ticket
@remaining = 3
def self.new
if @remaining > 0
@remaining -= 1
super
else
"IOU"
end
end
end
Ticket.new #=> Ticket
Ticket.new #=> Ticket
Ticket.new #=> Ticket
Ticket.new #=> "IOU"
Blocs, procs et lambdas. Vivez et respirez.
# know how to pack them into an object
block = lambda { |e| puts e }
# unpack them for a method
%w{ and then what? }.each(&block)
# create them as needed
%w{ I saw a ghost! }.each { |w| puts w.upcase }
# and from the method side, how to call them
def ok
yield :ok
end
# or pack them into a block to give to someone else
def ok_dokey_ok(&block)
ok(&block)
block[:dokey] # same as block.call(:dokey)
ok(&block)
end
# know where the parentheses go when a method takes arguments and a block.
%w{ a bunch of words }.inject(0) { |size,w| size + 1 } #=> 4
pusher = lambda { |array, Word| array.unshift(Word) }
%w{ eat more fish }.inject([], &pusher) #=> ['fish', 'more', 'eat' ]
Ce diaporama est assez complet sur les idiomes principaux Ruby, comme dans:
Échangez deux valeurs:
x, y = y, x
Paramètres qui, s'ils ne sont pas spécifiés, prennent une valeur par défaut
def somemethod(x, y=nil)
Met en place des paramètres parasites dans un tableau
def substitute(re, str, *rest)
Etc...
Quelques autres idiomes:
Utilisation des délimiteurs %w
, %r
et %(
%w{ An array of strings %}
%r{ ^http:// }
%{ I don't care if the string has 'single' or "double" strings }
Comparaison de type dans les déclarations de cas
def something(x)
case x
when Array
# Do something with array
when String
# Do something with string
else
# You should really teach your objects how to 'quack', don't you?
end
end
... et abus général de la méthode ===
dans les instructions case
case x
when 'something concrete' then ...
when SomeClass then ...
when /matches this/ then ...
when (10...20) then ...
when some_condition >= some_value then ...
else ...
end
Quelque chose qui devrait sembler naturel aux rubyistes, mais peut-être pas aux personnes venant d'autres langues: l'utilisation de each
en faveur de for .. in
some_iterable_object.each{|item| ... }
Dans Ruby 1.9+, Rails, ou en corrigeant la méthode Symbol # to_proc, this devient un idiome de plus en plus populaire:
strings.map(&:upcase)
Méthode conditionnelle/définition constante
SOME_CONSTANT = "value" unless defined?(SOME_CONSTANT)
Méthodes de requête et méthodes destructives
def is_awesome?
# Return some state of the object, usually a boolean
end
def make_awesome!
# Modify the state of the object
end
Paramètres de splat implicites
[[1, 2], [3, 4], [5, 6]].each{ |first, second| puts "(#{first}, #{second})" }
J'aime ça:
str = "Something evil this way comes!"
regexp = /(\w[aeiou])/
str[regexp, 1] # <- This
Ce qui équivaut (approximativement) à:
str_match = str.match(regexp)
str_match[1] unless str_match.nil?
Ou du moins c'est ce que j'ai utilisé pour remplacer ces blocs.
Je suggérerais de lire le code de plugins populaires et bien conçus ou de joyaux de personnes que vous admirez et respectez.
Quelques exemples que j'ai rencontrés:
if params[:controller] == 'discussions' or params[:controller] == 'account'
# do something here
end
correspond à
if ['account', 'discussions'].include? params[:controller]
# do something here
end
qui plus tard serait refactored à
if ALLOWED_CONTROLLERS.include? params[:controller]
# do something here
end
Par ailleurs, de la référencé question
a ||= b
est équivalent à
if a == nil a = b end
C'est subtilement incorrect et c'est une source de bugs dans les applications Ruby des nouveaux arrivants.
Puisque les deux (et seulement) nil
et false
correspondent à un booléen faux, a ||= b
est en réalité (presque *) équivalent à:
if a == nil || a == false
a = b
end
Ou, pour le réécrire avec un autre langage Ruby:
a = b unless a
(* Étant donné que chaque instruction a une valeur, techniquement, elle n'est pas équivalente à a ||= b
. Mais si vous ne vous fiez pas à la valeur de l'instruction, vous ne verrez aucune différence.)
En voici quelques exemples, provenant de diverses sources:
utilisez "à moins que" et "jusqu'à" au lieu de "sinon" et "tant que pas". Essayez de ne pas utiliser "sauf" quand une condition "else" existe cependant.
N'oubliez pas que vous pouvez affecter plusieurs variables à la fois:
a,b,c = 1,2,3
et même permuter variable sans temp:
a,b = b,a
Utilisez des conditions de fin, le cas échéant, par exemple.
do_something_interesting unless want_to_be_bored?
Soyez conscient d'un moyen couramment utilisé mais pas tout de suite évident (pour moi du moins) de définir les méthodes de classe:
class Animal
class<<self
def class_method
puts "call me using Animal.class_method"
end
end
end
Quelques références:
Je maintiens une page wiki qui couvre certains idiomes et formats de Ruby:
J'aime comment If-then-elses ou case-when peuvent être raccourcis car ils renvoient une valeur:
if test>0
result = "positive"
elsif test==0
result = "zero"
else
result = "negative"
end
pourrait être réécrite
result = if test>0
"positive"
elsif test==0
"zero"
else
"negative"
end
La même chose pourrait être appliquée au cas où:
result = case test
when test>0 ; "positive"
when test==0 ; "zero"
else "negative"
end
a = (b && b.attribute) || "default"
est à peu près:
if ( ! b.nil? && ! b == false) && ( ! b.attribute.nil? && ! b.attribute.false) a = b
else a = "default"
J'utilise ceci quand b est un enregistrement qui peut avoir été trouvé ou non, et je dois obtenir l'un de ses attributs.
Vous pouvez facilement copier en profondeur avec un objet Marshaling. - tiré du langage de programmation Ruby
def deepcopy(o)
Marshal.load(Marshal.dump(o))
end
Notez que les fichiers et les flux d’E/S, en tant que ainsi que les objets Method et Binding, sont trop dynamiques pour être marshalés; Là serait pas un moyen fiable pour restaurer leur état.
J'oublie toujours la syntaxe exacte de cette abréviation, si une autre instruction (et le nom de l'opérateur. Commente quelqu'un?) Je pense qu'elle est largement utilisée en dehors de Ruby, mais si quelqu'un d'autre veut la syntaxe ici, c'est:
refactor < 3 ? puts("No need to refactor YET") : puts("You need to refactor this into a method")
s'étend à
if refactor < 3
puts("No need to refactor YET")
else
puts("You need to refactor this into a method")
end
mettre à jour
appelé l'opérateur ternaire:
retourner myvar? myvar.size: 0
méthode manquant magick
class Dummy
def method_missing(m, *args, &block)
"You just called method with name #{m} and arguments- #{args}"
end
end
Dummy.new.anything(10, 20)
=> "You just called method with name anything and arguments- [10, 20]"
si vous appelez des méthodes qui n'existent pas dans les objets Ruby, l'interpréteur Ruby appellera une méthode appelée 'method_missing' si elle est définie, vous pouvez l'utiliser pour certaines astuces, comme écrire des wrappers api, ou dsl, où vous ne connaissez pas toutes les méthodes et paramètres. des noms
Bonne question!
Je pense que plus le code est intuitif et rapide, plus nous construisons un meilleur logiciel. Je vais vous montrer comment exprimer mes pensées en utilisant Ruby dans de petits extraits de code. Lire la suite ici
Carte
Nous pouvons utiliser la méthode map de différentes manières:
user_ids = users.map { |user| user.id }
Ou:
user_ids = users.map(&:id)
Échantillon
Nous pouvons utiliser la méthode Rand:
[1, 2, 3][Rand(3)]
Shuffle:
[1, 2, 3].shuffle.first
Et le moyen idiomatique, simple et le plus simple ... échantillon!
[1, 2, 3].sample
Double Pipe Equals/Mémorisation
Comme vous l'avez dit dans la description, nous pouvons utiliser la mémorisation:
some_variable ||= 10
puts some_variable # => 10
some_variable ||= 99
puts some_variable # => 10
Méthode statique/Méthode de classe
J'aime utiliser les méthodes de classe, je pense que c'est une façon très idiomatique de créer et d'utiliser des classes:
GetSearchResult.call(params)
Simple. Belle. Intuitif. Qu'est-ce qui se passe en arrière plan?
class GetSearchResult
def self.call(params)
new(params).call
end
def initialize(params)
@params = params
end
def call
# ... your code here ...
end
end
Pour plus d'informations sur l'écriture de code Ruby idiomatique, lisez ici
Array.pack et String.unpack pour travailler avec des fichiers binaires:
# extracts four binary sint32s to four Integers in an Array
data.unpack("iiii")