# SayCheezEx 📸
Captures a snapshot of the environment at build time, so you can display it at run-time. And has a handy syntax to include UML graphs in your ExDocs.
* Sometimes you want to reference the version of your Elixir project at run time, or when / where / from what sources it was built, but that information is not available anymore once you deploy your app somewhere else. It could be in `git`, or in `mix`, or exposed as an Erlang system property... good luck remembering it all. With SayCheezEx, it's all in one place.
* They say that in Eixir, documentation is a first-class citizen. I find that UML graphs are very useful to document GenServers and their interactions. Now you can write them right into your @doc/@moduledoc texts.
This library takes inspiration from an earlier Clojure library, which you can find at https://github.com/l3nz/say-cheez. The previous library has been very useful to me over the years, and I'm hoping that this one will be just as helpful!
[![Hex.pm](https://img.shields.io/hexpm/v/say_cheez_ex)](https://hex.pm/packages/say_cheez_ex)
[![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/say_cheez_ex)
[![Hex.pm](https://img.shields.io/hexpm/dt/say_cheez_ex)](https://hex.pm/packages/say_cheez_ex)
[![Hex.pm](https://img.shields.io/hexpm/l/say_cheez_ex)](https://github.com/l3nz/say_cheez_ex/blob/main/LICENSE)
## Using
### Embedding build information
Whenever you want to reference a version/build information,
create an Elixir attribute for the module and compute its value through SayCheezEx.
I often compute a "short" version number,
an User-Agent for performing HTTP requests,
and a full version that contains the full
build information.
For example:
```elixir
module Foo.Bar do
import SayCheezEx, only: [cheez!: 1]
# Create all attributes we need:
# ...a version string
# "v 0.1.5/d9a87c3 137 on server.local"
@version cheez!(
"v {:project_version}/{:git_commit_id} {:build_number} on {:build_on}"
)
# ...a longer version string, with build ane Erlang information
# "0.1.5 d9a87c3/230411.1227 B:137/230411.1434/prod Ex:1.14.3/OTP25"
@version_full cheez!(
"{:project_version} {:git_all} B:{:build_number,=-}/{:build_at}/{:build_mix_env} Ex:{:system}"
)
# ...an HTTP user-agent to be used by this module
# "Foo.Bar MyProject-0.1.5"
@user_agent cheez!(
"#{__MODULE__} {:project_name}-{:project_version}"
)
...
end
```
Always make sure that you assign those values to an attibute - **never call those functions directly**.
You can safely create such attributes in all modules that need them, as they are just one (usually very small) binary.
Strings composed through `cheez!` will interpolate attributes
between brackets, with the following rules:
- `{:project_version}` is an info tag. These is a long
list of those - see below.
- `{$HOST}` is the environment variable HOST
- `{=HELLO}` is a default value, in this case the literal string "HELLO"
- If multiple attributes are specified, they all are expanded,
and the first one that is defined will be output. So e.g.
`{$FOO,$BAR,=BAZ}` will first try to interpolate the variable FOO;
if that is undefined, it will try BAR, and if that too is undefined,
it will output "BAZ" (that, being a default value, is always defined)
They will also make sure that any module name embedded through `__MODULE__` appears as it does in Elixir.
#### What is available
- The name of this project, its version, the version of Elixir and OTP
- When the project was built, where was it built and by which user, the build number (if available)
- The current Git SHA that was built, when the last commit was made and by whom.
- A set of properties about the current BEAM VM (architecture, word size, etc.)
- The host name that this project was built on
- The mix environment that this project was built in (e.g. "prod" or "dev" or "test")
See https://hexdocs.pm/say_cheez_ex/SayCheezEx.html#info/1 for a full list.
You can also call `SayCheezEx.all()` for a
map with all available attributes:
```elixir
%{
build_at: "230411.1538",
build_at_day: "2023-04-11",
build_at_full: "2023-04-11.15:38:40",
build_by: "lenz",
build_number: "87",
build_on: "MacBook-Pro.local",
build_mix_env: "dev",
git_all: "b204919/230411.1509",
git_commit_id: "b204919",
git_commit_id_full: "b2049190312ef810875476398978c2b0387251d3",
git_date: "2023-04-11.15:09:50",
git_date_compact: "230411.1509",
git_last_committer: "Lenz",
project_full_version: "0.2.1/b204919/230411.1509",
project_name: "SayCheezEx",
project_version: "0.2.1",
sysinfo_arch: "aarch64-apple-darwin22.3.0",
sysinfo_banner: "Erlang/OTP 25 [erts-13.2] [source] [64-bit] [smp:10:10] [ds:10:10:10] [async-threads:1] [jit]",
sysinfo_beam: "BEAM jit 13.2",
sysinfo_c_compiler: "gnuc 4.2.1",
sysinfo_compat: "25",
sysinfo_driver: "3.3",
sysinfo_nif: "2.16",
sysinfo_ptr: "64bit",
sysinfo_word: "64bit",
system: "1.14.3/OTP25",
system_elixir: "1.14.3",
system_otp: "25",
....
}
```
### UML Graphs
You can now easily add graph created with GraphViz and PlantUML in your documentation.
At the moment, GraphViz needs to be installed, but PlantUML is computed through an online server.
And graphs will be cached aggressively, so they are generate donly once and won't slow down your
developement cycle.
```elixir
module Foo do
import SayCheezEx, only: [uml: 1, graphviz: 1]
@moduledoc """
Here goes a Graphviz graph:
#{graphviz("digraph { Sup -> GenServ }")}
Here a PlantUML graph:
#{uml("""
Bob -> Alice : I do love UML in documentation
Alice -> Bob : I love it too!
""")}
"""
...
end
```
## Installing
Just add to your `mix.exs` file:
```elixir
{:say_cheez_ex, "~> 0.3"}
```
- Full documentation: https://hexdocs.pm/say_cheez_ex
- Hex.pm: https://hex.pm/packages/say_cheez_ex