class Roda::RodaPlugins::TypecastParams::Params

  1. lib/roda/plugins/typecast_params.rb
Superclass: Object

Class handling conversion of submitted parameters to desired types.

Public Class methods

handle_type(type, opts=OPTS, &block)

Handle conversions for the given type using the given block. For a type named foo, this will create the following methods:

  • foo(key, default=nil)

  • foo!(key)

  • convert_foo(value) # private

  • _convert_array_foo(value) # private

  • _invalid_value_message_for_foo # private

  • _max_input_bytesize_for_foo # private

This method is used to define all type conversions, even the built in ones. It can be called in subclasses to setup subclass-specific types.

[show source]
    # File lib/roda/plugins/typecast_params.rb
457 def self.handle_type(type, opts=OPTS, &block)
458   convert_meth = :"convert_#{type}"
459   define_method(convert_meth, &block)
460 
461   convert_array_meth = :"_convert_array_#{type}"
462   define_method(convert_array_meth) do |v|
463     raise Error, "expected array but received #{v.inspect}" unless v.is_a?(Array)
464     v.map! do |val|
465       check_allowed_bytesize(val, _max_input_bytesize_for(type))
466       check_null_byte(val)
467       send(convert_meth, val)
468     end
469   end
470 
471   private convert_meth, convert_array_meth
472 
473   invalid_value_message(type, opts[:invalid_value_message])
474   max_input_bytesize(type, opts[:max_input_bytesize])
475 
476   define_method(type) do |key, default=nil|
477     process_arg(convert_meth, key, default, type) if require_hash!
478   end
479 
480   define_method(:"#{type}!") do |key|
481     send(type, key, CHECK_NIL)
482   end
483 end
invalid_value_message(type, message)

Set the invalid message for the given type.

[show source]
    # File lib/roda/plugins/typecast_params.rb
486 def self.invalid_value_message(type, message)
487   invalid_value_message_meth = :"_invalid_value_message_for_#{type}"
488   define_method(invalid_value_message_meth){message}
489   private invalid_value_message_meth
490   alias_method invalid_value_message_meth, invalid_value_message_meth
491 end
max_input_bytesize(type, bytesize)

Set the maximum input bytesize for the given type.

[show source]
    # File lib/roda/plugins/typecast_params.rb
494 def self.max_input_bytesize(type, bytesize)
495   max_input_bytesize_meth = :"_max_input_bytesize_for_#{type}"
496   define_method(max_input_bytesize_meth){bytesize}
497   private max_input_bytesize_meth
498   alias_method max_input_bytesize_meth, max_input_bytesize_meth
499 end
nest(obj, nesting)

Create a new instance with the given object and nesting level. obj should be an array or hash, and nesting should be an array. Designed for internal use, should not be called by external code.

[show source]
    # File lib/roda/plugins/typecast_params.rb
505 def self.nest(obj, nesting)
506   v = allocate
507   v.instance_variable_set(:@nesting, nesting)
508   v.send(:initialize, obj)
509   v
510 end
new(obj)

Set the object used for converting. Conversion methods will convert members of the passed object.

[show source]
    # File lib/roda/plugins/typecast_params.rb
599 def initialize(obj)
600   case @obj = obj
601   when Hash, Array
602     # nothing
603   else
604     if @nesting
605       handle_error(nil, (@obj.nil? ? :missing : :invalid_type), "value of #{param_name(nil)} parameter not an array or hash: #{obj.inspect}", true)
606     else
607       handle_error(nil, :invalid_type, "parameters given not an array or hash: #{obj.inspect}", true)
608     end
609   end
610 end

Public Instance methods

[](key)

Return a new Params instance for the given key. The value of key should be an array if key is an integer, or hash otherwise.

[show source]
    # File lib/roda/plugins/typecast_params.rb
629 def [](key)
630   @subs ||= {}
631   if sub = @subs[key]
632     return sub
633   end
634 
635   if @obj.is_a?(Array)
636     unless key.is_a?(Integer)
637       handle_error(key, :invalid_type, "invalid use of non-integer key for accessing array: #{key.inspect}", true)
638     end
639   else
640     if key.is_a?(Integer)
641       handle_error(key, :invalid_type, "invalid use of integer key for accessing hash: #{key}", true)
642     end
643   end
644 
645   v = @obj[key]
646   v = yield if v.nil? && defined?(yield)
647 
648   begin
649     sub = self.class.nest(v, Array(@nesting) + [key])
650   rescue => e
651     handle_error(key, :invalid_type, e, true)
652   end
653 
654   @subs[key] = sub
655   sub.sub_capture(@capture, @symbolize, @skip_missing)
656   sub
657 end
array(type, key, default=nil)

Convert the value of key to an array of values of the given type. If default is given, any nil values in the array are replaced with default. If key is an array then this returns an array of arrays, one for each respective value of key. If there is no value for key, nil is returned instead of an array.

[show source]
    # File lib/roda/plugins/typecast_params.rb
766 def array(type, key, default=nil)
767   meth = :"_convert_array_#{type}"
768   raise ProgrammerError, "no typecast_params type registered for #{type.inspect}" unless respond_to?(meth, true)
769   process_arg(meth, key, default, type) if require_hash!
770 end
array!(type, key, default=nil)

Call array with the type, key, and default, but if the return value is nil or any value in the returned array is nil, raise an Error.

[show source]
    # File lib/roda/plugins/typecast_params.rb
774 def array!(type, key, default=nil)
775   v = array(type, key, default)
776 
777   if key.is_a?(Array)
778     key.zip(v).each do |k, arr|
779       check_array!(k, arr)
780     end
781   else
782     check_array!(key, v)
783   end
784 
785   v
786 end
convert!(keys=nil, opts=OPTS)

Captures conversions inside the given block, and returns a hash of all conversions, including conversions of subkeys. keys should be an array of subkeys to access, or nil to convert the current object. If keys is given as a hash, it is used as the options hash. Options:

:raise

If set to false, do not raise errors for missing keys

:skip_missing

If set to true, does not store values if the key is not present in the params.

:symbolize

Convert any string keys in the resulting hash and for any conversions below

[show source]
    # File lib/roda/plugins/typecast_params.rb
675 def convert!(keys=nil, opts=OPTS)
676   if keys.is_a?(Hash)
677     opts = keys
678     keys = nil
679   end
680 
681   _capture!(:nested_params, opts) do
682     if sub = subkey(Array(keys).dup, opts.fetch(:raise, true))
683       yield sub
684     end
685   end
686 end
convert_each!(opts=OPTS, &block)

Runs conversions similar to convert! for each key specified by the :keys option. If :keys option is not given and the object is an array, runs conversions for all entries in the array. If the :keys option is not given and the object is a Hash with string keys ‘0’, ‘1’, …, ‘N’ (with no skipped keys), runs conversions for all entries in the hash. If :keys option is a Proc or a Method, calls the proc/method with the current object, which should return an array of keys to use. Supports options given to convert!, and this additional option:

:keys

The keys to extract from the object. If a proc or method, calls the value with the current object, which should return the array of keys to use.

[show source]
    # File lib/roda/plugins/typecast_params.rb
699 def convert_each!(opts=OPTS, &block)
700   np = !@capture
701 
702   _capture!(nil, opts) do
703     case keys = opts[:keys]
704     when nil
705       keys = (0...@obj.length)
706 
707       valid = if @obj.is_a?(Array)
708         true
709       else
710         keys = keys.map(&:to_s)
711         keys.all?{|k| @obj.has_key?(k)}
712       end
713 
714       unless valid
715         handle_error(nil, :invalid_type, "convert_each! called on object not an array or hash with keys '0'..'N'")
716         next 
717       end
718     when Array
719       # nothing to do
720     when Proc, Method
721       keys = keys.call(@obj)
722     else
723       raise ProgrammerError, "unsupported convert_each! :keys option: #{keys.inspect}"
724     end
725 
726     keys.map do |i|
727       begin
728         if v = subkey([i], opts.fetch(:raise, true))
729           yield v
730           v.nested_params if np 
731         end
732       rescue => e
733         handle_error(i, :invalid_type, e)
734       end
735     end
736   end
737 end
dig(type, *nest, key)

Convert values nested under the current obj. Traverses the current object using nest, then converts key on that object using type:

tp.dig(:pos_int, 'foo')               # tp.pos_int('foo')
tp.dig(:pos_int, 'foo', 'bar')        # tp['foo'].pos_int('bar')
tp.dig(:pos_int, 'foo', 'bar', 'baz') # tp['foo']['bar'].pos_int('baz')

Returns nil if any of the values are not present or not the expected type. If the nest path results in an object that is not an array or hash, then raises an Error.

You can use dig to get access to nested arrays by using :array or :array! as the first argument and providing the type in the second argument:

tp.dig(:array, :pos_int, 'foo', 'bar', 'baz')  # tp['foo']['bar'].array(:pos_int, 'baz')
[show source]
    # File lib/roda/plugins/typecast_params.rb
753 def dig(type, *nest, key)
754   _dig(false, type, nest, key)
755 end
dig!(type, *nest, key)

Similar to dig, but raises an Error instead of returning nil if no value is found.

[show source]
    # File lib/roda/plugins/typecast_params.rb
758 def dig!(type, *nest, key)
759   _dig(true, type, nest, key)
760 end
fetch(key)

Return the nested value for key. If there is no nested_value for key, calls the block to return the value, or returns nil if there is no block given.

[show source]
    # File lib/roda/plugins/typecast_params.rb
661 def fetch(key)
662   send(:[], key){return(yield if defined?(yield))}
663 end
present?(key)

If key is a String Return whether the key is present in the object,

[show source]
    # File lib/roda/plugins/typecast_params.rb
613 def present?(key)
614   case key
615   when String
616     !any(key).nil?
617   when Array
618     key.all? do |k|
619       raise ProgrammerError, "non-String element in array argument passed to present?: #{k.inspect}" unless k.is_a?(String)
620       !any(k).nil?
621     end
622   else
623     raise ProgrammerError, "unexpected argument passed to present?: #{key.inspect}"
624   end
625 end

Protected Instance methods

nested_params()

Recursively descendent into all known subkeys and get the converted params from each.

[show source]
    # File lib/roda/plugins/typecast_params.rb
791 def nested_params
792   return @nested_params if @nested_params
793 
794   params = @params
795 
796   if @subs
797     @subs.each do |key, v|
798       if key.is_a?(String) && symbolize?
799         key = key.to_sym
800       end
801       params[key] = v.nested_params
802     end
803   end
804   
805   params
806 end
sub_capture(capture, symbolize, skip_missing)

Inherit given capturing and symbolize setting from parent object.

[show source]
    # File lib/roda/plugins/typecast_params.rb
845 def sub_capture(capture, symbolize, skip_missing)
846   if @capture = capture
847     @symbolize = symbolize
848     @skip_missing = skip_missing
849     @params = @obj.class.new
850   end
851 end
subkey(keys, do_raise)

Recursive method to get subkeys.

[show source]
    # File lib/roda/plugins/typecast_params.rb
809 def subkey(keys, do_raise)
810   unless key = keys.shift
811     return self
812   end
813 
814   reason = :invalid_type
815 
816   case key
817   when String
818     unless @obj.is_a?(Hash)
819       raise Error, "parameter #{param_name(nil)} is not a hash" if do_raise
820       return
821     end
822     present = !@obj[key].nil?
823   when Integer
824     unless @obj.is_a?(Array)
825       raise Error, "parameter #{param_name(nil)} is not an array" if do_raise
826       return
827     end
828     present = key < @obj.length
829   else
830     raise ProgrammerError, "invalid argument used to traverse parameters: #{key.inspect}"
831   end
832 
833   unless present
834     reason = :missing
835     raise Error, "parameter #{param_name(key)} is not present" if do_raise
836     return
837   end
838 
839   self[key].subkey(keys, do_raise)
840 rescue => e
841   handle_error(key, reason, e)
842 end