lib/matchete.rb in matchete-0.0.1 vs lib/matchete.rb in matchete-0.0.2
- old
+ new
@@ -1,84 +1,112 @@
require 'set'
+require_relative 'matchete/exceptions'
module Matchete
- class NotResolvedError < StandardError
- end
-
def self.included(klass)
klass.extend ClassMethods
- klass.instance_variable_set "@functions", {}
- klass.instance_variable_set "@default_functions", {}
+ klass.instance_variable_set "@methods", {}
+ klass.instance_variable_set "@default_methods", {}
end
Any = -> (x) { true }
None = -> (x) { false }
module ClassMethods
- def on(*guards, function)
- @functions[function] ||= []
- @functions[function] << [guards, instance_method(function)]
- convert_to_matcher function
+ def on(*args, **kwargs)
+ if kwargs.count.zero?
+ *guard_args, method_name = args
+ guard_kwargs = {}
+ else
+ method_name = kwargs[:method]
+ kwargs.delete :method
+ guard_args = args
+ guard_kwargs = kwargs
+ end
+ @methods[method_name] ||= []
+ @methods[method_name] << [guard_args, guard_kwargs, instance_method(method_name)]
+ convert_to_matcher method_name
end
def default(method_name)
- @default_functions[method_name] = instance_method(method_name)
+ @default_methods[method_name] = instance_method(method_name)
convert_to_matcher method_name
end
- def duck(*method_names)
+ def supporting(*method_names)
-> object do
method_names.all? do |method_name|
object.respond_to? method_name
end
end
end
- def convert_to_matcher(function)
- define_method(function) do |*args|
- guards = self.class.instance_variable_get('@functions')[function].find do |guards, _|
- self.class.match_guards guards, args
- end
-
- handler = if guards.nil?
- default_method = self.class.instance_variable_get('@default_functions')[function]
- if default_method
- default_method
- else
- raise NotResolvedError.new("not resolved #{function} with #{args}")
- end
- else
- guards[1]
- end
-
- handler.bind(self).call *args
+ def convert_to_matcher(method_name)
+ define_method(method_name) do |*args, **kwargs|
+ call_overloaded(method_name, args: args, kwargs: kwargs)
end
end
-
- def match_guards(guards, args)
- guards.zip(args).all? do |guard, arg|
- match_guard guard, arg
+ end
+
+ def call_overloaded(method_name, args: [], kwargs: {})
+ handler = find_handler(method_name, args, kwargs)
+ if handler.parameters.any? do |type, _|
+ [:key, :keyrest, :keyreq].include? type
end
+ handler.bind(self).call *args, **kwargs
+ else
+ handler.bind(self).call *args
end
+ #insane workaround, because if you have
+ #def z(f);end
+ #and you call it like that
+ #a(2, **{e: 4})
+ #it raises wrong number of arguments (2 for 1)
+ #clean later
+ end
- def match_guard(guard, arg)
- case guard
- when Module
- arg.is_a? guard
- when Symbol
- send guard, arg
- when Proc
- guard.call arg
- when String
- guard == arg
- when Regexp
- arg.is_a? String and guard.match arg
- when Array
- arg.is_a?(Array) and
- guard.zip(arg).all? { |child_guard, child| match_guard child_guard, child }
- else
- guard == arg
+ def find_handler(method_name, args, kwargs)
+ guards = self.class.instance_variable_get('@methods')[method_name].find do |guard_args, guard_kwargs, _|
+ match_guards guard_args, guard_kwargs, args, kwargs
+ end
+
+ if guards.nil?
+ default_method = self.class.instance_variable_get('@default_methods')[method_name]
+ if default_method
+ default_method
+ else
+ raise NotResolvedError.new("No matching #{method_name} method for args #{args}")
end
+ else
+ guards[2]
+ end
+ end
+
+ def match_guards(guard_args, guard_kwargs, args, kwargs)
+ return false if guard_args.count != args.count || guard_kwargs.count != kwargs.count
+ guard_args.zip(args).all? do |guard, arg|
+ match_guard guard, arg
+ end and
+ guard_kwargs.all? do |label, guard|
+ match_guard guard, kwargs[label]
+ end
+ end
+
+ def match_guard(guard, arg)
+ case guard
+ when Module
+ arg.is_a? guard
+ when Symbol
+ send guard, arg
+ when Proc
+ guard.call arg
+ when Regexp
+ arg.is_a? String and guard.match arg
+ when Array
+ arg.is_a?(Array) and
+ guard.zip(arg).all? { |child_guard, child| match_guard child_guard, child }
+ else
+ guard == arg
end
end
end