Skip to content

Commit

Permalink
Ensure parsers are compiled (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
qcam authored May 18, 2020
1 parent 4bbf6c9 commit d127643
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 41 deletions.
91 changes: 55 additions & 36 deletions lib/nabo/compiler.ex
Original file line number Diff line number Diff line change
@@ -1,48 +1,71 @@
defmodule Nabo.Compiler do
@moduledoc false

@default_pattern ~r/[\s\r\n]---[\s\r\n]/s
alias Nabo.Post

alias Nabo.{
Parser,
Post
}
defmodule Options do
@moduledoc false

def compile(data, options) do
{front_parser, front_parser_opts} =
Keyword.get(options, :front_parser, {Parser.Front, []})
defstruct [
metadata_parser: {Nabo.Parser.Front, []},
excerpt_parser: {Nabo.Parser.Markdown, []},
body_parser: {Nabo.Parser.Markdown, []},
split_pattern: ~r/[\s\r\n]---[\s\r\n]/s,
log_level: :warn
]

def new(options) when is_list(options) do
options = struct!(__MODULE__, options)

{excerpt_parser, excerpt_parser_opts} =
Keyword.get(options, :excerpt_parser, {Parser.Markdown, []})
ensure_parsers(options)

{body_parser, body_parser_opts} =
Keyword.get(options, :body_parser, {Parser.Markdown, []})
options
end

defp ensure_parsers(options) do
options
|> Map.take([:metadata_parser, :excerpt_parser, :body_parser])
|> Map.values()
|> Enum.each(&ensure_parser/1)
end

split_pattern =
Keyword.get(options, :split_pattern, @default_pattern)
defp ensure_parser({module, _}) do
case Code.ensure_compiled(module) do
{:module, ^module} ->
if function_exported?(module, :parse, 2) do
module
else
raise ArgumentError, "Expect parser to be a Nabo.Parser"
end

assert_parsers_loaded!([front_parser, excerpt_parser, body_parser])
{:error, reason} ->
raise ArgumentError, "Configured parser #{inspect(module)} is not available due to #{inspect(reason)}"
end
end
end

case split_parts(data, split_pattern) do
{:ok, {front, excerpt, body}} ->
with {:ok, metadata} <- front_parser.parse(front, front_parser_opts),
{:ok, parsed_excerpt} <- excerpt_parser.parse(excerpt, excerpt_parser_opts),
{:ok, parsed_body} <- body_parser.parse(body, body_parser_opts) do
post = Post.new(metadata, excerpt, parsed_excerpt, body, parsed_body)
{:ok, post}
else
{:error, reason} ->
{:error, reason}
end
def compile(data, options) do
%Options{
metadata_parser: metadata_parser,
excerpt_parser: excerpt_parser,
body_parser: body_parser,
split_pattern: split_pattern
} = options

with {:ok, {front, excerpt, body}} <- split_parts(data, split_pattern),
{:ok, metadata} <- parse(front, metadata_parser),
{:ok, parsed_excerpt} <- parse(excerpt, excerpt_parser),
{:ok, parsed_body} <- parse(body, body_parser) do
{:ok, Post.new(metadata, excerpt, parsed_excerpt, body, parsed_body)}
else
{:error, reason} ->
{:error, reason}
end
end

defp split_parts(data, pattern) do
defp split_parts(raw_post, pattern) do
parts =
data
raw_post
|> String.trim_leading()
|> String.split(pattern, parts: 3)

Expand All @@ -53,16 +76,12 @@ defmodule Nabo.Compiler do
[front, excerpt, body] ->
{:ok, {front, excerpt, body}}

_other ->
{:error, "bad post format"}
_ ->
{:error, "cannot split post with the configured pattern #{inspect(pattern)}"}
end
end

defp assert_parsers_loaded!(parsers) do
Enum.each(parsers, fn parser ->
if not Code.ensure_loaded?(parser) do
raise ArgumentError, "Configured parser #{parser} is not available"
end
end)
defp parse(data, {parser, parser_options}) do
apply(parser, :parse, [data, parser_options])
end
end
7 changes: 5 additions & 2 deletions lib/nabo/repo.ex
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,10 @@ defmodule Nabo.Repo do
|> Keyword.fetch!(:root)
|> Path.relative_to_cwd()

compiler_options = Keyword.get(options, :compiler, [])
compiler_options =
options
|> Keyword.get(:compiler, [])
|> Nabo.Compiler.Options.new()

@root_path root_path
@compiler_options compiler_options
Expand Down Expand Up @@ -125,7 +128,7 @@ defmodule Nabo.Repo do
end

defp compile(path, options) do
log_level = Keyword.get(options, :log_level, :warn)
log_level = options.log_level
content = File.read!(path)

case Nabo.Compiler.compile(content, options) do
Expand Down
9 changes: 6 additions & 3 deletions test/nabo/compiler_test.exs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
defmodule Nabo.CompilerTest do
use ExUnit.Case, async: true

import Nabo.Compiler, only: [compile: 2]

defmodule IncompetentFrontParser do
@behaviour Nabo.Parser

Expand Down Expand Up @@ -77,10 +75,11 @@ defmodule Nabo.CompilerTest do
"""

options = [
front_parser: {IncompetentFrontParser, []},
metadata_parser: {IncompetentFrontParser, []},
excerpt_parser: {NegateParser, []},
body_parser: {NegateParser, []}
]

assert {:ok, post} = compile(raw_post, options)
assert post.title == "Incompetent Title"
assert post.slug == "incompetent-slug"
Expand All @@ -90,4 +89,8 @@ defmodule Nabo.CompilerTest do
assert post.body == "This is the **BODY**.\n"
assert post.body_html == "This IS NOT the **BODY**."
end

defp compile(data, options) do
Nabo.Compiler.compile(data, Nabo.Compiler.Options.new(options))
end
end

0 comments on commit d127643

Please sign in to comment.