August 11, 2019
Assuming you know the theory, in this example we’ll provide one login interface for different type of users.
mix new protdemo --sup
Structs
This example shows that passing different data types to our login function will trigger a different implementation.
Create a new admin directory under lib/protdemo and put there a new file admin.ex:
# lib/protdemo/admin/admin.ex
defmodule Protdemo.Admin do
defstruct [:admin_name, :password]
def login(who), do: {:ok, who.admin_name <> " logged in as Admin"}
end
# lib/protdemo/user/user.ex
defmodule Protdemo.User do
defstruct [:user_name, :password]
def login(who), do: {:ok, who.user_name <> " logged in as User"}
end
Protocols
The most important part now. Create a new file auth.ex under lib/protdemo
# lib/protdemo/auth.ex
defprotocol Protdemo.Auth do
def login(creds)
end
defimpl Protdemo.Auth, for: Protdemo.Admin do
def login(admin_creds), do: Protdemo.Admin.login(admin_creds)
end
defimpl Protdemo.Auth, for: Protdemo.User do
def login(user_creds), do: Protdemo.User.login(user_creds)
end
# lib/protdemo.ex
defmodule Protdemo do
alias Protdemo.Auth
def login(creds), do: Auth.login(creds)
end
Testing
# test/protdemo_test.exs
defmodule ProtdemoTest do
use ExUnit.Case
doctest Protdemo
alias Protdemo.Admin
alias Protdemo.Auth
alias Protdemo.User
test "Log in as User" do
{:ok, "rafal logged in as User"} = Auth.login(%User{user_name: "rafal", password: "test"})
end
test "Log in as Admin" do
{:ok, "rafal logged in as Admin"} = Auth.login(%Admin{admin_name: "rafal", password: "test"})
end
end