Skip to content

Instantly share code, notes, and snippets.

@pcreux
Last active June 12, 2018 17:08
Show Gist options
  • Save pcreux/2f87847e5e4aad37db02 to your computer and use it in GitHub Desktop.
Save pcreux/2f87847e5e4aad37db02 to your computer and use it in GitHub Desktop.
*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:
#
# HighlightMentions.call(RemoveSignature.call(email.body)).html_safe
#
# Ugly implementation starts here...
def pipe_it(input, filter)
# multiplexed input!
if input.is_a? Array
return input.map { |input_item| pipe_it(input_item, filter) }
end
case filter
when Symbol
input.send(filter)
when Hash
method = filter.keys.first
arguments = Array(filter.values.first)
input.send(method, *arguments)
when Array
# multiplex!
filter.map { |filter_item| pipe_it(input, filter_item) }
else
filter.call(input)
end
end
class Pipeline
def initialize(*filters)
@filters = filters
end
attr_accessor :filters
def call(input)
filters.inject(input) do |input, filter|
pipe_it(input, filter)
end
end
end
def pipable(input)
input.define_singleton_method(:|) do |filter|
pipable pipe_it(input, filter)
end
input
end
def pipe(input, *pipeline)
Pipeline.new(*pipeline).call(input)
end
# 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 = Struct.new(:recipient) do
def call(input)
puts %|Tweeting "#{input}" to #{@recipient}!|
input
end
end
# 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 | TweetTo.new('@pcreux') | { delete: 'o' }
puts result
# => cw said: 00m
# 2 - Pipe without defining any `|` method
puts pipe("moo", Mooify, :upcase)
# => COW SAID: MOO
# 3 - Pipeline object
pipeline = Pipeline.new(Mooify, :downcase, { gsub: ["o", "a"] })
pipeline.filters << ->(input) { input.gsub("moo", "maow") }
puts pipeline.call("moo")
# => caw said: maa
pipeline.filters.reverse!
puts pipeline.call("moo")
# => Cow said: maaw
# ZOMG! Multiplexing!
# "moo" => Mooify => :downcase => Reverse
# => :upcase => Reverse
p Pipeline.new(Mooify, [:downcase, :upcase], Reverse).call("moo")
# => ["oom :dias woc", "OOM :DIAS WOC"]
# Multi-Multiplexing... let me tell you...
p Pipeline.new(Mooify, [:downcase, :upcase], Reverse, [:reverse, Leet]).call("moo")
# => [["cow said: moo", "00m :d145 w0c"], ["COW SAID: MOO", "OOM :DIAS WOC"]]
@justinwiley
Copy link

Nice implementation

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