New Methods in Ruby 2.2

vector illustration of dark red shield with ruby programming lan

Most of the fanfare around Ruby 2.2 has focused on garbage collection (GC) upgrades. The GC will now clean up symbols and has a new incremental algorithm that reduces the pause time. These changes are very exciting, but there were some new methods added as well. Let’s see what other new toys 2.2 delivered.


If you’ve ever wanted to know the local variables defined in a scope, have I got a method for you. Stick this anywhere and find out what vars are in use:

def addition(x, y)
  puts binding.local_variables.inspect
  z = x + y
  puts binding.local_variables.inspect

> addition(2, 3)
[:x, :y, :z]
[:x, :y, :z]


Maybe you don’t care about the local vars. Do you want to know which object is receiving the method call?

class Cat
  def self.type

> Cat.type

class Tiger < Cat

> Tiger.type

Most of us don’t directly deal with bindings, but if you do life just got a little easier.


Getting the file descriptor for an IO object is nothing new. Now you can get it for a directory too. File descriptors are a way to reference a particular open file, directory, etc. You’ve probably used them before without even realizing it. On POSIX systems, 0, 1, and 2 are reserved for standard in, standard out, and standard error, respectively.
Have you ever used something like 2>&1 on the command line? You were referencing the file descriptors.

> $stdout.fileno

Be warned, this new method is only available on POSIX systems. If you try to call it on Windows, it’ll throw a NotImplementedError.


This method is a complement to the existing slice_before method. I haven’t run across slice_before in the wild so I had to take a look at how it works.

As the name suggests, slice_before is used to slice and dice enumerables. Given a way to match an element in the enumerable, it will find a match and cut it apart just prior to the match:

> [1, 'a', 2, 'b', 'c', 3, 'd', 'e', 'f'].slice_before { |e| e.is_a?(Integer) }.to_a
[[1, "a"], [2, "b", "c"], [3, "d", "e", "f"]]

In this case, it made cuts before the integers 1, 2, and 3.

An argument can be passed in place of a block. When that happens, the argument is checked using === (i.e. case equality). We could get the same result as above by passing Integer:

> [1, 'a', 2, 'b', 'c', 3, 'd', 'e', 'f'].slice_before(Integer).to_a
[[1, "a"], [2, "b", "c"], [3, "d", "e", "f"]]

Want to guess at what slice_after does? Rather than slicing before the match, it slices after:

> [1, 'a', 2, 'b', 'c', 3, 'd', 'e', 'f'].slice_after(Integer).to_a
[[1], ["a", 2], ["b", "c", 3], ["d", "e", "f"]]


A particularly fun addition is slice_when. Unlike slice_after, this method only accepts a block. It walks an enumerable, passing pairs of elements to the block. When the block returns true, the enumerable is sliced between the pair of elements:

> [1, 3, 4, 5, 7, 8, 9, 10, 12].slice_when { |a, b| a + 1 != b }.to_a
[[1], [3, 4, 5], [7, 8, 9, 10], [12]]

Here, we’ve found runs of consecutive numbers by slicing when the second number is not the first number plus one.

Given an array of numbers, you could easily find out how many of each one you have:

> { rand(3) + 1 }.sort.slice_when(&:!=).map { |x| [x.first, x.size] }
[[1, 4], [2, 4], [3, 2]]

On this run, we got 1 four times, 2 four times, and 3 twice.

Float#next_float, Float#prev_float

These functions return the next or previous representable float. Note the word “representable” in that sentence, not all floats can be represented.

> 1.0.next_float

Notice how it skipped 1.0000000000000001? It can’t be represented using a Float:

> 1.0000000000000001

It’s also worth noting that the distance between two steps isn’t always the same:

> 2.0.prev_float

> 2.0.next_float

Apparently, methods like this are useful for finding ULP values and doing other things that you’ll most likely never do. They might not be the most globally useful methods, but if you need them you’ll be glad they’re there.

File.birthtime, File#birthtime, File::Stat#birthtime

We’ve had atime, ctime, and mtime to check a variety of access and modification times related to a file. What we haven’t had before 2.2 is the birth time (i.e. creation time) for a file. You’ll notice that it, like the others, comes in both class and instance flavors:

>'test', 'w').birthtime
2015-01-06 19:24:44 -0600

> File.birthtime('test')
2015-01-06 19:24:44 -0600

Also available in File::Stat:

2015-01-06 19:24:44 -0600


Ruby went out and got itself an identity method. For those not familiar, an identity method returns the object it’s called on:

> 1.itself

At this point you might be wondering where this is useful. One of the most common examples is grouping:

> [2, 3, 3, 1, 2, 3, 3, 1, 1, 2].group_by(&:itself)
{2=>[2, 2, 2], 3=>[3, 3, 3, 3], 1=>[1, 1, 1]}

It can also be used as a way to no-op a method like map or select. In functional programming, this can be a good way to avoid what might otherwise be an awkward conditional.


You might not have realized that Ruby is capable of currying and partial application. In the past, you could only call curry on a Proc. This same power is now available to you on Method.

def sum(*args)

> inc = method(:sum).curry(2).(1)
#<Proc:0x007ff68ac96728 (lambda)>
> inc.(3)


Earlier we found the object receiving a method with binding.receiver. We can also find out about the method’s parent. Calling super_method returns the method that you would get if you called super. If the method has no parent, it returns nil.

class Cat
  def speak

class Tiger < Cat
  def speak

#<Method: Tiger#speak>
#<Method: Cat#speak>

String#unicode_normalize, String#unicode_normalize!, String#unicode_normalized?

Did you know that, in Unicode, some characters can be represented multiple ways? Take for example, an “e” with an acute accent. You can represent it with normalization form canonical composition (NFC) using a single code point:

> nfc = "\u{e9}"

You can also represent it with normalization form canonical decomposition (NFD) by combining two code points:

> nfd = "\u{65}\u{301}"

In this form, a regular “e” is combined with “◌́” to create the “é” you see above. While these two might produce a similar looking output they are not the same:

> nfc == nfd

What unicode_normalize does is convert strings from one form to another. By default it converts to NFC:

> nfc == nfd.unicode_normalize

You can also pass a symbol indicating the form to convert to:

> nfc.unicode_normalize(:nfd) == nfd

These forms exist because they have value in different situations. The NFC codes are shorter, but if you want to strip accents from a word for, let’s say search purposes, the NFD form would be easier to handle. While NFC and NFD are the most common, you can also pass :nfkc and :nfkd if needed.

The bang version of this does what you’d expect and modifies the string receiving the message.

Calling unicode_normalized? checks to see if the string matches the form you want. It takes the same argument as unicode_normalize:

> nfc.unicode_normalized?
> nfc.unicode_normalized?(:nfd)

Time to Refine

A lot of these methods aren’t every day tools. They’re incremental improvements that shore up the language. Ruby has experienced a lot of change in the last few years. The changes between 1.8, 1.9, and the 2.0 releases were monumental. Now, the language is focused on refining some of the rough edges created by that process. In fact, 2.2 lets you create quoted symbol keys in hashes with a trailing colon:

{ 'programming-language': :ruby }

No more symbol keyed hashes with an stray hash rocket thrown in because of a hyphened HTML attribute. I think that is something we can all get behind.


No Reader comments