lib/constrain.rb in constrain-0.2.1 vs lib/constrain.rb in constrain-0.2.2

- old
+ new

@@ -1,7 +1,21 @@ require "constrain/version" +module Foo + module InstanceMethods + def bar1 + 'bar1' + end + end + + module ClassMethods + def bar2 + 'bar2' + end + end +end + module Constrain # Raised on any error class Error < StandardError; end # Raised if types doesn't match a class expression @@ -9,51 +23,86 @@ def initialize(value, exprs, msg = nil, unwind: 0) super msg || "Expected #{value.inspect} to match #{Constrain.fmt_exprs(exprs)}" end end + def self.included base + base.extend ClassMethods + end + + # See Constrain.constrain + def constrain(value, *exprs) + Constrain.do_constrain(value, *exprs) + end + + def constrain?(value, expr) + Constrain.do_constrain?(value, expr) + end + # :call-seq: # constrain(value, *class-expressions, unwind: 0) # # Check that value matches one of the class expressions. Raises a # Constrain::Error if the expression is invalid and a Constrain::TypeError if - # the value doesn't match. The exception's backtrace skips :unwind number of entries - def constrain(value, *exprs) #, unwind: 0) + # the value doesn't match. The exception's backtrace skips :unwind number of + # entries + def self.constrain(value, *exprs) + do_constrain(value, *exprs) + end + + # Return true if the value matches the class expression. Raises a + # Constrain::Error if the expression is invalid + # + # TODO: Allow *exprs + def self.constrain?(value, expr) + do_constrain?(value, expr) + end + + module ClassMethods + # See Constrain.constrain + def constrain(*args) Constrain.do_constrain(*args) end + + # See Constrain.constrain? + def constrain?(*args) Constrain.do_constrain?(*args) end + end + + # unwind is automatically incremented by one because ::do_constrain is always + # called from one of the other constrain methods + # + def self.do_constrain(value, *exprs) if exprs.last.is_a?(Hash) - unwind = exprs.last.delete(:unwind) || 0 + unwind = (exprs.last.delete(:unwind) || 0) + 1 !exprs.last.empty? or exprs.pop else - unwind = 0 + unwind = 1 end msg = exprs.pop if exprs.last.is_a?(String) begin !exprs.empty? or raise Error, "Empty class expression" - exprs.any? { |expr| Constrain.constrain?(value, expr) } or + exprs.any? { |expr| Constrain.do_constrain?(value, expr) } or raise TypeError.new(value, exprs, msg, unwind: unwind) rescue Error => ex ex.set_backtrace(caller[1 + unwind..-1]) raise end value end - # Return true if the value matches the class expression. Raises a - # Constrain::Error if the expression is invalid - def self.constrain?(value, expr) + def self.do_constrain?(value, expr) case expr when Class, Module value.is_a?(expr) when Array !expr.empty? or raise Error, "Empty array" - value.is_a?(Array) && value.all? { |elem| expr.any? { |e| constrain?(elem, e) } } + value.is_a?(Array) && value.all? { |elem| expr.any? { |e| Constrain.constrain?(elem, e) } } when Hash value.is_a?(Hash) && value.all? { |key, value| expr.any? { |key_expr, value_expr| [[key, key_expr], [value, value_expr]].all? { |value, expr| if expr.is_a?(Array) && (expr.size > 1 || expr.first.is_a?(Array)) - expr.any? { |e| constrain?(value, e) } + expr.any? { |e| Constrain.do_constrain?(value, e) } else - constrain?(value, expr) + Constrain.constrain?(value, expr) end } } } when Proc