# Conion
[![.github/workflows/elixir.yml](https://github.com/iboard/conion/actions/workflows/elixir.yml/badge.svg)](https://github.com/iboard/conion/actions/workflows/elixir.yml)
Conion is a package containing an Elixir application that provides some
common modules for general tasks in your Elixir application.
**At the moment, this is just a POC. Lets see how mature this application can get.**
### A persistent, supervised key/value store
- A key/value store (`Conion.Store`) that handles `Conion.Store.Bucket`s
- The `Conion.Store.Persistor` is a `Behaviour` that implements `read!` and `write!`
for those buckets.
- Buckets are supervised
- A central `Conion.Common.Configuration` module to deal with compile and runtime
configuration.
- A central `Conion.Common.CentralLogger` module to do logging in a common manner.
...more to come
## Install and prepare
git clone https://github.com/iboard/conion.git
cd conion
mkdir -p data/test data/dev
mix test
## Use it as a dependency in your `mix.exs`
```
defp deps do
[
{ :conion, "~> 0.1"}
]
```
## Usage Examples
### Configuration
Define the following function in your top module
```
def configurations,
do: [
# { {ENV, :app, :key, default}, set-function/1 }
{{"LOG_LEVEL", :logger, :level, :info}, &Configuration.set_log_level/1}
]
```
and use it like
```
def configure(),
do:
configurations()
|> Enum.reduce(%{}, &Configuration.load_configuration_for/2)
```
### CentralLogger
```
use Conion.Common.CentralLogger
def .... do
log(element_to_log, :warning, "Any message" )
end
```
`:warning` is an example for any log-level (:info, :notice, :warning, :error, ...)
The `element_to_log` will be "inspected" and passed through, thus you can use this
log-function in a pipe.
### CommonServer
To define your GenServers use ...
```
use Conion.CommonServer
def prepare_state_to_start(args) do
# define any term that will be passed to `start_link(__MODULE__, state)` as
# the initial state passed to GenServer's `init` function.
end
def initial_state(init_state_from_prepare_function) do
# complete the "loading", "initializing" in the GenServer's init-callback here.
end
# use the `call/2` function to safely call a "handle_call" or "handle_cast"
def bucket_name(pid), do: call(pid, :bucket_name)
def persist(bucket), do: cast(process_name(bucket), :persist)
# and implement the callbacks as usual
def handle_call(:bucket_name, _, state), do: calculate_the_return; {:reply, state[:name], state}
def handle_cast(:persist, state), do: do_something; {:noreply, state}
```
### Store
`Store.Persistor.File` is a simple implementation of the `Store.Persistor` behaviour
that writes the data into the given file, using `:erlang.term_to_binary` and reads it back
using `:erlang.binary_to_term`
More implementations of "persistors" will follow.
```
alias CentralScrutinizer.Store
Store.new_bucket :family, Store.Persistor.File, filename: "data/dev/family.data"
# where `Store.Persistor.File` implements the `Persistor`-behaviour and writes the data to the
# given file.
{:ok, id_father} = Store.insert_new(:family, %{ name: "Father", age: 50})
{:ok, id_mother} = Store.insert_new(:family, %{ name: "Mother", age: 45})
{:ok, id_child} = Store.insert_new(:family, %{ name: "Child", age: 5})
Store.list(:family)
# [ {id, %{...}}, {id, %{...}}, ...]
Store.replace(:family, id_father, %{ name: "Papa", age: 51})
Store.get(:family, id_father) # => %{ name: "Papa", age: 51}
Store.remove(:family, id_father)
Store.persist(:family)
```
## Generate an XREF image
to create an xref-graph use the following command:
mix xref graph --format dot && \
dot -Tpng xref_graph.dot -o xref_graph.png && \
open xref_graph.png && \
sleep 5 && rm xref_graph.*
## Generate documentation
mix docs && open doc/index.html