Last active
April 8, 2018 02:17
-
-
Save bkerley/754df43c98e116e82003 to your computer and use it in GitHub Desktop.
elixir-style function chaining that is also awful!
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
require 'pp' | |
class Object | |
def mutate | |
Mutagen.new(self) | |
end | |
end | |
class Mutagen < BasicObject | |
def initialize(collection) | |
@collection = collection | |
end | |
def method_missing(method_name, *args, &block) | |
return const_method_missing(method_name) if probably_constant(method_name) | |
instance_method_missing(method_name, *args, &block) | |
end | |
private | |
def const_method_missing(method_name) | |
const_callee = ::Kernel.const_get method_name | |
::ConstMutation.new const_callee, @collection | |
end | |
def instance_method_missing(method_name, *args, &block) | |
callee = @collection | |
::SelfMutation.new(@collection, method_name, args, block).invoke | |
end | |
def probably_constant(method_name) | |
method_name_start = method_name.to_s.chars.first | |
is_uppercase = method_name_start.upcase == method_name_start | |
end | |
end | |
class SelfMutation < BasicObject | |
def initialize(collection, method_name, args, block) | |
@collection = collection | |
@method_name = method_name | |
@args = args | |
@block = block | |
end | |
def invoke | |
::Mutagen.new @collection.send(@method_name, *@args, &@block) | |
end | |
end | |
class ConstMutation < BasicObject | |
def initialize(callee, collection) | |
@callee = callee | |
@collection = collection | |
end | |
def method_missing(method_name, *args, &block) | |
returned = @callee.send method_name, @collection, *args, &block | |
::Mutagen.new returned | |
end | |
end | |
module Manipulator | |
def self.filter(collection, block) | |
collection.select &block | |
end | |
end | |
module Printer | |
def self.pretty_print(collection) | |
pp collection | |
end | |
end | |
pp @collection = (1..20).to_a | |
@collection.mutate | |
.Manipulator.filter( ->(x) { x.even? } ) | |
.shuffle | |
.take(5) | |
.Printer.pretty_print | |
"steve".mutate | |
.upcase | |
.chars | |
.to_a | |
.Manipulator.filter( ->(x) { x.ord > 'F'.ord}) | |
.join | |
.Printer.pretty_print | |
# visualize: | |
# collection | |
# |> Manipulator.filter(fn(x) -> is_even(x) end) | |
# |> Printer.pretty_print |
@akitaonrails, @bkerley, have you seen this trick with >->
'operator' https://github.com/txus/kleisli
;)
# check out this more verbose, but explicit style
CM("hello http:///www.google.com world")
|-> text { URI.extract(text) }
.first
|-> uri { URI.parse(uri) }
|-> url { open(url) }
.read
|-> html { HTML.parse(html) }
.css("h1")
.first
.text
.strip
It feels like current solution with .
is not very friendly to method's like Array(42), URI("http://foo.com/")
, etc...
I wonder if single argument functions (with unnamed arguments) are too simple?
Good DSLs are pretty often chainable out of the box without pipelines -- but 'connecting' others sure sounds very interesting.
Thank you for sharing!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Would you mind if I borrow your const_method_missing idea and add it to my chainable_methods gem? Or if you want to send a PR with that I'd be thrilled :-)