Commit c8551dc3 authored by rinpatch's avatar rinpatch

Add ability to configure sname and spawn timeout and change decode to

respond with a tuple
parent ec1e3cf1
......@@ -8,7 +8,7 @@ defmodule :fast_html do
## Example
iex> :fast_html.decode("<h1>Hello world</h1>")
{"html", [], [{"head", [], []}, {"body", [], [{"h1", [], ["Hello world"]}]}]}
{:ok, {"html", [], [{"head", [], []}, {"body", [], [{"h1", [], ["Hello world"]}]}]}}
Benchmark results (removed Nif calling mode) on various file sizes on a 2,5Ghz Core i7:
......@@ -47,20 +47,20 @@ defmodule :fast_html do
## Examples
iex> :fast_html.decode("<h1>Hello world</h1>")
{"html", [], [{"head", [], []}, {"body", [], [{"h1", [], ["Hello world"]}]}]}
{:ok, {"html", [], [{"head", [], []}, {"body", [], [{"h1", [], ["Hello world"]}]}]}}
iex> :fast_html.decode("<span class='hello'>Hi there</span>")
{"html", [],
{:ok, {"html", [],
[{"head", [], []},
{"body", [], [{"span", [{"class", "hello"}], ["Hi there"]}]}]}
{"body", [], [{"span", [{"class", "hello"}], ["Hi there"]}]}]}}
iex> :fast_html.decode("<body><!-- a comment --!></body>")
{"html", [], [{"head", [], []}, {"body", [], [comment: " a comment "]}]}
{:ok, {"html", [], [{"head", [], []}, {"body", [], [comment: " a comment "]}]}}
iex> :fast_html.decode("<br>")
{"html", [], [{"head", [], []}, {"body", [], [{"br", [], []}]}]}
{:ok, {"html", [], [{"head", [], []}, {"body", [], [{"br", [], []}]}]}}
"""
@spec decode(String.t()) :: tree()
@spec decode(String.t()) :: {:ok, tree()} | {:error, String.t() | atom()}
def decode(bin) do
decode(bin, format: [])
end
......@@ -79,24 +79,24 @@ defmodule :fast_html do
## Examples
iex> :fast_html.decode("<h1>Hello world</h1>", format: [:html_atoms])
{:html, [], [{:head, [], []}, {:body, [], [{:h1, [], ["Hello world"]}]}]}
{:ok, {:html, [], [{:head, [], []}, {:body, [], [{:h1, [], ["Hello world"]}]}]}}
iex> :fast_html.decode("<br>", format: [:nil_self_closing])
{"html", [], [{"head", [], []}, {"body", [], [{"br", [], nil}]}]}
{:ok, {"html", [], [{"head", [], []}, {"body", [], [{"br", [], nil}]}]}}
iex> :fast_html.decode("<body><!-- a comment --!></body>", format: [:comment_tuple3])
{"html", [], [{"head", [], []}, {"body", [], [{:comment, [], " a comment "}]}]}
{:ok, {"html", [], [{"head", [], []}, {"body", [], [{:comment, [], " a comment "}]}]}}
iex> html = "<body><!-- a comment --!><unknown /></body>"
iex> :fast_html.decode(html, format: [:html_atoms, :nil_self_closing, :comment_tuple3])
{:html, [],
{:ok, {:html, [],
[{:head, [], []},
{:body, [], [{:comment, [], " a comment "}, {"unknown", [], nil}]}]}
{:body, [], [{:comment, [], " a comment "}, {"unknown", [], nil}]}]}}
"""
@spec decode(String.t(), format: [format_flag()]) :: tree()
@spec decode(String.t(), format: [format_flag()]) ::
{:ok, tree()} | {:error, String.t() | atom()}
def decode(bin, format: flags) do
{:ok, res} = FastHtml.Cnode.call({:decode, bin, flags})
res
FastHtml.Cnode.call({:decode, bin, flags})
end
end
......@@ -11,7 +11,10 @@ defmodule FastHtml.Application do
_ -> :ok
end
Supervisor.start_link([FastHtml.Cnode], strategy: :one_for_one, name: FastHtml.Supervisor)
Supervisor.start_link([{FastHtml.Cnode, Application.get_env(:fast_html, :cnode, [])}],
strategy: :one_for_one,
name: FastHtml.Supervisor
)
end
defp maybe_setup_node() do
......
defmodule FastHtml.Cnode do
@moduledoc false
@moduledoc """
Manages myhtml c-node.
## Configuration
```elixir
config :fast_html, :cnode,
sname: "myhtml_worker", # Defaults to myhtml_<random bytes>
spawn_inactive_timeout: 5000 # Defaults to 10000
```
"""
@spawn_inactive_timeout 10000
......@@ -8,25 +17,20 @@ defmodule FastHtml.Cnode do
use GenServer
require Logger
@doc false
def start_link(args) do
GenServer.start_link(__MODULE__, args, name: __MODULE__)
end
@doc false
def init(args) do
args =
if args == [] do
%{}
else
args
end
exec_path = Path.join(:code.priv_dir(unquote(application)), "myhtml_worker")
sname = Map.get_lazy(args, :sname, &default_sname/0)
hostname = Map.get_lazy(args, :hostname, &master_hostname/0)
sname = Keyword.get_lazy(args, :sname, &default_sname/0)
hostname = master_hostname()
addr = :"#{sname}@#{hostname}"
spawn_inactive_timeout = Map.get(args, :spawn_inactive_timeout, @spawn_inactive_timeout)
spawn_inactive_timeout = Keyword.get(args, :spawn_inactive_timeout, @spawn_inactive_timeout)
state = %{
exec_path: exec_path,
......@@ -106,24 +110,29 @@ defmodule FastHtml.Cnode do
end
end
@doc false
def handle_info({:nodedown, _cnode}, state) do
{:stop, :nodedown, state}
end
@doc false
def handle_info(msg, state) do
Logger.warn("unhandled handle_info: #{inspect(msg)}")
{:noreply, state}
end
@doc false
def handle_call(:addr, _from, %{addr: addr} = state) do
{:reply, addr, state}
end
@doc false
def terminate(_reason, %{pid: pid}) when pid != nil do
System.cmd("kill", ["-9", to_string(pid)])
:normal
end
@doc "Call into myhtml cnode"
def call(msg, timeout \\ 10000) do
node = GenServer.call(__MODULE__, :addr)
send({nil, node}, msg)
......
......@@ -3,138 +3,150 @@ defmodule :fast_html_test do
doctest :fast_html
test "doesn't segfault when <!----> is encountered" do
assert {"html", _attrs, _children} = :fast_html.decode("<div> <!----> </div>")
assert {:ok, {"html", _attrs, _children}} = :fast_html.decode("<div> <!----> </div>")
end
test "builds a tree, formatted like mochiweb by default" do
assert {"html", [],
[
{"head", [], []},
{"body", [],
[
{"br", [], []}
]}
]} = :fast_html.decode("<br>")
assert {:ok,
{"html", [],
[
{"head", [], []},
{"body", [],
[
{"br", [], []}
]}
]}} = :fast_html.decode("<br>")
end
test "builds a tree, html tags as atoms" do
assert {:html, [],
[
{:head, [], []},
{:body, [],
[
{:br, [], []}
]}
]} = :fast_html.decode("<br>", format: [:html_atoms])
assert {:ok,
{:html, [],
[
{:head, [], []},
{:body, [],
[
{:br, [], []}
]}
]}} = :fast_html.decode("<br>", format: [:html_atoms])
end
test "builds a tree, nil self closing" do
assert {"html", [],
[
{"head", [], []},
{"body", [],
[
{"br", [], nil},
{"esi:include", [], nil}
]}
]} = :fast_html.decode("<br><esi:include />", format: [:nil_self_closing])
assert {:ok,
{"html", [],
[
{"head", [], []},
{"body", [],
[
{"br", [], nil},
{"esi:include", [], nil}
]}
]}} = :fast_html.decode("<br><esi:include />", format: [:nil_self_closing])
end
test "builds a tree, multiple format options" do
assert {:html, [],
[
{:head, [], []},
{:body, [],
[
{:br, [], nil}
]}
]} = :fast_html.decode("<br>", format: [:html_atoms, :nil_self_closing])
assert {:ok,
{:html, [],
[
{:head, [], []},
{:body, [],
[
{:br, [], nil}
]}
]}} = :fast_html.decode("<br>", format: [:html_atoms, :nil_self_closing])
end
test "attributes" do
assert {:html, [],
[
{:head, [], []},
{:body, [],
[
{:span, [{"id", "test"}, {"class", "foo garble"}], []}
]}
]} =
assert {:ok,
{:html, [],
[
{:head, [], []},
{:body, [],
[
{:span, [{"id", "test"}, {"class", "foo garble"}], []}
]}
]}} =
:fast_html.decode(~s'<span id="test" class="foo garble"></span>',
format: [:html_atoms]
)
end
test "single attributes" do
assert {:html, [],
[
{:head, [], []},
{:body, [],
[
{:button, [{"disabled", "disabled"}, {"class", "foo garble"}], []}
]}
]} =
assert {:ok,
{:html, [],
[
{:head, [], []},
{:body, [],
[
{:button, [{"disabled", "disabled"}, {"class", "foo garble"}], []}
]}
]}} =
:fast_html.decode(~s'<button disabled class="foo garble"></span>',
format: [:html_atoms]
)
end
test "text nodes" do
assert {:html, [],
[
{:head, [], []},
{:body, [],
[
"text node"
]}
]} = :fast_html.decode(~s'<body>text node</body>', format: [:html_atoms])
assert {:ok,
{:html, [],
[
{:head, [], []},
{:body, [],
[
"text node"
]}
]}} = :fast_html.decode(~s'<body>text node</body>', format: [:html_atoms])
end
test "broken input" do
assert {:html, [],
[
{:head, [], []},
{:body, [],
[
{:a, [{"<", "<"}], [" asdf"]}
]}
]} = :fast_html.decode(~s'<a <> asdf', format: [:html_atoms])
assert {:ok,
{:html, [],
[
{:head, [], []},
{:body, [],
[
{:a, [{"<", "<"}], [" asdf"]}
]}
]}} = :fast_html.decode(~s'<a <> asdf', format: [:html_atoms])
end
test "namespaced tags" do
assert {:html, [],
[
{:head, [], []},
{:body, [],
[
{"svg:svg", [],
[
{"svg:path", [], []},
{"svg:a", [], []}
]}
]}
]} = :fast_html.decode(~s'<svg><path></path><a></a></svg>', format: [:html_atoms])
assert {:ok,
{:html, [],
[
{:head, [], []},
{:body, [],
[
{"svg:svg", [],
[
{"svg:path", [], []},
{"svg:a", [], []}
]}
]}
]}} = :fast_html.decode(~s'<svg><path></path><a></a></svg>', format: [:html_atoms])
end
test "custom namespaced tags" do
assert {:html, [],
[
{:head, [], []},
{:body, [],
[
{"esi:include", [], nil}
]}
]} = :fast_html.decode(~s'<esi:include />', format: [:html_atoms, :nil_self_closing])
assert {:ok,
{:html, [],
[
{:head, [], []},
{:body, [],
[
{"esi:include", [], nil}
]}
]}} =
:fast_html.decode(~s'<esi:include />', format: [:html_atoms, :nil_self_closing])
end
test "html comments" do
assert {:html, [],
[
{:head, [], []},
{:body, [],
[
comment: " a comment "
]}
]} = :fast_html.decode(~s'<body><!-- a comment --></body>', format: [:html_atoms])
assert {:ok,
{:html, [],
[
{:head, [], []},
{:body, [],
[
comment: " a comment "
]}
]}} = :fast_html.decode(~s'<body><!-- a comment --></body>', format: [:html_atoms])
end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment