tags:

views:

351

answers:

8

It seems to me like Ruby has a lot of syntactic flexibility, and many things may be written in several ways.

Are there any language features/syntactic sugar/coding conventions that you avoid as a Ruby programmer for clarity? I am asking about things that you choose not to use on purpose, not stuff you still have to learn.

If your answer is, "I use everything!," do you ever comment code that would be otherwise obvious if the reader knows the relevant Ruby syntax?

[I'm particularly interested in Ruby in a RoR context, but everything is welcome.]

+8  A: 

I generally refrain from going overboard with monkey patching because it could lead to some maintainability and readability issues. It's a great feature if used correctly, but it's easy to get carried away.

Firas Assaad
Thanks Firas, nice first answer: this is the kind of thing I'm looking for, to get an idea of what could be dangerous/hard-to-read etc.
Yar
This is widely used by RoR framework itself.
kgiannakakis
+11  A: 

The whole range of "$" globals (see Pickaxe2 pp333-336) mostly inherited from Perl, are pretty ghastly, although I have on occasion found myself using $: instead of $LOAD_PATH.

Mike Woodhouse
+2, if I could, for including the link...
Yar
I looked up the ref, that stuff is horrible. Thanks for pointing it out...
Yar
+7  A: 

The for ... in ... loop. It directly compiles to obj.each (and throws a strange error message accordingly) and is totally unnecessary. I don't even see where it improves readability - if you've been around ruby for more than a week, #each should be natural.

Skade
I totally agree. It's that pound sign to indicate instance methods that I never understood :) Thanks for your answer.
Yar
Isn't scope different with `for ... in` compared to `obj.each`?
Andrew Grimm
Thats true, especially in Ruby 1.9, but I see that as another reason not to use it. It just adds to the oddball behaviour.
Skade
+6  A: 

This might be obvious but I generally avoid using eval if there's any alternative.

Sam
True. Though eval is so tempting....
Yar
+1  A: 

One thing I really loathe is "improper" use of {} and do ... end for blocks. I can't seem to find exactly where I learnt the practice myself but it is generally accepted to do {} for single line blocks and do ... end for multiline blocks.

Proper use:

[1, 2, 3, 4].map {|n| n * n }.inject(1) { |n,product| n * product }

or

[1, 2, 3, 4].inject do |n,product|
  n = n * n
  product = n * product
end

Improper use:

[1,2,3,4].map do |n| n * n end.inject(1) do |n,product| n * product end

or

[1, 2, 3, 4].inject { |n,product|
  n = n * n
  product = n * product
}

All of which, of course, will execute giving 576

Ryan Neufeld
true, but I don't know if it answers the question. Thanks though!
Yar
+3  A: 

First off: I'll break many of these rules if it's for a short one-off script, or a one-liner on the command line, or in irb. But most of my time is spent in medium sized or larger scripts or applications. So:

Avoid:

  • using class << self code block for class methods. It's a cute trick, but no better than def self.foo, and less readable (especially after the first page).
  • for i in collection: Use collection.each instead.
  • proc {...}: usually lambda {...} is better.
  • class variables (e.g. @@foo). They are problematic and can usually be replaced with class-level instance variables without much effort.
  • Anything that causes a warning, and preferably anything that causes a warning when run via the more strict ruby -w. This is especially important if you are writing a gem to be used by others.
  • 'else' on a 'begin ... rescue ... end' block. Personal preference: It's too much of an edge case and so few people even know it exists or how it works to be worth it.
  • ObjectSpace and GC. You probably don't need to go there. You definitely don't want to go there.
  • =begin and =end multi-line comments. Personal preference for line-wise comments. These just annoy the hell out of me.

Use it, but sparingly or as a last resort (and comment it appropriately):

  • eval (or class_eval, etc) when passing in a string. There are some metaprogramming tricks you just can't do without passing in a string. And occasionally, the string version performs dramatically better (and sometimes that matters). Otherwise, I prefer to send in blocks of actual ruby code for my metaprogramming. eval can be avoided entirely for many metaprogramming tasks.
  • Adding or redefining methods on classes which were not created by me and may be used by code outside my control; a.k.a. monkey-patching. This rule is mostly for larger codebases and libraries; I'll gladly and quickly make an exception for small one-off scripts. I'll also make an exception for fixing buggy third-party libraries (although you may shoot yourself in the foot when you upgrade!). Selector namespaces (or something similar) would go a long way to make ruby nice in this regard. That said, sometimes it is worth the trouble. ;-)
  • Global variables (except for classes). I'll even pass in $stdout as a parameter to my objects or methods, rather than use them directly. It makes re-use of the code much easier and safer. Sometimes you can't avoid it (e.g. $0, $:, $$ and other environmental variables, but even then you can limit your usage).
    • speaking of which, I prefer to limit my usage of the perlish symbol globals entirely, but if they need to be used more than a little bit, then require "English".
  • break, redo, next, try: Often they make a block, loop, or method much more elegant than it otherwise could be. Usually they just make you scratch your head for a few minutes when you haven't seen that code for a while.
  • __END__ for a data-block. Excellent for a small one-file script. Unhelpful for a multi-file app.

Don't use it, but don't really avoid it either:

  • try/catch
  • continuations

Things I use often, that others might not care for, or I don't see often:

  • 'and' and 'or' keywords: they have different precedence from && and ||, so you need to be careful with them. I find their different precedence to be very useful.
  • regex black magic (provided I've got some examples in unit tests for it)
  • HEREDOC strings
nicholas a. evans
Actually, in Ruby 1.9 there is almost no reason to use eval except performance. The common case of dynamically defining methods with complex argument lists is gone after blocks take the same argument lists as methods.Good writeup, thanks!
Skade
Yeah, I'm looking forward to eventually moving all my apps forward to 1.9. :-)
nicholas a. evans
Interesting list, thanks! I'm learning quite a bit about ruby in the responses to this question.
Yar
+1  A: 

Avoid chaining too many method calls together. It is very common in Ruby to chain methods together.

user.friends.each {|friend| friend.invite_to_party}

This may seem ok, but breaks Law of Demeter:

More formally, the Law of Demeter for functions requires that a method M of an object O may only invoke the methods of the following kinds of objects:

  1. O itself
  2. M's parameters
  3. any objects created/instantiated within M
  4. O's direct component objects

The example above isn't perfect and a better solution would be something like this:

user.invite_friends_to_party

The example's problem isn't Ruby's fault but it is very easy to produce code that breaks Law of Demeter and makes code unreadable.

In short, avoid features that decreases code readability. It is a very important that the code you produce is easy to read.

Pekka Mattila
his is true, but I was looking more for Ruby-specific things that seem like good ideas but in fact are confusing... Thanks for the response, it's a good point.
Yar
Andrew Grimm
A: 
begin nil+234 rescue '' end

above syntax is valid, but you should never use it.

Subba Rao
not very ruby specific, I think, but thanks for that anyway.
Yar