Browse Source

add formatter

merge-requests/1/head
Egor Kislitsyn 1 year ago
parent
commit
34e4e2f953
8 changed files with 170 additions and 119 deletions
  1. +4
    -0
      .formatter.exs
  2. +1
    -2
      lib/auto_linker.ex
  3. +18
    -9
      lib/auto_linker/builder.ex
  4. +48
    -22
      lib/auto_linker/parser.ex
  5. +9
    -7
      mix.exs
  6. +6
    -8
      test/auto_linker_test.exs
  7. +11
    -4
      test/builder_test.exs
  8. +73
    -67
      test/parser_test.exs

+ 4
- 0
.formatter.exs

@ -0,0 +1,4 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

+ 1
- 2
lib/auto_linker.ex

@ -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

+ 18
- 9
lib/auto_linker/builder.ex

@ -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

+ 48
- 22
lib/auto_linker/parser.ex

@ -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

+ 9
- 7
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

+ 6
- 8
test/auto_linker_test.exs

@ -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

+ 11
- 4
test/builder_test.exs

@ -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

+ 73
- 67
test/parser_test.exs

@ -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 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 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_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 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 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…
Cancel
Save