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
-
_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.
# 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
Set the invalid message for the given type.
# 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
Set the maximum input bytesize for the given type.
# 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
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 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
Set the object used for converting. Conversion methods will convert members of the passed object.
# 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
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 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
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 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
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 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
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 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
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 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
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 755 def dig(type, *nest, key) 756 _dig(false, type, nest, key) 757 end
Similar to dig, but raises an Error instead of returning nil if no value is found.
# File lib/roda/plugins/typecast_params.rb 760 def dig!(type, *nest, key) 761 _dig(true, type, nest, key) 762 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 663 def fetch(key) 664 send(:[], key){return(yield if defined?(yield))} 665 end
If key is a String Return whether the key is present in the object,
# 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
Recursively descendent into all known subkeys and get the converted params from each.
# 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
Inherit given capturing and symbolize setting from parent object.
# 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
Recursive method to get subkeys.
# 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