Skip to content

Commit

Permalink
remainder of exml -> exmerl
Browse files Browse the repository at this point in the history
  • Loading branch information
pwoolcoc committed May 14, 2014
1 parent 4035498 commit dbb4ca1
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 0 deletions.
53 changes: 53 additions & 0 deletions lib/exmerl.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
defmodule Exmerl do
@moduledoc ~s"""
Exmerl is an Elixir wrapper around xmerl_*.
Currently not very much is supported, only parsing
xml into elixir data, and selecting elements through Xpath
expressions.
"""

@typedoc "The parsed XML document"
@type document :: term

@typedoc "Rest"
@type rest :: [term]

@typedoc "keys for option lists"
@type key :: atom

@typedoc "values for option lists"
@type value :: term

@doc ~s"""
Parses xml from a file. See http://www.erlang.org/doc/man/xmerl_scan.html
for a description of the available options
"""
@spec from_file(String.t, [{key, value}]) :: {document, rest}
def from_file(fname, opts \\ []) do
:xmerl_scan.file(fname, opts)
end

@doc ~s"""
Parses xml from a string. See http://www.erlang.org/doc/man/xmerl_scan.html
for a description of the available options
"""
@spec from_string(String.t, [{key, value}]) :: {document, rest}
def from_string(str, opts \\ []) do
:xmerl_scan.string(to_char_list(str), opts)
end

@doc ~s"""
Tries to guess wether the first argument is a filename or not, and calls the
appropriate function. See http://www.erlang.org/doc/man/xmerl_scan.html
for a description of the available options
"""
@spec parse(String.t, [{key, value}]) :: {document, rest}
def parse(string_or_fname, opts \\ []) do
if File.exists?(string_or_fname) do
from_file(string_or_fname, opts)
else
from_string(string_or_fname, opts)
end
end
end
73 changes: 73 additions & 0 deletions lib/exmerl/xpath.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
defmodule Exmerl.XPath do
@moduledoc ~s"""
Select xml elements by an XPath selector
Currently only provides one function, `find/2,3,5`
"""

@typedoc "xml document"
@type document :: term

@typedoc "Rest"
@type rest :: [term]

@typedoc "xml element"
@type element :: term

@typedoc "key for option list"
@type key :: atom

@typedoc "value for option list"
@type value :: term

@typedoc "xpath string"
@type xpath_string :: String.t

@typedoc "xpath result"
@type xpath_result :: term

@doc ~s"""
Select one or more XML elements using an XPath expression
An alternate form of this allows `{document, rest}` to be passed in
as the first parameter, and returns it along with the xpath result,
for easy piping from Exmerl.parse calls.
Example:
iex(1)> Exmerl.parse("my_xml_file.xml") |> Exmerl.XPath.find("//somenode")
{
# this is the actual result from the .find call
[ ... ],
# this is the `rest` result from the .parse call
[ ... ]
}
iex(2)>
"""
@spec find(document, xpath_string, [{key, value}]) :: xpath_result
def find(doc, str, opts \\ [])

@spec find({document, rest}, xpath_string, [{key, value}]) :: {xpath_result, rest}
def find({doc, rest}, str, opts) do
{:xmerl_xpath.string(to_char_list(str), doc, opts), rest}
end

def find(doc, str, opts) do
:xmerl_xpath.string(to_char_list(str), doc, opts)
end

@doc ~s"""
Same as http://www.erlang.org/doc/man/xmerl_xpath.html#string-5,
except for rearranging of the function parameters to allow piping.
"""
def find(doc, str, node, parents, opts \\ [])

def find({doc, rest}, str, node, parents, opts) do
{:xmerl_xpath.string(to_char_list(str), node, parents, doc, opts), rest}
end

def find(doc, str, node, parents, opts) do
:xmerl_xpath.string(to_char_list(str), node, parents, doc, opts)
end

end
36 changes: 36 additions & 0 deletions test/exmerl_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
defmodule ExmerlTest do
use ExUnit.Case
import PathHelpers
import TestHelpers

test "parse basic file" do
resultpath = to_char_list(Path.dirname __DIR__)
{document, _} = Exmerl.parse(File.read!(fixture_path("justroot.xml")))
assert document == {:xmlElement, :rootnode, :rootnode, [], {:xmlNamespace, [], []}, [], 1, [],
[{:xmlText, [rootnode: 1], 1, [], '\n', :text}],
[],
resultpath, :undeclared}
end

test "parse file" do
{document, _} = Exmerl.from_file(fixture_path("test.xml"))
assert document == result("/test/fixtures")
end

test "parse string" do
doc = File.read!(fixture_path("test.xml"))
{document, _} = Exmerl.from_string(doc)
assert document == result("", char_list: true)
end

test "parse method given filename" do
{document, _} = Exmerl.parse(fixture_path("test.xml"))
assert document == result("/test/fixtures")
end

test "parse method given string" do
doc = File.read!(fixture_path("test.xml"))
{document, _} = Exmerl.parse(doc)
assert document == result("", char_list: true)
end
end

0 comments on commit dbb4ca1

Please sign in to comment.