Last active
June 12, 2018 17:08
-
-
Save pcreux/2f87847e5e4aad37db02 to your computer and use it in GitHub Desktop.
*nix has pipes, Elixir has pipes, Ruby deserves pipes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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"]] | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Nice implementation