Skip to content
This repository has been archived by the owner on Nov 3, 2019. It is now read-only.

Commit

Permalink
Merge pull request #2 from silathdiir/developer
Browse files Browse the repository at this point in the history
Implements `0.0.1` features. (#1)
  • Loading branch information
silathdiir committed Sep 30, 2017
2 parents 0fb327b + 438d535 commit 1841060
Show file tree
Hide file tree
Showing 15 changed files with 480 additions and 46 deletions.
1 change: 1 addition & 0 deletions .iex.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
use RatError
9 changes: 4 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,10 @@

## TODO

* define functions and error fields of first version (in README).
* write tests.

## Schedule

### Sep.22.2017
### Release `0.0.1`

* init project.
* Exports function `rat_error` (after calling `use RatError`).
* Exports `rat_error` configuration (see `config/*.exs` for detail).
* Implements module `RatError.Formatter` and `RatError.Structure`.
67 changes: 65 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# RatError
# Rat Error

Provides helper functions for error handling:

* detailed error description.
* default and configurable error fields.
* ...

## Installation

Expand All @@ -15,3 +14,67 @@ def deps do
]
end
```

## Configuration

```elixir
config :rat_error, RatError.Structure,
# Node Name, the default value is 'error'.
#
# If node is 'err', the structure is,
#
# %{
# err:
# %{
# code: :invalid_argument,
# file: "/home/dummy/my_app/web/user_controller.ex",
# function: {:authenticate, 1},
# line: 123,
# message: "wrong token!",
# module: Elixir.MyApp.Registration.UserController
# }
# }
#
# If node is nil or an empty string, the node is removed. The fields are
# exposed outside, and the below configuration 'prefix' could be set to
# distinguish with other caller's keys.
node: :error,

# Field Prefix, the default value is nil (NO prefix).
#
# If node is nil and prefix is 'err_', the structure is,
#
# %{
# err_code: :invalid_argument,
# err_file: "/home/dummy/my_app/web/user_controller.ex",
# err_function: {:authenticate, 1},
# err_line: 123,
# err_message: "wrong token!",
# err_module: Elixir.MyApp.Registration.UserController
# }
#
prefix: nil,

# Support Keys
keys:
[
# Error code defined by caller, e.g. an atom :no_entry, an integer 9 or a
# string "unexpected".
:code,

# Error file path.
:file,

# Error function name.
:function,

# Error file line.
:line,

# Error message of string passed in by caller.
:message,

# Error module.
:module
]
```
29 changes: 1 addition & 28 deletions config/config.exs
Original file line number Diff line number Diff line change
@@ -1,30 +1,3 @@
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config

# This configuration is loaded before any dependency and is restricted
# to this project. If another project depends on this project, this
# file won't be loaded nor affect the parent project. For this reason,
# if you want to provide default values for your application for
# 3rd-party users, it should be done in your "mix.exs" file.

# You can configure your application as:
#
# config :rat_error, key: :value
#
# and access this configuration in your application as:
#
# Application.get_env(:rat_error, :key)
#
# You can also configure a 3rd-party app:
#
# config :logger, level: :info
#

# It is also possible to import configuration files, relative to this
# directory. For example, you can emulate configuration per environment
# by uncommenting the line below and defining dev.exs, test.exs and such.
# Configuration from the imported file will override the ones defined
# here (which is why it is important to import them last).
#
# import_config "#{Mix.env}.exs"
import_config "#{Mix.env}.exs"
61 changes: 61 additions & 0 deletions config/dev.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
use Mix.Config

config :rat_error, RatError.Structure,
# Node Name, the default value is 'error'.
#
# If node is 'err', the structure is,
#
# %{
# err:
# %{
# code: :invalid_argument,
# file: "/home/dummy/my_app/web/user_controller.ex",
# function: {:authenticate, 1},
# line: 123,
# message: "wrong token!",
# module: Elixir.MyApp.Registration.UserController
# }
# }
#
# If node is nil or an empty string, the node is removed. The fields are
# exposed outside, and the below configuration 'prefix' could be set to
# distinguish with other caller's keys.
node: :error,

# Field Prefix, the default value is nil (NO prefix).
#
# If node is nil and prefix is 'err_', the structure is,
#
# %{
# err_code: :invalid_argument,
# err_file: "/home/dummy/my_app/web/user_controller.ex",
# err_function: {:authenticate, 1},
# err_line: 123,
# err_message: "wrong token!",
# err_module: Elixir.MyApp.Registration.UserController
# }
#
prefix: nil,

# Support Keys
keys:
[
# Error code defined by caller, e.g. an atom :no_entry, an integer 9 or a
# string "unexpected".
:code,

# Error file path.
:file,

# Error function name.
:function,

# Error file line.
:line,

# Error message of string passed in by caller.
:message,

# Error module.
:module
]
6 changes: 6 additions & 0 deletions config/prod.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
use Mix.Config

config :rat_error, RatError.Structure,
node: :error,
prefix: nil,
keys: :code
14 changes: 14 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
use Mix.Config

config :rat_error, RatError.Structure,
node: :error,
prefix: nil,
keys:
[
:code,
:file,
:function,
:line,
:message,
:module
]
49 changes: 41 additions & 8 deletions lib/rat_error.ex
Original file line number Diff line number Diff line change
@@ -1,18 +1,51 @@
defmodule RatError do
@moduledoc """
Documentation for RatError.
"""
@moduledoc File.read!("README.md")

alias RatError.{Formatter, Structure}

@doc false
defmacro __using__(opts \\ []) do
quote(bind_quoted: [opts: opts], location: :keep) do
structure =
Structure.create_from_default_config
|> Structure.update(opts)

@structure structure

import RatError
end
end

@doc """
Hello world.
Retrieves a RAT error with the specified parameters.
This function is the main API of the plugin.
Parameters 'error_code' and 'error_message' correspond to the support keys
:code and :message (see 'config/*.exs' for detail). Both could be ignored by
using the default values if keys :code and :message are disabled.
Parameter 'opts' corresponds the Keyword configuration of 'RatError.Structure'
(see 'config/*.exs' for detail). If this parameter is passed in, it merges
with the configuration of 'RatError.Structure'.
## Examples
iex> RatError.hello
:world
Parameter 'opts' merges with the configuration in 'config/test.exs'.
iex> opts = [keys: [:code, :message]]
iex> rat_error(:wrong_password, "Wrong password!", opts)
%{error: %{code: :wrong_password, message: "Wrong password!"}}
"""
def hello do
:world
defmacro rat_error(error_code \\ nil, error_message \\ "", opts \\ []) do
quote(bind_quoted: [error_code: error_code,
error_message: error_message,
opts: opts],
location: :keep) do
structure = Structure.update(@structure, opts)

Formatter.format(structure, __ENV__, error_code, error_message)
end
end
end
73 changes: 73 additions & 0 deletions lib/rat_error/formatter.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
defmodule RatError.Formatter do
@moduledoc """
Formats a RAT error.
Formatter is used to retrieve the error Map result by formatting the
parameters (error code, message, environment variables and so on) with the
specified Structure (see 'config/*.exs' for detail).
"""

alias RatError.Structure

@env_keys [
:file,
:function,
:line,
:module
]

@doc """
Format a RAT error with the specified Structure.
## Examples
iex> structure = %Structure{node: :err, keys: [:code, :message]}
iex> message = "Bad response!"
iex> Formatter.format(structure, __ENV__, :bad_response, message)
%{err: %{code: :bad_response, message: "Bad response!"}}
iex> structure = %Structure{keys: [:code, :message]}
iex> message = "Out of memory!"
iex> Formatter.format(structure, __ENV__, :no_memory, message)
%{code: :no_memory, message: "Out of memory!"}
"""
def format(%Structure{} = structure,
%Macro.Env{} = env,
error_code,
error_message) do

params =
%{}
|> format_code(structure, error_code)
|> format_message(structure, error_message)
|> format_env_values(structure, env)

if node = structure.node do
%{node => params}
else
params
end
end

defp format_code(params, structure, value),
do: format_entry(params, structure, :code, value)

defp format_entry(params, structure, key, value) when is_atom(key) do
if structure.keys |> List.wrap |> Enum.member?(key) do
new_key = String.to_atom(to_string(structure.prefix) <> to_string(key))

Map.put(params, new_key, value)
else
params
end
end

defp format_env_values(params, structure, env) do
Enum.reduce(@env_keys, params,
&format_entry(&2, structure, &1, Map.get(env, &1)))
end

defp format_message(params, structure, value),
do: format_entry(params, structure, :message, value)
end
Loading

0 comments on commit 1841060

Please sign in to comment.