Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring ok #434

Merged
merged 1 commit into from
Oct 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 5 additions & 40 deletions lib/earmark.ex
Original file line number Diff line number Diff line change
Expand Up @@ -218,8 +218,7 @@ defmodule Earmark do
and are to serve the produced HTML on the Web.
"""

alias Earmark.Error
alias Earmark.Options
alias Earmark.{Error, Internal, Options, Transform}
import Earmark.Message, only: [emit_messages: 2]

@doc ~S"""
Expand Down Expand Up @@ -251,8 +250,8 @@ defmodule Earmark do
end

def as_html(lines, options) do
{status, ast, messages} = postprocessed_ast(lines, options)
{status, Earmark.Transform.transform(ast, options), messages}
{status, ast, messages} = Transform.postprocessed_ast(lines, options)
{status, Transform.transform(ast, options), messages}
end

@doc """
Expand All @@ -269,20 +268,6 @@ defmodule Earmark do
{status, ast, messages1}
end

@line_end ~r{\n\r?}
def postprocessed_ast(lines, options \\ %Options{})
def postprocessed_ast(lines, options) when is_binary(lines), do: lines |> String.split(@line_end) |> postprocessed_ast(options)
# This is an optimisation (buuuuuh) but we want a minimal impact of postprocessing code when it is not required
# It is also a case of the mantra "Handle the simple case first" (yeeeeah)
def postprocessed_ast(lines, %Options{registered_processors: [], postprocessor: nil}=options), do: EarmarkParser.as_ast(lines, options)
def postprocessed_ast(lines, %Options{}=options) do
{status, ast, messages} = EarmarkParser.as_ast(lines, options)
prep = Earmark.Transform.make_postprocessor(options)
ast1 = Earmark.Transform.map_ast(ast, prep, Map.get(options, :ignore_strings))
{status, ast1, messages}
end
def postprocessed_ast(lines, options), do: postprocessed_ast(lines, Options.make_options!(options))

@doc """
A convenience method that *always* returns an HTML representation of the markdown document passed in.
In case of the presence of any error messages they are printed to stderr.
Expand All @@ -296,7 +281,7 @@ defmodule Earmark do
html
end

defdelegate transform(ast, options \\ []), to: Earmark.Transform
defdelegate transform(ast, options \\ []), to: Transform

@doc """
Accesses current hex version of the `Earmark` application. Convenience for
Expand All @@ -308,13 +293,7 @@ defmodule Earmark do
end

@default_timeout_in_ms 5000
@doc false
def pmap(collection, func, timeout \\ @default_timeout_in_ms) do
collection
|> Enum.map(fn item -> Task.async(fn -> func.(item) end) end)
|> Task.yield_many(timeout)
|> Enum.map(&_join_pmap_results_or_raise(&1, timeout))
end
defdelegate pmap(collection, func, timeout \\ @default_timeout_in_ms), to: Internal

defp _as_ast(lines, options)

Expand All @@ -325,19 +304,5 @@ defmodule Earmark do
defp _as_ast(lines, options) do
EarmarkParser.as_ast(lines, options)
end

defp _join_pmap_results_or_raise(yield_tuples, timeout)
defp _join_pmap_results_or_raise({_task, {:ok, result}}, _timeout), do: result

defp _join_pmap_results_or_raise({task, {:error, reason}}, _timeout),
do: raise(Error, "#{inspect(task)} has died with reason #{inspect(reason)}")

defp _join_pmap_results_or_raise({task, nil}, timeout),
do:
raise(
Error,
"#{inspect(task)} has not responded within the set timeout of #{timeout}ms, consider increasing it"
)

end
# SPDX-License-Identifier: Apache-2.0
32 changes: 32 additions & 0 deletions lib/earmark/internal.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
defmodule Earmark.Internal do

alias Earmark.Error
@moduledoc ~S"""
All public functions that are internal to Earmark, so that **only** external API
functions are public in `Earmark`
"""

@default_timeout_in_ms 5000
@doc false
def pmap(collection, func, timeout \\ @default_timeout_in_ms) do
collection
|> Enum.map(fn item -> Task.async(fn -> func.(item) end) end)
|> Task.yield_many(timeout)
|> Enum.map(&_join_pmap_results_or_raise(&1, timeout))
end

defp _join_pmap_results_or_raise(yield_tuples, timeout)
defp _join_pmap_results_or_raise({_task, {:ok, result}}, _timeout), do: result

defp _join_pmap_results_or_raise({task, {:error, reason}}, _timeout),
do: raise(Error, "#{inspect(task)} has died with reason #{inspect(reason)}")

defp _join_pmap_results_or_raise({task, nil}, timeout),
do:
raise(
Error,
"#{inspect(task)} has not responded within the set timeout of #{timeout}ms, consider increasing it"
)

end
# SPDX-License-Identifier: Apache-2.0
14 changes: 14 additions & 0 deletions lib/earmark/transform.ex
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,20 @@
def make_postprocessor(%{postprocessor: nil, registered_processors: rps}), do: _make_postprocessor(rps)
def make_postprocessor(%{postprocessor: pp, registered_processors: rps}), do: _make_postprocessor([pp|rps])

@line_end ~r{\n\r?}
def postprocessed_ast(lines, options \\ %Options{})
def postprocessed_ast(lines, options) when is_binary(lines), do: lines |> String.split(@line_end) |> postprocessed_ast(options)
# This is an optimisation (buuuuuh) but we want a minimal impact of postprocessing code when it is not required
# It is also a case of the mantra "Handle the simple case first" (yeeeeah)
def postprocessed_ast(lines, %Options{registered_processors: [], postprocessor: nil}=options), do: EarmarkParser.as_ast(lines, options)
def postprocessed_ast(lines, %Options{}=options) do
{status, ast, messages} = EarmarkParser.as_ast(lines, options)
prep = make_postprocessor(options)
ast1 = map_ast(ast, prep, Map.get(options, :ignore_strings))
{status, ast1, messages}
end
def postprocessed_ast(lines, options), do: postprocessed_ast(lines, Options.make_options!(options))

@doc """
Transforms an AST to html, also accepts the result of `map_ast_with` for convenience
"""
Expand Down
3 changes: 1 addition & 2 deletions test/acceptance/postprocessor_test.exs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
defmodule Acceptance.PostprocessorTest do
use ExUnit.Case


describe "nop" do
test "empty edge case" do
assert post("", id()) == {:ok, [], []}
Expand Down Expand Up @@ -36,7 +35,7 @@ defmodule Acceptance.PostprocessorTest do


defp post(markdown, fun, ignore_strings \\ false) do
Earmark.postprocessed_ast(markdown, %{postprocessor: fun, ignore_strings: ignore_strings})
Earmark.Transform.postprocessed_ast(markdown, %{postprocessor: fun, ignore_strings: ignore_strings})
end
defp id() do
fn x -> x end
Expand Down