web-dev-qa-db-fra.com

Idiomes communs au rubis

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é.

62
DanSingerman

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' ]
50
rampion

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...

11
VonC

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})" }
8
Chubas

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.

7
Daemin

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
7
ucron

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.)

5
Ian Terrell

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:

5
Mike Woodhouse

Je maintiens une page wiki qui couvre certains idiomes et formats de Ruby:

https://github.com/tokland/tokland/wiki/RubyIdioms

4
tokland

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
1
marcel massana
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.

1
jakeonrails

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.

1
Özgür

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

1
Danny

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

0
edikgat

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

0
leandrotk

Array.pack et String.unpack pour travailler avec des fichiers binaires:

# extracts four binary sint32s to four Integers in an Array
data.unpack("iiii") 
0
SztupY