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

Set the invalid message for the given type.

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

Set the maximum input bytesize for the given type.

[show source]
    # File lib/roda/plugins/typecast_params.rb
496 def self.max_input_bytesize(type, bytesize)
497   max_input_bytesize_meth = :"_max_input_bytesize_for_#{type}"
498   define_method(max_input_bytesize_meth){bytesize}
499   private max_input_bytesize_meth
500   alias_method max_input_bytesize_meth, max_input_bytesize_meth
501 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
507 def self.nest(obj, nesting)
508   v = allocate
509   v.instance_variable_set(:@nesting, nesting)
510   v.send(:initialize, obj)
511   v
512 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
601 def initialize(obj)
602   case @obj = obj
603   when Hash, Array
604     # nothing
605   else
606     if @nesting
607       handle_error(nil, (@obj.nil? ? :missing : :invalid_type), "value of #{param_name(nil)} parameter not an array or hash: #{obj.inspect}", true)
608     else
609       handle_error(nil, :invalid_type, "parameters given not an array or hash: #{obj.inspect}", true)
610     end
611   end
612 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
631 def [](key)
632   @subs ||= {}
633   if sub = @subs[key]
634     return sub
635   end
636 
637   if @obj.is_a?(Array)
638     unless key.is_a?(Integer)
639       handle_error(key, :invalid_type, "invalid use of non-integer key for accessing array: #{key.inspect}", true)
640     end
641   else
642     if key.is_a?(Integer)
643       handle_error(key, :invalid_type, "invalid use of integer key for accessing hash: #{key}", true)
644     end
645   end
646 
647   v = @obj[key]
648   v = yield if v.nil? && defined?(yield)
649 
650   begin
651     sub = self.class.nest(v, Array(@nesting) + [key])
652   rescue => e
653     handle_error(key, :invalid_type, e, true)
654   end
655 
656   @subs[key] = sub
657   sub.sub_capture(@capture, @symbolize, @skip_missing)
658   sub
659 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
768 def array(type, key, default=nil)
769   meth = :"_convert_array_#{type}"
770   raise ProgrammerError, "no typecast_params type registered for #{type.inspect}" unless respond_to?(meth, true)
771   process_arg(meth, key, default, type) if require_hash!
772 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
776 def array!(type, key, default=nil)
777   v = array(type, key, default)
778 
779   if key.is_a?(Array)
780     key.zip(v).each do |k, arr|
781       check_array!(k, arr)
782     end
783   else
784     check_array!(key, v)
785   end
786 
787   v
788 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
677 def convert!(keys=nil, opts=OPTS)
678   if keys.is_a?(Hash)
679     opts = keys
680     keys = nil
681   end
682 
683   _capture!(:nested_params, opts) do
684     if sub = subkey(Array(keys).dup, opts.fetch(:raise, true))
685       yield sub
686     end
687   end
688 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
701 def convert_each!(opts=OPTS, &block)
702   np = !@capture
703 
704   _capture!(nil, opts) do
705     case keys = opts[:keys]
706     when nil
707       keys = (0...@obj.length)
708 
709       valid = if @obj.is_a?(Array)
710         true
711       else
712         keys = keys.map(&:to_s)
713         keys.all?{|k| @obj.has_key?(k)}
714       end
715 
716       unless valid
717         handle_error(nil, :invalid_type, "convert_each! called on object not an array or hash with keys '0'..'N'")
718         next 
719       end
720     when Array
721       # nothing to do
722     when Proc, Method
723       keys = keys.call(@obj)
724     else
725       raise ProgrammerError, "unsupported convert_each! :keys option: #{keys.inspect}"
726     end
727 
728     keys.map do |i|
729       begin
730         if v = subkey([i], opts.fetch(:raise, true))
731           yield v
732           v.nested_params if np 
733         end
734       rescue => e
735         handle_error(i, :invalid_type, e)
736       end
737     end
738   end
739 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
755 def dig(type, *nest, key)
756   _dig(false, type, nest, key)
757 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
760 def dig!(type, *nest, key)
761   _dig(true, type, nest, key)
762 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
663 def fetch(key)
664   send(:[], key){return(yield if defined?(yield))}
665 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
615 def present?(key)
616   case key
617   when String
618     !any(key).nil?
619   when Array
620     key.all? do |k|
621       raise ProgrammerError, "non-String element in array argument passed to present?: #{k.inspect}" unless k.is_a?(String)
622       !any(k).nil?
623     end
624   else
625     raise ProgrammerError, "unexpected argument passed to present?: #{key.inspect}"
626   end
627 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
793 def nested_params
794   return @nested_params if @nested_params
795 
796   params = @params
797 
798   if @subs
799     @subs.each do |key, v|
800       if key.is_a?(String) && symbolize?
801         key = key.to_sym
802       end
803       params[key] = v.nested_params
804     end
805   end
806   
807   params
808 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
847 def sub_capture(capture, symbolize, skip_missing)
848   if @capture = capture
849     @symbolize = symbolize
850     @skip_missing = skip_missing
851     @params = @obj.class.new
852   end
853 end
subkey(keys, do_raise)

Recursive method to get subkeys.

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