Packages

A Statistic Counter designed for simplicity and ease of use. Stats are rolled into time periods of your choice.

Current section

Files

Jump to
simplestatex lib server simple_stat_set.ex
Raw

lib/server/simple_stat_set.ex

defmodule SimpleStatEx.Server.SimpleStatSet do
@moduledoc """
A service that holds statistic counters mapped by time period in memory.
"""
use GenServer
alias SimpleStatEx.{SimpleStat, SimpleStatQuery}
@doc """
Start with a name that was generated by simple stat protocol
"""
def start_link(name) do
GenServer.start_link(__MODULE__, :ok, [name: name])
end
def init(:ok) do
{:ok, %{}}
end
# Client
##########################
@doc """
Add a statistic to the state
"""
def add_stat(category_bucket, %SimpleStat{} = simple_stat) do
GenServer.cast(category_bucket, {:add, simple_stat})
category_bucket
end
@doc """
Select stats from state based on query parameters
"""
def query_stats(category_bucket, %SimpleStat{} = simple_stat, %SimpleStatQuery{} = simple_stat_query) do
result = GenServer.call(category_bucket, {:query, simple_stat, simple_stat_query})
{:ok, result}
end
def query_stats!(category_bucket, %SimpleStat{} = simple_stat, %SimpleStatQuery{} = simple_stat_query) do
{:ok, result} = query_stats(category_bucket, simple_stat, simple_stat_query)
result
end
@doc """
Reset the stat bucket
"""
def reset(category_bucket) do
GenServer.cast(category_bucket, :reset)
end
def get_stats(category_bucket, period \\ :all) do
result = GenServer.call(category_bucket, {:retrieve_all, period})
{:ok, result}
end
def get_stats!(category_bucket) do
{:ok, result} = get_stats(category_bucket)
result
end
# Server
###########################
def handle_cast({:add, %SimpleStat{period: period} = simple_stat}, category_state) do
# Get the list of stats in the current period
{status, stat_period_list} = get_stat_period(category_state, period)
updated_category_state = case status do
:new_period ->
Map.put(category_state, period, [])
:ok ->
category_state
end
new_stat_period_list = update_stat_period_list(stat_period_list, simple_stat)
{:noreply, %{updated_category_state | period => new_stat_period_list}}
end
def handle_cast(:reset, _) do
{:noreply, %{}}
end
def handle_call({:retrieve_all, period}, category_state) do
stats = case period do
:all ->
Enum.flat_map(category_state, fn {_, period_stats} -> period_stats end)
period_queried ->
Map.get(category_state, period_queried)
end
{:reply, stats, category_state}
end
def handle_call({:query, %SimpleStat{period: period}, %SimpleStatQuery{limit: limit, offset: offset}}, _from, category_state) do
simple_stat_set = if Map.has_key?(category_state, period) do
Enum.slice(Map.get(category_state, period), offset, limit)
else
[]
end
{:reply, simple_stat_set, category_state}
end
def handle_info(_, state), do: {:noreply, state}
# Helper
defp get_stat_period(category_state, period) do
if Map.has_key?(category_state, period) do
{:ok, Map.get(category_state, period)}
else
{:new_period, []}
end
end
defp update_stat_period_list(stat_period_list, %SimpleStat{count: count, time: time} = simple_stat) do
case stat_period_list do
[] ->
[simple_stat]
[%SimpleStat{time: latest_time, count: period_count} = latest_simple_stat|tail] ->
cond do
latest_time == time ->
[%{latest_simple_stat | :count => period_count + count, :updated_at => Timex.now()}|tail]
true ->
[%{simple_stat | :updated_at => Timex.now()}, latest_simple_stat|tail]
end
end
end
end