Skip to content

Commit

Permalink
add typespecs and first pass documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
derek-schaefer committed Nov 23, 2017
1 parent d8ad4ab commit 2ca1fc6
Show file tree
Hide file tree
Showing 8 changed files with 93 additions and 6 deletions.
55 changes: 51 additions & 4 deletions lib/ddwrt.ex
Original file line number Diff line number Diff line change
@@ -1,45 +1,92 @@
defmodule DDWRT do
@moduledoc """
DDWRT
"""

alias DDWRT.{DHCP, Wireless}

@type t :: %__MODULE__{address: String.t, username: String.t, password: String.t}
defstruct address: "http://192.168.1.1", username: "root", password: nil

@doc """
Retrieves the wireless status and extracts the results as a `DDWRT.Wireless` record.
"""
@spec status_wireless(__MODULE__.t) :: {:ok, Wireless.t} | {:error, HTTPoison.Error.t}
def status_wireless(%__MODULE__{} = settings) do
with {:ok, results} <- get_results(settings, "/Status_Wireless.live.asp"), do: results |> Wireless.new |> success
with {:ok, results} <- get_results(settings, "/Status_Wireless.live.asp") do
results |> Wireless.new |> success
end
end

@doc """
Retrieves the LAN status and extracts the results as a `DDWRT.DHCP` record.
"""
@spec status_lan(__MODULE__.t) :: {:ok, DHCP.t} | {:error, HTTPoison.Error.t}
def status_lan(%__MODULE__{} = settings) do
with {:ok, results} <- get_results(settings, "/Status_Lan.live.asp"), do: results |> DHCP.new |> success
with {:ok, results} <- get_results(settings, "/Status_Lan.live.asp") do
results |> DHCP.new |> success
end
end

@doc """
Issues a GET request and extracts the results as a map if successful.
"""
@spec get_results(__MODULE__.t, String.t) :: {:ok, %{String.t => String.t}} | {:error, HTTPoison.Error.t}
def get_results(%__MODULE__{} = settings, path) do
with {:ok, response} <- get(settings, path), do: response.body |> extract_results |> success
with {:ok, response} <- get(settings, path) do
response.body |> extract_results |> success
end
end

@doc """
Issues an authenticated GET request to the given path relative to the configured address.
"""
@spec get(__MODULE__.t, String.t) :: {:ok, HTTPoison.Response.t} | {:error, HTTPoison.Error.t}
def get(%__MODULE__{} = settings, path) do
HTTPoison.get(settings.address <> path, headers(settings))
end

@doc """
Constructs a list of tuples to be used as HTTP request headers.
"""
@spec headers(__MODULE__.t) :: [{String.t, String.t}]
def headers(%__MODULE__{} = settings) do
[authorization: "Basic " <> authorization(settings)]
[{"Authorization", "Basic " <> authorization(settings)}]
end

@doc """
Encodes the username and password to be used for HTTP basic authentication.
"""
@spec authorization(__MODULE__.t) :: String.t
def authorization(%__MODULE__{} = settings) do
Base.encode64(settings.username <> ":" <> settings.password)
end

@doc """
Extracts a map of results from a payload with a pattern of `{key::value}` pairs.
"""
@spec extract_results(String.t) :: %{String.t => String.t}
def extract_results(payload) do
Regex.scan(~r/{(\w+)::(.*)}/, payload)
|> Enum.map(&Enum.slice(&1, 1..2))
|> Enum.map(fn result -> Enum.map(result, &String.trim/1) end)
|> Map.new(&List.to_tuple/1)
end

@doc """
Extracts a list of entries from a result value with a pattern of `'entry1','entry2','entry3'`.
"""
@spec extract_entries(String.t) :: [String.t]
def extract_entries(value) do
Regex.scan(~r/'((?:[^'\\]|\\.)*)'/, value)
|> Enum.map(&Enum.at(&1, 1))
|> Enum.map(&String.trim/1)
end

@doc """
A convenience function for extracting and chunking entries from a result value.
"""
@spec extract_and_chunk_entries(String.t, integer) :: [[String.t]]
def extract_and_chunk_entries(value, size) do
value |> extract_entries |> Enum.chunk_every(size)
end
Expand Down
10 changes: 10 additions & 0 deletions lib/ddwrt/dhcp.ex
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
defmodule DDWRT.DHCP do
@moduledoc """
DDWRT.DHCP
"""

alias DDWRT.DHCP.Lease

@type t :: %__MODULE__{leases: [Lease.t]}
defstruct leases: []

@dhcp_leases "dhcp_leases"

@doc """
new
"""
@spec new(%{String.t => String.t}) :: __MODULE__.t
def new(results) when is_map(results) do
%__MODULE__{
leases: leases(results)
}
end

@spec leases(%{String.t => String.t}) :: [Lease.t]
defp leases(results) do
results
|> Map.fetch!(@dhcp_leases)
Expand Down
9 changes: 9 additions & 0 deletions lib/ddwrt/dhcp/lease.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
defmodule DDWRT.DHCP.Lease do
@moduledoc """
DDWRT.DHCP.Lease
"""

@type t :: %__MODULE__{name: String.t, ip: String.t, mac: String.t, period: String.t}
defstruct [:name, :ip, :mac, :period]

@doc """
new
"""
@spec new([String.t]) :: __MODULE__.t
def new(entry) when is_list(entry) do
%__MODULE__{
name: Enum.at(entry, 0),
Expand Down
10 changes: 10 additions & 0 deletions lib/ddwrt/wireless.ex
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
defmodule DDWRT.Wireless do
@moduledoc """
DDWRT.Wireless
"""

alias DDWRT.Wireless.Client

@type t :: %__MODULE__{clients: [Client.t]}
defstruct clients: []

@active_wireless "active_wireless"

@doc """
new
"""
@spec new(%{String.t => String.t}) :: __MODULE__.t
def new(results) when is_map(results) do
%__MODULE__{
clients: clients(results)
}
end

@spec clients(%{String.t => String.t}) :: [Client.t]
defp clients(results) do
results
|> Map.fetch!(@active_wireless)
Expand Down
9 changes: 9 additions & 0 deletions lib/ddwrt/wireless/client.ex
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
defmodule DDWRT.Wireless.Client do
@moduledoc """
DDWRT.Wireless.Client
"""

@type t :: %__MODULE__{mac: String.t}
defstruct [:mac]

@doc """
new
"""
@spec new([String.t]) :: __MODULE__.t
def new(entry) when is_list(entry) do
%__MODULE__{
mac: Enum.at(entry, 0)
Expand Down
3 changes: 2 additions & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ defmodule DDWRT.Mixfile do
defp deps do
[
{:httpoison, "~> 0.13"},
{:ex_doc, ">= 0.0.0", only: :dev}
{:ex_doc, ">= 0.0.0", only: :dev},
{:dialyxir, "~> 0.5", only: :dev, runtime: false}
]
end

Expand Down
1 change: 1 addition & 0 deletions mix.lock
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
%{"certifi": {:hex, :certifi, "2.0.0", "a0c0e475107135f76b8c1d5bc7efb33cd3815cb3cf3dea7aefdd174dabead064", [:rebar3], [], "hexpm"},
"dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [], [], "hexpm"},
"earmark": {:hex, :earmark, "1.2.3", "206eb2e2ac1a794aa5256f3982de7a76bf4579ff91cb28d0e17ea2c9491e46a4", [], [], "hexpm"},
"ex_doc": {:hex, :ex_doc, "0.18.1", "37c69d2ef62f24928c1f4fdc7c724ea04aecfdf500c4329185f8e3649c915baf", [], [{:earmark, "~> 1.1", [hex: :earmark, repo: "hexpm", optional: false]}], "hexpm"},
"hackney": {:hex, :hackney, "1.10.1", "c38d0ca52ea80254936a32c45bb7eb414e7a96a521b4ce76d00a69753b157f21", [:rebar3], [{:certifi, "2.0.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "5.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "1.0.2", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.1", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
Expand Down
2 changes: 1 addition & 1 deletion test/ddwrt_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ defmodule DDWRTTest do

describe "#headers" do
test "builds a header keyword list with an authorization value" do
assert %DDWRT{username: "test", password: "test"} |> DDWRT.headers == [authorization: "Basic " <> Base.encode64("test:test")]
assert %DDWRT{username: "test", password: "test"} |> DDWRT.headers == [{"Authorization", "Basic " <> Base.encode64("test:test")}]
end
end

Expand Down

0 comments on commit 2ca1fc6

Please sign in to comment.