class Rack::NestedParams::UrlEncodedPairParser

Constants

BRACKETED_KEY_REGEXP
KEY_REGEXP

Attributes

parent[R]
result[R]
top[R]

Public Class Methods

new(pairs = []) click to toggle source
Calls superclass method
# File lib/rack/contrib/nested_params.rb, line 52
def initialize(pairs = [])
  super('')
  @result = {}
  pairs.each { |key, value| parse(key, value) }
end

Public Instance Methods

parse(key, value) click to toggle source

Parse the query string

# File lib/rack/contrib/nested_params.rb, line 62
def parse(key, value)
  self.string = key
  @top, @parent = result, nil

  # First scan the bare key
  key = scan(KEY_REGEXP) or return
  key = post_key_check(key)

  # Then scan as many nestings as present
  until eos?
    r = scan(BRACKETED_KEY_REGEXP) or return
    key = self[1]
    key = post_key_check(key)
  end

  bind(key, value)
end

Private Instance Methods

bind(key, value) click to toggle source

Bind a key (which may be nil for items in an array) to the provided value.

# File lib/rack/contrib/nested_params.rb, line 113
def bind(key, value)
  if top.is_a? Array
    if key
      if top[-1].is_a?(Hash) && ! top[-1].key?(key)
        top[-1][key] = value
      else
        top << {key => value}
      end
      push top.last
      return top[key]
    else
      top << value
      return value
    end
  elsif top.is_a? Hash
    key = CGI.unescape(key)
    parent << (@top = {}) if top.key?(key) && parent.is_a?(Array)
    top[key] ||= value
    return top[key]
  else
    raise ArgumentError, "Don't know what to do: top is #{top.inspect}"
  end
end
container(key, klass) click to toggle source

Add a container to the stack.

# File lib/rack/contrib/nested_params.rb, line 100
def container(key, klass)
  type_conflict! klass, top[key] if top.is_a?(Hash) && top.key?(key) && ! top[key].is_a?(klass)
  value = bind(key, klass.new)
  type_conflict! klass, value unless value.is_a?(klass)
  push(value)
end
post_key_check(key) click to toggle source

After we see a key, we must look ahead to determine our next action. Cases:

[] follows the key. Then the value must be an array.
= follows the key. (A value comes next)
& or the end of string follows the key. Then the key is a flag.
otherwise, a hash follows the key.
# File lib/rack/contrib/nested_params.rb, line 87
def post_key_check(key)
  if scan(/\[\]/) # a[b][] indicates that b is an array
    container(key, Array)
    nil
  elsif check(/\[[^\]]/) # a[b] indicates that a is a hash
    container(key, Hash)
    nil
  else # End of key? We do nothing.
    key
  end
end
push(value) click to toggle source

Push a value onto the 'stack', which is actually only the top 2 items.

# File lib/rack/contrib/nested_params.rb, line 108
def push(value)
  @parent, @top = @top, value
end
type_conflict!(klass, value) click to toggle source
# File lib/rack/contrib/nested_params.rb, line 137
def type_conflict!(klass, value)
  raise TypeError, "Conflicting types for parameter containers. Expected an instance of #{klass} but found an instance of #{value.class}. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value. (The parameters received were #{value.inspect}.)"
end