Class handling conversion of submitted parameters to desired types.
Methods
Public Class
Public Instance
Protected Instance
Public Class methods
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
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.
# File lib/roda/plugins/typecast_params.rb 448 def self.handle_type(type, opts=OPTS, &block) 449 convert_meth = :"convert_#{type}" 450 define_method(convert_meth, &block) 451 452 max_input_bytesize = opts[:max_input_bytesize] 453 max_input_bytesize_meth = :"_max_input_bytesize_for_#{type}" 454 define_method(max_input_bytesize_meth){max_input_bytesize} 455 456 convert_array_meth = :"_convert_array_#{type}" 457 define_method(convert_array_meth) do |v| 458 raise Error, "expected array but received #{v.inspect}" unless v.is_a?(Array) 459 v.map! do |val| 460 check_allowed_bytesize(val, send(max_input_bytesize_meth)) 461 check_null_byte(val) 462 send(convert_meth, val) 463 end 464 end 465 466 private convert_meth, convert_array_meth, max_input_bytesize_meth 467 alias_method max_input_bytesize_meth, max_input_bytesize_meth 468 469 define_method(type) do |key, default=nil| 470 process_arg(convert_meth, key, default, send(max_input_bytesize_meth)) if require_hash! 471 end 472 473 define_method(:"#{type}!") do |key| 474 send(type, key, CHECK_NIL) 475 end 476 end
Override the maximum input bytesize for the given type. This is mostly useful for overriding the sizes for the default input types.
# File lib/roda/plugins/typecast_params.rb 480 def self.max_input_bytesize(type, bytesize) 481 max_input_bytesize_meth = :"_max_input_bytesize_for_#{type}" 482 define_method(max_input_bytesize_meth){bytesize} 483 private max_input_bytesize_meth 484 alias_method max_input_bytesize_meth, max_input_bytesize_meth 485 end
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.
# File lib/roda/plugins/typecast_params.rb 491 def self.nest(obj, nesting) 492 v = allocate 493 v.instance_variable_set(:@nesting, nesting) 494 v.send(:initialize, obj) 495 v 496 end
Set the object used for converting. Conversion methods will convert members of the passed object.
# File lib/roda/plugins/typecast_params.rb 585 def initialize(obj) 586 case @obj = obj 587 when Hash, Array 588 # nothing 589 else 590 if @nesting 591 handle_error(nil, (@obj.nil? ? :missing : :invalid_type), "value of #{param_name(nil)} parameter not an array or hash: #{obj.inspect}", true) 592 else 593 handle_error(nil, :invalid_type, "parameters given not an array or hash: #{obj.inspect}", true) 594 end 595 end 596 end
Public Instance methods
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.
# File lib/roda/plugins/typecast_params.rb 615 def [](key) 616 @subs ||= {} 617 if sub = @subs[key] 618 return sub 619 end 620 621 if @obj.is_a?(Array) 622 unless key.is_a?(Integer) 623 handle_error(key, :invalid_type, "invalid use of non-integer key for accessing array: #{key.inspect}", true) 624 end 625 else 626 if key.is_a?(Integer) 627 handle_error(key, :invalid_type, "invalid use of integer key for accessing hash: #{key}", true) 628 end 629 end 630 631 v = @obj[key] 632 v = yield if v.nil? && defined?(yield) 633 634 begin 635 sub = self.class.nest(v, Array(@nesting) + [key]) 636 rescue => e 637 handle_error(key, :invalid_type, e, true) 638 end 639 640 @subs[key] = sub 641 sub.sub_capture(@capture, @symbolize, @skip_missing) 642 sub 643 end
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.
# File lib/roda/plugins/typecast_params.rb 752 def array(type, key, default=nil) 753 meth = :"_convert_array_#{type}" 754 raise ProgrammerError, "no typecast_params type registered for #{type.inspect}" unless respond_to?(meth, true) 755 process_arg(meth, key, default, send(:"_max_input_bytesize_for_#{type}")) if require_hash! 756 end
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
.
# File lib/roda/plugins/typecast_params.rb 760 def array!(type, key, default=nil) 761 v = array(type, key, default) 762 763 if key.is_a?(Array) 764 key.zip(v).each do |k, arr| 765 check_array!(k, arr) 766 end 767 else 768 check_array!(key, v) 769 end 770 771 v 772 end
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 |
# File lib/roda/plugins/typecast_params.rb 661 def convert!(keys=nil, opts=OPTS) 662 if keys.is_a?(Hash) 663 opts = keys 664 keys = nil 665 end 666 667 _capture!(:nested_params, opts) do 668 if sub = subkey(Array(keys).dup, opts.fetch(:raise, true)) 669 yield sub 670 end 671 end 672 end
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. |
# File lib/roda/plugins/typecast_params.rb 685 def convert_each!(opts=OPTS, &block) 686 np = !@capture 687 688 _capture!(nil, opts) do 689 case keys = opts[:keys] 690 when nil 691 keys = (0...@obj.length) 692 693 valid = if @obj.is_a?(Array) 694 true 695 else 696 keys = keys.map(&:to_s) 697 keys.all?{|k| @obj.has_key?(k)} 698 end 699 700 unless valid 701 handle_error(nil, :invalid_type, "convert_each! called on object not an array or hash with keys '0'..'N'") 702 next 703 end 704 when Array 705 # nothing to do 706 when Proc, Method 707 keys = keys.call(@obj) 708 else 709 raise ProgrammerError, "unsupported convert_each! :keys option: #{keys.inspect}" 710 end 711 712 keys.map do |i| 713 begin 714 if v = subkey([i], opts.fetch(:raise, true)) 715 yield v 716 v.nested_params if np 717 end 718 rescue => e 719 handle_error(i, :invalid_type, e) 720 end 721 end 722 end 723 end
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')
# File lib/roda/plugins/typecast_params.rb 739 def dig(type, *nest, key) 740 _dig(false, type, nest, key) 741 end
Similar to dig
, but raises an Error
instead of returning nil
if no value is found.
# File lib/roda/plugins/typecast_params.rb 744 def dig!(type, *nest, key) 745 _dig(true, type, nest, key) 746 end
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.
# File lib/roda/plugins/typecast_params.rb 647 def fetch(key) 648 send(:[], key){return(yield if defined?(yield))} 649 end
If key is a String Return whether the key is present in the object,
# File lib/roda/plugins/typecast_params.rb 599 def present?(key) 600 case key 601 when String 602 !any(key).nil? 603 when Array 604 key.all? do |k| 605 raise ProgrammerError, "non-String element in array argument passed to present?: #{k.inspect}" unless k.is_a?(String) 606 !any(k).nil? 607 end 608 else 609 raise ProgrammerError, "unexpected argument passed to present?: #{key.inspect}" 610 end 611 end
Protected Instance methods
Recursively descendent into all known subkeys and get the converted params from each.
# File lib/roda/plugins/typecast_params.rb 777 def nested_params 778 return @nested_params if @nested_params 779 780 params = @params 781 782 if @subs 783 @subs.each do |key, v| 784 if key.is_a?(String) && symbolize? 785 key = key.to_sym 786 end 787 params[key] = v.nested_params 788 end 789 end 790 791 params 792 end
Inherit given capturing and symbolize setting from parent object.
# File lib/roda/plugins/typecast_params.rb 831 def sub_capture(capture, symbolize, skip_missing) 832 if @capture = capture 833 @symbolize = symbolize 834 @skip_missing = skip_missing 835 @params = @obj.class.new 836 end 837 end
Recursive method to get subkeys.
# File lib/roda/plugins/typecast_params.rb 795 def subkey(keys, do_raise) 796 unless key = keys.shift 797 return self 798 end 799 800 reason = :invalid_type 801 802 case key 803 when String 804 unless @obj.is_a?(Hash) 805 raise Error, "parameter #{param_name(nil)} is not a hash" if do_raise 806 return 807 end 808 present = !@obj[key].nil? 809 when Integer 810 unless @obj.is_a?(Array) 811 raise Error, "parameter #{param_name(nil)} is not an array" if do_raise 812 return 813 end 814 present = key < @obj.length 815 else 816 raise ProgrammerError, "invalid argument used to traverse parameters: #{key.inspect}" 817 end 818 819 unless present 820 reason = :missing 821 raise Error, "parameter #{param_name(key)} is not present" if do_raise 822 return 823 end 824 825 self[key].subkey(keys, do_raise) 826 rescue => e 827 handle_error(key, reason, e) 828 end