From b492d338cde199275adfd727dc6de04bbf77e41c Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 5 Oct 2020 18:45:56 -0400 Subject: [PATCH] Refactor to delegate pattern --- Vagrantfile | 4 +-- lib/shortnr/url.ex | 56 +++++++++++++++++++++--------------- lib/shortnr/url/endpoints.ex | 8 +++--- lib/shortnr/url/repo.ex | 19 ++++++++---- lib/shortnr/url/repo/ets.ex | 8 ------ 5 files changed, 53 insertions(+), 42 deletions(-) diff --git a/Vagrantfile b/Vagrantfile index 938fafd..20d7b35 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -14,7 +14,7 @@ Vagrant.configure('2') do |config| # rubocop:disable Metrics/BlockLength dev.vm.network 'private_network', ip: '192.168.50.11' dev.vm.provider 'virtualbox' do |vb| - vb.memory = '2048' + vb.memory = '4096' vb.cpus = 4 end @@ -131,7 +131,7 @@ Vagrant.configure('2') do |config| # rubocop:disable Metrics/BlockLength prod.vm.network 'private_network', ip: '192.168.50.10' prod.vm.provider 'virtualbox' do |vb| - vb.memory = '2048' + vb.memory = '4096' vb.cpus = 4 end diff --git a/lib/shortnr/url.ex b/lib/shortnr/url.ex index d02fdc1..29453b5 100644 --- a/lib/shortnr/url.ex +++ b/lib/shortnr/url.ex @@ -4,6 +4,7 @@ defmodule Shortnr.URL do domain service. """ alias Shortnr.Transport + alias Shortnr.URL.Repo defstruct id: "", created_at: DateTime.utc_now(), @@ -17,37 +18,39 @@ defmodule Shortnr.URL do url: URI.t() } - @spec create(String.t(), module()) :: {:ok, String.t()} | Transport.error() - def create(url, repo) do + @spec create(String.t()) :: {:ok, String.t()} | Transport.error() + def create(url) when is_binary(url) do url_struct = %__MODULE__{id: generate_id(), url: URI.parse(url)} - {:ok, extant_url} = repo.get(url_struct.id) - - if extant_url do - create(url, repo) - else - :ok = repo.put(url_struct) - {:ok, url_struct.id} - end + url_struct.id + |> Repo.get() + |> create_if_no_collision(url_struct, url) end - @spec get(String.t(), module()) :: {:ok, t()} | Transport.error() - def get(key, repo) do - case repo.get(key) do - {:ok, nil} -> {:error, {:not_found, "url could not be found with the given id"}} - {:ok, url} -> {:ok, url} - end + defp create_if_no_collision({:ok, nil}, %__MODULE__{id: id} = url, _url) do + :ok = Repo.put(url) + {:ok, id} end - @spec list(module()) :: {:ok, list(t())} | Transport.error() - def list(repo) do - {:ok, _} = repo.list + defp create_if_no_collision({:ok, _}, _url_struct, url) when is_binary(url), do: create(url) + + @spec get(String.t()) :: {:ok, t()} | Transport.error() + def get(key) do + key + |> Repo.get() + |> handle_errors end - @spec delete(String.t(), module()) :: {:ok, String.t()} | Transport.error() - def delete(key, repo) do - :ok = repo.delete(key) - {:ok, "Success"} + @spec list() :: {:ok, list(t())} + def list do + handle_errors(Repo.list()) + end + + @spec delete(String.t()) :: {:ok, String.t()} + def delete(key) do + key + |> Repo.delete() + |> handle_errors end @id_chars String.codepoints("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXWYZ0123456789") @@ -58,6 +61,13 @@ defmodule Shortnr.URL do do: Enum.random(@id_chars) end + defp handle_errors({:ok, nil}), + do: {:error, {:not_found, "url could not be found with the given id"}} + + defp handle_errors({:ok, _} = response), do: response + + defp handle_errors(:ok), do: {:ok, "Success"} + defimpl Transport.Text.Encodable do alias Shortnr.URL @spec encode(URL.t()) :: String.t() diff --git a/lib/shortnr/url/endpoints.ex b/lib/shortnr/url/endpoints.ex index dabe7ad..4df8234 100644 --- a/lib/shortnr/url/endpoints.ex +++ b/lib/shortnr/url/endpoints.ex @@ -8,7 +8,7 @@ defmodule Shortnr.URL.Endpoints do def list(conn) do conn |> HTTP.wrap() - |> HTTP.handle(fn -> URL.list(URL.Repo.ETS) end) + |> HTTP.handle(&URL.list/0) |> Text.encode() |> HTTP.send(:ok) end @@ -16,7 +16,7 @@ defmodule Shortnr.URL.Endpoints do def create(conn, url) do conn |> HTTP.wrap(url) - |> HTTP.handle(&URL.create(&1, URL.Repo.ETS)) + |> HTTP.handle(&URL.create(&1)) |> Text.encode() |> HTTP.send(:created) end @@ -24,7 +24,7 @@ defmodule Shortnr.URL.Endpoints do def get(conn, id) do conn |> HTTP.wrap(id) - |> HTTP.handle(&URL.get(&1, URL.Repo.ETS)) + |> HTTP.handle(&URL.get(&1)) |> Text.encode() |> HTTP.send(:found) end @@ -32,7 +32,7 @@ defmodule Shortnr.URL.Endpoints do def delete(conn, id) do conn |> HTTP.wrap(id) - |> HTTP.handle(&URL.delete(&1, URL.Repo.ETS)) + |> HTTP.handle(&URL.delete(&1)) |> Text.encode() |> HTTP.send(:ok) end diff --git a/lib/shortnr/url/repo.ex b/lib/shortnr/url/repo.ex index 598cbe3..70f5f75 100644 --- a/lib/shortnr/url/repo.ex +++ b/lib/shortnr/url/repo.ex @@ -7,9 +7,18 @@ defmodule Shortnr.URL.Repo do alias Shortnr.Transport alias Shortnr.URL - @callback put(URL.t()) :: :ok | Transport.error() - @callback get(String.t()) :: {:ok, URL.t()} | Transport.error() - @callback delete(String.t()) :: :ok | Transport.error() - @callback list() :: {:ok, list(URL.t())} | Transport.error() - @callback reset() :: :ok | Transport.error() + @spec put(URL.t()) :: :ok | Transport.error() + defdelegate put(url), to: Shortnr.URL.Repo.ETS + + @spec get(String.t()) :: {:ok, URL.t() | nil} | Transport.error() + defdelegate get(url), to: Shortnr.URL.Repo.ETS + + @spec delete(String.t()) :: :ok | Transport.error() + defdelegate delete(url), to: Shortnr.URL.Repo.ETS + + @spec list() :: {:ok, list(URL.t())} | Transport.error() + defdelegate list(), to: Shortnr.URL.Repo.ETS + + @spec reset() :: :ok | Transport.error() + defdelegate reset(), to: Shortnr.URL.Repo.ETS end diff --git a/lib/shortnr/url/repo/ets.ex b/lib/shortnr/url/repo/ets.ex index e64a57b..a233ec2 100644 --- a/lib/shortnr/url/repo/ets.ex +++ b/lib/shortnr/url/repo/ets.ex @@ -2,11 +2,7 @@ defmodule Shortnr.URL.Repo.ETS do @moduledoc """ This module is an implemention of the Repo behaviour using the Erlang ETS library. """ - alias Shortnr.URL.Repo - @behaviour Repo - - @impl true def get(key) do case ets().lookup(:urls, key) |> List.first() do {_, url} -> {:ok, url} @@ -14,25 +10,21 @@ defmodule Shortnr.URL.Repo.ETS do end end - @impl true def put(url) do :ok = ets().insert(:urls, {url.id, url}) :ok end - @impl true def list do resp = ets().select(:urls, [{:"$1", [], [:"$1"]}]) {:ok, resp |> Enum.map(&elem(&1, 1))} end - @impl true def delete(key) do :ok = ets().delete(:urls, key) :ok end - @impl true def reset do :ok = ets().delete_all_objects(:urls) end