company-logo

Ruby Stylista

Stylista-1.jpg

The thing is, like a lot of programming language fans, I’m fascinated by programming as communication, and therefore also by thing like style guides that bridge the gap between the formal requirements of the compiler and the cognitive needs of the programmer writing and reading the code. At best, consistent style makes code easier to produce and read in much the same way that Rails itself makes code easier to produce and read — by suggesting a consistent placement for your code and reducing unneeded choice in the name of useful conventions.

For me, at least, there’s also something of a pride in workmanship issue, in much the same way that a professional chef will take pride in keeping their kitchen area clean even in the midst of chaos.

So, thanks to those of you who took the time to comment on the style guide I posted last week. I am planning to put the guide up someplace more permanent where the conversation on these issues can continue, but in the meantime, I wanted to promote some comments and talk a little bit more about a couple of issues that were flagged by commenters.

Typos

I apologize again for a couple of typos and for an issue we have with our blog about indentation in code samples not showing up properly on Safari. Normally, it’s not that big a deal, but it did make a couple of the examples I posted look totally nonsensical, which was… unfortunate. I’ve got a workaround now, though, so it should be better from here on out.

80 Columns and splitting lines.

A couple of commenters suggested that my 80 column limit was ridiculous on large monitors. (A couple also bucked the 2-space indent guide. Look, like it or not 2 spaces is clearly the community standard for Ruby — don’t do it if you don’t want to, but at least realize that many Ruby programmers will find it weird-looking)

I should say, about the 80 column thing, that I use larger fonts and a narrower window in my code editor than pretty much any programmer I know without actual vision problems. Three reasons: 1) Seems to lower eyestrain over the day 2) Most of the code is in the left 40-60 columns anyway, so extending out to 120 columns or more seems a waste of space, but most importantly 3) it’s a not-so-subtle guide to keep methods small, lines of code simple and distinct, and generally keep things in order. My goal is not to be able to see as much code as possible, but to be able to focus on the code I’m working on.

When you are breaking up code over multiple lines, it’s much better to keep logical parts of the code together, rather than forcing every line as close to the 80 character boundary as possible. I mean, you could do this:

post :create, :relationship => { :kind => "relative",
    :user_id => @clark.id }, :person => {:first_name => "Martha",
    :last_name => "Kent"}

But isn’t this a lot more readible…

post :create,
    :relationship => { :kind => "relative", :user_id => @clark.id },
    :person => {:first_name => "Martha", :last_name => "Kent"}

And and or or && and ||

There was some back and forth in the comments about when you should use the and/or versus the &&/|| form of boolean operator.

The issue here is binding and precedence. The &&/|| form has very high precedence, while and/or has very low precedence. So the following two statements are equivalent, since the || has top priority:

x = y || z
x = (y || z)

Also, the following two statements are equivalent, since the assignment operator has higher priority than and:

x = y or z
(x = y) or z

About 99 times out of a hundred, the || is what you want, while the or will cause a subtle bug that you will never find. Hence, the advice to just stick to &&/||.

If expressions, then and ternary

I was most surprised in the comments by the idea that then is “almost a deprecated keyword”, but if that’s the case, then I’ll back off from using it in multiline epxressions. I also don’t think I was completely clear on exactly what it is I do with if expressions.

For a conditional assignment that is simple and fits on one line, I use an if expression:

x = if current_user then 3 else 0 end

I prefer that to the ternary operator

x = current_user ? 3 : 0

On the ground that it’s more readable and I can never remember which part of the ternary goes where.

If the expression or the values gets a little long for one line, but is still basically a simple assignment, then I split the line like this:

x = if a_very_long_boolean_expression_is_true
    then the_true_response
    else the_false_response
    end

I use then and keep both return statements in the same line as a marker to the reader that this is still basically a simple assignment. If you want to leave out the then, fine by me. I prefer that to:

if a_very_long_boolean_expression_is_true
  x = the_true_response
else
  x = the_false_response
end

Because I feel like it’s easy for a later programmer (including myself) to break the parallelism. But I don’t feel that strongly about it. If the assignment gets any more complicated, then I tend to break it out into it’s own method, which also eliminates the duplicate assignment:

x = the_response

def the_response
  if a_very_long_boolean_expression_is_true
    the_true_response
  else
    the_false_response
  end
end

Obviously, there are a bunch of ways to format the separate method that might be preferable depending on how complicated it really is.

Symbol#to_proc

A couple of people pointed out that this is a Rails add on, and not a core Ruby thing until Ruby 1.9. Seriously, I forget that fact all the time. The Rails implementation is also kind of slow, if that bothers you, supposedly it’s faster in Ruby 1.9

Quick hits

Three quick comments that I agree with, slightly edited. The first from Alin:

The Ruby way: return if x.nil? instead of
return if x == nil

Sean O’Halpin, with two nice clarifications of things I said:

The general rule for parentheses is to use them in functional expressions, leave them out in statements.

and:

To clarify your point: chaining is fine on enumerable methods that return enumerables – you’ll always have an enumerable to act on. Chaining is not such a good idea where any intermediate method may return nil (your second example)

  1. Todd Werth Reply

    After many years of experience, and too many arguments about style, my only advice is to tread lightly, and only choose to include, in a style guide, things that have significant benefit to the community. Otherwise all you’re really doing is yelling into the wind, as you’ll never get consensus on anything.

    I think style is important to readability, and readability is very, very important (professionals create boring readable code, amateurs favor clever code). The problem is trying to come up with rules that apply to most situations; the beauty of a language like Ruby, over a very strict language like Java, is it allows for many different styles, allowing the code poet to choose the perfect style that matches her code.

    A good example of an important style rule is indentation. This has a significant payoff for things like source code repositories, and thus is worth the loss of stylistic freedom. Bad examples include trying to enforce the length of method name, or favoring long code over wide code (maximum width or inversely the ternary operator over if/else/end).

    I have my opinions on many things like method length (long as they need to be to make it 100% clear) and the ternary operator (rarely readable), but I’ll refrain from voicing them, you don’t need more wind to yell into.

  2. seydar Reply

    > The general rule for parentheses is to use them in functional expressions, leave them out
    > in statements.

    What would you classify as “functional expressions”?

  3. Reg Braithwaite Reply

    I use “and” and “or” for imperative, conditional execution. For example:

    x = y or logger.debug(‘Darn it, y is false or nil again!’)
    model.method_with_side_effect_returning_whether_successful and model.save!

    These can be accomplished with if/unless, but I prefer to keep actions with side effects out of the predicate and I also want the main thing first.

    So I don’t use:

    logger.debug(‘Darn it, y is false or nil again!’) unless x = y

    or:

    if model.method_with_side_effect_returning_whether_successful then model.save!

    because I don’t like side effects in the predicate, and I don’t like the main thing out on the RHS. There ar eother ways to do these kinds of things, but I just wanted to share where I use “or” and “and.” In these cases, the low binding is exactly what I want.

  4. Fred Lee Reply

    Hey Noel,

    I think these style posts are great. Keep posting them regardless of what some may say. These are some “style” issues I wonder about all the time. Keep it up!

  5. Pingback: ESPN Dev Blog » Blog Archive » Ruby Style Guides and Tools

  6. Pingback: Ruby Style Guides and Tools: How to Write Good Looking Ruby : WebNetiques

  7. Pingback: เร็วส์ หกสิบหก » นั่งเทียนเขียนข่าว#22

  8. Pingback: Ruby on Rails » 2008: A Year That Was » Pathfinder Development

Leave a Reply

*

captcha *