15 Weird Things About Ruby That You Should Know

15 Weird Things About Ruby That You Should Know

By Jesus Castello

Ruby is an amazing language with a lot of interesting details that you may not have seen before...

…in this post I compiled some of those details for your own enjoyment in a nice-looking list :)​

1

Heredoc + Method

If you have some data that you want to embed into your program you may want to use a “heredoc”.

Like this:

This will give you a string. But you may want to do some post-processing, like splitting this string into an array of strings.

Ruby lets you do this:

Bonus tip:
Ruby 2.3 introduced the "squiggly heredoc" <<~. This will remove all the extra spaces introduced by indentation, which is a common problem when using heredocs for text.

2

Call a Method Using Double Colon

Apparently this is a thing…

3

Puts with Multiple Arguments

Pretty simple, but could be useful in some situations I guess.

4

Infinite Indexing

Example:

This works because [] is just a method & it keeps returning the first character, which is also a string.

5

De-structuring Block Arguments (or whatever you want to call this)

Want to get rid of some local variables? You will love this trick!

This has the same effect as if we did this:

But it saves you one line of code 🙂

6

Special Global Variables

When you use a regular expression with capture groups it will set the global variable $1 to the first group, $2 for the second group, etc.

The thing about these is that they don't behave like normal variables. They are ‘method-local’ & ‘thread-local’, as described by the documentation.

Also they can’t be directly assigned to like regular global variables.

7

Shovel Method on Strings

There is this “shovel” method on string which doesn’t do what you would expect when you use it with a number

…it’s interpreting the number as an ASCII character.

Here’s another way to do that:

8

Character Literals

Not sure if there are any practical uses for this one…

Let me know in the comments what you think 🙂

9

The RbConfig Module

RbConfig is a module which is not documented & it contains some info about your Ruby installation.

There is some useful info under RbConfig::CONFIG (like compile flags, ruby version & operating system).

10

Spaces, Spaces Everywhere!

You can put as many spaces as you want between a method call & the receiver of that call.

Yes, this is valid Ruby syntax 🙂

11

Infinite Nesting of Constants

You can have an infinite amount of nested constants like this:

The reason this works is that all top-level constants (defined outside any class) are contained in the Object class & every class inherits from Object by default.

Try Object.constants to see what I mean.

12

Chaining the Shovel Operator

You can chain the shovel << operator multiple times:

13

BEGIN & END

Two keywords that you don’t see very often are BEGIN & END. I believe these come from the Perl / Unix world, where it’s common to write short scripts for processing output from other programs.

Let’s see an example of how this works:

This code will print "Program starting..." before it prints 123. It could be useful if you are writing the kind of short scripts that this is meant for, but probably not very useful in web applications.

Update:
Reader Ronald sent me some interesting uses for this trick. Here is what he said:

"It is very useful, for example for fiddling with the RUBYLIB path for the 'require' statements, because it is guaranteed to be executed before all the 'require'. I also use it to set $VERBOSE to true, or set some environment variables, etc."

14

Flip-Flop

I don’t even know why this is a thing, but I would advice to stay away from it because it can be confusing & most people are not familiar with this feature ?

But it could be useful to know in case you find this in other people’s code.

This is the syntax:

The idea is that once the first condition is true an invisible “switch” will turn on & everything from there will evaluate as true until the 2nd condition is true.

Example:

This prints all the numbers from 3 to 15, but if you skip 15 it will keep printing.

15

Redo Keyword

Another keyword that you don’t see very often is redo, this allows you to repeat the same iteration inside a loop…

…but unless you use next or break you will have an infinite loop. So I think you should not use this feature.

Conclusion

You learned about a few cool Ruby tricks & tips. If you want more see my other post here.

Don’t forget to share this post so more people can see it!

18 thoughts on “15 Weird Things About Ruby That You Should Know”

  1. Ah, the destructuring of block arguments is cool. I see you can do a.each {|(a)| # something with a} and discard b, any way to discard the first argument? This throws an error as ruby tries to assign nil: a.each {|(nil, b)| # something with b}.

    • Hey Ben,
      you could try a.each { |(_, b)| # something with b }, that’s the general convention for what you are trying to do.

  2. Number 9 is especially useful when it comes to feature flagging ruby/rails upgrades. You can wrap up deprecated methods and then push the code for testing.

  3. I sometimes use the character literal for calls to split (e.g., "snake_cased_string".split(?_)) or when building a string consisting of n times the same symbol (e.g., ?**5 for "*****") even though rubocop yells at me not to do this 🙂

    Avdi Grimm shows an example where the redo keyword comes in handy in rubytapas episode 259 (only members can access it), but I’d agree that it is something that one usually does not need and even then there’s probably more idiomatic ways to go about it.

    • Thanks for your comment @kaikuchn. I have to agree with Rubocop on this one, but still interesting uses 🙂

  4. For #8, ?a syntax, I find it more useful for intention documentation purposes. For example, using this in a comparison or assignment conveys that we are specifically interested in a single character. As well, you don’t incur the potential interpolation check overhead when doing like “a” vs ‘a’.

  5. Indefinite indexing: this amused me, so here’s a similar one: indefinite calling ^^

    public def call () () end
    ().().().().().().().().().().().()
    

    Destructuring: There is also some limited support for nested destructuring

    -> ((a, (b, c), d)) {
      a # => :a
      b # => :b
      c # => :c
      d # => :d
    }.([:a,[:b,:c],:d])
    

    And you can destructure hashes in blocks, as well

    -> (a:, b:1, c:2, **d) {
      a # => :a
      b # => :b
      c # => 2
      d # => {:d1=>:d1, :d2=>:d2}
    }.(a: :a, b: :b, d1: :d1, d2: :d2)
    

    Special Globals: There aren’t hard rules for these, they have a lot of options when they define them, so you won’t necessarily see consistent behaviour across them. Eg. $_ is scope local, but you can assign to it:

    $_ = 'main'
    class A
      $_ # => nil
      $_ = "A"
    end
    $_ # => "main"
    

    You can find their definitions by grepping MRI source for rb_define_hooked_variable and rb_define_hooked_variable.

    Character Literals: My fav use is ?? as a place holder, eg I might write this, and then go back and figure out what those vars are

    ∆x = ??
    ∆y = ??
    Math.sqrt ∆x**2 + ∆y**2
    

    Also, their existence is historical rather than practical, back when chars were still int literals (1.8, IIRC), it was useful to have a syntax for going from the char to the ordinal, which is what this was for. Then when chars became single-letter strings, they made the syntax resolve to that rather than deprecating it and breaking lots of code.

    RbConfig Module: I totally didn’t realize about host_os! That will be useful, ty!

    Chaining the Shovel Operator: Note that this is b/c Array#<< returns self, if it didn’t, then this wouldn’t be true (compare to assignment, which always returns the assigned value)

    class A
      def <<(*) 123 end
      def m=(*) 123 end
    end
    A.new << 456  # => 123
    A.new.m = 456 # => 456
    

    BEGIN & END

    I think they come from awk, they’re useful on the command line when you use the -n and -p flags, eg 5 most common words in parse.y

    cache curl -sL 'https://raw.githubusercontent.com/ruby/ruby/ruby_2_4/parse.y' | ruby -ne '
    BEGIN { words = Hash.new 0 }
    END   { words.sort_by { |w, n| -n }.take(5).each { |c, n| puts "#{c} #{n}" } }
    $_.scan(/\w+/).each { |w| words[w] += 1 }
    ' | column -t
    

    Flip-Flop

    Another command-line one, the best use cases for it have shortcuts, eg directly dropping $. will match line numbers and dropping regexes into it will match the current line (this is actually a boolean thing, not a flip-flop thing). Sadly, .. vs ... chooses whether it will test the second condition on the first line rather than whether it will include the last line, which would have been more useful, IMO.

    Eg: Print the environment variables section of Ruby’s man page:

    $ man ruby | col -b | ruby -ne 'print if /^ENV/.../^\w/'

  6. Number 5 does not work as you mention, it asigns 1 to ‘first’ and empty to ‘last’ and then 2 to ‘memo’ at least in my Ruby 2.3.0 on Mac

  7. Re 1: Way back in the day people used DATA sections (everything after __END__) at the end of Ruby scripts for storing data. Here’s a blog post on this: http://blog.honeybadger.io/data-and-end-in-ruby/

    Re 2: This works because :: is used for scope resolution in Ruby: https://en.wikipedia.org/wiki/Scope_resolution_operator#Ruby

    Re 6: The special variables become a lot less magic if you require 'English: https://ruby-doc.org/stdlib-2.3.3/libdoc/English/rdoc/English.html

    Re 8: I mainly use them in code golf 😉 Or when I want to be really explicit about expecting a one character string.

    Re 13 and 14: both the BEGIN and END block as well as the flip-flop operator are remnants of Ruby’s Perl legacy. Though it’s been a long time since I actually saw them in code.

    Now for some of my personal favorites:

    *) Inheritance: the right-hand side of < can be any valid Ruby expression as long as it resolves to a class.

    class IdentityCrisis < [Array, Hash].sample ; end

    *) Unary minus: to define a unary minus operation, you have to use -@.

    class String
       alias_method :-@, :upcase
    end
    
    -"foo"
    #=> "FOO
    

    Also if you wanna confuse your friends, make them guess what this does: (1..5).map(&:-@) 😉

    Infinity:

    Before Float::INFINITY was added in 1.9 we used to make our own: Inf = 1 / 0.0 #=> Infinity

    Destructuring addendum: fairly obvious, but restructuring works for assignments as well:

    first, second, _, *rest = [*1..10]
    first
    #=> 1
    second
    #=> 2
    rest
    #=> [4, 5, 6, 7, 8, 9, 10]
    

Comments are closed.