def uri_encode(path, opts = { :allow_fragment => false })
    raise ArgumentError.new(_('path may not be nil')) if path.nil?

    # ensure string starts as UTF-8 for the sake of Ruby 1.9.3
    encoded = ''.encode!(Encoding::UTF_8)

    # parse uri into named matches, then reassemble properly encoded
    parts = path.match(RFC_3986_URI_REGEX)

    encoded += parts[:scheme] unless parts[:scheme].nil?
    encoded += parts[:authority] unless parts[:authority].nil?

    # path requires space to be encoded as %20 (NEVER +)
    # + should be left unencoded
    # URI::parse and URI::Generic.build don't like paths encoded with CGI.escape
    # URI.escape does not change / to %2F and : to %3A like CGI.escape
    encoded += URI.escape(parts[:path]) unless parts[:path].nil?

    # each query parameter
    if !parts[:query].nil?
      query_string = parts[:query].split('&').map do |pair|
        # can optionally be separated by an =
        pair.split('=').map do |v|
          uri_query_encode(v)
        end.join('=')
      end.join('&')
      encoded += '?' + query_string
    end

    encoded += ((opts[:allow_fragment] ? '#' : '%23') + uri_query_encode(parts[:fragment])) unless parts[:fragment].nil?

    encoded
  end
def uri_encode(path, opts = { :allow_fragment => false })
    raise ArgumentError.new(_('path may not be nil')) if path.nil?

    # ensure string starts as UTF-8 for the sake of Ruby 1.9.3
    encoded = ''.encode!(Encoding::UTF_8)

    # parse uri into named matches, then reassemble properly encoded
    parts = path.match(RFC_3986_URI_REGEX)

    encoded += parts[:scheme] unless parts[:scheme].nil?
    encoded += parts[:authority] unless parts[:authority].nil?

    # path requires space to be encoded as %20 (NEVER +)
    # + should be left unencoded
    # URI::parse and URI::Generic.build don't like paths encoded with CGI.escape
    # URI.escape does not change / to %2F and : to %3A like CGI.escape
    encoded += URI.escape(parts[:path]) unless parts[:path].nil?

    # each query parameter
    if !parts[:query].nil?
      query_string = parts[:query].split('&').map do |pair|
        # can optionally be separated by an =
        pair.split('=').map do |v|
          uri_query_encode(v)
        end.join('=')
      end.join('&')
      encoded += '?' + query_string
    end

    encoded += ((opts[:allow_fragment] ? '#' : '%23') + uri_query_encode(parts[:fragment])) unless parts[:fragment].nil?

    encoded
  end
Percent-encodes a URI string per RFC3986 - https://tools.ietf.org/html/rfc3986

 Properly handles escaping rules for paths, query strings and fragments
 independently

 The output is safe to pass to URI.parse or URI::Generic.build and will
 correctly round-trip through URI.unescape

 @param [String path] A URI string that may be in the form of:

   http://foo.com/bar?query
   file://tmp/foo bar
   //foo.com/bar?query
   /bar?query
   bar?query
   bar
   .
   C:\Windows\Temp

   Note that with no specified scheme, authority or query parameter delimiter
   ? that a naked string will be treated as a path.

   Note that if query parameters need to contain data such as & or =
   that this method should not be used, as there is no way to differentiate
   query parameter data from query delimiters when multiple parameters
   are specified

 @param [Hash{Symbol=>String} opts] Options to alter encoding
 @option opts [Array<Symbol>] :allow_fragment defaults to false. When false
   will treat # as part of a path or query and not a fragment delimiter

 @return [String] a new string containing appropriate portions of the URI
   encoded per the rules of RFC3986.
   In particular,
   path will not encode +, but will encode space as %20
   query will encode + as %2B and space as %20
   fragment behaves like query