Ruby 101: Naming Conventions 24

Posted by jeff Thursday, October 18, 2007 00:36:00 GMT

UPDATE: Fixed some typos in the DialUpModem example code.

Ruby enforces certain coding conventions, while others are considered to be community-accepted idiomatic Ruby. I’d like to cover them here.

I can’t cover everything in one blog post. Instead, I’ll review the more egregious cases that seem to pop up on the Ruby and Rails mailing lists. Most of these seem to come from our Java friends who are now using Ruby without regard to learning Ruby style. .NET developers are prone to the same criminal activities, so I hope this will help both Java and .NET developers that are new to Rails.

Today all of these situations involve method names.

lowercase_and_underscored

Suppose we have a class DialUpModem with a method that will terminate the connection.

Please don’t do this, you formerly-Java people:

def hangUp()

nor this, formerly-.NET people:

def HangUp()

Instead, do this, all you seen-the-light-and-now-very-cool people:

def hang_up

Notice two things here:

  • everything is in lowercase
  • underscores separate words
  • don’t append empty parentheses if there are no parameters

Ok, that was three things, not two, just to see if you’re awake.

Methods that return boolean values

Don’t do this, formerly-Java people:

def isOnline()

nor this, formerly-.NET people:

def IsOnline()

Instead, do this, all you seen-the-light-and-now-very-cool people:

def online?

See? We don’t use the word “is” to start a method name. Instead, the Ruby language recommends that you use a question mark at the end of a method name to indicate that it will return a boolean value. (It’s not required by the language, it’s just a convention you should follow.)

By the way, you can still take parameters if you need to:

def high_speed?(min_speed = 19200)

Properties Are For Monopoly Players

...not Ruby programmers. So puleeeze don’t do this:

class DialUpModem

    def get_speed
      return @speed
    end

    def set_speed(value)
      @speed = value
    end

end

Don’t use getters and setters when there’s no logic involved. Do this:

class DialUpModem

    attr_accessor :speed

end

Done. Nice. Clean. Simple.

If you do have logic involved, you can still override the getter or setter as appropriate:

class DialUpModem

    attr_accessor :speed

    def speed=(value)
      # Limit the speed to 19200 baud.
      if new_speed > 19200
        @speed = 19200
      else
        @speed = value
      end
    end

end

Notice the ’=’ sign at the end of the method name – that’s how you define a “setter” method in Ruby.

If/Else Statements Are Suspicious

I know I said this post was just about method names, but now that you’ve made it this far I can’t resist one more Ruby tip.

See that setter method we just wrote? That is some very, very ugly Ruby code.

Learn instead to wield the power of the Enumerable module:

def speed=(value)
    @speed = [value, 19200].min
end

How to Learn Idiomatic Ruby

Read the good Ruby books by the good authors: David A. Black, Dave Thomas, Hal Fulton, and others. And read as much Ruby code as you can, so you can start to become assimilated into the Ruby style of programming.

Comments

Leave a response

  1. Daniel Fischer   October 18, 2007 @ 02:19 AM

    DialUpModem should be this instead, no?

    class DialUpModem

    attr_accessor :speed
    
    def speed=(value)
      # Limit the speed to 19200 baud.
      if value > 19200
        @speed = 19200
      else
        @speed = value
      end
    end
    

    end

    p.s, I think you should go further into the "Enumerable module", that'd be very beneficial with that snippet of syntax. Why that works, why that is better, etc.

    Thanks !

  2. Jörg W Mittag   October 18, 2007 @ 02:30 AM

    That last example should probably have 'min' instead of 'max', right?

  3. Jeff   October 18, 2007 @ 03:28 AM

    Daniel and Jorg: Thanks for spotting those! I've updated the code. Glad to see someone is paying attention (obviously not the author :-).

  4. Leon   October 18, 2007 @ 05:02 AM

    My problem is that I take Ruby idioms back to C#! Great post that I will certainly share with my co-workers. :)

  5. Norman   October 18, 2007 @ 07:02 AM

    I have just recently found your blog and absolutely love the posts. Thank you, thank you. I am still .NET programming but can't wait to try these things in Ruby and it is a great help to see such clear examples.

  6. Eric   October 18, 2007 @ 07:58 AM

    Thanks for the tips!

    I just took a look at ruby scripts I wrote 2 years ago: My motto was "f**k conventions!" back then. I wish I knew a little bit more at that time!

    BTW, you can replace this attraccessor by an attrreader now that speed= has been defined!

  7. Maxime   October 18, 2007 @ 08:57 PM

    Thanks, I'm not used to read ruby code, and it's sometimes perturbing.

  8. dgurba   October 18, 2007 @ 10:41 PM

    do you really see: @speed = [value, 19200].min

    used widely in Ruby source code? Could you please supply some references.

    I see that as allocating 2 chunks of memory and calling a function. Rather than just dispatching across 4 (?) methods (I believe that Ruby if / else really get converted into method calls ... or did I just suddenly get confused with various comparisons of Ruby to SmallTalk there...)

    I guess my point being is I see that particular idiom as more wasteful than "simple" business logic, when using such a contrived/simple example as comparing 2 values.

    Good post nonetheless :]

  9. Eric   October 19, 2007 @ 08:22 AM

    @dqurba

    If you don't like creating an array for 2 elements:

    ((value+19200)+(value-19200).abs)/2

    will do!

  10. devnull   October 19, 2007 @ 01:26 PM

    why not?

    @speed == (value > 19200) ? 19200 : value

    one liner, easy to read, and still fast.

  11. dgurba   October 19, 2007 @ 03:01 PM

    @devnull

    I do that in php all the time in my return statements ... and sometimes in my ruby code. like I said I just hadn't seen the packing of values into an array for finding the min of 2 values. Maybe the min of 30 or 40 values via a collect() [or some other way to gather the data into 1 array] I would do so I could then call min() ... but not 2 items :)

  12. Jeremy Weiskotten   October 19, 2007 @ 03:33 PM

    I blogged about using [x, y].min to limit a value at http://jeronrails.blogspot.com/2007/08/lesser-of-two-weebles.html. (I referred to it as Array#min, but Arrays are Enumerable and it is an Array in this case.)

    It's a terse idiom, and if you're concerned about the performance you must not have a database or anything else that consumes resources orders of magnitude greater. Consider yourself lucky, otherwise don't optimize at this level unless you really need to.

  13. Mark Wilden   October 21, 2007 @ 04:52 PM

    @speed == (value > 19200) ? 19200 : value

    This contains duplication (and it would still contain duplication if the literals were replaced by an identifier) while the array min example doesn't. Also the latter is more expressive. The above code is still an if-statement. The array min example explicitly says it's looking for the smallest of two values (the definition of min).

    And yes, anyone who cares or even thinks about the speed implications (as opposed to the readability) is probably using the wrong language.

    ///ark

  14. Baba Yaga   October 29, 2007 @ 08:18 AM

    Nice tips. Thanks

  15. TomC   October 31, 2007 @ 12:19 PM

    Love your 101 articles...thanks Jeff !

  16. Robert   November 13, 2007 @ 04:50 AM

    Great article - enjoying the entire 101 series. Just a quick thought - it would be really slick if you could tag the 101 articles with "101" (or whatever) so that they can be more easily read as a group.

  17. Sam Smoot   November 13, 2007 @ 08:35 PM

    Jeremy Weiskotten, Mark Wilden: You might want to re-think the comments about speed. ;) Compound habits like this, and Ruby will quickly become more taxing than most database calls you'll be making, so this wasteful idiom should die...

    And what happens when you have [nil, 19200].min ? Or ['bob', 19200].min ? Do you really want to see "ArgumentError: comparison of Fixnum with nil failed" when you call #speed= ?

    @speed = (value.to_i > 19200) ? 19200 : value

    Simple. And as PragProg said: Readability first. Focusing on repeated character patterns is missing the point of DRY. The following alternative is perfectly DRY in every sense of the original intent:

    @speed = (value.toi > MAXSPEED) ? MAX_SPEED : value

  18. David Lewis   November 27, 2007 @ 09:09 PM

    Please, everybody, remember the rules of designing and coding for performance...

    • in-the-large -- make a reasonably efficient big-picture architecture, with at least some idea of performance weaknesses that are hard to fix at first, but can be handled later

    • in-the-small -- code for clarity and perspicacity, with very little attention to small, local efficiency (that is, only where you know for sure it is very important, like at the innermost core of your most crucial loop -- maybe)

    • afterwards, go back and make adjustments in both the-large and the-small based on actual experience, in priority order based an actual measurements, profiling, points with obvious observable performance impact, etc.

    That way, you don't waste time and ruin code making things efficient that will never make a discernible runtime difference.

    (I'm sure there's a pithy saying to summarize all this -- but I call it "smart optimization".)

  19. David Lewis   November 27, 2007 @ 10:49 PM

    Please, everybody, remember the rules of designing and coding for performance...

    • in-the-large -- make a reasonably efficient big-picture architecture, with at least some idea of performance weaknesses that are hard to fix at first, but can be handled later. Remember, it's much more important to use, say, an O(n*log(n)) algorithm vs an O(n^2)) one than to sweat a small improvement in the constant factor.

    • in-the-small -- code for clarity and perspicacity, with very little attention to small, local efficiency (that is, only where you know for sure it is very important, like at the innermost core of your most crucial loop -- maybe)

    • afterwards, go back and make adjustments in both the-large and the-small based on actual experience, in priority order based an actual measurements, profiling, points with obvious observable performance impact, etc.

    That way, you don't waste time and ruin code making things efficient that will never make a discernible runtime difference.

    (I'm sure there's a pithy saying to summarize all this -- but I call it "smart optimization".)

  20. orchard apply   December 11, 2007 @ 03:04 AM

    There are lots of good examples how a smart person can make those credit cards work for him. I hope to do the same by applying for a credit card at

    do balance transfers lower your credit rating

  21. kino   May 24, 2008 @ 01:13 AM

    Because of our necessary ignorance of the conditions, our a priori concepts, in natural theology, would thereby be made to contradict the Categories; in the study of the transcendental unity of apperception, the transcendental unity of apperception (and let us suppose that this is true) is the key to understanding the transcendental objects in space and time.

  22. Ellroy   May 30, 2008 @ 11:23 PM

    The epoche calls our attention to the fact that experiences, where this is still wanting, are unified synthetically, by reconciling with noematic descriptions.

  23. Rimpy   March 16, 2009 @ 03:14 PM

    Hi Jeff, Thanks of your tip. It is simple and clean.

  24. eddy   April 23, 2009 @ 01:24 AM
    class DialUpModem attr_accessor :speed def speed=(value) # Limit the speed to 19200 baud. if new_speed > 19200 @speed = 19200 else @speed = value end end end

    Shouldn’t be the new_speed be value instead?

Comment


(won't be published)