Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F116133
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
7 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/lib/auto_linker/parser.ex b/lib/auto_linker/parser.ex
index 7cef83f..88ecd7a 100644
--- a/lib/auto_linker/parser.ex
+++ b/lib/auto_linker/parser.ex
@@ -1,96 +1,107 @@
defmodule AutoLinker.Parser do
@moduledoc """
Module to handle parsing the the input string.
"""
alias AutoLinker.Builder
@doc """
Parse the given string.
Parses the string, replacing the matching urls with an html link.
## Examples
iex> AutoLinker.Parser.parse("Check out google.com")
"Check out <a href='http://google.com' class='auto-linker' target='_blank' rel='noopener noreferrer'>google.com</a>"
"""
+ @match_dots ~r/\.\.+/
+
+ @match_url ~r{^[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$}
+ @match_scheme ~r{^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$}
+
def parse(text, opts \\ %{})
def parse(text, list) when is_list(list), do: parse(text, Enum.into(list, %{}))
def parse(text, opts) do
if (exclude = Map.get(opts, :exclude_pattern, false)) && String.starts_with?(text, exclude) do
text
else
parse(text, Map.get(opts, :scheme, false), opts, {"", "", :parsing})
end
end
# state = {buffer, acc, state}
defp parse("", _scheme, _opts ,{"", acc, _}),
do: acc
defp parse("", scheme, opts ,{buffer, acc, _}),
do: acc <> check_and_link(buffer, scheme, opts)
defp parse("<" <> text, scheme, opts, {"", acc, :parsing}),
do: parse(text, scheme, opts, {"<", acc, {:open, 1}})
defp parse(">" <> text, scheme, opts, {buffer, acc, {:attrs, level}}),
do: parse(text, scheme, opts, {"", acc <> buffer <> ">", {:html, level}})
defp parse(<<ch::8>> <> text, scheme, opts, {"", acc, {:attrs, level}}),
do: parse(text, scheme, opts, {"", acc <> <<ch::8>>, {:attrs, level}})
defp parse("</" <> text, scheme, opts, {buffer, acc, {:html, level}}),
do: parse(text, scheme, opts,
{"", acc <> check_and_link(buffer, scheme, opts) <> "</", {:close, level}})
defp parse(">" <> text, scheme, opts, {buffer, acc, {:close, 1}}),
do: parse(text, scheme, opts, {"", acc <> buffer <> ">", :parsing})
defp parse(">" <> text, scheme, opts, {buffer, acc, {:close, level}}),
do: parse(text, scheme, opts, {"", acc <> buffer <> ">", {:html, level - 1}})
defp parse(" " <> text, scheme, opts, {buffer, acc, {:open, level}}),
do: parse(text, scheme, opts, {"", acc <> buffer <> " ", {:attrs, level}})
defp parse("\n" <> text, scheme, opts, {buffer, acc, {:open, level}}),
do: parse(text, scheme, opts, {"", acc <> buffer <> "\n", {:attrs, level}})
# default cases where state is not important
defp parse(" " <> text, scheme, opts, {buffer, acc, state}),
do: parse(text, scheme, opts,
{"", acc <> check_and_link(buffer, scheme, opts) <> " ", state})
defp parse("\n" <> text, scheme, opts, {buffer, acc, state}),
do: parse(text, scheme, opts,
{"", acc <> check_and_link(buffer, scheme, opts) <> "\n", state})
defp parse(<<ch::8>> <> text, scheme, opts, {buffer, acc, state}),
do: parse(text, scheme, opts, {buffer <> <<ch::8>>, acc, state})
defp check_and_link(buffer, scheme, opts) do
buffer
|> is_url?(scheme)
|> link_url(buffer, opts)
end
@doc false
def is_url?(buffer, true) do
- re = ~r{^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$}
- Regex.match? re, buffer
+ if Regex.match? @match_dots, buffer do
+ false
+ else
+ Regex.match? @match_scheme, buffer
+ end
end
def is_url?(buffer, _) do
- re = ~r{^[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$}
- Regex.match? re, buffer
+ if Regex.match? @match_dots, buffer do
+ false
+ else
+ Regex.match? @match_url, buffer
+ end
end
@doc false
def link_url(true, buffer, opts) do
Builder.create_link(buffer, opts)
end
def link_url(_, buffer, _opts), do: buffer
end
diff --git a/mix.exs b/mix.exs
index 3a3bbeb..5954486 100644
--- a/mix.exs
+++ b/mix.exs
@@ -1,43 +1,43 @@
defmodule AutoLinker.Mixfile do
use Mix.Project
- @version "0.1.0"
+ @version "0.1.1"
def project do
[
app: :auto_linker,
version: @version,
elixir: "~> 1.4",
build_embedded: Mix.env == :prod,
start_permanent: Mix.env == :prod,
deps: deps(),
docs: [extras: ["README.md"]],
package: package(),
name: "AutoLinker",
description: """
AutoLinker is a basic package for turning website names into links.
"""
]
end
# Configuration for the OTP application
def application do
# Specify extra applications you'll use from Erlang/Elixir
[extra_applications: [:logger]]
end
# Dependencies can be Hex packages:
defp deps do
[
{:ex_doc, "~> 0.15", only: :dev},
{:earmark, "~> 1.2", only: :dev, override: true},
]
end
defp package do
[ maintainers: ["Stephen Pallen"],
licenses: ["MIT"],
links: %{ "Github" => "https://github.com/smpallen99/auto_linker" },
files: ~w(lib README.md mix.exs LICENSE)]
end
end
diff --git a/test/parser_test.exs b/test/parser_test.exs
index 3553ddb..5c43a10 100644
--- a/test/parser_test.exs
+++ b/test/parser_test.exs
@@ -1,92 +1,93 @@
defmodule AutoLinker.ParserTest do
use ExUnit.Case
doctest AutoLinker.Parser
import AutoLinker.Parser
describe "is_url" do
test "valid scheme true" do
valid_scheme_urls()
|> Enum.each(fn url ->
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 ->
refute is_url?(url, false)
end)
end
end
describe "parse" do
test "does not link attributes" do
text = "Check out <a href='google.com'>google</a>"
assert parse(text) == text
text = "Check out <img src='google.com' alt='google.com'/>"
assert parse(text) == text
text = "Check out <span><img src='google.com' alt='google.com'/></span>"
assert parse(text) == text
end
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
end
test "excludes html with specified class" do
text = "```Check out <div class='section'>google.com</div>```"
assert parse(text, exclude_pattern: "```") == text
end
end
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."
+ "invalid.",
+ "hi..there"
]
end
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sat, Nov 30, 1:41 PM (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
41436
Default Alt Text
(7 KB)
Attached To
Mode
R19 linkify
Attached
Detach File
Event Timeline
Log In to Comment