Hex pm Build
Status Inline docs

Kaur

Pronounced |kɔː|

A bunch of helper functions to ease the development of your applications.

Installation

def deps do
  [{:kaur, "~> 1.1.0"}]
end

Usage

:ok, :error tuples A.K.A Result tuples

{:ok, value} and {:error, reason} is a common pattern in Erlang and Elixir. The Kaur.Result module adds functions to help deal with these values without getting out of your pipeline.

You can have a look at the documentation to know what’s available or you can take a look at how we use it in Kaur itself.

Just below you will find a small example of how your code could look like using Kaur.Result. In this example we try to determine if a person can rent a car. People can rent a car if they are between 21 and 99 year old and have a bonus greater than 0.8.

Example without Kaur

defmodule Person do
  defstruct [:name, :age, :bonus]
end

defmodule MyModule do
  def rent_a_car(person = %Person{}) do
    with {:ok, person1} <- validate_age(person),
         {:ok, person2} <- validate_bonus(person1)
    do
      {:ok, display_driving_message(person2)}
    else
      {:error, reason} ->
        {:error, handle_error(person, reason)}
    end
  end

  defp display_driving_message(person) do
    "Welcome #{person.name}, you can rent a car"
  end

  defp handle_error(person, {:bonus, expected_bonus}) do
    "Sorry #{person.name}, but you need a bonus of #{expected_bonus} but have only #{person.bonus}."
  end
  defp handle_error(person, {:license_type, expected_license}) do
    "Sorry #{person.name}, but you need a #{expected_license} license but have a #{person.license_type} license."
  end
  defp handle_error(person, {:too_old, maximum_age}) do
    "Sorry #{person.name}, but you need to be younger than #{maximum_age}"
  end
  defp handle_error(person, {:too_young, minimum_age}) do
    "Sorry #{person.name}, but you need to be older than #{minimum_age}"
  end

  defp validate_age(%{age: age}) when age > 99, do: {:error, {:too_old, 99}}
  defp validate_age(%{age: age}) when age < 21, do: {:error, {:too_young, 21}}
  defp validate_age(person), do: {:ok, person}

  defp validate_bonus(person = %{bonus: bonus}) when bonus > 0.8, do: {:ok, person}
  defp validate_bonus(_person), do: {:error, {:bonus, 0.8}}
end

Example using Kaur

defmodule MyModule do
  alias Kaur.Result

  def rent_a_car(person = %Person{}) do
    person
    |> validate_age()
    |> Result.and_then(&validate_bonus/1)
    |> Result.map(&display_driving_message/1)
    |> Result.map_error(&handle_error(person, &1))
  end

  # ... Same business logic as before
end

Execution

iex> MyModule.rent_a_car %Person{name: "Jane", age: 42, bonus: 0.9}
{:ok, "Welcome Jane, you can rent a car"}

iex> MyModule.rent_a_car %Person{name: "John", age: 42, bonus: 0.5}
{:error, Sorry John, but you need a bonus of 0.8 but have only 0.5."}

iex> MyModule.rent_a_car %Person{name: "Robert", age: 11, bonus: 0.9}
{:error, "Sorry Robert, but you need to be older than 21"}

iex> MyModule.rent_a_car %Person{name: "Mary", age: 122, bonus: 0.8}
{:error, "Sorry Mary, but you need to be younger than 99"}

Security

A small module which can generate API keys:

iex>  Kaur.Secure.generate_api_key
"UtiE9qs-7FbJs8OIt5nCiw=="

iex> Kaur.Secure.generate_api_key
"BTxaJNrA_QsAhWSLKOMj8A==

Environment Variables

We love environment variables but, unfortunately, Elixir configuration doesn’t play well with them. If we use System.get_env in config/*.exs files, they will be evaluated at compile time.

We would really want to have our configuration based on environment variables. A common pattern is to use {:system, "ENVIRONMENT_VARIABLE"} wherever we need a value to be fetched at runtime. That’s common but, unfortunately, that’s not built-in so we have to handle this behaviour ourselves.

Kaur.Environment abstracts how we read application configuration so it can automatically handle the loading of environment variables when it’s needed.

# config/config.exs
config :my_app, :my_key, {:system, "MY_KEY"}
config :my_app, :my_key2, "MY STATIC VALUE"

iex> Kaur.Environment.read(:my_app, :my_key)
{:ok, "VALUE DYNAMICALLY LOADED"}

iex> Kaur.Environment.read(:my_app, :my_key2)
{:ok, "MY STATIC VALUE"}

iex> Kaur.Environment.read(:my_app, :something_else)
{:error, :no_value}

Code of Conduct

By participating in this project, you agree to abide by its CODE OF CONDUCT.

Contributing

You can see the specific CONTRIBUTING guide.

License

Kaur is released under The MIT License (MIT).