Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Implement decode_query in URI #284

Merged
merged 3 commits into from

4 participants

Alexei Sholik Don't Add Me To Your Organization a.k.a The Travis Bot José Valim Anthony Grimes
Alexei Sholik
Owner

No description provided.

Don't Add Me To Your Organization a.k.a The Travis Bot

This pull request passes (merged 5c97d0da into c7e4cfa).

Don't Add Me To Your Organization a.k.a The Travis Bot

This pull request passes (merged 94f37ddc into c7e4cfa).

lib/uri.ex
@@ -21,6 +21,33 @@ defmodule URI do
"""
def encode_query(l), do: Enum.join(Enum.map(l, pair(&1)), "&")
+ @doc """
+ Given a query string of the form "key1=value1&key=value2...", produces an
+ orddict with one entry for each key-value pair. Each key and value will be a
+ binary. It also does percent-unescaping of both keys and values.
+
+ Returns nil if the query string is malformed.
+ """
+ def decode_query(q) do
+ parts = Regex.split %r/&/, to_binary(q)
+ # Set up a try block to return quickly from List.foldl in case of an error
José Valim Owner

If the purpose is to quickly recover from a scenario, can we use try/catch instead?
In general, exceptions should not be used for control flow. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
lib/uri.ex
@@ -21,6 +21,33 @@ defmodule URI do
"""
def encode_query(l), do: Enum.join(Enum.map(l, pair(&1)), "&")
+ @doc """
+ Given a query string of the form "key1=value1&key=value2...", produces an
+ orddict with one entry for each key-value pair. Each key and value will be a
+ binary. It also does percent-unescaping of both keys and values.
+
+ Returns nil if the query string is malformed.
+ """
+ def decode_query(q) do
+ parts = Regex.split %r/&/, to_binary(q)
+ # Set up a try block to return quickly from List.foldl in case of an error
+ try do
+ List.foldl parts, Orddict.new, fn(kvstr, dict) ->
José Valim Owner

Maybe we could receive the dict as argument (the default would be Orddict.new) and simply call Dict.put? This way a developer can pass any type that implements the Dict protocol as argument :)

Alexei Sholik Owner
alco added a note

I don't think it's a necessity. The developer will get back a type that conforms to the Dict protocol anyway. What if they pass a non-empty dict? Being able to do that feels strange.

José Valim Owner

The different dict implementations exist because they have different storage and performance requirements. In my opinion, if we can allow the user to choose his favorite implementation without imposing one, I would prefer if we did so.

If the user wants to pass a non-empty dict, that's also up to him (i.e. i see no reason to limit it) :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
alco added some commits
Alexei Sholik alco Implement decode_query in URI fa96466
Alexei Sholik alco Handle charlist queries aee124d
Alexei Sholik alco Improve query parsing
Return a { :error, msg } tuple instead of nil. Better handle some edge
cases.
e31cca0
Alexei Sholik
Owner

Update.

Don't Add Me To Your Organization a.k.a The Travis Bot

This pull request passes (merged e31cca0 into da67254).

José Valim josevalim merged commit 463c588 into from
José Valim
Owner

Merged, thanks a bunch! I have allowed the dict as argument here: 72b0e5f

Anthony Grimes

Yay for people implementing things I'm too lazy to implement myself!

Alexei Sholik alco deleted the branch
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on May 6, 2012
  1. Alexei Sholik

    Implement decode_query in URI

    alco authored
  2. Alexei Sholik

    Handle charlist queries

    alco authored
  3. Alexei Sholik

    Improve query parsing

    alco authored
    Return a { :error, msg } tuple instead of nil. Better handle some edge
    cases.
This page is out of date. Refresh to see the latest.
Showing with 46 additions and 0 deletions.
  1. +31 −0 lib/uri.ex
  2. +15 −0 test/elixir/uri_test.exs
31 lib/uri.ex
View
@@ -21,6 +21,37 @@ defmodule URI do
"""
def encode_query(l), do: Enum.join(Enum.map(l, pair(&1)), "&")
+ @doc """
+ Given a query string of the form "key1=value1&key=value2...", produces an
+ orddict with one entry for each key-value pair. Each key and value will be a
+ binary. It also does percent-unescaping of both keys and values.
+
+ Returns nil if the query string is malformed.
+ """
+ def decode_query(q) do
+ # If the string is blank, return an empty dict
+ empty_dict = Orddict.new
+ if Regex.match?(%r"^\s*$", q) do
+ empty_dict
+ else:
+ parts = Regex.split %r/&/, to_binary(q)
+ # Set up a try block to return quickly from List.foldl in case of an error
+ try do
+ List.foldl parts, empty_dict, fn(kvstr, dict) ->
+ pair = case Regex.split(%r/=/, kvstr) do
+ match: [ key, value ] when size(key) > 0
+ { decode(key), decode(value) }
+ else:
+ throw { :error, "Malformed query string" }
+ end
+ Dict.Orddict.Record.put dict, pair
+ end
+ catch: x
+ x
+ end
+ end
+ end
+
defp pair({k, v}) do
encode(to_binary(k)) <> "=" <> encode(to_binary(v))
end
15 test/elixir/uri_test.exs
View
@@ -28,6 +28,21 @@ defmodule URITest do
assert URI.encode_query([{:foo, 'bar'}]) == "foo=bar"
end
+ test :decode_query do
+ error = { :error, "Malformed query string" }
+
+ assert URI.decode_query("q=search%20query&cookie=ab%26cd&block%20buster=") ==
+ Orddict.new [{"block buster", ""}, {"cookie", "ab&cd"}, {"q", "search query"}]
+ assert URI.decode_query("") == Orddict.new
+ assert URI.decode_query('list=works') == Orddict.new [{"list", "works"}]
+ assert URI.decode_query("garbage") == error
+ assert URI.decode_query("=value") == error
+
+ assert URI.decode_query("something=weird=happening") == error
+ assert URI.decode_query("something=weird%3Dhappening") == Orddict.new [{"something", "weird=happening"}]
+
+ end
+
test :decode do
data_to_be_decoded = "%26%3C%3E%22+%E3%82%86%E3%82%93%E3%82%86%E3%82%93"
assert URI.decode(data_to_be_decoded) == "&<>\" ゆんゆん"
Something went wrong with that request. Please try again.