You know what is the one thing everybody will tell you if you ask them about the ruby programming language? They will tell you that everything is an object, bolding the “everything” like I just did. I think every rubyists in the world said this sentence at least once in their life. After telling you that everything is an object, they will probably tell you about code blocks, ducktyping or whatever cool feature ruby has to offer (that’s what I did). And finally, they will tell you that ruby gives you several ways to achieve a single thing.
This is great, I mean, this is really great. Ruby lets you express yourself, how nice is that? But that delightful feeling of freedom comes with one responsibility : you have to keep your code consistent.
Sure, you can go all funky and do that :
if(var == "xyz")
var2 = "hohoho" unless x || y
unless var3
#do something on a single line
end
a_collection.each do |item|
puts item
end
another_collection.each { |item|
puts item
}
do_something if !x
do_something_else unless y
end
It works! But God… where is the consistency?
- On line 1, parenthesis. On line 4, no parenthesis? Hey!!
- Line 2 : Those great modifiers… perfect for some clean one liners! Line #4 : Why no modifier this time?
- Line 8 : You enjoy using do and end for your blocks? Cool! Line 12 : Hmm, now you’re using those braces just to confuse the hell out of me? The convention I use (i don’t know if it’s official or not but I read it somewhere) is to write “do” and “end” when the block takes more than one line of code. Otherwise I use braces and put the whole expression on a single line, like that : my_collection.each {|item| puts item}. But whatever your preference, the important is to stick with a convention.
- Line 16 : The if modifier is used in conjunction with the ! operator. Line 17 : unless modifier is used.
This kind of things won’t break your code, that’s for sure, but inconsistency is bad because it makes our applications look more random, more chaotic.
With ruby, it is so easy to write anything the way we want at the moment we are writing it. I admit I do it sometime. However, we… must… resist…
Pick a style
Recently, I began to create my own conventions. I will put some of them here just as an example. What’s important here isn’t the conventions per se, but the fact that I do my best to follow them.
- “unless” over “if !”
- Indentation : two white spaces
- if operation precedence isn’t an issue, no parenthesis
- Blocks : “do” and “end” if more than a line, “{” and “}” otherwise
- At the end of a function, no use of the “return” keyword (e.g. obj instead of return obj)
- Whenever possible, no “global” looping. In other words : “collection.each do |item|” over “for item in collection…”
- String concatenation : << over += (and it's more efficient!)
There are a lot more… but I don’t see the point of writing them all.
What are your conventions? How important is it for you to be consistent in your code?
Just remember that do .. end and { .. } does not mean _exactly_ the same thing: braces have a higher precedence than do .. end.
@Robert : When can this be useful ?
@pangel: never? I’m sure it’s possible to construct an example where it can be used, but I consider the difference a bug, since it is so non-intuitive.
Why no use of return in 5.? It seems like it makes things more clear to use the return. Also, in 3., I feel it’s better to have the parenthesis to make absolutely clear what it is you’re trying to do rather than count on precedence. OK, surround the above with a IMHO.
@Robert : I didn’t know about that. I think I prefer to not consider this when deciding to use the “do end” vs the braces. But it’s good to know, thanks!
@slabounty : I have to say that these are all personal preferences. I do not pretend that my current conventions are the best coding practices. I even change my own conventions when I realize I don’t like them. It takes some adjustment. For example, the “no return” thing you mention, I’m still not sure if I like it or not. Currently I am not using return if there is no need for it… but I might change my mind after a few projects. What I believe is important is to remain consistent in our coding style during the course of a single project… and one day, yes, to make up our mind.
Well, I certainly agree with you on the consistency aspect. That is probably the most important thing.
When working on teams, people should use “more common” conventions, it’s good for everyone and help you make friends 🙂
> There are a lot more… but I don’t see the point of writing them all.
I would be intersted in seeing some more. The ones you posted I agree with and the more often a common style gets published the more it will get adopted.
Regarding if ! and unless: I think whatever makes it more readable makes sense. Sometimes if I say ‘do_this unless x’ is less readable than ‘do_this if !x’ because when you speak that aloud or in your head it just makes more sense. But this is assuming that the person reading the code already converts exclamation points into inverters in their head.
The thing that bugs me most about reading other people’s (readable) code is inconsistent whitespace and indentation. When proper whitespace and indentation are used it is easy to look at a chunk of code and _see_ how modular it is rather than trying to decipher how modular it is. I love the fact that python is such a whiner about indentation. I think if Ruby were a bit _less_ expressive then I would want Ruby to be a stickler for indentation and whitespace too.
One very subtle convention I’ve run into in the past over and over is whether to use double quotes (“) or single quotes (‘) for strings. For example, should one use: :id => “myID”, or :id => ‘myId’ ? My convention has become to only use double quotes when I need to do some string insertions, such as: :id => “myId_#{profile.id}”. Otherwise always (or basically always… you know what I mean – by convention!) use single quotes. I’ve never really found any hard evidence to support my conclusion as to why I do this… which, in my mind, is that single quotes must be faster! Why? Because ruby doesn’t parse over single quoted strings to find #{} insertions. So unless I need them, don’t use the double quotes – which allow for string insertions and cause extra parsing. Does this make any sense? Agree/Disagree? 🙂
@Gerry : I’ll take good note of this. I’ll think of other conventions I am using and I will plug them in a future post.
@Adam : Very good point. Sometimes, it just feels more natural to use “if !” over unless (or the other way around). That “convention” shouldn’t be too strict. The important part I think is to use one form or the other in a consistent manner.
@Justin : That’s good material for a post, thanks! Personally, I try to always use double-quotes. If I’m using both forms, there is a risk that I forget when to use which one. But your convention on this issue makes perfect sense to me.
Worth mentioning also is that two-space indent is typically the standard just about everywhere in the Ruby community. Regarding the precedence of {} over do..end, it’s not something that’s necessarily *useful*, so much as it is something that you need to watch out for, especially coupled with a propensity for leaving out parentheses. In the case where parentheses are dropped and you include {} on the tail end, the block won’t be a block passed to the overall method, it will be a block passed _to the last parameter_, which makes a sizeable difference. It’s not bad per se, it’s just worth it to make sure that that’s what you intend on doing.
As for obj vs return obj — initially, I was more comfortable with return obj, but you get used to the other form very quickly, and it fast starts making more sense. The clarity isn’t lost, because the return can be seen as implied. It just takes some acclimation to the Ruby way of doing things to get there.
Regarding the “two-space indent vs. tabs” issue. With dynamic [interpreted] languages, we have script files being parsed by a parser each time they are executed. As programmers, we know that I/O is bad. Good programmers constantly strive to minimize I/O [disk I/O, RAM I/O, DB I/O, etc]. So, isn’t it wise to minimize the number of bytes that must pass through a language parser? Using two spaces instead of a single tab can increases the number of bytes in a single code file by 5% to 20%. This increases the number of bytes coming off the drives, into RAM, and through the parser, thereby reducing performance. It also increases the physical size of your entire project unnecessarily. Furthermore, it defeats the entire Ruby, Rails, and Web 2.0 modus operandi, “less is more”. Why would a good programmer do these things intentionally? Lastly, it makes for code that is more difficult and slower to navigate and manage using the mouse and keyboard. More characters exist that the cursor and pointer must travel through. Because there are more characters, there is more room for error when highlighting code to move or copy it.
Less code volume is better. Use tabs.
This really only applies to dynamic languages that execute at runtime. Although, I would even argue that “fewer bytes is better” with compiled languages because that code still must be read and parsed at compile time, so by minimizing code volume, you minimize compile time.
Osti j’ai Ă©tĂ© stumblĂ© dans ton site!
Quel hasard! ;P