# MapSchema
It´s a Simple, Agile, Map schema in Elixir **with types check** , with **integer and float number autocasting** of string to number, let define **custom types with casting and validation** and include **json encoding** using the popular Jason library.
**Next release**
Note: Now, I am working in improve it. You can use it but return here for check updates and documentation.
## Installation
```elixir
def deps do
[
{:map_schema, "~> 0.2.2"}
]
end
```
## Usage
The map_schema will include in the module multiple methods
with documentation even with some doctest examples... ;)
then it´s simple create your schema, add ex_doc in mix, and use ``mix docs`` and your team will can see all methods that your module will have thanks the "witchcraft" of elixir macros, all ready to use it.
```elixir
defmodule MapSchema.Examples.Person do
@moduledoc """
Example of Person Model Map using MapSchema
"""
use MapSchema,
schema: %{
"name" => :string,
"surname" => :string,
"country" => :string,
"lang" => :language_iso639,
"age" => :integer,
"contact" => %{
"email" => :string,
"phone" => :string,
"others" => :any
}
},
custom_types: [
MapSchema.Examples.CustomTypeLang
]
end
```
### Basics functions
| Method | Description |
| :---: | :---: |
| new | Constructor |
| schema | Return the Schema |
| is_valid?(map) | Is valid the map? |
### Gets and Puts functions
```elixir
test "Example get and put usage" do
person = Person.new() # %{}
|> Person.put_name("Leo") # %{"name" => "Leo"}
|> Person.put_surname("Messi") # %{"name" => "Leo", "surname" => "Messi" }
|> Person.put_country("Argentina") # %{"name" => "Leo", "surname" => "Messi", "country" => "Argentina" }
|> Person.put_age(33) # %{"name" => "Leo", "surname" => "Messi", "country" => "Argentina", "age" => 33 }
|> Person.put_lang("ES") # %{"name" => "Leo", "surname" => "Messi", "country" => "Argentina", "age" => 33 , "lang" => "es"}
# the lang field it´s custom type :language_iso639 make automatic # the downcase in strings before of validate.
# Review the example MapSchema.Examples.CustomTypeLang
assert Person.get_name(person) == "Leo"
assert Person.get_surname(person) == "Messi"
assert Person.get_country(person) == "Argentina"
assert Person.get_age(person) == 33
assert Person.get_lang(person) == "es"
end
```
| Gets | Puts |
| :--- | :--- |
| get_name(map) | put_name(map,value) |
| get_surname(map) | put_surname(map,value) |
| get_country(map) | put_country(map,value) |
| get_age(map) | put_age(map,value) |
| get_contact_email(map) | put_contact_email(map,value) |
| get_contact_phone(map) | put_contact_phone(map,value) |
| get_contact_others(map) | put_contact_others(map,value) |
### General Put and Put_ifmatch
You can update many fields using a general put, every field will be cast and type check before of update. But if you try put a field that dont exist in the schema the method put will return a exception because you tried break the schema. Well there are a other option, you can use `put_ifmatch` that if a field dont exist in the schema it will omited.
```elixir
test "Example general put function" do
person = Person.new() # %{}
person = Person.put(person, %{
"contact" => %{"email" => "example@mail.com" },
"country" => "Spain"
}) # %{"country" => "Spain","contact" => %{"email" => "example@mail.com"}}
assert Person.get_contact_email(person) == "example@mail.com"
assert Person.get_country(person) == "Spain"
end
```
```elixir
test "Using put with exception and put_ifmatch without exception" do
try do
Person.new()
|> Person.put(%{"name" => "ric", "not_exist_field"=> "something"})
assert false
catch
e ->
assert e == Exceptions.not_exist_field_in_schema("not_exist_field")
person = Person.new()
|> Person.put_ifmatch(%{"name" => "ric", "not_exist_field"=> "something"})
assert Person.get_name(person) == "ric"
end
end
```
### Mutation functions
```elixir
test "Example mutation of age" do
person = Person.new() # %{}
|> Person.put_age(29) # %{"age" => 29}
|> Person.mut_age(&(&1 + 1)) # %{"age" => 30}
assert Person.get_age(person) == 30
end
```
| Method | Description |
| :--- | :---: |
| mut_name(map,fn_mut) | Change the value of name using fn_mut |
| mut_surname(map,fn_mut) | Change the value of surname using fn_mut |
| mut_country(map,fn_mut) | Change the value of country using fn_mut |
| mut_age(map,fn_mut) | Change the value of age using fn_mut |
| mut_contact_email(map,fn_mut) | Change the value using fn_mut |
| mut_contact_phone(map,fn_mut) | Change the value using fn_mut |
| mut_contact_others(map,fn_mut) | Change the value using fn_mut |
### JSON ENCONDING
```elixir
test "Example of json encoding" do
person = Person.new()
person = Person.put(person, %{
"contact" => %{"email" => "hi@mail.com" },
"age" => 45
})
json = Person.json_encode(person)
json_expected ="{\"age\":45,\"contact\":{\"email\":\"hi@mail.com\"}}"
assert json == json_expected
person_json = Person.json_decode(json)
assert Person.get_contact_email(person_json) == "hi@mail.com"
assert Person.get_age(person_json) == 45
end
```
| Method | Description |
| :--- | :---: |
| json_encode(map) | Map to Json |
| json_encode(json) | Json to Map (Check typing, and cast) |
| json_encode(mapa, json) | Json to Existing Map (Checking typing, and cast) |
### Table of Types
Note:
**:string_to_integer** and **:string_to_float** make **implicit the cast of string to number** then automatic and simple you will have your information in the right format and type following the schema define. ;)
| Type | Use Guard |
| :---: | :---: |
| :integer | :is_integer |
| :float | :is_float |
| :string_to_integer | :is_integer |
| :string_to_float | :is_float |
| :string | :is_bitstring |
| :bool | :is_boolean |
| :boolean | :is_boolean |
| :map | :is_map |
| :list | :is_list |
| :any | NONE |
| in othercase | NONE |
### Features
- Simple definition of data schema
A simple map %{} and ;)
- Implicit types check and casting
Forgot the type checks and casting
- Compatible with Json.
Perfect, for APIs, webapps... so on.
- Independent data of module
A map would can be compatible with multiples schemas always that it follow each schema types.
... and more now working ...