lib/abstractivator/proc_ext.rb in abstractivator-0.4.0 vs lib/abstractivator/proc_ext.rb in abstractivator-0.4.1
- old
+ new
@@ -1,12 +1,13 @@
require 'abstractivator/enumerable_ext'
+require 'abstractivator/array_ext'
module MethodAndProcExtensions
# returns a version of the procedure that accepts any number of arguments
def loosen_args
- proc do |*args, &block|
- Proc.loose_call(self, args, &block)
+ proc do |*args, **kws, &block|
+ Proc.loose_call(self, args, kws, &block)
end
end
end
class Proc
@@ -52,14 +53,34 @@
end
# tries to coerce x into a procedure, then calls it with
# the given argument list.
# If x cannot be coerced into a procedure, returns x.
- def self.loose_call(x, args, &block)
+ def self.loose_call(x, args, kws={}, &block)
x = x.to_proc if x.respond_to?(:to_proc)
x.callable? or return x
- args = args.take(x.arity).pad_right(x.arity) if x.arity >= 0
- x.call(*args, &block)
+ arg_types = x.parameters.map(&:first)
+ # customize args
+ req_arity = arg_types.select{|x| x == :req}.size
+ total_arity = req_arity + arg_types.select{|x| x == :opt}.size
+ accepts_arg_splat = arg_types.include?(:rest)
+ unless accepts_arg_splat
+ args = args.take(total_arity).pad_right(req_arity)
+ end
+ # customize keywords
+ accepts_kw_splat = arg_types.include?(:keyrest)
+ unless accepts_kw_splat
+ opt_key_names = x.parameters.select{|(type, name)| type == :key && !name.nil?}.map(&:value)
+ req_key_names = x.parameters.select{|(type, name)| type == :keyreq && !name.nil?}.map(&:value)
+ all_key_names = opt_key_names + req_key_names
+ padding = req_key_names.hash_map{nil}
+ kws = padding.merge(kws.select{|k| all_key_names.include?(k)})
+ end
+ if kws.any?
+ x.call(*args, **kws, &block)
+ else
+ x.call(*args, &block)
+ end
end
end
class Method
include MethodAndProcExtensions