Skip to content

Commit 493e10f

Browse files
committed
Merge branch 'master' into parser
Merged with the current master: * Implemented behavior for Onigumo.Parser. * Added Parser to CLI and environment.
2 parents 7223f36 + 0d890b9 commit 493e10f

13 files changed

+162
-41
lines changed

config/dev.exs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
import Config
22

33
config(:onigumo, :http_client, HTTPoison)
4+
config(:onigumo, :downloader, Onigumo.Downloader)
5+
config(:onigumo, :parser, Onigumo.Parser)

config/test.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
import Config
22

33
config(:onigumo, :http_client, HTTPoisonMock)
4+
config(:onigumo, :downloader, OnigumoDownloaderMock)

lib/cli.ex

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,37 @@
11
defmodule Onigumo.CLI do
2-
def main([component]) do
3-
module = Module.safe_concat("Onigumo", component)
4-
root_path = File.cwd!()
5-
module.main(root_path)
2+
@components %{
3+
:downloader => Application.compile_env(:onigumo, :downloader),
4+
:parser => Application.compile_env(:onigumo, :parser)
5+
}
6+
7+
def main(argv) do
8+
case OptionParser.parse(
9+
argv,
10+
aliases: [C: :working_dir],
11+
strict: [working_dir: :string]
12+
) do
13+
{parsed_switches, [component], []} ->
14+
{:ok, module} = Map.fetch(@components, String.to_atom(component))
15+
working_dir = Keyword.get(parsed_switches, :working_dir, File.cwd!())
16+
module.main(working_dir)
17+
18+
_ ->
19+
usage_message()
20+
end
21+
end
22+
23+
defp usage_message() do
24+
components = Enum.join(Map.keys(@components), ", ")
25+
26+
IO.puts("""
27+
Usage: onigumo [OPTION]... [COMPONENT]
28+
29+
Simple program that retrieves HTTP web content as structured data.
30+
31+
COMPONENT\tOnigumo component to run, available: #{components}
32+
33+
OPTIONS:
34+
-C, --working-dir <dir>\tChange working dir to <dir> before running
35+
""")
636
end
737
end

lib/onigumo/component.ex

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
defmodule Onigumo.Component do
2+
@doc "Runs the component."
3+
@callback main(root_path :: String.t()) :: :ok
4+
end

lib/onigumo/downloader.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ defmodule Onigumo.Downloader do
22
@moduledoc """
33
Web scraper
44
"""
5+
@behaviour Onigumo.Component
56

7+
@impl Onigumo.Component
68
def main(root_path) do
79
http_client().start()
810

lib/onigumo/parser.ex

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ defmodule Onigumo.Parser do
22
@moduledoc """
33
Web scraper
44
"""
5+
@behaviour Onigumo.Component
56

7+
@impl Onigumo.Component
68
def main(root_path) do
79
root_path
810
|> list_downloaded()
@@ -17,6 +19,6 @@ defmodule Onigumo.Parser do
1719

1820
defp is_downloaded(path) do
1921
suffix = Application.get_env(:onigumo, :downloaded_suffix)
20-
Path.extname(path) == ".#{suffix}"
22+
Path.extname(path) == suffix
2123
end
2224
end

mix.exs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ defmodule Onigumo.MixProject do
22
use Mix.Project
33

44
def project do
5+
env = Mix.env()
6+
57
[
68
app: :onigumo,
79
version: "0.1.0",
810
elixir: "~> 1.10",
9-
start_permanent: Mix.env() == :prod,
11+
start_permanent: env == :prod,
1012
deps: deps(),
11-
escript: escript()
13+
escript: escript(),
14+
elixirc_paths: elixirc_paths(env)
1215
]
1316
end
1417

@@ -37,4 +40,10 @@ defmodule Onigumo.MixProject do
3740
main_module: Onigumo.CLI
3841
]
3942
end
43+
44+
defp elixirc_paths(:test), do: elixirc_paths_default() ++ ["test/support"]
45+
46+
defp elixirc_paths(_), do: elixirc_paths_default()
47+
48+
defp elixirc_paths_default(), do: Mix.Project.config()[:elixirc_paths]
4049
end

mix.lock

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
%{
2-
"certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"},
3-
"floki": {:hex, :floki, "0.32.1", "dfe3b8db3b793939c264e6f785bca01753d17318d144bd44b407fb3493acaa87", [:mix], [{:html_entities, "~> 0.5.0", [hex: :html_entities, repo: "hexpm", optional: false]}], "hexpm", "d4b91c713e4a784a3f7b1e3cc016eefc619f6b1c3898464222867cafd3c681a3"},
4-
"hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~>2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"},
2+
"certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"},
3+
"floki": {:hex, :floki, "0.35.4", "cc947b446024732c07274ac656600c5c4dc014caa1f8fb2dfff93d275b83890d", [:mix], [], "hexpm", "27fa185d3469bd8fc5947ef0f8d5c4e47f0af02eb6b070b63c868f69e3af0204"},
4+
"hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~>2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~>6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~>1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~>1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"},
55
"html_entities": {:hex, :html_entities, "0.5.2", "9e47e70598da7de2a9ff6af8758399251db6dbb7eebe2b013f2bbd2515895c3c", [:mix], [], "hexpm", "c53ba390403485615623b9531e97696f076ed415e8d8058b1dbaa28181f4fdcc"},
66
"httpoison": {:hex, :httpoison, "1.8.2", "9eb9c63ae289296a544842ef816a85d881d4a31f518a0fec089aaa744beae290", [:mix], [{:hackney, "~> 1.17", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm", "2bb350d26972e30c96e2ca74a1aaf8293d61d0742ff17f01e0279fef11599921"},
77
"idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~>0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"},
88
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"},
99
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"},
10-
"mox": {:hex, :mox, "1.0.2", "dc2057289ac478b35760ba74165b4b3f402f68803dd5aecd3bfd19c183815d64", [:mix], [], "hexpm", "f9864921b3aaf763c8741b5b8e6f908f44566f1e427b2630e89e9a73b981fef2"},
11-
"parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"},
12-
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"},
10+
"mox": {:hex, :mox, "1.1.0", "0f5e399649ce9ab7602f72e718305c0f9cdc351190f72844599545e4996af73c", [:mix], [], "hexpm", "d44474c50be02d5b72131070281a5d3895c0e7a95c780e90bc0cfe712f633a13"},
11+
"parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"},
12+
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"},
1313
"unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"},
1414
}

test/onigumo_cli_test.exs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
defmodule OnigumoCLITest do
2+
use ExUnit.Case
3+
import ExUnit.CaptureIO
4+
import Mox
5+
6+
@invalid_arguments [
7+
"Downloader",
8+
"uploader"
9+
]
10+
11+
@invalid_switches [
12+
"--invalid",
13+
"-c"
14+
]
15+
16+
@working_dir_switches [
17+
"--working-dir",
18+
"-C"
19+
]
20+
21+
describe("Onigumo.CLI.main/1") do
22+
for argument <- @invalid_arguments do
23+
test("run CLI with invalid argument #{inspect(argument)}") do
24+
assert_raise(MatchError, fn -> Onigumo.CLI.main([unquote(argument)]) end)
25+
end
26+
end
27+
28+
test("run CLI with no arguments") do
29+
assert usage_message_printed?(fn -> Onigumo.CLI.main([]) end)
30+
end
31+
32+
test("run CLI with more than one argument") do
33+
assert usage_message_printed?(fn -> Onigumo.CLI.main(["Downloader", "Parser"]) end)
34+
end
35+
36+
for switch <- @invalid_switches do
37+
test("run CLI with invalid switch #{inspect(switch)}") do
38+
assert usage_message_printed?(fn -> Onigumo.CLI.main([unquote(switch)]) end)
39+
end
40+
end
41+
42+
@tag :tmp_dir
43+
test("run CLI with 'downloader' argument passing cwd", %{tmp_dir: tmp_dir}) do
44+
expect(OnigumoDownloaderMock, :main, fn working_dir -> working_dir end)
45+
46+
File.cd(tmp_dir)
47+
assert Onigumo.CLI.main(["downloader"]) == tmp_dir
48+
end
49+
50+
for switch <- @working_dir_switches do
51+
@tag :tmp_dir
52+
test("run CLI 'downloader' with #{inspect(switch)} switch", %{tmp_dir: tmp_dir}) do
53+
expect(OnigumoDownloaderMock, :main, fn working_dir -> working_dir end)
54+
55+
assert Onigumo.CLI.main(["downloader", unquote(switch), tmp_dir]) == tmp_dir
56+
end
57+
58+
test("run CLI 'downloader' with #{inspect(switch)} without any value") do
59+
assert usage_message_printed?(fn -> Onigumo.CLI.main(["downloader", unquote(switch)]) end)
60+
end
61+
end
62+
63+
defp usage_message_printed?(function) do
64+
output = capture_io(function)
65+
String.starts_with?(output, "Usage: onigumo ")
66+
end
67+
end
68+
end

test/onigumo_downloader_test.exs

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@ defmodule OnigumoDownloaderTest do
1414
@tag :tmp_dir
1515
test("run Downloader", %{tmp_dir: tmp_dir}) do
1616
expect(HTTPoisonMock, :start, fn -> nil end)
17-
expect(HTTPoisonMock, :get!, length(@urls), &prepare_response/1)
17+
expect(HTTPoisonMock, :get!, length(@urls), &HttpSupport.response/1)
1818

1919
input_path_env = Application.get_env(:onigumo, :input_path)
2020
input_path_tmp = Path.join(tmp_dir, input_path_env)
21-
input_file_content = prepare_input(@urls)
21+
input_file_content = InputSupport.url_list(@urls)
2222
File.write!(input_path_tmp, input_file_content)
2323

2424
Onigumo.Downloader.main(tmp_dir)
@@ -30,11 +30,11 @@ defmodule OnigumoDownloaderTest do
3030
describe("Onigumo.Downloader.create_download_stream/1") do
3131
@tag :tmp_dir
3232
test("download URLs from the input file with a created stream", %{tmp_dir: tmp_dir}) do
33-
expect(HTTPoisonMock, :get!, length(@urls), &prepare_response/1)
33+
expect(HTTPoisonMock, :get!, length(@urls), &HttpSupport.response/1)
3434

3535
input_path_env = Application.get_env(:onigumo, :input_path)
3636
input_path_tmp = Path.join(tmp_dir, input_path_env)
37-
input_file_content = prepare_input(@urls)
37+
input_file_content = InputSupport.url_list(@urls)
3838
File.write!(input_path_tmp, input_file_content)
3939

4040
Onigumo.Downloader.create_download_stream(tmp_dir) |> Stream.run()
@@ -46,36 +46,36 @@ defmodule OnigumoDownloaderTest do
4646
describe("Onigumo.Downloader.download_url/2") do
4747
@tag :tmp_dir
4848
test("download a URL", %{tmp_dir: tmp_dir}) do
49-
expect(HTTPoisonMock, :get!, &prepare_response/1)
49+
expect(HTTPoisonMock, :get!, &HttpSupport.response/1)
5050

5151
input_url = Enum.at(@urls, 0)
5252
Onigumo.Downloader.download_url(input_url, tmp_dir)
5353

5454
output_file_name = Onigumo.Downloader.create_file_name(input_url)
5555
output_path = Path.join(tmp_dir, output_file_name)
5656
read_output = File.read!(output_path)
57-
expected_output = body(input_url)
57+
expected_output = HttpSupport.body(input_url)
5858
assert(read_output == expected_output)
5959
end
6060
end
6161

6262
describe("Onigumo.Downloader.get_url/1") do
6363
test("get response by HTTP request") do
64-
expect(HTTPoisonMock, :get!, &prepare_response/1)
64+
expect(HTTPoisonMock, :get!, &HttpSupport.response/1)
6565

6666
url = Enum.at(@urls, 0)
6767
get_response = Onigumo.Downloader.get_url(url)
68-
expected_response = prepare_response(url)
68+
expected_response = HttpSupport.response(url)
6969
assert(get_response == expected_response)
7070
end
7171
end
7272

7373
describe("Onigumo.Downloader.get_body/1") do
7474
test("extract body from URL response") do
7575
url = Enum.at(@urls, 0)
76-
response = prepare_response(url)
76+
response = HttpSupport.response(url)
7777
get_body = Onigumo.Downloader.get_body(response)
78-
expected_body = body(url)
78+
expected_body = HttpSupport.body(url)
7979
assert(get_body == expected_body)
8080
end
8181
end
@@ -101,7 +101,7 @@ defmodule OnigumoDownloaderTest do
101101

102102
input_path_env = Application.get_env(:onigumo, :input_path)
103103
input_path_tmp = Path.join(tmp_dir, input_path_env)
104-
input_file_content = prepare_input(input_urls)
104+
input_file_content = InputSupport.url_list(input_urls)
105105
File.write!(input_path_tmp, input_file_content)
106106

107107
loaded_urls = Onigumo.Downloader.load_urls(tmp_dir) |> Enum.to_list()
@@ -124,27 +124,11 @@ defmodule OnigumoDownloaderTest do
124124
end
125125
end
126126

127-
defp prepare_response(url) do
128-
%HTTPoison.Response{
129-
status_code: 200,
130-
body: body(url)
131-
}
132-
end
133-
134-
defp prepare_input(urls) do
135-
Enum.map(urls, &(&1 <> "\n"))
136-
|> Enum.join()
137-
end
138-
139-
defp body(url) do
140-
"Body from: #{url}\n"
141-
end
142-
143127
defp assert_downloaded(url, tmp_dir) do
144128
file_name = Onigumo.Downloader.create_file_name(url)
145129
output_path = Path.join(tmp_dir, file_name)
146130
read_output = File.read!(output_path)
147-
expected_output = body(url)
131+
expected_output = HttpSupport.body(url)
148132
assert(read_output == expected_output)
149133
end
150134
end

test/support/http.ex

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
defmodule HttpSupport do
2+
def response(url) do
3+
%HTTPoison.Response{
4+
status_code: 200,
5+
body: body(url)
6+
}
7+
end
8+
9+
def body(url) do
10+
"Body from: #{url}\n"
11+
end
12+
end

test/support/input.ex

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
defmodule InputSupport do
2+
def url_list(urls) do
3+
Enum.map(urls, &(&1 <> "\n"))
4+
|> Enum.join()
5+
end
6+
end

test/test_helper.exs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
ExUnit.start()
22

33
Mox.defmock(HTTPoisonMock, for: HTTPoison.Base)
4+
Mox.defmock(OnigumoDownloaderMock, for: Onigumo.Component)

0 commit comments

Comments
 (0)