Slightly more readable Ruby

— June 19, 2011 at 23:00 PDT


A simple coding style for slightly more readable Ruby: symbols as flag arguments.

Use a symbol with a meaningful name instead of true. This makes it clear what you're doing and is just as terse. For example:

def run(log_it = false)
  log_action if log_it
  run_action
end

command.run(true)           # mysterious use of `true`
command.run(:with_logging)  # obvious

The second call to #run is functionally equivalent to the first, because every symbol is a truthy value. But it's a lot easier to read the code with the symbol and understand what the argument means.

The other common pattern I see in Rails is to use an options hash. That call would look like

command.run(:with_logging => true)

If you have a bunch of options, that's fine. When it's just a single optional flag, I prefer passing a symbol with a meaningful name.

15 commentsruby, style

Comments
  1. kikito2011-06-19 23:13:22

    Others might say that boolean parameters aren't such a good idea; that you should use two methods instead.

    On this particular case that is particularly appropriate since there's already a run_action method.

    def run_with_log
      log_action
      run_action
    end
    
    alias :run :run_action
    
  2. Simon2011-06-20 00:00:37

    i've found something similar in some of YUI's apis:

    domNode.show("fadeIn"); // same as domNode.show(true);

  3. Morgan2011-06-20 01:20:43

    Greetings,

    I like to recommend using Ruby 1.9.2 style hashes along with options hashes as follows:

    def run(options={})
      log_action if options[:with_logging]
    end
    
    command.run(with_logging:true)
    

    It's the same as your second version but looks a little cleaner, and starts looking better and better as you get more parameters. It starts to feel like named parameters, which is nice.

    -- Morgan

  4. Zach Moazeni2011-06-20 09:12:02

    This is slick, I haven't thought about calling it that way before. I also prefer a single optional variable over going the opts = {} route when you only have one option, but I've also run into the 'what does "true" mean in this context?' question when calling it.

    I haven't tried this out yet, but I imagine this will work in ActiveRecord:

    some_model.some_has_many_relationship(:reload) # instead of
    some_model.some_has_many_relationship(true)
    

    Thanks for sharing Josh.

  5. Henrik N2011-06-20 10:46:56

    I've done

    command.run(logging=true)
    

    at times. The upside compared to the symbol is that you can use this for non-obvious non-bools as well.

  6. Mark Wilden2011-06-20 13:06:01

    I agree about the problem with boolean parameters. However, I don't know if using a symbol is the answer.

    To begin with, that symbol looks awfully like a comment to me. By that, I mean that you could change it without affecting the program. That's because it's never used anywhere else. So, like comments, I think it's an excuse for unclear code.

    Then there's the issue of intentionality. At first glance, a reasonable assumption that :without_logging might be useful.

    But it's better than a bare boolean, which in this case provides 0 context for what it might do.

  7. Andy Delcambre2011-06-21 03:50:22

    I like the style, but I think I'd prefer checking for the symbol in the method.

    The problem is that that this also works the same way, rather than turning logging off like you might expect.

    command.run(:without_logging)
    
  8. Nick Parker2011-06-21 10:11:46

    Which is one of the nice features within Objective-C, allowing you to encode the names of your parameters as part of the method call itself - it helps increase the readability of the code in my opinion.

  9. Mark Wilden2011-06-21 16:20:45

    @Nick: They must've got this from Smalltalk, where the parameter names are part of the method name. It's pretty nice.

  10. Josh Susser2011-06-21 23:03:08

    Yes, the named params in Obj-C were lifted directly from Smalltalk-80. Brad Cox based Obj-C heavily on Smalltalk.

    And yes, using a symbol instead of true is essentially a comment. But I don't think it's worth worrying about accidentally typing command.run(:without_logging) instead of command.run(:with_logging). I'm not talking so much about API design here as how to make using a badly designed API a little better.

  11. Mark Wilden2011-06-22 21:18:46

    I see what you mean. The canonical example in Rails is object.children(:reload), and even I've used that.

  12. Mark Wilden2011-06-23 07:44:37

    Coincidentally, Martin Fowler weighs in on this very subject today: http://martinfowler.com/bliki/FlagArgument.html

  13. Marcus Ventrue2011-06-23 19:36:07

    I like it. is there a similarly readable technique for passing false?

  14. bt2011-06-24 09:03:01

    For someone new to Ruby this will be very irritating. You wonder what kind of value :with_logging has and why you can't find it anywhere else. So in my opinion this is -1 for clarity. Therefore I'd prefer @Morgan's idea.

  15. Josh Susser2011-06-24 21:06:51

    @bt: I don't understand why you'd be especially concerned about someone new to Ruby. What values count as true and false is pretty fundamental, and something people pick up quickly. And while using keyword args is nice, there are plenty of APIs that don't work that way.

Sorry, comments for this article are closed.