add formatter
This commit is contained in:
parent
0d72ff37a4
commit
34e4e2f953
8 changed files with 165 additions and 114 deletions
4
.formatter.exs
Normal file
4
.formatter.exs
Normal file
|
@ -0,0 +1,4 @@
|
|||
# Used by "mix format"
|
||||
[
|
||||
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
|
||||
]
|
|
@ -51,7 +51,6 @@ defmodule AutoLinker do
|
|||
Note that passing opts to `link/2` will override the configuration settings.
|
||||
"""
|
||||
def link(text, opts \\ []) do
|
||||
parse text, opts
|
||||
parse(text, opts)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -24,20 +24,21 @@ defmodule AutoLinker.Builder do
|
|||
end
|
||||
|
||||
defp build_attrs(attrs, _, opts, :rel) do
|
||||
if rel = Map.get(opts, :rel, "noopener noreferrer"),
|
||||
do: [{:rel, rel} | attrs], else: attrs
|
||||
if rel = Map.get(opts, :rel, "noopener noreferrer"), do: [{:rel, rel} | attrs], else: attrs
|
||||
end
|
||||
|
||||
defp build_attrs(attrs, _, opts, :target) do
|
||||
if Map.get(opts, :new_window, true),
|
||||
do: [{:target, :_blank} | attrs], else: attrs
|
||||
if Map.get(opts, :new_window, true), do: [{:target, :_blank} | attrs], else: attrs
|
||||
end
|
||||
|
||||
defp build_attrs(attrs, _, opts, :class) do
|
||||
if cls = Map.get(opts, :class, "auto-linker"),
|
||||
do: [{:class, cls} | attrs], else: attrs
|
||||
if cls = Map.get(opts, :class, "auto-linker"), do: [{:class, cls} | attrs], else: attrs
|
||||
end
|
||||
|
||||
defp build_attrs(attrs, url, _opts, :scheme) do
|
||||
if String.starts_with?(url, ["http://", "https://"]),
|
||||
do: [{:href, url} | attrs], else: [{:href, "http://" <> url} | attrs]
|
||||
do: [{:href, url} | attrs],
|
||||
else: [{:href, "http://" <> url} | attrs]
|
||||
end
|
||||
|
||||
defp format_url(attrs, url, opts) do
|
||||
|
@ -45,6 +46,7 @@ defmodule AutoLinker.Builder do
|
|||
url
|
||||
|> strip_prefix(Map.get(opts, :strip_prefix, true))
|
||||
|> truncate(Map.get(opts, :truncate, false))
|
||||
|
||||
attrs = format_attrs(attrs)
|
||||
"<a #{attrs}>" <> url <> "</a>"
|
||||
end
|
||||
|
@ -61,11 +63,13 @@ defmodule AutoLinker.Builder do
|
|||
"" -> ""
|
||||
attrs -> " " <> attrs
|
||||
end
|
||||
|
||||
Regex.replace(~r/\[(.+?)\]\((.+?)\)/, text, "<a href='\\2'#{attrs}>\\1</a>")
|
||||
end
|
||||
|
||||
defp truncate(url, false), do: url
|
||||
defp truncate(url, len) when len < 3, do: url
|
||||
|
||||
defp truncate(url, len) do
|
||||
if String.length(url) > len, do: String.slice(url, 0, len - 2) <> "..", else: url
|
||||
end
|
||||
|
@ -75,13 +79,15 @@ defmodule AutoLinker.Builder do
|
|||
|> String.replace(~r/^https?:\/\//, "")
|
||||
|> String.replace(~r/^www\./, "")
|
||||
end
|
||||
|
||||
defp strip_prefix(url, _), do: url
|
||||
|
||||
def create_phone_link([], buffer, _) do
|
||||
buffer
|
||||
end
|
||||
|
||||
def create_phone_link([h | t], buffer, opts) do
|
||||
create_phone_link t, format_phone_link(h, buffer, opts), opts
|
||||
create_phone_link(t, format_phone_link(h, buffer, opts), opts)
|
||||
end
|
||||
|
||||
def format_phone_link([h | _], buffer, opts) do
|
||||
|
@ -89,6 +95,7 @@ defmodule AutoLinker.Builder do
|
|||
h
|
||||
|> String.replace(~r/[\.\+\- x\(\)]+/, "")
|
||||
|> format_phone_link(h, opts)
|
||||
|
||||
# val = ~s'<a href="#" class="phone-number" data-phone="#{number}">#{h}</a>'
|
||||
String.replace(buffer, h, val)
|
||||
end
|
||||
|
@ -100,7 +107,9 @@ defmodule AutoLinker.Builder do
|
|||
attrs = format_attributes(opts[:attributes] || [])
|
||||
href = opts[:href] || "#"
|
||||
|
||||
~s'<#{tag} href="#{href}" class="#{class}" #{data_phone}="#{number}"#{attrs}>#{original}</#{tag}>'
|
||||
~s'<#{tag} href="#{href}" class="#{class}" #{data_phone}="#{number}"#{attrs}>#{original}</#{
|
||||
tag
|
||||
}>'
|
||||
end
|
||||
|
||||
defp format_attributes(attrs) do
|
||||
|
|
|
@ -43,20 +43,21 @@ defmodule AutoLinker.Parser do
|
|||
:auto_linker
|
||||
|> Application.get_env(:opts, [])
|
||||
|> Enum.into(%{})
|
||||
|> Map.put(:attributes,
|
||||
|> Map.put(
|
||||
:attributes,
|
||||
Application.get_env(:auto_linker, :attributes, [])
|
||||
)
|
||||
|
||||
opts =
|
||||
Enum.reduce @default_opts, opts, fn opt, acc ->
|
||||
Enum.reduce(@default_opts, opts, fn opt, acc ->
|
||||
if is_nil(opts[opt]) and is_nil(config[opt]) do
|
||||
Map.put acc, opt, true
|
||||
Map.put(acc, opt, true)
|
||||
else
|
||||
acc
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
do_parse text, Map.merge(config, opts)
|
||||
do_parse(text, Map.merge(config, opts))
|
||||
end
|
||||
|
||||
defp do_parse(text, %{phone: false} = opts), do: do_parse(text, Map.delete(opts, :phone))
|
||||
|
@ -85,11 +86,12 @@ defmodule AutoLinker.Parser do
|
|||
|
||||
defp do_parse(text, _), do: text
|
||||
|
||||
defp do_parse("", _scheme, _opts ,{"", acc, _}, _handler),
|
||||
defp do_parse("", _scheme, _opts, {"", acc, _}, _handler),
|
||||
do: acc
|
||||
|
||||
defp do_parse("", scheme, opts ,{buffer, acc, _}, handler),
|
||||
defp do_parse("", scheme, opts, {buffer, acc, _}, handler),
|
||||
do: acc <> handler.(buffer, scheme, opts)
|
||||
|
||||
defp do_parse("<a" <> text, scheme, opts, {buffer, acc, :parsing}, handler),
|
||||
do: do_parse(text, scheme, opts, {"", acc <> buffer <> "<a", :skip}, handler)
|
||||
|
||||
|
@ -106,8 +108,14 @@ defmodule AutoLinker.Parser do
|
|||
do: do_parse(text, scheme, opts, {"", acc <> <<ch::8>>, {:attrs, level}}, handler)
|
||||
|
||||
defp do_parse("</" <> text, scheme, opts, {buffer, acc, {:html, level}}, handler),
|
||||
do: do_parse(text, scheme, opts,
|
||||
{"", acc <> handler.(buffer, scheme, opts) <> "</", {:close, level}}, handler)
|
||||
do:
|
||||
do_parse(
|
||||
text,
|
||||
scheme,
|
||||
opts,
|
||||
{"", acc <> handler.(buffer, scheme, opts) <> "</", {:close, level}},
|
||||
handler
|
||||
)
|
||||
|
||||
defp do_parse(">" <> text, scheme, opts, {buffer, acc, {:close, 1}}, handler),
|
||||
do: do_parse(text, scheme, opts, {"", acc <> buffer <> ">", :parsing}, handler)
|
||||
|
@ -126,16 +134,34 @@ defmodule AutoLinker.Parser do
|
|||
do: do_parse(text, scheme, opts, {buffer <> " ", acc, state}, handler)
|
||||
|
||||
defp do_parse(" " <> text, scheme, opts, {buffer, acc, state}, handler),
|
||||
do: do_parse(text, scheme, opts,
|
||||
{"", acc <> handler.(buffer, scheme, opts) <> " ", state}, handler)
|
||||
do:
|
||||
do_parse(
|
||||
text,
|
||||
scheme,
|
||||
opts,
|
||||
{"", acc <> handler.(buffer, scheme, opts) <> " ", state},
|
||||
handler
|
||||
)
|
||||
|
||||
defp do_parse("\n" <> text, scheme, opts, {buffer, acc, state}, handler),
|
||||
do: do_parse(text, scheme, opts,
|
||||
{"", acc <> handler.(buffer, scheme, opts) <> "\n", state}, handler)
|
||||
do:
|
||||
do_parse(
|
||||
text,
|
||||
scheme,
|
||||
opts,
|
||||
{"", acc <> handler.(buffer, scheme, opts) <> "\n", state},
|
||||
handler
|
||||
)
|
||||
|
||||
defp do_parse(<<ch::8>>, scheme, opts, {buffer, acc, state}, handler),
|
||||
do: do_parse("", scheme, opts,
|
||||
{"", acc <> handler.(buffer <> <<ch::8>>, scheme, opts), state}, handler)
|
||||
do:
|
||||
do_parse(
|
||||
"",
|
||||
scheme,
|
||||
opts,
|
||||
{"", acc <> handler.(buffer <> <<ch::8>>, scheme, opts), state},
|
||||
handler
|
||||
)
|
||||
|
||||
defp do_parse(<<ch::8>> <> text, scheme, opts, {buffer, acc, state}, handler),
|
||||
do: do_parse(text, scheme, opts, {buffer <> <<ch::8>>, acc, state}, handler)
|
||||
|
@ -154,24 +180,24 @@ defmodule AutoLinker.Parser do
|
|||
|
||||
@doc false
|
||||
def is_url?(buffer, true) do
|
||||
if Regex.match? @invalid_url, buffer do
|
||||
if Regex.match?(@invalid_url, buffer) do
|
||||
false
|
||||
else
|
||||
Regex.match? @match_scheme, buffer
|
||||
Regex.match?(@match_scheme, buffer)
|
||||
end
|
||||
end
|
||||
|
||||
def is_url?(buffer, _) do
|
||||
if Regex.match? @invalid_url, buffer do
|
||||
if Regex.match?(@invalid_url, buffer) do
|
||||
false
|
||||
else
|
||||
Regex.match? @match_url, buffer
|
||||
Regex.match?(@match_url, buffer)
|
||||
end
|
||||
end
|
||||
|
||||
@doc false
|
||||
def match_phone(buffer) do
|
||||
case Regex.scan @match_phone, buffer do
|
||||
case Regex.scan(@match_phone, buffer) do
|
||||
[] -> nil
|
||||
other -> other
|
||||
end
|
||||
|
@ -180,13 +206,13 @@ defmodule AutoLinker.Parser do
|
|||
def link_phone(nil, buffer, _), do: buffer
|
||||
|
||||
def link_phone(list, buffer, opts) do
|
||||
Builder.create_phone_link list, buffer, opts
|
||||
Builder.create_phone_link(list, buffer, opts)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def link_url(true, buffer, opts) do
|
||||
Builder.create_link(buffer, opts)
|
||||
end
|
||||
def link_url(_, buffer, _opts), do: buffer
|
||||
|
||||
def link_url(_, buffer, _opts), do: buffer
|
||||
end
|
||||
|
|
16
mix.exs
16
mix.exs
|
@ -8,8 +8,8 @@ defmodule AutoLinker.Mixfile do
|
|||
app: :auto_linker,
|
||||
version: @version,
|
||||
elixir: "~> 1.4",
|
||||
build_embedded: Mix.env == :prod,
|
||||
start_permanent: Mix.env == :prod,
|
||||
build_embedded: Mix.env() == :prod,
|
||||
start_permanent: Mix.env() == :prod,
|
||||
deps: deps(),
|
||||
docs: [extras: ["README.md"]],
|
||||
package: package(),
|
||||
|
@ -17,7 +17,7 @@ defmodule AutoLinker.Mixfile do
|
|||
description: """
|
||||
AutoLinker is a basic package for turning website names into links.
|
||||
"""
|
||||
]
|
||||
]
|
||||
end
|
||||
|
||||
# Configuration for the OTP application
|
||||
|
@ -30,14 +30,16 @@ defmodule AutoLinker.Mixfile do
|
|||
defp deps do
|
||||
[
|
||||
{:ex_doc, "~> 0.18", only: :dev},
|
||||
{:earmark, "~> 1.2", only: :dev, override: true},
|
||||
{:earmark, "~> 1.2", only: :dev, override: true}
|
||||
]
|
||||
end
|
||||
|
||||
defp package do
|
||||
[ maintainers: ["Stephen Pallen"],
|
||||
[
|
||||
maintainers: ["Stephen Pallen"],
|
||||
licenses: ["MIT"],
|
||||
links: %{ "Github" => "https://github.com/smpallen99/auto_linker" },
|
||||
files: ~w(lib README.md mix.exs LICENSE)]
|
||||
links: %{"Github" => "https://github.com/smpallen99/auto_linker"},
|
||||
files: ~w(lib README.md mix.exs LICENSE)
|
||||
]
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,31 +2,29 @@ defmodule AutoLinkerTest do
|
|||
use ExUnit.Case
|
||||
doctest AutoLinker
|
||||
|
||||
|
||||
test "phone number" do
|
||||
assert AutoLinker.link(", work (555) 555-5555", phone: true) ==
|
||||
~s{, work <a href="#" class="phone-number" data-phone="5555555555">(555) 555-5555</a>}
|
||||
~s{, work <a href="#" class="phone-number" data-phone="5555555555">(555) 555-5555</a>}
|
||||
end
|
||||
|
||||
test "default link" do
|
||||
assert AutoLinker.link("google.com") ==
|
||||
"<a href='http://google.com' class='auto-linker' target='_blank' rel='noopener noreferrer'>google.com</a>"
|
||||
"<a href='http://google.com' class='auto-linker' target='_blank' rel='noopener noreferrer'>google.com</a>"
|
||||
end
|
||||
|
||||
test "markdown" do
|
||||
assert AutoLinker.link("[google.com](http://google.com)", markdown: true) ==
|
||||
"<a href='http://google.com' class='auto-linker' target='_blank' rel='noopener noreferrer'>google.com</a>"
|
||||
"<a href='http://google.com' class='auto-linker' target='_blank' rel='noopener noreferrer'>google.com</a>"
|
||||
end
|
||||
|
||||
test "does on link existing links" do
|
||||
assert AutoLinker.link("<a href='http://google.com'>google.com</a>") ==
|
||||
"<a href='http://google.com'>google.com</a>"
|
||||
"<a href='http://google.com'>google.com</a>"
|
||||
end
|
||||
|
||||
test "phone number and markdown link" do
|
||||
assert AutoLinker.link("888 888-8888 [ab](a.com)", phone: true, markdown: true) ==
|
||||
"<a href=\"#\" class=\"phone-number\" data-phone=\"8888888888\">888 888-8888</a>" <>
|
||||
" <a href='a.com' class='auto-linker' target='_blank' rel='noopener noreferrer'>ab</a>"
|
||||
"<a href=\"#\" class=\"phone-number\" data-phone=\"8888888888\">888 888-8888</a>" <>
|
||||
" <a href='a.com' class='auto-linker' target='_blank' rel='noopener noreferrer'>ab</a>"
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -8,17 +8,24 @@ defmodule AutoLinker.BuilderTest do
|
|||
test "finishes" do
|
||||
assert create_phone_link([], "", []) == ""
|
||||
end
|
||||
|
||||
test "handles one link" do
|
||||
phrase = "my exten is x888. Call me."
|
||||
expected = ~s'my exten is <a href="#" class="phone-number" data-phone="888">x888</a>. Call me.'
|
||||
|
||||
expected =
|
||||
~s'my exten is <a href="#" class="phone-number" data-phone="888">x888</a>. Call me.'
|
||||
|
||||
assert create_phone_link([["x888", ""]], phrase, []) == expected
|
||||
end
|
||||
|
||||
test "handles multiple links" do
|
||||
phrase = "555.555.5555 or (555) 888-8888"
|
||||
expected = ~s'<a href="#" class="phone-number" data-phone="5555555555">555.555.5555</a> or ' <>
|
||||
~s'<a href="#" class="phone-number" data-phone="5558888888">(555) 888-8888</a>'
|
||||
|
||||
expected =
|
||||
~s'<a href="#" class="phone-number" data-phone="5555555555">555.555.5555</a> or ' <>
|
||||
~s'<a href="#" class="phone-number" data-phone="5558888888">(555) 888-8888</a>'
|
||||
|
||||
assert create_phone_link([["555.555.5555", ""], ["(555) 888-8888"]], phrase, []) == expected
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -11,18 +11,21 @@ defmodule AutoLinker.ParserTest do
|
|||
assert is_url?(url, true)
|
||||
end)
|
||||
end
|
||||
|
||||
test "invalid scheme true" do
|
||||
invalid_scheme_urls()
|
||||
|> Enum.each(fn url ->
|
||||
refute is_url?(url, true)
|
||||
end)
|
||||
end
|
||||
|
||||
test "valid scheme false" do
|
||||
valid_non_scheme_urls()
|
||||
|> Enum.each(fn url ->
|
||||
assert is_url?(url, false)
|
||||
end)
|
||||
end
|
||||
|
||||
test "invalid scheme false" do
|
||||
invalid_non_scheme_urls()
|
||||
|> Enum.each(fn url ->
|
||||
|
@ -47,7 +50,6 @@ defmodule AutoLinker.ParserTest do
|
|||
end
|
||||
end
|
||||
|
||||
|
||||
describe "parse" do
|
||||
test "does not link attributes" do
|
||||
text = "Check out <a href='google.com'>google</a>"
|
||||
|
@ -61,7 +63,7 @@ defmodule AutoLinker.ParserTest do
|
|||
test "links url inside html" do
|
||||
text = "Check out <div class='section'>google.com</div>"
|
||||
expected = "Check out <div class='section'><a href='http://google.com'>google.com</a></div>"
|
||||
assert parse(text, class: false, rel: false, new_window: false) == expected
|
||||
assert parse(text, class: false, rel: false, new_window: false) == expected
|
||||
end
|
||||
|
||||
test "excludes html with specified class" do
|
||||
|
@ -76,69 +78,73 @@ defmodule AutoLinker.ParserTest do
|
|||
|
||||
def valid_number?(_, _), do: false
|
||||
|
||||
def valid_scheme_urls, do: [
|
||||
"https://www.example.com",
|
||||
"http://www2.example.com",
|
||||
"http://home.example-site.com",
|
||||
"http://blog.example.com",
|
||||
"http://www.example.com/product",
|
||||
"http://www.example.com/products?id=1&page=2",
|
||||
"http://www.example.com#up",
|
||||
"http://255.255.255.255",
|
||||
"http://www.site.com:8008"
|
||||
]
|
||||
def valid_scheme_urls,
|
||||
do: [
|
||||
"https://www.example.com",
|
||||
"http://www2.example.com",
|
||||
"http://home.example-site.com",
|
||||
"http://blog.example.com",
|
||||
"http://www.example.com/product",
|
||||
"http://www.example.com/products?id=1&page=2",
|
||||
"http://www.example.com#up",
|
||||
"http://255.255.255.255",
|
||||
"http://www.site.com:8008"
|
||||
]
|
||||
|
||||
def invalid_scheme_urls, do: [
|
||||
"http://invalid.com/perl.cgi?key= | http://web-site.com/cgi-bin/perl.cgi?key1=value1&key2",
|
||||
]
|
||||
def invalid_scheme_urls,
|
||||
do: [
|
||||
"http://invalid.com/perl.cgi?key= | http://web-site.com/cgi-bin/perl.cgi?key1=value1&key2"
|
||||
]
|
||||
|
||||
def valid_non_scheme_urls, do: [
|
||||
"www.example.com",
|
||||
"www2.example.com",
|
||||
"www.example.com:2000",
|
||||
"www.example.com?abc=1",
|
||||
"example.example-site.com",
|
||||
"example.com",
|
||||
"example.ca",
|
||||
"example.tv",
|
||||
"example.com:999?one=one",
|
||||
"255.255.255.255",
|
||||
"255.255.255.255:3000?one=1&two=2",
|
||||
]
|
||||
def valid_non_scheme_urls,
|
||||
do: [
|
||||
"www.example.com",
|
||||
"www2.example.com",
|
||||
"www.example.com:2000",
|
||||
"www.example.com?abc=1",
|
||||
"example.example-site.com",
|
||||
"example.com",
|
||||
"example.ca",
|
||||
"example.tv",
|
||||
"example.com:999?one=one",
|
||||
"255.255.255.255",
|
||||
"255.255.255.255:3000?one=1&two=2"
|
||||
]
|
||||
|
||||
def invalid_non_scheme_urls, do: [
|
||||
"invalid.com/perl.cgi?key= | web-site.com/cgi-bin/perl.cgi?key1=value1&key2",
|
||||
"invalid.",
|
||||
"hi..there",
|
||||
"555.555.5555"
|
||||
]
|
||||
def invalid_non_scheme_urls,
|
||||
do: [
|
||||
"invalid.com/perl.cgi?key= | web-site.com/cgi-bin/perl.cgi?key1=value1&key2",
|
||||
"invalid.",
|
||||
"hi..there",
|
||||
"555.555.5555"
|
||||
]
|
||||
|
||||
def valid_phone_nunbers, do: [
|
||||
"x55",
|
||||
"x555",
|
||||
"x5555",
|
||||
"x12345",
|
||||
"+1 555 555-5555",
|
||||
"555 555-5555",
|
||||
"555.555.5555",
|
||||
"613-555-5555",
|
||||
"1 (555) 555-5555",
|
||||
"(555) 555-5555",
|
||||
"1.555.555.5555",
|
||||
"800 555-5555",
|
||||
"1.800.555.5555",
|
||||
"1 (800) 555-5555",
|
||||
"888 555-5555",
|
||||
"887 555-5555",
|
||||
"1-877-555-5555",
|
||||
"1 800 710-5515"
|
||||
]
|
||||
|
||||
def invalid_phone_numbers, do: [
|
||||
"5555",
|
||||
"x5",
|
||||
"(555) 555-55",
|
||||
]
|
||||
def valid_phone_nunbers,
|
||||
do: [
|
||||
"x55",
|
||||
"x555",
|
||||
"x5555",
|
||||
"x12345",
|
||||
"+1 555 555-5555",
|
||||
"555 555-5555",
|
||||
"555.555.5555",
|
||||
"613-555-5555",
|
||||
"1 (555) 555-5555",
|
||||
"(555) 555-5555",
|
||||
"1.555.555.5555",
|
||||
"800 555-5555",
|
||||
"1.800.555.5555",
|
||||
"1 (800) 555-5555",
|
||||
"888 555-5555",
|
||||
"887 555-5555",
|
||||
"1-877-555-5555",
|
||||
"1 800 710-5515"
|
||||
]
|
||||
|
||||
def invalid_phone_numbers,
|
||||
do: [
|
||||
"5555",
|
||||
"x5",
|
||||
"(555) 555-55"
|
||||
]
|
||||
end
|
||||
|
||||
|
|
Loading…
Reference in a new issue