DEV Community

Kevin Murphy
Kevin Murphy

Posted on • Originally published at kevinjmurphy.com

What Is It (in Ruby 3.4)?

This post may appear to be about new functionality in Ruby. However, it may instead be an attempt to write an article with the worst possible SEO.

Anyway, hit it.

What Is It?

As of Ruby 3.4, it is another way to refer to the first parameter in a block. This may be more obvious by way of an example.

["What is it?"].map { it + " It's it" } * 4

=>
[
  "What is it? It's it",
  "What is it? It's it",
  "What is it? It's it",
  "What is it? It's it",
]
Enter fullscreen mode Exit fullscreen mode

The block we pass to map does not define a name for the parameter to represent the variable we're manipulating in each iteration. Instead, Ruby knows it refers to that variable.

Starting in Ruby 2.7, we had access to numbered parameters in a block with an underscore. We can write that same example using numbered parameters.

["What is it?"].map { _1 + " It's it" } * 4

=>
[
  "What is it? It's it",
  "What is it? It's it",
  "What is it? It's it",
  "What is it? It's it",
]
Enter fullscreen mode Exit fullscreen mode

Before Ruby 2.7, we would define the name of a variable in a block, which we can still do with more recent versions of Ruby.

["What is it?"].map { |i| i + " It's it" } * 4

=>
[
  "What is it? It's it",
  "What is it? It's it",
  "What is it? It's it",
  "What is it? It's it",
]
Enter fullscreen mode Exit fullscreen mode

Handling nested calls

Should you have nested blocks that refer to it, Ruby will take it to refer to the parameter of the innermost block.

[
  "You want it all, but you can't have it",
  "Yeah",
  "It's in your face, but you can't grab it",
  "Yeah",
]
  .map { it == "Yeah" ? [it, it, it].map { it.upcase }.join(", ") : it }

=>
[
  "You want it all, but you can't have it",
  "YEAH, YEAH, YEAH",
  "It's in your face, but you can't grab it",
  "YEAH, YEAH, YEAH",
]
Enter fullscreen mode Exit fullscreen mode

The same is not possible with numbered parameters. An exception is raised due to the ambiguity of what _1 refers to.

[
  "You want it all, but you can't have it",
  "Yeah",
  "It's in your face, but you can't grab it",
  "Yeah",
]
  .map { _1 == "Yeah" ? [_1, _1, _1].map { _1.upcase }.join(", ") : _1 }

syntax error found (SyntaxError)
  5 |   "Yeah",
  6 | ]
> 7 | ... _1.upcase }.join(", ") : _1 }
    |     ^~ numbered parameter is already used in outer block
Enter fullscreen mode Exit fullscreen mode

Method names in scope

Ruby also will infer that it refers to the block parameter should there be a method called it in scope.

def it = "This isn't it"

[
  "Can you feel it, see it, hear it today?",
  "If you can't, then it doesn't matter anyway",
]
  .map { it.gsub("it", "IT") }

=>
[
  "Can you feel IT, see IT, hear IT today?",
  "If you can't, then IT doesn't matter anyway",
]
Enter fullscreen mode Exit fullscreen mode

Variable names in scope

However, if there is a variable defined as it, that will take precedence over the block parameter in the block.

it = "This isn't it"

[
  "You will never understand it, 'cause it happens too fast",
  "And it feels so good, it's like walking on glass",
]
  .map { it.gsub("it", "IT") }

=>
[
  "This isn't IT",
  "This isn't IT",
]
Enter fullscreen mode Exit fullscreen mode

You've got to share it, so you dare it

If you want to learn more about this new feature, check out the proposal and the documentation. Read the Ruby 3.4 changelog to discover all the fun available in Ruby 3.4.

You can touch it, smell it, taste it, so sweet

But it makes no difference 'cause it knocks you off your feet

That's it.

Top comments (1)

Collapse
 
ben profile image
Ben Halpern

Got it