module Merb::Parse

Constants

CONTENT_TYPE_REGEX
CRLF
EOL
FILENAME_REGEX
NAME_REGEX

Public Class Methods

escape(s) click to toggle source

Parameters

s<String>

String to URL escape.

returns

String

The escaped string.

:api: public

# File lib/merb-core/dispatch/request_parsers.rb, line 187
def self.escape(s)
  s.to_s.gsub(/([^ a-zA-Z0-9_.-]+)/n) {
    '%'+$1.unpack('H2'*$1.size).join('%').upcase
  }.tr(' ', '+')
end
escape_xml(s) click to toggle source

Parameters

s<String>

String to XML escape.

returns

String

The escaped string.

:api: public

# File lib/merb-core/dispatch/request_parsers.rb, line 222
def self.escape_xml(s)
  Erubis::XmlHelper.escape_xml(s)
end
multipart(request, boundary, content_length) click to toggle source

Parameters

request<IO>

The raw request.

boundary<String>

The boundary string.

content_length<Fixnum>

The length of the content.

Raises

ControllerExceptions::MultiPartParseError

Failed to parse request.

Returns

Hash

The parsed request.

:api: plugin

# File lib/merb-core/dispatch/request_parsers.rb, line 49
def self.multipart(request, boundary, content_length)
  boundary = "--#{boundary}"
  paramhsh = {}
  buf      = ""
  input    = request
  input.binmode if defined? input.binmode
  boundary_size = boundary.size + EOL.size
  bufsize       = 16384
  content_length -= boundary_size
  key_memo = []
  # status is boundary delimiter line
  status = input.read(boundary_size)
  return {} if status == nil || status.empty?
  raise ControllerExceptions::MultiPartParseError, "bad content body:\n'#{status}' should == '#{boundary + EOL}'"  unless status == boundary + EOL

  rx = /(?:#{EOL})?#{Regexp.quote(boundary)}(#{EOL}|--)/n
  loop {
    head      = nil
    body      = ''
    filename  = content_type = name = nil
    read_size = 0
    until head && buf =~ rx
      i = buf.index("\r\n\r\n")
      if( i == nil && read_size == 0 && content_length == 0 )
        content_length = -1
        break
      end
      if !head && i
        head = buf.slice!(0, i+2) # First \r\n
        buf.slice!(0, 2)          # Second \r\n

        # String#[] with 2nd arg here is returning
        # a group from match data
        filename     = head[FILENAME_REGEX, 1]
        content_type = head[CONTENT_TYPE_REGEX, 1]
        name         = head[NAME_REGEX, 1]

        if filename && !filename.empty?
          body = Tempfile.new(:Merb)
          body.binmode if defined? body.binmode
        end
        next
      end

      # Save the read body part.
      if head && (boundary_size+4 < buf.size)
        body << buf.slice!(0, buf.size - (boundary_size+4))
      end

      read_size = bufsize < content_length ? bufsize : content_length
      if( read_size > 0 )
        c = input.read(read_size)
        raise ControllerExceptions::MultiPartParseError, "bad content body"  if c.nil? || c.empty?
        buf << c
        content_length -= c.size
      end
    end

    # Save the rest.
    if i = buf.index(rx)
      # correct value of i for some edge cases
      if (i > 2) && (j = buf.index(rx, i-2)) && (j < i)
         i = j
       end
      body << buf.slice!(0, i)
      buf.slice!(0, boundary_size+2)

      content_length = -1  if $1 == "--"
    end

    if filename && !filename.empty?
      body.rewind
      data = {
        :filename => File.basename(filename),
        :content_type => content_type,
        :tempfile => body,
        :size => File.size(body.path)
      }
    else
      data = body
    end

    unless key_memo.include?(name) && name !~ /\[\]/ 
      paramhsh = normalize_params(paramhsh,name,data) 
    end

    # Prevent from double processing files but process other params
    key_memo << name if filename && !filename.empty?

    break  if buf.empty? || content_length == -1
  }

  paramhsh
end
params_to_query_string(value, prefix = nil) click to toggle source

Parameters

value<Array, Hash, Dictionary ~to_s>

The value for the query string.

prefix<~to_s>

The prefix to add to the query string keys.

Returns

String

The query string.

Alternatives

If the value is a string, the prefix will be used as the key.

Examples

params_to_query_string(10, "page")
  # => "page=10"
params_to_query_string({ :page => 10, :word => "ruby" })
  # => "page=10&word=ruby"
params_to_query_string({ :page => 10, :word => "ruby" }, "search")
  # => "search[page]=10&search[word]=ruby"
params_to_query_string([ "ice-cream", "cake" ], "shopping_list")
  # => "shopping_list[]=ice-cream&shopping_list[]=cake"

:api: plugin

# File lib/merb-core/dispatch/request_parsers.rb, line 165
def self.params_to_query_string(value, prefix = nil)
  case value
  when Array
    value.map { |v|
      params_to_query_string(v, "#{prefix}[]")
    } * "&"
  when Hash, Dictionary
    value.map { |k, v|
      params_to_query_string(v, prefix ? "#{prefix}[#{escape(k)}]" : escape(k))
    } * "&"
  else
    "#{prefix}=#{escape(value)}"
  end
end
query(query_string, delimiter = '&;', preserve_order = false) click to toggle source

Parameters

query_string<String>

The query string.

delimiter<String>

The query string divider. Defaults to “&”.

preserve_order<Boolean>

Preserve order of args. Defaults to false.

Returns

Mash

The parsed query string (Dictionary if preserve_order is set).

Examples

Merb::Parse.query("bar=nik&post[body]=heya")
  # => { :bar => "nik", :post => { :body => "heya" } }

:api: plugin

# File lib/merb-core/dispatch/request_parsers.rb, line 17
def self.query(query_string, delimiter = '&;', preserve_order = false)
  query = preserve_order ? Dictionary.new : {}
  for pair in (query_string || '').split(/[#{delimiter}] */n)
    key, value = unescape(pair).split('=',2)
    next if key.nil?
    if key.include?('[')
      normalize_params(query, key, value)
    else
      query[key] = value
    end
  end
  preserve_order ? query : query.to_mash
end
unescape(s, encoding = nil) click to toggle source

Parameter

s<String>

String to URL unescape.

encoding<String>

Encoding which we force to return. Only for Ruby 1.9. If encoding is not passed it defaults to Encoding.default_internal. When this is nil (default) no encoding forcing is done.

returns

String

The unescaped string.

:api: public

# File lib/merb-core/dispatch/request_parsers.rb, line 204
def self.unescape(s, encoding = nil)
  s = s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){
    [$1.delete('%')].pack('H*')
  }
  if RUBY_VERSION >= '1.9'
    encoding ||= Encoding.default_internal
    s.force_encoding(encoding) if encoding
  end
  s
end

Private Class Methods

normalize_params(parms, name, val=nil) click to toggle source

Converts a query string snippet to a hash and adds it to existing parameters.

Parameters

parms<Hash>

Parameters to add the normalized parameters to.

name<String>

The key of the parameter to normalize.

val<String>

The value of the parameter.

Returns

Hash

Normalized parameters

:api: private

# File lib/merb-core/dispatch/request_parsers.rb, line 240
def self.normalize_params(parms, name, val=nil)
  name =~ %r([\[\]]*([^\[\]]+)\]*)
  key = $1 || ''
  after = $' || ''

  if after == ""
    parms[key] = val
  elsif after == "[]"
    (parms[key] ||= []) << val
  elsif after =~ %r(^\[\]\[([^\[\]]+)\]$)
    child_key = $1
    parms[key] ||= []
    if parms[key].last.is_a?(Hash) && !parms[key].last.key?(child_key)
      parms[key].last.update(child_key => val)
    else
      parms[key] << { child_key => val }
    end
  else
    parms[key] ||= {}
    parms[key] = normalize_params(parms[key], after, val)
  end
  parms
end