Skip to content

Instantly share code, notes, and snippets.

@baweaver
Created October 4, 2018 22:21
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save baweaver/7f8cdc8cebed93799a2c64da088447c1 to your computer and use it in GitHub Desktop.
Save baweaver/7f8cdc8cebed93799a2c64da088447c1 to your computer and use it in GitHub Desktop.
def destructure(method_name)
# Intercept the original class
meta_klass = class << self; self end
# Save the original method as a proc
method_proc = method(method_name)
# We only want to do this for keyword argument type
# methods
unless method_proc.parameters.all? { |t, _| t == :key }
raise "Only works with keyword arguments"
end
# Extract the names from the arguments
arguments = method_proc.parameters.map(&:last)
# Wrap the original method call in a proc to intercept
# potential destructuring
destructure_proc = -> object {
# If the values are a hash, pass it through like
# normal keyword arguments
values = if object.is_a?(Hash)
object
# Otherwise, use the names of those keywords to extract
# (or rather destructure) values from the object passed
# in. Map this to a hash so we can pass it as such to
# the original method
else
arguments.map { |a| [a, object.public_send(a)] }.to_h
end
# Call that original method with either a plain hash
# or values extracted from an object by key name
method_proc.call(values)
}
# Then redefine the method where it lives
meta_klass.send(:define_method, method_name, destructure_proc)
end
require 'ostruct'
# Just prefix a method definition with `destructure`
destructure def adds(a: 1, b: 2)
a + b
end
=> :adds
# And we can now call it with an object:
adds(OpenStruct.new(a: 1, b: 3))
=> 4
# Or keywords:
adds(a: 1, b: 2)
=> 3
# Or a plain old hash: (though that's the same as above)
adds({a: 1, b: 2})
=> 3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment