Skip to content

Commit

Permalink
Merge pull request #2 from mazingstudio/dev
Browse files Browse the repository at this point in the history
Handle more cases
  • Loading branch information
ZuraGuerra authored Nov 27, 2017
2 parents 59e06e0 + 71d45e8 commit 382ad27
Show file tree
Hide file tree
Showing 5 changed files with 98 additions and 18 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,12 @@ Norma depends heavily on the implementation of the `URI` module (part of the sta
iex> Norma.normalize("mazing.studio")
> "http://mazing.studio"

iex> Norma.normalize_if_valid("mazing.studio")
> {:ok, "http://mazing.studio"}

iex> Norma.normalize_if_valid("mazing")
> {:error, "Not an URL."}

iex> options = %{remove_fragment: true, force_root_path: true, remove_www: true}
iex> Norma.normalize("//www.mazing.studio:1337/test#test", options)
> "http://mazing.studio/"
Expand Down
58 changes: 56 additions & 2 deletions lib/norma.ex
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,66 @@ defmodule Norma do
## Examples
iex> Norma.normalize("//www.mazing.studio", %{remove_www: true})
"http://mazing.studio"
{:ok, "http://mazing.studio"}
"""
def normalize_if_valid(url, options \\ %{}) do
if is_url?(url) do
normalized_url = url
|> safe_parse
|> Normalizer.normalize(options)
{:ok, normalized_url}
else
{:error, "Not an URL."}
end
end

@doc """
Similar to `normalize_if_valid/2`, but will return the given string unchanged
if it's not an URL.
## Examples
iex> Norma.normalize!("//www.mazing.studio", %{remove_www: true})
"http://mazing.studio"
"""
def normalize(url, options \\ %{}) do
case normalize_if_valid(url, options) do
{:ok, normalized_url} -> normalized_url
{:error, _} -> url
end
end

@doc """
Solve an issue related to the regex provided by the URI spec
(see https://tools.ietf.org/html/rfc3986#appendix-B).
If trying to parse from string to %URI{} something like "mazing.studio:80",
the result will be:
%URI{scheme: "mazing.studio", path: "21", host: nil}
_(Other keys skipped for brevity, but their value is `nil`.)_
But "//mazing.studio:80", will be parsed correctly:
%URI{host: "mazing.studio", authority: "mazing.studio:80", port: 80}
"""
defp safe_parse(url) do
url
|> URI.parse
|> Normalizer.normalize(options)
|> has_valid_host?
end

defp has_valid_host?(url = %URI{host: nil}) do
url = url |> URI.to_string

"//" <> url
|> safe_parse
end

defp has_valid_host?(url = %URI{host: host})
when host != nil, do: url

@doc """
This sure looks dumb, but a valid host will normally have at least a dot.
"""
defp is_url?(url), do: url |> String.contains?(".")
end
29 changes: 19 additions & 10 deletions lib/norma/utils.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
defmodule Norma.Utils do
def port_handler (port) do
@masked_ports ["443", "80", "8080", "21"]

def port_handler(port) do
case port do
443 -> "https"
80 -> "http"
Expand All @@ -24,15 +26,17 @@ defmodule Norma.Utils do
Help would be appreciated here to solve it better.
"""
def form_url(%URI{host: host,
path: path,
scheme: scheme,
query: query,
fragment: fragment}) do
form_scheme(scheme) # "http://"
|> safe_concat(host) # "http://google.com"
|> safe_concat(path) # "http://google.com/test"
|> Kernel.<>(form_fragment(fragment)) # "http://google.com/test#cats"
|> Kernel.<>(form_query(query)) # "http://google.com/test#cats?dogs_allowed=ño"
path: path,
scheme: scheme,
query: query,
port: port,
fragment: fragment}) do
form_scheme(scheme) # "http://"
|> safe_concat(host) # "http://google.com"
|> Kernel.<>(form_port(port |> to_string)) # "http://google.com:1337"
|> safe_concat(path) # "http://google.com:1337/test"
|> Kernel.<>(form_fragment(fragment)) # "http://google.com:1337/test#cats"
|> Kernel.<>(form_query(query)) # "http://google.com:1337/test#cats?dogs_allowed=ño"
end

defp form_scheme(""), do: ""
Expand All @@ -44,6 +48,11 @@ defmodule Norma.Utils do
defp form_query(nil), do: ""
defp form_query(query), do: "?" <> query

defp form_port(""), do: ""
defp form_port(port)
when port in @masked_ports, do: ""
defp form_port(port), do: ":" <> port

defp safe_concat(left, right) do
left = left || ""
right = right || ""
Expand Down
4 changes: 2 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ defmodule Norma.Mixfile do
def project do
[
app: :norma,
version: "1.2.0",
elixir: "~> 1.2",
version: "1.4.2",
elixir: "~> 1.3",
start_permanent: Mix.env == :prod,
description: description(),
package: package(),
Expand Down
19 changes: 15 additions & 4 deletions test/norma_test.exs
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
defmodule NormaTest do
use ExUnit.Case

@with_scheme "https://mazing.studio"
@without_scheme "mazing.studio"
@without_scheme_but_port "//mazing.studio:21"
@without_scheme_but_path "mazing.studio/test"
@without_scheme_but_port "mazing.studio:21"
@without_scheme_but_port_alt "mazing.studio:1337"
@without_scheme_but_port_and_path "mazing.studio:1337/test"
@with_path "https://mazing.studio/test"
@with_fragment "https://mazing.studio#test"
@with_www "https://www.mazing.studio"
@full_example "//www.mazing.studio:1337/test#test"

test "scheme defaults to `http` when not provided (nor a port)",
do: assert Norma.normalize(@without_scheme) == "http://mazing.studio"
test "scheme defaults to `http` when not provided (nor a port)" do
assert Norma.normalize(@without_scheme) == "http://mazing.studio"
assert Norma.normalize(@without_scheme_but_path) == "http://mazing.studio/test"
assert Norma.normalize(@without_scheme_but_port_alt) == "http://mazing.studio:1337"
assert Norma.normalize(@without_scheme_but_port_and_path) == "http://mazing.studio:1337/test"
end

test "scheme gets infered from port",
do: assert Norma.normalize(@without_scheme_but_port) == "ftp://mazing.studio"
Expand All @@ -34,6 +42,9 @@ defmodule NormaTest do
do: assert Norma.normalize(@full_example,
%{force_root_path: true,
remove_fragment: true,
remove_www: true}) == "http://mazing.studio/"
remove_www: true}) == "http://mazing.studio:1337/"

test "identify invalid URL",
do: assert Norma.normalize_if_valid("mazing") == {:error, "Not an URL."}

end

0 comments on commit 382ad27

Please sign in to comment.