Skip to content

Instantly share code, notes, and snippets.

Last active Jun 12, 2018
What would you like to do?
*nix has pipes, Elixir has pipes, Ruby deserves pipes.
# Elixir has pipes `|>`. Let's try to implement those in Ruby.
# I want to write this:
# email.body | RemoveSignature | HighlightMentions | :html_safe
# instead of:
# Ugly implementation starts here...
def pipe_it(input, filter)
# multiplexed input!
if input.is_a? Array
return { |input_item| pipe_it(input_item, filter) }
case filter
when Symbol
when Hash
method = filter.keys.first
arguments = Array(filter.values.first)
input.send(method, *arguments)
when Array
# multiplex! { |filter_item| pipe_it(input, filter_item) }
class Pipeline
def initialize(*filters)
@filters = filters
attr_accessor :filters
def call(input)
filters.inject(input) do |input, filter|
pipe_it(input, filter)
def pipable(input)
input.define_singleton_method(:|) do |filter|
pipable pipe_it(input, filter)
def pipe(input, *pipeline)*pipeline).call(input)
# Let's define a few filters
Reverse = ->(string) { string.reverse }
Leet = ->(string) { string.gsub(/[aeiost]/,'a'=>'4','e'=>'3','i'=>'1','o'=>'0','s'=>'5','t'=>'7') }
Mooify = ->(string) { "Cow said: " + string }
Say = ->(string) { system %|say "#{string}"|; string }
TweetTo = do
def call(input)
puts %|Tweeting "#{input}" to #{@recipient}!|
# Time to play with different approaches...
# 1 - We make the first element pipable and we can then just pipe through!
result = pipable("moo") | Reverse | Leet | Mooify | :downcase |'@pcreux') | { delete: 'o' }
puts result
# => cw said: 00m
# 2 - Pipe without defining any `|` method
puts pipe("moo", Mooify, :upcase)
# 3 - Pipeline object
pipeline =, :downcase, { gsub: ["o", "a"] })
pipeline.filters << ->(input) { input.gsub("moo", "maow") }
# => caw said: maa
# => Cow said: maaw
# ZOMG! Multiplexing!
# "moo" => Mooify => :downcase => Reverse
# => :upcase => Reverse
p, [:downcase, :upcase], Reverse).call("moo")
# => ["oom :dias woc", "OOM :DIAS WOC"]
# Multi-Multiplexing... let me tell you...
p, [:downcase, :upcase], Reverse, [:reverse, Leet]).call("moo")
# => [["cow said: moo", "00m :d145 w0c"], ["COW SAID: MOO", "OOM :DIAS WOC"]]
Copy link

jmgarnier commented Sep 24, 2014

Interesting 😄 I much prefer the functional style compared to the OOP. Much more readable.

I have spent 5 minutes reading the code and I don't understand how the | magically works in:

result = pipable("moo") | Reverse | Leet | Mooify

Copy link

pcreux commented Sep 24, 2014

pipable("moo") defines the singleton method | on "moo". | will then define | on its arguments. The result will be a "pipable object" - it responds to |. It reads well, but I don't like defining singleton methods as it could overwrite existing behaviour (e.g. Array).

Copy link

vidarh commented Sep 25, 2014

If you don't want to use singleton methods, use a Delegator object with "|" defined, and wrap the object that's passed in. You'll still "overwrite" existing behaviour for something that receives the wrapped object, but anyone getting the unwrapped object will only see the real behaviour.

Copy link

douglascamata commented Sep 25, 2014

I don't feel the need for pipes in Ruby. Instead of building an structure that calls this:

I would build something that would be:

# or maybe, :highlight_mentions, :remove_signature, :html_false).result

Copy link

akitaonrails commented Sep 25, 2014

I agree with @douglascamata, instead of having an operator, why not just method chaining similarly to how Arel works in Rails nowadays?

Copy link

fcoury commented Sep 25, 2014

@akitaonrails I think the key difference is that method chaining is usually bound to an object of a given class, whereas the pipe objects are completely ignorant of who's sending the message in.

Copy link

petrachi commented Sep 26, 2014

I followed you on your ground, and tried to implement this myself. As I don't have much time, this is still a basic version, but what do you think of this : ?

Also here :

Copy link

baweaver commented Sep 30, 2014

Elixir pipes in Ruby feature request: - thoughts?

Copy link

kek commented Oct 1, 2014

I tried to do something similar in

Copy link

petrachi commented Oct 1, 2014

Honestly, I don't see the point in pipes. I mean, this is a nice demo of code, but my though is that the problem lies elsewhere, in modules used at wrapper.

In the firsts lines of this git, it's wrote (I shrink the quote)

# I want to write this:
# email.body | RemoveSignature
# instead of:

So, there is a RemoveSignature module somewhere, with a singleton method call. This is the problem you want to solve. And my guess is that the call you may want to make will be email.body.remove_signature

I think this can be achieved by safely monkey patch the String class, using the Refinements of ruby 2+.

Or, if you don't want to use that, you can extend the body variable with a module using definig instance methods, like this:

module RemoveSignature
  def remove_signature
    # code that remove signature, should modify self

class Email
  def body


Then, you will not have callings like, nor your 'pipe' need.

Copy link

justinwiley commented Oct 14, 2014

Nice implementation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment