Russell
Russell

Reputation: 12439

Is there a `pipe` equivalent in ruby?

Occasionally when writing Ruby I find myself wanting a pipe method, similar to tap but returning the result of calling the block with self as a parameter, like this:

class Object
  def pipe(&block)
    block.call(self)
  end
end

some_operation.pipe { |x| some_other_operation(x) }

..but so far I haven't managed to find out what it's called, if it exists. Does it exist?

If it doesn't, I know I could monkey-patch object to add it but, y'know, that's bad. Unless there's a brilliant, guaranteed to never clash (and descriptive and short) name I could use for it...

Upvotes: 8

Views: 1229

Answers (3)

tokland
tokland

Reputation: 67900

This abstraction doesn't exist in the core. I usually call it as, it's short and declarative:

class Object
  def as
    yield(self)
  end
end

"3".to_i.as { |x| x*x } #=> 9

Raganwald usually mentions that abstraction in his posts, he calls it into.

So, summing it up, some names: pipe, as, into, peg, thru.

Upvotes: 12

elyalvarado
elyalvarado

Reputation: 1296

Ruby 2.5 introduced Object.yield_self which is exactly the pipe operator you're using: it receives a block, passes self as the first argument to it and returns the result of evaluating the block.

class Object
  def yield_self(*args)
    yield(self, *args)
  end
end

Example usage:

"Hello".yield_self { |str| str + " World" }
# Returns "Hello World"

You can also read a little more about it in the following blog posts:

  1. Explains the difference with Rails' try and Ruby's tap methods
  2. Some very nice examples of using yield_self to simplify code

Upvotes: 5

Kurt Mueller
Kurt Mueller

Reputation: 3224

Here's the the technique I use to chain objects. It's pretty much exactly as above except I don't reopen the Object class. Instead, I create a Module which I will use to extend whatever object instance I'm working with. See below:

module Chainable
  def as
    (yield self.dup).extend(Chainable)
  end
end

I've defined this method to prohibit mutative methods from altering the original object. Below is a trivial example of using this module:

[3] pry(main)> m = 'hi'
=> "hi"
[4] pry(main)> m.extend(Chainable).as { |m| m << '!' }.as { |m| m+'?'}
=> "hi!?"
[5] pry(main)> m
=> "hi"

If anybody sees anything wrong with this code, please let me know! Hope this helps.

Upvotes: 0

Related Questions