Class: Pakyow::Connection::QueryParser Private
- Inherits:
-
Object
- Object
- Pakyow::Connection::QueryParser
- Defined in:
- lib/pakyow/connection/query_parser.rb
Overview
This class is part of a private API. You should avoid using this class if possible, as it may be removed or be changed in the future.
Parses one or more query strings, building up a params hash. Supports nested query strings, and enforces limits for key space size and total nested parameter depth.
Aspects of this were inspired by Rack’s query parser, including key space and depth limits. We decided it was worth writing our own for several reasons:
1) Avoid Rack as a dependency for the majority use-case.
2) Improve the interface so you don't have to know ahead of time if you're dealing with a
nested query string or not, and to allow for params to be built up from many strings.
3) Improve performance (up to 90% faster for a simple query string, 10% for nested).
Defined Under Namespace
Classes: DepthLimitExceeded, InvalidParameter, KeySpaceLimitExceeded
Constant Summary collapse
- DEFAULT_DELIMETER =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
/[&;,]/
- DEFAULT_KEY_SPACE_LIMIT =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
102_400
- DEFAULT_DEPTH_LIMIT =
This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.
100
Instance Attribute Summary collapse
- #depth_limit ⇒ Object readonly private
- #key_space_limit ⇒ Object readonly private
- #params ⇒ Object readonly private
Instance Method Summary collapse
- #add(key, value, params = @params) ⇒ Object private
- #add_value_for_key(value, key, params = @params, depth = 0) ⇒ Object private
-
#initialize(key_space_limit: DEFAULT_KEY_SPACE_LIMIT, depth_limit: DEFAULT_DEPTH_LIMIT, params: {}) ⇒ QueryParser
constructor
private
A new instance of QueryParser.
- #parse(input, delimiter = DEFAULT_DELIMETER) ⇒ Object private
Constructor Details
#initialize(key_space_limit: DEFAULT_KEY_SPACE_LIMIT, depth_limit: DEFAULT_DEPTH_LIMIT, params: {}) ⇒ QueryParser
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Returns a new instance of QueryParser.
34 35 36 37 38 39 |
# File 'lib/pakyow/connection/query_parser.rb', line 34 def initialize(key_space_limit: DEFAULT_KEY_SPACE_LIMIT, depth_limit: DEFAULT_DEPTH_LIMIT, params: {}) @params = params @key_space_limit = key_space_limit @key_space_size = 0 @depth_limit = depth_limit end |
Instance Attribute Details
#depth_limit ⇒ Object (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
32 33 34 |
# File 'lib/pakyow/connection/query_parser.rb', line 32 def depth_limit @depth_limit end |
#key_space_limit ⇒ Object (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
32 33 34 |
# File 'lib/pakyow/connection/query_parser.rb', line 32 def key_space_limit @key_space_limit end |
#params ⇒ Object (readonly)
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
32 33 34 |
# File 'lib/pakyow/connection/query_parser.rb', line 32 def params @params end |
Instance Method Details
#add(key, value, params = @params) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
52 53 54 55 56 57 58 59 60 61 62 |
# File 'lib/pakyow/connection/query_parser.rb', line 52 def add(key, value, params = @params) unless params.key?(key) @key_space_size += key.size end if @key_space_size > @key_space_limit raise KeySpaceLimitExceeded, "key space limit (#{@key_space_limit}) exceeded by `#{key}'" else params[key] = value end end |
#add_value_for_key(value, key, params = @params, depth = 0) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/pakyow/connection/query_parser.rb', line 64 def add_value_for_key(value, key, params = @params, depth = 0) if depth > @depth_limit raise DepthLimitExceeded, "depth limit (#{@depth_limit}) exceeded by `#{key}'" end if key && key.include?("[") && key.include?("]") opened = false read, nested = String.new, nil key.length.times do |i| char = key[i] if char == "[" opened = true elsif char == "]" && opened opened = false case params when Array nested_value = if nested if current_nested_value = params.last unless current_nested_value.is_a?(@params.class) raise InvalidParameter, "expected `#{read}' to be #{@params.class} (got #{current_nested_value.class})" end if current_nested_value.key?(nested) (params << @params.class.new).last else current_nested_value end else (params << @params.class.new).last end else if current_nested_value = params[read] unless current_nested_value.is_a?(Array) raise InvalidParameter, "expected `#{read}' to be Array (got #{current_nested_value.class})" end current_nested_value else (params << []).last end end when @params.class nested_value = if nested if current_nested_value = params[read] unless current_nested_value.is_a?(@params.class) raise InvalidParameter, "expected `#{read}' to be #{@params.class} (got #{current_nested_value.class})" end current_nested_value else @params.class.new end else if current_nested_value = params[read] unless current_nested_value.is_a?(Array) raise InvalidParameter, "expected `#{read}' to be Array (got #{current_nested_value.class})" end current_nested_value else [] end end add(read, nested_value, params) end j = i + 1 if (next_char = key[j]) && next_char != "[" raise InvalidParameter, "expected `#{nested}' to be #{params.class} (got String)" else add_value_for_key(value, (nested || String.new) << key[j..-1], nested_value, depth + 1); break end elsif opened (nested ||= String.new) << char else read << char end end else case params when Array params << value when @params.class if depth == 0 && (current_value = params[key]) && !(current_value.is_a?(Array) || current_value.is_a?(@params.class)) if current_value.is_a?(Array) current_value << value else current_value = [current_value, value] add(key, current_value, params) end else if key && !key.empty? add(key, value, params) end end end end end |
#parse(input, delimiter = DEFAULT_DELIMETER) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
41 42 43 44 45 46 47 48 49 50 |
# File 'lib/pakyow/connection/query_parser.rb', line 41 def parse(input, delimiter = DEFAULT_DELIMETER) input.to_s.split(delimiter).each do |part| key, value = part.split("=", 2) key = unescape(key).strip if key value = unescape(value).strip if value add_value_for_key(value, key) end @params end |