class Foo
extends HashMethods
def bar(a, b=1)
[a, b]
end
instance_hash_method :bar
define_hash_method :spam, :a => 1, :b => 2 do |a, b|
[a, b]
end
end
Foo.new.bar(:a => 1) #=> [1, 1]
Foo.new.bar(:a => 1, :b => 2) #=> [1, 2]
Foo.new.spam #=> [1, 2]
Foo.new.spam(:b => 1) #=> [1, 1]
Foo.new.spam(:a => 2, :b => 1) #=> [2, 1]
Created
May 23, 2012 09:42
-
-
Save barbuza/2774280 to your computer and use it in GitHub Desktop.
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
module HashMethods | |
def instance_hash_method(method_name) | |
required_args = [] | |
optional_args = [] | |
original_method = instance_method(method_name) | |
original_method.parameters.each do |type, name| | |
if type == :req | |
required_args.push name | |
elsif type == :opt | |
optional_args.push name | |
else | |
raise ArgumentError.new("rest arguments are not supported with `hash_instance_method`") | |
end | |
end | |
allowed_args = required_args + optional_args | |
define_method(method_name) do |params={}| | |
call_args = [] | |
params.each_key do |name| | |
raise ArgumentError.new("#{name} is not allowed arg for this method") unless allowed_args.include? name | |
end | |
required_args.each do |name| | |
raise ArgumentError.new("#{name} is required") unless params.include? name | |
call_args.push params.delete(name) | |
end | |
optional_args.each do |name| | |
break unless params.include? name | |
call_args.push params.delete(name) | |
end | |
raise ArgumentError.new("params #{params.keys.join ', '} can not be applied due to optional args order") unless params.empty? | |
original_method.bind(self).call *call_args | |
end | |
end | |
def define_hash_method(method_name, defaults={}, &blk) | |
method_args = [] | |
blk.parameters.each do |type, name| | |
if type == :rest | |
raise ArgumentError.new("rest arguments are not supported with `hash_method`") | |
else | |
method_args.push name | |
end | |
end | |
defaults.each_key do |name| | |
raise ArgumentError.new("default value specified for unused argument #{name}") unless method_args.include? name | |
end | |
define_method(method_name) do |params={}| | |
params = params.clone | |
call_args = [] | |
method_args.each do |name| | |
if params.include? name | |
call_args.push params.delete(name) | |
elsif defaults.include? name | |
call_args.push defaults[name] | |
else | |
raise ArgumentError.new("#{name} is required") | |
end | |
end | |
raise ArgumentError.new("#{params.keys.first} is not allowed arg for this method") unless params.empty? | |
instance_exec *call_args, &blk | |
end | |
end | |
end |
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
class HashStruct | |
def self.new(*args) | |
required_args = [] | |
optional_args = {} | |
for arg in args | |
if arg.is_a? Symbol | |
required_args.push arg | |
elsif arg.is_a? Hash | |
optional_args.merge! arg | |
end | |
end | |
raise ArgumentError.new("there are intersections between required and optional params") unless (required_args & optional_args.keys).empty? | |
Class.new do |m| | |
(required_args + optional_args.keys).each do |name| | |
attr_reader name | |
end | |
define_method :initialize do |params={}| | |
required = required_args.clone | |
required_count = required.size | |
optional = optional_args.keys | |
allowed_params = required + optional | |
until params.empty? | |
name, value = params.shift | |
if required.include? name | |
required.delete name | |
elsif optional.include? name | |
optional.delete name | |
else | |
raise ArgumentError.new("unknown param #{name}, allowed params are: #{allowed_params.join ', '}") | |
end | |
instance_variable_set "@#{name}", value | |
end | |
raise ArgumentError.new "missing required params: #{required.join ', '}" unless required.empty? | |
optional.each do |name| | |
instance_variable_set "@#{name}", optional_args[name] | |
end | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment