Module: HashKeywordArgs::Hash
- Defined in:
- lib/hash_keyword_args/hash.rb
Instance Method Summary collapse
-
#keyword_args(*args) ⇒ Object
given an argument hash and a description of acceptable keyword args and their default values, checks validity of the arguments and raises any errors, otherwise returns a new hash containing all arg values with defaults filled in as needed.
Instance Method Details
#keyword_args(*args) ⇒ Object
given an argument hash and a description of acceptable keyword args and their default values, checks validity of the arguments and raises any errors, otherwise returns a new hash containing all arg values with defaults filled in as needed.
args = hash.keyword_args(:a, :b, :c, # these are all optional args
:d => :optional, # same as listing it the arg up front
:e => :required, # raises an error if arg not provided
:f => "hello" # default value
:g => [:x,:y,:z] # valid values
:h => Integer # valid type
:i => :enumerable # expect/coerce an enumerable, check all items, default is []
:j => { :valid => Integer, :allow_nil => true} # Integer or nil
:j => { :valid => [:x,:y,:z], :default => :x, :required => true } # combo
)
by default this will raise an error if the hash contains any keys not listed. however, if :OTHERS is specified as a keyword arg, that test will be disabled and any other key/value pairs will be passed through.
Returns a Struct whose values are the
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/hash_keyword_args/hash.rb', line 27 def keyword_args(*args) argshash = args[-1].is_a?(Hash) ? args.pop : {} argshash = args.hashify(:optional).merge(argshash) others_OK = argshash.delete(:OTHERS) ret = {} # defaults, required, and checked required = [] check = {} argshash.each do |key, val| # construct fleshed-out attribute hash for all args attrs = case val when Hash val[:default] ||= [] if val[:enumerable] val when :required { :required => true } when :optional {} when :enumerable { :enumerable => true, :default => [] } when Array { :valid => val } when Class, Module { :valid => val } else { :default => val } end # extract required flag, validity checks, and default vlues from attribute hash required << key if attrs[:required] check[key] = case valid = attrs[:valid] when Enumerable [:one_of, valid] when Class, Module [:is_a, valid] else [:ok] end check[key][2] = [:allow_nil, :enumerable].select{|mod| attrs[mod]} ret[key] = attrs[:default] if attrs.include?(:default) end # check validity of keys unless others_OK or (others = self.keys - argshash.keys).empty? raise ArgumentError, "Invalid keyword arg#{others.length>1 && "s" or ""} #{others.collect{|a| a.inspect}.join(', ')}", caller end # process values, checking validity self.each do |key, val| code, valid, mods = check[key] mods ||= [] val = [ val ] unless mods.include?(:enumerable) and val.is_a?(Enumerable) ok = val.all? { |v| if mods.include?(:allow_nil) and v.nil? true else case code when nil then true # for OTHERS args, there's no code when :ok then true when :one_of then valid.include?(v) when :is_a then v.is_a?(valid) end end } val = val.first unless mods.include?(:enumerable) raise ArgumentError, "Invalid value for keyword arg #{key.inspect} => #{val.inspect}", caller unless ok ret[key] = val required.delete(key) end unless required.empty? raise ArgumentError, "Missing required keyword arg#{required.length>1 && "s" or ""} #{required.collect{|a| a.inspect}.join(', ')}", caller end (class << ret ; self ; end).class_eval do argshash.keys.each do |key| define_method(key) do self[key] end end end ret end |