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