Want to stay on top? Ruby Weekly is a once-weekly e-mail newsletter covering the latest Ruby and Rails news.
     Feed Icon

21 Ruby Tricks You Should Be Using In Your Own Code

By Peter Cooper / May 26, 2008

Writing for Ruby Inside, I get to see a lot of Ruby code. Most is good, but sometimes we forget some of Ruby's shortcuts and tricks and reinvent the wheel instead. In this post I present 21 different Ruby "tricks," from those that most experienced developers use every day to the more obscure. Whatever your level, a refresh may help you the next time you encounter certain coding scenarios.

Note to beginners: If you're still learning Ruby, check out my Beginning Ruby book.

2009 Update: This post was written in early 2008 and looking back on it, there are a couple of tricks that I wouldn't recommend anymore - or to which extra warnings need to be added. I've added paragraphs like this where necessary. Enjoy! :-)

1 - Extract regular expression matches quickly

A typical way to extract data from text using a regular expression is to use the match method. There is a shortcut, however, that can take the pain out of the process:

email = "Fred Bloggs <fred@bloggs.com>"
email.match(/<(.*?)>/)[1]            # => "fred@bloggs.com"
email[/<(.*?)>/, 1]                  # => "fred@bloggs.com"
email.match(/(x)/)[1]                # => NoMethodError [:(]
email[/(x)/, 1]                      # => nil
email[/([bcd]).*?([fgh])/, 2]        # => "g"

Ultimately, using the String#[] approach is cleaner though it might seem more "magic" to you. It's also possible to use it without including an extra argument if you just want to match the entire regular expression. For example:

x = 'this is a test'
x[/[aeiou].+?[aeiou]/]    # => 'is i'

In this example, we match the first example of "a vowel, some other characters, then another vowel."

2 - Shortcut for Array#join

It's common knowledge that Array#*, when supplied with a number, multiplies the size of the array by using duplicate elements:

[1, 2, 3] * 3 == [1, 2, 3, 1, 2, 3, 1, 2, 3]

It's not well known, however, that when given a string as an argument Array#* does a join!

%w{this is a test} * ", "                 # => "this, is, a, test"
h = { :name => "Fred", :age => 77 }
h.map { |i| i * "=" } * "n"              # => "age=77nname=Fred"

3 - Format decimal amounts quickly

Formatting floating point numbers into a form used for prices can be done with sprintf or, alternatively, with a formatting interpolation:

money = 9.5
"%.2f" % money                       # => "9.50"

4 - Interpolate text quickly

The formatting interpolation technique from #3 comes out again, this time to insert a string inside another:

"[%s]" % "same old drag"             # => "[same old drag]"

You can use an array of elements to substitute in too:

x = %w{p hello p}
"<%s>%s</%s>" % x                    # => "<p>hello</p>"

5 - Delete trees of files

Don't resort to using the shell to delete directories. Ruby comes with a handy file utilities library called FileUtils that can do the hard work:

require 'fileutils'
FileUtils.rm_r 'somedir'

Be careful how you use this one! There's a FileUtils.rm_rf too..

6 - Exploding enumerables

* can be used to "explode" enumerables (arrays and hashes). "Exploding" is sort of an implicit on-the-fly conversion from an array to regular method arguments. We'll let the examples do the talking:

a = %w{a b}
b = %w{c d}
[a + b]                              # => [["a", "b", "c", "d"]]
[*a + b]                             # => ["a", "b", "c", "d"]
a = { :name => "Fred", :age => 93 }
[a]                                  # => [{:name => "Fred", :age =>93}]
[*a]                                 # => [[:name, "Fred"], [:age, 93]]
a = %w{a b c d e f g h}
b = [0, 5, 6]
a.values_at(*b).inspect              # => ["a", "f", "g"]

7 - Cut down on local variable definitions

Instead of defining a local variable with some initial content (often just an empty hash or array), you can instead define it "on the go" so you can perform operations on it at the same time:

(z ||= []) << 'test'

2009 Update: This is pretty rancid, to be honest. I've changed my mind; you shouldn't be doing this :)

8 - Using non-strings or symbols as hash keys

It's rare you see anyone use non-strings or symbols as hash keys. It's totally possible though, and sometimes handy (and, no, this isn't necessarily a great example!):

does = is = { true => 'Yes', false => 'No' }
does[10 == 50]                       # => "No"
is[10 > 5]                           # => "Yes"

9 - Use 'and' and 'or' to group operations for single liners

This is a trick that more confident Ruby developers use to tighten up their code and remove short multi-line if and unless statements:

queue = []
%w{hello x world}.each do |word|
  queue << word and puts "Added to queue" unless word.length <  2
end
puts queue.inspect

# Output:
#   Added to queue
#   Added to queue
#   ["hello", "world"]

2009 Update: Be careful here - this one can sting you in the butt if your first expression returns nil even when it works. A key example of this is with the puts method which returns nil even after printing the supplied arguments.

10 - Do something only if the code is being implicitly run, not required

This is a very common pattern amongst experienced Ruby developers. If you're writing a Ruby script that could be used either as a library OR directly from the command line, you can use this trick to determine whether you're running the script directly or not:

if __FILE__ == $0
  # Do something.. run tests, call a method, etc. We're direct.
end

11 - Quick mass assignments

Mass assignment is something most Ruby developers learn early on, but it's amazing how little it's used relative to its terseness:

a, b, c, d = 1, 2, 3, 4

It can come in particularly useful for slurping method arguments that have been bundled into an array with *:

def my_method(*args)
  a, b, c, d = args
end

If you want to get really smart (although this is more 'clever' than truly wise):

def initialize(args)
  args.keys.each { |name| instance_variable_set "@" + name.to_s, args[name] }
end

12 - Use ranges instead of complex comparisons for numbers

No more if x > 1000 && x < 2000 nonsense. Instead:

year = 1972
puts  case year
        when 1970..1979: "Seventies"
        when 1980..1989: "Eighties"
        when 1990..1999: "Nineties"
      end

13 - Use enumerations to cut down repetitive code

Rubyists are often keen to remove repetition - often espousing "DRY" (Don't Repeat Yourself). You can take this to extremes using Ruby's enumerators to perform similar operations multiple times. Consider requiring multiple files, for instance:

%w{rubygems daemons eventmachine}.each { |x| require x }

14 - The Ternary Operator

Another trick that's usually learned early on by Ruby developers but rarely in less experienced developers' code is the "ternary operator." The ternary operator is not a fix-all, but it can sometimes make things tighter, particularly in view templates.

puts x == 10 ? "x is ten" : "x is not ten"

# Or.. an assignment based on the results of a ternary operation:
LOG.sev_threshold = ENVIRONMENT == :development ? Logger::DEBUG : Logger::INFO

15 - Nested Ternary Operators

It can be asking for trouble but ternary operators can be nested within each other (after all, they only return objects, like everything else):

qty = 1
qty == 0 ? 'none' : qty == 1 ? 'one' : 'many'
# Just to illustrate, in case of confusion:
(qty == 0 ? 'none' : (qty == 1 ? 'one' : 'many'))

16 - Fight redundancy with Ruby's "logic" features

I commonly see methods using this sort of pattern:

def is_odd(x)
  # Wayyyy too long..
  if x % 2 == 0
    return false
  else
    return true
  end
end

Perhaps we can use a ternary operator to improve things?

def is_odd(x)
  x % 2 == 0 ? false : true
end

It's shorter, and I've seen that pattern a lot (sadly) but you should go one step further and rely on the true / false responses Ruby's comparison operators already give!

def is_odd(x)
  # Use the logical results provided to you by Ruby already..
  x % 2 != 0
end

Sometimes, though, you want to explicitly convert implicit true/false scenarios into explicit true/false results:

class String
  def contains_digits?
    self[/\d/] ? true : false
  end
end

If we hadn't done this, you'd get back either nil or the first matched digit rather than true or false.

17 - See the whole of an exception's backtrace

def do_division_by_zero; 5 / 0; end
begin
  do_division_by_zero
rescue => exception
  puts exception.backtrace
end

18 - Allow both single items AND arrays to be enumerated against

# [*items] converts a single object into an array with that single object
# of converts an array back into, well, an array again
[*items].each do |item|
  # ...
end

19 - Rescue blocks don't need to be tied to a 'begin'

def x
  begin
    # ...
  rescue
    # ...
  end
end
def x
  # ...
rescue
  # ...
end

20 - Block comments

I tend to see this in more 'old-school' Ruby code. It's surprisingly under-used though, but looks a lot better than a giant row of pound signs in many cases:

puts "x"
=begin
  this is a block comment
  You can put anything you like here!

  puts "y"
=end
puts "z"

2009 Update: Curiously, I've not seen any significant uptake of block comments in Ruby but.. I don't use them myself either anymore. I suspect with column editing and keyboard shortcuts in common text editors, the motivation here has lessened.

21 - Rescue to the rescue

You can use rescue in its single line form to return a value when other things on the line go awry:

h = { :age => 10 }
h[:name].downcase                         # ERROR
h[:name].downcase rescue "No name"        # => "No name"

If you want to post your own list of Ruby tricks to your blog, send trackback here or leave a comment, and I'll link to all of them in a future post. Alternatively, feel free to post your own Ruby tricks as comments here, or critique or improve on those above.

As an aside, Ruby Inside is exactly two years old today. Thanks for your support! Intriguingly, the first post was another Ruby trick that I forgot to include above, so check that out too.

Comments

  1. Gianni Chiappetta says:

    Hey, great article! Will be using tons of these tips! You need to escape your right and left angled brackets in the first example string though.

  2. AkitaOnRails says:

    Awesome compilations, didn't know some bits of it myself!

  3. Henrik N says:

    Another way to do #18 is with Array(items). Though IIRC ActiveRecord proxies don't play nice with that, since their method_missing magic calls the deprecated Object#to_a.

  4. Giles Bowkett says:

    Some of these I like a lot, but I think a few could actually be a sign of knowing Ruby too well. The rescue stuff, exception.backtrace, the use of ranges in case statements, I think the world would be a better place with more of that, but #7 is just asking for trouble.

  5. Peter Cooper says:

    Gianni: Thanks! I've edited this piece about 10 times already due to the "tweaks" WordPress has made to it.. :) Got that one now.

  6. Austin Putman says:

    Hey Peter,

    this looks great! I am still not seeing the full email-address string for the first example in safari, ff, or opera. The example becomes confusing quickly unless you know what it should be looking for.

  7. Soleone says:

    Wow, really nice indeed!
    That is some sweet Ruby code right there, I need to suck it all up!!

    Great post! Someone should make a screencast out of it...

    ...no not me, I am too lazy...

  8. Fabio Cevasco says:

    Really, really awesome! Thanks for sharing those nice tips Peter, I'd like to see more of these posts, personally!

  9. Chris says:

    There's a secret #22 in #17... being able to exclude Exception on a rescue. :)

    begin
    jump
    rescue => exception
    put exception.backtrace
    end

    is short for

    begin
    jump
    rescue Exception => exception
    put exception.backtrace
    end

  10. Stefan Lang says:

    Please refrain from #9. The given example is especially horrible. I'm using Ruby for a couple of years now and I would have interpreted it wrong.

    If you really want to put it on one line use parens and semicolon:

    (queue << word; puts "Added to queue") unless word.length < 2

    And "puts queue.inspect" is highly unidiomatic, it does the same as "p queue".

    Otherwise a nice list, thanks.

  11. Peter Cooper says:

    Seems I had to fix something else too :) #1 should now be working okay. Thanks!

  12. Peter Cooper says:

    Stefan: One place where #9 can come in useful is in Rails when you want to render something and return all based on a condition. Rather than do four lines (if / render / return / end), you can use that technique to get it on one line. (At least, you did at one point, whether things have changed now I don't know as I haven't used Rails 2.1 yet.)

  13. Mark Wilden says:

    Some of these are good tricks, some are blindingly obvious to decent programmers, some are unnecessary, and some are actually bad (all IMHO, of course). They are certainly not all things we "should" be using in our own code.

    1. Good tip, but the first line is wrong, I think.

    2. As Array#join is so prevalent, I think it should be continued to be used in place of the utterly unmnemonic * .

    4. The Ruby idiom for interpolating text is #{}. Using the C equivalent is just going to confuse people (even if just for a second).

    7. What's wrong with z = ['test'] ?

    8. If this is such a good tip, let's see a good example!

    9. This, like a lot of these tips, is predicated on shorter code being better. Most of the time I would agree, but I see no point in using 'and' here instead of just putting a separate operation on a separate line. Besides, wouldn't 'or' work just as well? That's the sign of a bad construct, it seems to me.

    16. It's sad that people use true and false in this way, but they do, so it's a good tip. Unfortunately.

    18. Not sure if this is a good example. If 'items' is a single object, it shouldn't be called 'items.' I would hope it to be rare to want to enumerate over a single object anyway. Certainly, it should be rare, in which case, this code expands an array and then immediately collapses it again.

    20. I think comments are a code smell.

    21. Using exception handling in place of a control structure is generally frowned upon. I have to admit, though, that this is nice and neat, it avoids things like andand, and I do see it in "real" code. It's probably way slower than the more straightforward technique, but that's not a real issue.

    Overall, an interesting article - interesting enough to take the time to disagree with some of it. :)

    ///ark

  14. Zoran Rilak says:

    Hi Peter,

    good list! Most of those really can be seen "in the wild", especially among more experienced Rubyists. I suspect there will be some dissent over certain points, as there always is, and I imagine point number 7 to be the first to come under close scrutiny :) For example,

    # generate the first 12 Fibonacci numbers
    fib = [1, 1]
    10.times { fib << fib[-1] + fib[-2] }

    is (to me, at least) more immediately accessible than

    # generate the first 12 Fibonacci numbers
    10.times { (fib ||= [1, 1]) << fib[-1] + fib[-2] }

    Also, I've come to realize that grouping local initializations at the beginning of the function helps me immensely later when debugging or writing tests. Probably a matter of taste. :)

    Speaking of grouping things, this is how I test for valid arguments:

    def send_letter(address, letter)
    throw "Address must be a string" unless address.is_a?(String)
    throw "Need a Letter instance" unless letter.is_a?(Letter)
    throw "Can't send as a letter" unless letter.mass <= 16.oz
    ...
    end

    Which is fairly obvious to some, if not most people, but perhaps bears a mention.

    Cheers,
    Zoran

  15. zimbatm says:

    #12 : Some other handy case usage

    var = "hello"
    case var
    when /el/
    puts "Regexp matched"
    when String
    puts "Class matched"
    when 1,2,3
    puts "Multiple arguments matching"
    end

    #21: This is generally a bad practice for two reasons :
    1) Running ruby with --debug will show all those unnecessary exceptions
    2) The backtrace gets built even if not used and it is expensive

  16. Peter Cooper says:

    Mark:

    1: WordPress didn't escape the brackets. Should be sorted now.

    2: I mostly agree, although this is a very cute trick that I am sure to use in certain, off the cuff situations.

    7: It's really for situations where you repeat something over and over. For example, appending something to an instance variable in a class. I tend to use it with class variables that store references to objects of that class. (@@objs ||= []) << self, for example.

    18: There are some instances where external data define whether you have a single object or an array, and you /could/ check for that and fix it to always be an array, but it's easier just to use this trick. I believe one instance is when Rails converts params.. if there are multiples with the same name, you can get an array of strings, otherwise you just get the string.. if you want to iterate over whatever you get back, this trick sorts it out.

  17. Peter Cooper says:

    Anyway, critiques are certainly encouraged and welcomed on this. If people can "correct" any bad usages, that will help everyone else who comes here to check out the tricks. That said, they are "tricks" and not all of them are 100% smell-free :-)

  18. David Black says:

    Hi Peter --

    It's good to see this stuff -- I've definitely been too immersed recently in discussions of changing Ruby, and not enough of what one can actually do with it! Like everyone else, I have my likes and dislikes, but I think overall it's a good list and every single one is worth knowing about and thinking about.

    I'd just like to expand on #19 by pointing out the possibly obvious, which is that leaving out 'begin' only works in method definitions (where 'def' carries a kind of implicit begin). Also, there will of course be occasions where one wants a begin in a method, namely in cases where you don't want the whole method governed by the rescue clause.

    David

  19. Stephen Celis says:

    Re: 12

    I wouldn't encourage people to get in the habit of using

    - `when something: another_thing`

    The colon is deprecated (and raises an exception in 1.9). Use either `then` or semi-colon:

    - `when something then another_thing`
    - `when something; another_thing`

  20. Phil says:

    > x % 2 != 0

    You can also do this: not (x % 2).zero?

    Not sure how much better that is, but it's definitely an overlooked trick.

    Code that doesn't use #16 is a personal pet peeve of mine.

    Chris: no no no... "rescue => e" does NOT imply Exception, it implies StandardError. Big difference. Getting the two confused is another pet peeve of mine, as it makes code impossible to escape from via Ctrl-C since Interrupts get rescued. no fun.

  21. topfunky says:

    I can tell that this list was written by an ex-Perl programmer, especially #15. ;-)

    It's too bad that Ruby doesn't have the wantarray method as in Perl. I've used it a few times for situations like #11, mass assignment and returning arrays.

  22. Roman Le Négrate says:

    6 - Why "explode"? I believe it's called the "splat" operator. Makes more sense to me.

    7 - Could be just: (z = []) < '-u', :group => '-g'}[kind] or raise ArgumentError.

    17 - In Ruby 1.9, Array#to_s is an alias of #inspect so the backtrace wouldn't be displayed in a readable manner.

    18 - Ok in Ruby 1.9. But in 1.8, be careful with strings: the splat operator calls #to_a that splits strings on new lines, which might not be the expected behaviour. [items].flatten is a safer alternative.

  23. Roman Le Négrate says:

    Sorry about my comment above: my comment about point 7 has been garbaged and the one about point 8 dropped. Here they are:

    7 - Could be just: (z = []).push "test".

    8 - I like to use Hashes to make one line case statements like: flag = {:user => '-u', :group => '-g'}[kind] or raise ArgumentError.

  24. Peter Cooper says:

    Roman: On your comment for number 7, what you are suggesting wouldn't want for what was intended. If you repeat that over and over, the array doesn't get bigger as z is reinitialized to [] each time, hence the ||= variation.

    On your comment for #18.. very interesting! I didn't realize that. Unfortunately even Array("x\ny\nz") does that split too. Your flatten technique appears to be a good one here.

    Your one line case statements using hashes.. that's a good trick! I might try that myself..

    You should consider making your own list, Roman. I'd definitely link to it. You clearly have a deep experience of some of the pitfalls regarding Ruby trickery! :)

  25. Tom H says:

    Thanks!

    Some are great, but some are clever (or just plain obtuse) at the expense of readability. Elegance is not the same as terseness. Elegance is always good.

    Terseness and its evil twin verbosity will always be in bitter war against each other, both in pursuit of elegance. The winner is not always clear.

    C, C++, Perl, Smalltalk and others have all allowed its practitioners to fall into the "terse is good" trap. We should aim for succinct. Or, as Einstein may have said, "everything should be as simple as possible, but no simpler."

    Tom

  26. Shalon Wood says:

    #15... no, no, a thousand times no! I dislike the use of the ternary operator in general, honestly, but if you _are_ going to use it, never, ever, _ever_ nest it! Down that path lies madness for whoever comes along behind you and has to maintain the code.

  27. Andrew Buntine says:

    Great list. I don't know if "should be using" applies to all of these tricks, however.

    I have a friend who is currently serving 27 years in prison for nesting ternary operators. Please don't do it!!

  28. Yeah says:

    My, all this BRAINPOWER and it has not occurred to anyone to make a FUCKING PRINT FRIENDLY version available??!!??

  29. Paul Prestidge says:

    "[items].flatten is a safer alternative"

    Won't that break nested arrays? You can write a "flatten one level" method, but otherwise I think #18 is the best way. You should know if your code is expecting strings or not!

  30. segfault says:

    Nice List Peter.

    As with many of the other comments, any issues I have with the items are derived from personal taste.

    [item].flatten ftw.
    and ( mylist ||= []) << "stuff" can cause some interesting trouble when used without considering scoping.

    A note to Zoran tho, throw != raise. It's deceptive when coming from a java,c#,c++ background as throw seems more akin to goto.

    Cheers

  31. Todd Werth says:

    This is an interesting list, I learned a few things. Thank you.

    However, the ternary operator is a pet peeve of mine. It seems to be loved by those who were taught, wrongly, that having fewer lines of code is better; that somehow vertical size is worse than horizontal size. if/else is concise enough, very readable, and everyone understands it.

  32. indeed says:

    you can just put a normal case statement on a line. the multiple 'when' is pretty ugly tho

  33. Piers Cawley says:

    I've been bitten badly by inline rescue a little too often I'm afraid. It catches too many types of exception, and you then have a hard time tracking down the spurious nil and what caused it because you've thrown away the exception information.

    @Mark Wilden: I've used it for memoizing the calculation of candidates in a kakuro solver. Skeleton solution looked something like:

    @@result_cache = Hash.new {|args| self[args] = calculate_possibles(*args)}

    def self.possible_values(target_sum, places, candidates)
    @@result_cache[[target_sum, places, candidates]]
    end

    def calculate_possible_values(target_sum, places, candidates)
    ...
    end

    I've also used it in a rails plugin that did some tricks with the params hash. Because the params hash is just a hash (a mistake IMHO) rather than a hashlike object, you can't monkeypatch it easily. The only way I could pass information around was to come up with some kind of hash key that I could use which would never clash with the 'real' hash keys. So I did something like:

    module DistinguishedHashKey
    end

    ...

    params[DistinguishedHashKey] = ...

    Admittedly that's not a _good_ solution, a better one would probably have involved submitting a patch introducing a RequestParams class to Rails, but life is short, time was marching on and I needed the problem solved then and there.

    @Peter - one line case statements using hash. Those are especially useful when you start using lambas in the hash values. An _old_ perl trick that one...

    Roman: The problem with [items].flatten is that it's too eager. Sure it fixes the string case, but it breaks [*[:foo, [:bar]]]. One fix:

    begin
    old, $/ = $/, ""
    [*items]
    ensure
    $/ = old
    end

    Not even slightly pretty though.

  34. Tom Bille says:

    I always liked those tricks - but then again if they get used alot ruby will become the next read only language after perl. The next step would be the obfuscated ruby contest.
    Wordpress can be a little insisting on reformating, you could try the plugin wp-unformatted - http://alexking.org/projects/wordpress/readme?project=wp-unformatted.

  35. François Vaux says:

    Nice list, I already knew some of these and I use them frequently.

    If you combine 6. and 11. you can do things like

    > a, b, *c = 1, 2, 3, 4, 5, 6
    a => 1
    b => 2
    c => [3, 4, 5, 6]

    That's pretty useful when used with Array#split :-)

    I relly like String#% because it makes more readable code than the common interpolation method ( #{} ). Compare :
    " %s " % [tag, contents, tag]
    " #{content} "

    (Ok maybe it's not the best possible example :-/)

    Thanks for the post Peter !

  36. François Vaux says:

    Hmm, tags get stripped :/

    The last example was :

    "<%s> %s </%s>" % [tag, contents, tag]
    "<#{tag}> #{contents} </#{tag}>"

  37. Jim Deville says:

    Todd Werth:
    (IMHO) The ternary is understood by everyone too. I usually prefer it in assignments from simple conditionals.

    Peter:
    Sweet tricks. A lot of these I never knew, a few of them are good reminders.

  38. mike from africa says:

    like most programmers, i was never taught having few lines of vertical code is better than horizontal code, never mind whether it's wrong or correct. the use of ternary operators is certainly a personal preference, but why write in 5 lines can you can write in 1, that is readable and much more concise. and why not make use of those brackets to improve readability.

    output = (x == 10)? "x is ten" : "x is not ten"

  39. Neil Wilson says:

    Ternary operators are evil. Nested ternary operators are really evil.

    It is just so much more readable to use the words - remembering that conditionals in Ruby are just expressions and yield a value.

    amount = if qty == 1 then "one" else "many" end

    and it's even better if you use more than one line.

  40. Matteo Alessani says:

    Thanks for sharing these useful tips! The 16th is wonderful!

  41. Alan says:

    Everyone knows ternary operators. Use them only in simple cases and don't worry about it. Nothing wrong with x = (condition) ? bar : baz

    Mark Wilden: We have all had to maintain uncommented masses of poorly-written code. Some of us respond by swearing that from now on, our code will have comments. Some of us respond by swearing that from now on, our code will be more readable.

    Why not do both?

  42. Austin says:

    Who are these people who can't read ternaries? Bad factoring makes code hard to maintain, not succinct syntax.

    You forgot to mention !! in relation to number #16.

    "abc"[/(d}/, 1] # => nil
    !!"abc"[/(d}/, 1] # => false

  43. Csaba says:

    Hi,
    Interesting reading. Unfortunatly, for me, (far to) much look Perllish (= obfuscated). Not really elgant readable code that Ruby is supposed to promote.

    #2 Can't Ruby with all its string functions format a floating number in a lucid way like: money.to_s(3.2f) or in the Basic style money.to_s("0.00")? I can do it in .NET Basic!

    # 14 Well used terniary operator is good. Even if easily missused. But so can everything.

    # 16 This example could be implemented in Basic since it got functions.
    (Well at least in HP9830 Basic since 1976). But few realize it!
    I don't understand why the textbooks don't show it.

    But the sigel line version returning sometbing else that the if test result requires the terniary operator.

  44. Mike Cowden says:

    My basic rule of thumb: if I get to a line of code that requires me to stop and put mental parenthesis around it--someone is trying to be cute or show how clever they are.

    Please don't nest ternary operators.

  45. Peter Theill says:

    Great article. I would really like to use tip #21 but according to all the comments I'm not sure I dare :)

    I see many uses for this, though and I'm wondering if there might be a better solution if I want to do stuff like this:

    is_available = user.presence.availability == Online

    and I really don't care if either "user" or "presence" is nil?

    I see myself do code such as:

    is_available = (user && user.presence ? user.presence.availability == Online : Offline)

    What I really would like is something to eval a string and return either the value in the ending object or nil if the object tree can't be followed till the end e.g.:

    availability = find_availability("user.presence.last_year.lastest_city.available")

    Does anyone see my point? But then again, maybe "rescue" is acceptable enough in the above example? :)

  46. Todd Werth says:

    If everyone knows ternary operators, why are they in a list of 21 ruby "tricks"? From years of mentoring junior programmers, I can tell you that no, not everyone does.

    There are cases when ternary operators are useful and make code more readable, but those cases are rare.

    Mike Cowden, I totally agree.

    I do understand that these are age old arguments, and partially a matter of style. So this is just my ever so humble opinion.

  47. Avdi says:

    Fantastic list! There were actually a couple of points that were new to me, which doesn't happen much any more.

    One nitpick: #18 would probably be better done as Array(items), as Object#to_ary is deprecated. Kernel.Array() is the canonical way to say "make sure this is an array".

  48. John says:

    @mark - "Using exception handling in place of a control structure is generally frowned upon. I have to admit, though, that this is nice and neat, it avoids things like andand, and I do see it in “real” code. It’s probably way slower than the more straightforward technique, but that’s not a real issue."

    Not true. Exceptions are encouraged and should be used when an exception happens in your code. Someone did some benchmarking on this subject and found that Ruby exceptions are reasonably fast; about as fast as an equivalent control structure.

    The only problem I do see with the example is that you are not specifying which exception you want to rescue, which, if you are not careful, can hide problems deep in your code.

  49. Piers Cawley says:

    Oh dear, someone suggested !!. Nil is perfectly falsy, pretty much everything else that isn't false is truthy. Why spend cycles to throw away information?

  50. John says:

    @Peter - " I’m wondering if there might be a better solution if I want to do stuff like this"

    Based on your example, I find it's usually better to make the method available in the "base" object:

    class User
    def availability
    presence ? presence.availability : Offline
    end
    end

    If you're running into this problem often, you might want to rethink your design. Although there are certainly cases where the rescue is your best bet.

    Evaling a string like you describe is pretty straightforward.

    class String
    def try(string, caller)
    string.split('.').inject(caller) do |result,method|
    result.send(:eval, method) if result
    end
    end
    end

    "thing(10).to_s.fail".try(self)

    But, again, I'd reevaluate your design first.

  51. Dirceu Jr. says:

    wow™
    really nice!

  52. Marek says:

    As for #9, it could be written in a more functional (i.e. no side effects) way, like this:

    queue = %w{hello x world}.inject([]) do |queue, word|
    (queue <= 2; queue
    end
    puts queue.inspect

    though it requires getting used to the inject construct...

  53. Marek says:

    The example is not very readable so I'a posting it again:

    queue = %w{hello x world}.inject([]) do |queue, word|
    (queue << word; puts "Added to queue") if word.length >= 2; queue
    end
    puts queue.inspect

  54. Greg W says:

    In the age where readable code is getting so much emphasis, and people are encouraged to use more than one language, I'm not convinced that any of the "shortcuts" are an improved coding style. Explicit code = maintainable code IMO. Ruby is already cryptic enough IMO, eliminating words in favor of symbols and one-liners isn't an advantage to anyone but the original coder's "tricky" ego.

  55. Mark Wilden says:

    @John: That's one of the reason why exception handling for control is often bad - the swallowing.

    If the code is truly responding to an exceptional circumstance, then exceptions are the way to go. This is because exceptions allow the code with the best understanding of what to do handle the situation, whether it's local or somewhere up the callstack. But if the code is merely providing a default, and the circumstance isn't truly exceptional I don't think exceptions are the most "intentional" (i.e. showing the intent of the code) way to do it.

    @Alan: The issue of comments is a huge and eminently debatable. My summary comment is "All code that's tricky to understand should be commented. Code should not be tricky to understand." :)

    @Piers: You probably know this, but the Rails params hash isn't actually just a Hash. It's a HashWithIndifferentAccess, allowing lookup by symbols or strings. :)

    !! is an old C trick to convert an expression to 1 or 0 (true or false, in C). I agree it's not necessary in Ruby.

    @Francois: The reason I don't like string formatting like the old C sprintf is that it makes the code less readable. To understand the output, you have to bop back and forth, rather than reading it inline.

    @All: It's not the "ternary" (or even "terniary") operator. It's -a- ternary operator (the only one, as it happens). Its name is the "conditional operator," and yes, nesting them is evil.

    ///ark

  56. Evan says:

    By far the most interesting thing I learned is that ; is an alternative to newline. (Who would have thought!) But the ability to do ternary 1-liners with multi-line blocks is pretty awesome.

    "ruby" == "awesome" ? ((response = "i think so"); puts response) : (if true: puts "you know damn right" end)

  57. Piers Cawley says:

    @Mark: The thing is, HashWithIndifferentAccess is still sending a datastructure to do an object's job. Where's the information about which route matched to provide that set of params? Where's the information about how the URI was parsed into keys? I get depressed by the amount of useful information that Rails throws away during route recognition. Some of that stuff, it it were available, could allow for a radical slimming down of controllers to the point where they become nearly invisible.

    But that's a soapbox I probably shouldn't be climbing onto here.

  58. frank says:

    Just a little combining ...

    queue = %w{hello x world}.map { |word| word.length >= 2 ? (puts "Added to queue"; word) : nil }.compact

    p queue

  59. Mark Wilden says:

    @Piers: You're absolutely right. I was just joking around, since I think HashWithIndifferentAccess is such a funny name. :)

    @Frank: I have to say that adding nil to the queue then removing it later - just for code brevity - would not be my first choice.

    ///ark

  60. frank says:

    Sure.

    p queue = %w{hello x world}.select { |word| word.length >= 2 }

  61. Darcy says:

    Yikes - some of that is scary. Just because you can do it doesn't mean you should! :-)

  62. K says:

    I might be a little slow, but I don't get #7.

    What is it supposed to replace? why is it better?

  63. Peter Cooper says:

    K: It can be useful to remove the need to initialize variables. It's particularly useful for instance variables being used in one-liners.

    Consider this somewhat arcane example of adding together "columns" within an array:

    a = [ [1,2,3], [4,5], [6, 7, 8] ]
    a.each { |y| y.each_with_index { |i, j| (@r ||= [])[j] = @r.fetch(j, 0) + i.to_i } }

    The (@r ||= []) part means I didn't need to do a @r = [] on the previous line. Whether or not this "shortcut" is suitable for you or not is a matter of personal taste, however :)

  64. Alex says:

    @Peter Theill: There's a dynamic hash-tree snippet over at RubyForge.

    class Node < Hash
    attr_accessor :value
    def method_missing(key)
    self[key]==nil ? self[key]=Node.new : self[key]
    end
    end

    tree = Node.new
    p tree.branch.leaf.value = 'Hello Worldd!'
    p tree.branch.leaf.value == 'Hello World!'
    p tree.branch.leaf.xxx.value == 'Hello World!'
    p tree.branch.value

    Similarly you can create multidimensional hashes ( http://www.ruby-forum.com/topic/130324 ).

  65. Tak says:

    Wow.
    The basic stuff (use fileutils instead of shell, get exception backtrace) is useful, although I wouldn't call these things 'tricks.' The other things, (nested ternaries, cramming as many operations as possible into as few characters as possible), ...

    Ruby is not perl; I'd like to keep it that way.

  66. Name required says:

    What's that saying about those who fail to learn the lessons of the past are doomed to repeat its mistakes?

    Using "tricks" results in code with lower maintainability, because fewer people will understand it.

  67. carmen says:

    #18 wording is misleading. it will really call to_splat, or to_a, and therefore a 'single object' wont necessarily stay a single object. a hash will be converted to an array and so forth

  68. defsdoor says:

    13 is just wrong on so many levels.

  69. Peter Cooper says:

    #68: Very common though.

  70. Kurt says:

    Cool list, I learned some new ways to confuse my co-workers! :) I liked #2.

    Some of these create useless garbage objects that must be collected in the common cases and are much less efficient than other techniques. Just be cause a Ruby program doesn't have to call free() doesn't mean that Ruby wont have to do it.

    Ruby does not memoize constants, so it may look like a constant Range or String in your code, but it's just garbage. Consider naming them with CONSTANTS.

    Sorry I have to say it: using Floats for monetary values is always a bad idea.

  71. Peter Cooper says:

    Kurt: I use integers based on the lowest denomination of the currency (cents, pence, etc.) I still use the same format though, once I've divided the price by 100 (or whatever). "%.2f" % (amount / 100)

  72. Pingback: Churadesign

  73. Tammo Freese says:

    Nice compilation!

    As Mark Wilder already pointed out, using #{} instead of
    sprintf would be more idiomatic. Your example was:

    x = %w{p hello p}
    "%s" % x

    If we give tag and text names, it would look like:

    tag, text = *%w{p hello}

    "%s" % [tag, text, tag]

    We may avoid the duplication by giving explicit positions
    in the format string:

    "%2$s" % [tag, text]

    The #{} form is a lot more readable:

    "#{text}"

  74. Neil says:

    #68 This is one reason why _why's Camping is so few lines.

  75. Tammo Freese says:

    Unfortunately the comment function does not display most of the code I entered. Peter, could you please correct that?

  76. Peter Cooper says:

    Tammo: I see the same here as I did in the e-mail from the WordPress installation to me, so if anything has been removed, it was removed at the earliest stage.. sanitization perhaps. Ideally code would be put into a pastie and then linked here.

  77. Pingback: vBharat.com » 21 Ruby Tricks You Should Be Using In Your Own Code

Other Posts to Enjoy

Twitter Mentions