J'ai un collègue qui essaie activement de me convaincre que je ne devrais pas utiliser end..end et utiliser des accolades pour définir des blocs multilignes dans Ruby.
Je suis fermement convaincu que je n'utilise que des accolades pour les doublés et que je… finis avec tout le reste. Mais je pensais que je me rapprocherais de la grande communauté pour obtenir une résolution.
Alors qui est-ce et pourquoi? (Exemple de code Shoulda)
context do
setup { do_some_setup() }
should "do somthing" do
# some more code...
end
end
ou
context {
setup { do_some_setup() }
should("do somthing") {
# some more code...
}
}
Personnellement, le simple fait de regarder ce qui précède répond à la question qui m'est posée, mais je voulais ouvrir cette question à l'ensemble de la communauté.
La convention générale est d'utiliser do..end pour les blocs multilignes et les accolades pour les blocs monolignes, mais il existe également une différence entre les deux qui peut être illustrée avec cet exemple:
puts [1,2,3].map{ |k| k+1 }
2
3
4
=> nil
puts [1,2,3].map do |k| k+1; end
#<Enumerator:0x0000010a06d140>
=> nil
Cela signifie que {} a une priorité plus élevée que do..end, donc gardez cela à l'esprit lorsque vous décidez de ce que vous voulez utiliser.
P.S: Un exemple supplémentaire à garder à l’esprit lorsque vous développez vos préférences.
Le code suivant:
task :rake => pre_rake_task do
something
end
signifie vraiment:
task(:rake => pre_rake_task){ something }
Et ce code:
task :rake => pre_rake_task {
something
}
signifie vraiment:
task :rake => (pre_rake_task { something })
Donc, pour obtenir la définition que vous voulez, avec des accolades, vous devez faire:
task(:rake => pre_rake_task) {
something
}
De toute façon, vous voudrez peut-être utiliser des accolades pour les paramètres, mais si vous ne le faites pas, il vaut probablement mieux utiliser faire .. finissez dans ces cas pour éviter cette confusion.
De programmation Ruby :
Les accolades ont une haute préséance; do a une faible préséance. Si l'invocation de méthode a des paramètres qui ne sont pas entre parenthèses, la forme d'accolade d'un bloc sera liée au dernier paramètre, pas à l'appel global. Le formulaire do sera lié à l'invocation.
Donc le code
f param {do_something()}
Lie le bloc à la variable param
tandis que le code
f param do do_something() end
Lie le bloc à la fonction f
.
Cependant, ceci n’est pas un problème si vous placez les arguments de la fonction entre parenthèses.
Il y a quelques points de vue là-dessus, c'est vraiment une question de préférence personnelle. Beaucoup de rubyists prennent l'approche que vous faites. Cependant, deux autres styles communs consistent à toujours utiliser l'un ou l'autre, ou à utiliser {}
pour les blocs qui renvoient des valeurs et do ... end
pour les blocs exécutés pour les effets secondaires.
Les accolades ont un avantage majeur: de nombreux éditeurs ont BEAUCOUP de temps pour les faire correspondre, ce qui facilite beaucoup certains types de débogage. Pendant ce temps, le mot clé "do ... end" est un peu plus difficile à associer, surtout que "end" s correspond également à "if" s.
La règle la plus courante que j'ai vue (plus récemment dans Eloquent Ruby ) est la suivante:
La convention est do .. end
Pour multiligne et { ... }
Pour une ligne.
Mais j'aime mieux do .. end
, Alors quand j'ai une ligne, j'utilise quand même do .. end
, Mais le formate comme d'habitude pour faire/terminer en trois lignes. Cela rend tout le monde heureux.
10.times do
puts ...
end
Un problème avec { }
Est qu’il est hostile en mode poésie (parce qu’ils se lient étroitement au dernier paramètre et non à l’appel de méthode entier, vous devez donc inclure des parens de méthode) et qu’ils ne font que pas l'air aussi gentil. Ce ne sont pas des groupes d'instructions et ils se heurtent aux constantes de hachage pour des raisons de lisibilité.
De plus, j'ai vu assez de programmes en $ C { }
. La manière de Ruby, comme d'habitude, est meilleure. Il existe exactement un type de bloc if
et vous ne devez jamais revenir en arrière et convertir une instruction en instruction composée.
Un couple de rubyists influents suggère d'utiliser des accolades lorsque vous utilisez la valeur de retour et de faire/terminer lorsque vous ne l'utilisez pas.
http://talklikeaduck.denhaven2.com/2007/10/02/Ruby-blocks-do-or-brace (sur archive.org)
http://onestepback.org/index.cgi/Tech/Ruby/BraceVsDoEnd.rdoc (sur archive.org)
Cela semble être une bonne pratique en général.
Je modifierais un peu ce principe pour dire que vous devriez éviter d'utiliser do/end sur une seule ligne car il est plus difficile à lire.
Vous devez faire plus attention en utilisant des accolades car cela liera au paramètre final d'une méthode au lieu de l'appel de la méthode entière. Ajoutez juste des parenthèses pour éviter cela.
Je mets une autre réponse, bien que la différence grande ait déjà été soulignée (prédcence/liaison), ce qui peut poser des problèmes difficiles à trouver (le Tin Man, et d’autres l’ont souligné). Je pense que mon exemple montre le problème avec un extrait de code pas si habituel, même les programmeurs expérimentés ne lisent pas comme le dimanche:
module I18n
extend Module.new {
old_translate=I18n.method(:translate)
define_method(:translate) do |*args|
InplaceTrans.translate(old_translate, *args)
end
alias :t :translate
}
end
module InplaceTrans
extend Module.new {
def translate(old_translate, *args)
Translator.new.translate(old_translate, *args)
end
}
end
Puis j'ai fait un peu de code embellissant ...
#this code is wrong!
#just made it 'better looking'
module I18n
extend Module.new do
old_translate=I18n.method(:translate)
define_method(:translate) do |*args|
InplaceTrans.translate(old_translate, *args)
end
alias :t :translate
end
end
si vous changez le {}
ici pour do/end
vous obtiendrez l'erreur, cette méthode translate
n'existe pas ...
Pourquoi cela se produit est souligné ici plus d’une priorité. Mais où mettre des bretelles ici? (@ the Tin Man: J'utilise toujours des accolades, comme vous, mais ici ... supervisé)
donc chaque réponse comme
If it's a multi-line block, use do/end If it's a single line block, use {}
est juste faux si utilisé sans le "MAIS gardez un œil sur les accolades/la priorité!"
encore:
extend Module.new {} evolves to extend(Module.new {})
et
extend Module.new do/end evolves to extend(Module.new) do/end
(quel que soit le résultat de extend fait avec le bloc ...)
Donc si vous voulez utiliser do/end utilisez ceci:
#this code is ok!
#just made it 'better looking'?
module I18n
extend(Module.new do
old_translate=I18n.method(:translate)
define_method(:translate) do |*args|
InplaceTrans.translate(old_translate, *args)
end
alias :t :translate
end)
end
En ce qui concerne les préjugés personnels, je préfère les accolades aux blocs do/end, car ils sont plus compréhensibles pour un plus grand nombre de développeurs car une majorité de langages d’arrière-plan les utilisent par rapport à la convention do/end. Cela dit, la clé est de parvenir à un accord au sein de votre boutique. Si 6 développeurs sur 10 utilisent do/end, alors que TOUT LE MONDE devrait les utiliser, si 6/10 utilise des accolades, respectez ce paradigme.
Il s’agit de créer un modèle afin que l’équipe dans son ensemble puisse identifier les structures de code plus rapidement.
Il y a une différence subtile entre eux, mais {} est plus étroit que do/end.
Mon style personnel est de mettre l’accent sur la lisibilité plutôt que sur les règles rigides de choix {
... }
Vs do
...end
, lorsque ce choix est possible. Mon idée de lisibilité est la suivante:
[ 1, 2, 3 ].map { |e| e + 1 } # preferred
[ 1, 2, 3 ].map do |e| e + 1 end # acceptable
[ 1, 2, 3 ].each_with_object [] do |e, o| o << e + 1 end # preferred, reads like a sentence
[ 1, 2, 3 ].each_with_object( [] ) { |e, o| o << e + 1 } # parens make it less readable
Foo = Module.new do # preferred for a multiline block, other things being equal
include Comparable
end
Foo = Module.new { # less preferred
include Comparable
}
Foo = Module.new { include Comparable } # preferred for a oneliner
Foo = module.new do include Comparable end # imo less readable for a oneliner
[ [ 1 ], [ 1, 2 ] ].map { |e| e.map do |e| e + 1 end } # slightly better
[ [ 1 ], [ 1, 2 ] ].map { |e| e.map { |e| e + 1 } } # slightly worse
Dans une syntaxe plus complexe, telle que des blocs imbriqués sur plusieurs lignes, j'essaie de séparer les délimiteurs {
... }
Et do
...end
pour les plus naturels résultat, par exemple.
Foo = Module.new {
if true then
Bar = Module.new { # I intersperse {} and keyword delimiters
def quux
"quux".tap do |string| # I choose not to intersperse here, because
puts "(#{string.size} characters)" # for multiline tap, do ... end in this
end # case still loks more readable to me.
end
}
end
}
Bien que l’absence de règles rigides puisse donner lieu à des choix très différents pour différents programmeurs, j’estime que l’optimisation au cas par cas de la lisibilité, bien que subjective, est un gain net par rapport au respect de règles rigides.
En fait, c’est une préférence personnelle, mais cela dit, au cours des trois dernières années de mon Ruby expérience que j’ai appris, c’est que Ruby a son style.
Par exemple, si vous venez d’un Java arrière-plan, pour une méthode booléenne, vous pourriez utiliser
def isExpired
#some code
end
remarquez le cas du chameau et préférez le plus souvent 'is' pour l'identifier comme une méthode booléenne.
Mais dans le monde Ruby, la même méthode serait
def expired?
#code
end
donc personnellement, je pense qu'il vaut mieux aller avec 'Ruby way' (mais je sais que cela prend un certain temps pour que l'on comprenne (cela m'a pris environ un an: D)).
Enfin, j'irais avec
do
#code
end
des blocs.