# Decoratex
[![Travis](https://img.shields.io/travis/acutario/decoratex.svg?maxAge=2592000&&style=flat-square)](https://travis-ci.org/acutario/decoratex)
[![Hex.pm](https://img.shields.io/hexpm/dt/decoratex.svg?maxAge=2592000&style=flat-square)](https://hex.pm/packages/decoratex)
Decoratex provides an easy way to add calculated data to your Ecto model structs.
## Requirements
- Ecto 2.0 or higher
## What does this package do?
Maybe you have been in some situations where you need some related data of a model that is not straight stored with it's attributes and it requires a complex logic to calculate their value that can't be solved with a query. Maybe you will need this data in multiple points of the instace life cicle and you want the data available in a standard way instead of using an external module function each time you need it's value.
In this cases, this is what decoratex can do for you:
* Add virtual fields to the model schema to let you use your model like the same model struct with these new fields.
* Provide a function to load data in all or some of these fields whenever you want.
## Installation
The package can be installed as simply as adding `decoratex` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[{:decoratex, "~> 1.0.0"}]
end
```
## Usage
1. Add `use Decoratex` to your models.
2. Set the decorate fields inside a block of `decorations` function.
3. Declare each field with `decorate_field name, type, function, options`.
* Name of the virtual field.
* Type of the virtual field.
* Function to calculate the value of the virtual field. Always receives a struct model as first param.
* Default options for the function (arity 2) in case you need to use diferent options in each decoration.
4. Add `add_decorations` inside schema definition.
5. Use `decorate` function of your model module.
```elixir
defmodule Post do
use Ecto.Schema
use Decoratex
decorations do
decorate_field :happy_comments_count, :integer, &PostHelper.count_happy_comments/1
decorate_field :troll_comments_count, :integer, &PostHelper.count_troll_comments/1
decorate_field :mention_comments_count, :integer, &PostHelper.count_mention_comments/2, ""
...
end
schema "posts" do
has_many :comments, Comment, on_delete: :delete_all
field :title, :string
field :body, :string
add_decorations
end
end
```
The decorations definition needs to be placed before schema definition, and then, you should add `add_decorations` inside the schema block. This will automatically add the virtual fields to your model.
Finally, you can use the `decorate` function of your model module to populate the fields that you need with the function associated to them.
```elixir
post = Post
|> Repo.get(1)
|> Repo.preload(:comments))
# Decorate all fields
|> Post.decorate
# Decorate one field with an atom
|> Post.decorate(:happy_comments_count)
# Decorate some fields with a list
|> Post.decorate([:happy_comments_count, ...])
# Decorate all fields except one with except key and an atom
|> Post.decorate(except: :happy_comments_count)
# Decorate all fields except some with except key and a list
|> Post.decorate(except: [:happy_comments_count, ...])
post.happy_comments_count
234
```
### Decorate with options
When you need to send some options to the decoration functions, you can define a function with arity 2, and set a default value in declaration. The default options value is mandatory for default decorations:
```
decorate_field :mention_comments_count, :integer, &PostHelper.count_mention_comments/2, ""
```
Then, you can pass the options value when the struct is decorated
```
|> Post.decorate(count_mention_comments: user.nickname)
```
You can use a keyword list for a complex logic, but you need to care about how to manage options in the decoration function (always with arity/2), and the default options in the configurtion.
```
decorate_field :censured_comments, {:array, Comment}, &PostHelper.censured_comments/2, pattern: "frack", replace: "*"
```
```
|> Post.decorate(censured_comments: [pattern: list_of_words, replace: "*"])
```
And you can mix simple and decorations with options with a list:
```
|> Post.decorate([:happy_comments_count, censured_comments: [pattern: list_of_words, replace: "*"]])
```