lib/option_initializer.rb in option_initializer-1.3.0 vs lib/option_initializer.rb in option_initializer-1.5.0

- old
+ new

@@ -1,14 +1,13 @@ -require "option_initializer/version" +require 'option_initializer/version' +require 'set' module OptionInitializer class OptionInitializingTemplate attr_reader :options alias to_h options - const_set :VALIDATORS, [] - def initialize base, options, need_validation validate options if need_validation @base = base @options = options end @@ -40,14 +39,17 @@ validate opts self.class.new @base, @options.merge(opts), false end def validate hash - self.class.const_get(:VALIDATORS).each do |validator| - hash.each do |k, v| - validator.call k, v - end + avals, vals = [:ARG_VALIDATORS, :VALIDATORS].map { |s| + self.class.const_get(s) + } + hash.each do |k, v| + avals[k] && avals[k].call(v) + vals[k] && vals[k].call(v) + vals[nil] && vals[nil].call(k, v) end end end module MethodCallShortcut @@ -63,37 +65,44 @@ def validate_options options raise TypeError, "wrong argument type #{options.class} (expected Hash)" unless options.is_a?(Hash) - return if options.respond_to?(:option_validated?) - validators = self.class.const_get(:OptionInitializing).const_get(:VALIDATORS) - validators.each do |validator| - options.each do |k, v| - validator.call k, v - end + return if options.respond_to?(:option_validated?) && options.option_validated? + avals, vals = [:ARG_VALIDATORS, :VALIDATORS].map { |s| + self.class.const_get(:OptionInitializing).const_get(s) + } + options.each do |k, v| + avals[k] && avals[k].call(v) + vals[k] && vals[k].call(v) + vals[nil] && vals[nil].call(k, v) end options end def self.included base unless base.constants.map(&:to_sym).include?(:OptionInitializing) - base.const_set :OptionInitializing, OptionInitializingTemplate.dup + base.const_set :OptionInitializing, oi = OptionInitializingTemplate.dup + oi.class_eval do + const_set :VALIDATORS, {} + const_set :ARG_VALIDATORS, {} + end end base.class_eval do class << self [:option_initializer, :option_initializer!, :option_validator].each do |m| undef_method(m) if method_defined?(m) end end - def base.option_validator &block + def base.option_validator sym = nil, &block raise ArgumentError, "block must be given" unless block - raise ArgumentError, "invalid arity (expected: 2)" unless block.arity == 2 + a = sym ? 1 : 2 + raise ArgumentError, "invalid arity (expected: #{a})" unless block.arity == a oi = self.const_get(:OptionInitializing) - oi.const_get(:VALIDATORS).push block + oi.const_get(:VALIDATORS)[sym] = block end def base.option_initializer *syms oi = self.const_get(:OptionInitializing) @@ -101,20 +110,102 @@ case sym when Symbol, String arr << [sym.to_sym, 1] when Hash arr.concat sym.map { |k, v| - unless (v.is_a?(Fixnum) && v > 0) || (v.is_a?(Range) && v.begin > 0) || v == :block - raise ArgumentError, "invalid number of arguments specified for #{k}" + case v + when Fixnum + raise ArgumentError, "invalid number of arguments specified for #{k}" if v <= 0 + when Range + raise ArgumentError, "invalid number of arguments specified for #{k}" if v.begin < 0 + when Set + raise ArgumentError, "empty set of values specified for #{k}" if v.length == 0 + when Array + raise ArgumentError, "invalid option definition: `#{v}'" unless v.all? { |e| e.is_a?(Class) || e.is_a?(Set) } + when Class, :*, :& + # noop + else + raise ArgumentError, "invalid option definition: `#{v}'" end [k.to_sym, v] } else - raise ArgumentError, "invalid option specification" + raise ArgumentError, "invalid option definition" end } + # Setup validators + vals = oi.const_get(:ARG_VALIDATORS) + pairs.each do |pair| + sym, nargs = pair + case nargs + when :& + vals[sym] = proc { |v| + if !v.is_a?(Proc) + raise TypeError, "wrong argument type #{v.class} (expected Proc)" + end + } + when 1 + # good to go + vals.delete sym + when :* + vals[sym] = proc { |v| + if !v.is_a?(Array) + raise ArgumentError, "wrong number of arguments (1 for #{nargs})" + end + } + when Fixnum + vals[sym] = proc { |v| + if !v.is_a?(Array) + raise ArgumentError, "wrong number of arguments (1 for #{nargs})" + elsif nargs != v.length + raise ArgumentError, "wrong number of arguments (#{v.length} for #{nargs})" + end + } + when Range + vals[sym] = proc { |v| + if !v.is_a?(Array) + raise ArgumentError, "wrong number of arguments (1 for #{nargs})" + elsif !nargs.include?(v.length) + raise ArgumentError, "wrong number of arguments (#{v.length} for #{nargs})" + end + } + when Set + vals[sym] = proc { |v| + if !nargs.include?(v) + raise ArgumentError, "invalid option value: `#{v}' (expected one of #{nargs.to_a.inspect})" + end + } + when Array + vals[sym] = proc { |v| + if !v.is_a?(Array) + raise ArgumentError, "wrong number of arguments (1 for #{nargs.length})" + elsif nargs.length != v.length + raise ArgumentError, "wrong number of arguments (#{v.length} for #{nargs.length})" + else + v.zip(nargs).each do |ec| + e, c = ec + case c + when Class + raise TypeError, "wrong argument type #{e.class} (expected #{c})" unless e.is_a?(c) + when Set + unless c.include?(e) + raise ArgumentError, "invalid option value: `#{e}' (expected one of #{c.to_a.inspect})" + end + end + end + end + } + when Class + vals[sym] = proc { |v| + if !v.is_a?(nargs) + raise TypeError, "wrong argument type #{v.class} (expected #{nargs})" + end + } + end + end + # Class methods pairs.each do |pair| sym = pair.first self.class_eval do @@ -131,41 +222,28 @@ oi.class_eval do pairs.each do |pair| sym, nargs = pair undef_method(sym) if method_defined?(sym) define_method(sym) do |*v, &b| - case nargs - when :block - if b - if v.empty? - merge(sym => b) - else - raise ArgumentError, "only block expected" - end - else - raise ArgumentError, "block expected but not given" - end - when 1 - if b && v.empty? + if nargs == :& + if v.empty? merge(sym => b) - elsif b && !v.empty? - raise ArgumentError, "wrong number of arguments (#{v.length} for 0 when block given)" - elsif v.length == 1 - merge(sym => v.first) else - raise ArgumentError, "wrong number of arguments (#{v.length} for 1)" + raise ArgumentError, "wrong number of arguments (#{v.length} for 0)" end - when Range, Fixnum - if b - raise ArgumentError, "block not expected" - elsif (nargs.is_a?(Range) && !nargs.include?(v.length)) || - (nargs.is_a?(Fixnum) && nargs != v.length) - raise ArgumentError, "wrong number of arguments (#{v.length} for #{nargs})" + elsif b + raise ArgumentError, "block not expected" + else + case nargs + when 1, Class, Set + if v.length == 1 + merge(sym => v.first) + else + raise ArgumentError, "wrong number of arguments (#{v.length} for 1)" + end else merge(sym => v) end - else - raise ArgumentError, "invalid option specification" end end end end end