Created
December 18, 2022 17:48
-
-
Save kaspth/402e8c71e33a963eb4b39df2bad2b4fe to your computer and use it in GitHub Desktop.
A half written-up implementation of progressively enhancing an options hash through method chaining on a context object.
This file contains hidden or 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
class Method::Chain | |
def initialize(descriptor, **options) | |
@descriptor, @options = descriptor, options | |
end | |
def self.define(context, *methods, &block) | |
instance = new Definition.new(&block) | |
instance.apply context, *methods | |
instance | |
end | |
def apply(context, *methods) | |
context.const_set :ChainableMethods, Module.new { | |
source = methods.map do |name| | |
<<~RUBY | |
def #{name}(*arguments, **options, &block) | |
chain.handle(*arguments, **options, &block) do |final_options| | |
final_options.merge! options | |
super(*arguments, **final_options, &block) | |
end | |
end | |
RUBY | |
end | |
class_eval source.join("\n\n"), __FILE__, __LINE__ + 1 | |
} | |
end | |
class Definition | |
DEFAULT_TERMINATOR = ->(_, arguments, options, _) { arguments.any? || options.any? } | |
def initialize(&) | |
@terminator, @values = DEFAULT_TERMINATOR, {} | |
instance_eval(&) | |
end | |
def terminator(value = nil, &block) | |
@terminator = value || block | |
end | |
def self.store(name, key: name, value: name) | |
values[name] = { key:, value: } | |
self.class.alias_method name, :exec | |
end | |
def finalize(options = nil) | |
@block.call options ? options.merge @options : @options | |
end | |
private | |
def exec(*arguments, **options, &block) | |
assign __callee__ | |
if @terminator.call(__callee__, arguments, options, &block) | |
finalize options | |
else | |
self | |
end | |
end | |
def assign(name) | |
hash = values.fetch(name) | |
@options.store hash[:key], hash[:value] | |
end | |
end | |
end | |
Method::Chain.define self, :get do | |
store :json, key: :as | |
store :xhr, value: true | |
# terminator ->(_, arguments, options, &block) { arguments.any? || options.any? } | |
end | |
# Each get call kicks off a new chain, which terminates when an argument is passed | |
get.json root_url | |
get.json.xhr root_url | |
# The original `get` is none the wiser | |
def get(...) | |
request(:get, ...) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment