Starchoice.Decoder behaviour (starchoice v0.3.0) View Source
This module can be used from two different ways:
As a macro
To be defining decoders as macro, you need to use the Starchoice.Decoder
module in your struct. It's declaration is highly (like totally) inspired by Ecto's schemas.
To see available options, take a look at put_field/3
's documentation
Examples
defmodule User do
use Starchoice.Decoder
defstruct first_name: nil, age: nil
defdecoder do
field(:first_name)
field(:age)
end
end
User.__decoder__() # %Decoder{}
User.__decoder__(:default) # %Decoder{}
When you don't pass a name to the defdecoder/2
function, it defaults to default
. So calling defdecoder do
and defdecoder :default do
is identical. This is because you might be interested in creating multiple decoders for the same struct like below:
defmodule User do
use Starchoice.Decoder
defstruct first_name: nil, last_name: nil, email: nil, age: nil
defdecoder do
field(:first_name)
field(:age)
end
defdecoder :full do
field(:first_name)
field(:last_name)
field(:email)
field(:age)
end
end
User.__decoder__() # %Decoder{fields: [{:first_name, _}, {:age, _}]}
User.__decoder__(:default) # %Decoder{fields: [{:first_name, _}, {:age, _}]}
User.__decoder__(:full) # %Decoder{fields: [{:first_name, _}, {:last_name, _}, {:email, _}, {:age, _}]}
And you can now use the module directly when calling Starchoice.decode/3
like:
iex> Starchoice.decode(input, User)
iex> Starchoice.decode(input, {User, :full})
Source mapper
While each field supports a :source
option to specify what key to use in the
parsed map, you can provide a module to perform the casting. If, as an example,
the map is using camelCaseKeys
, you can provide a source mapper that perform
the appropriate conversion.
defmodule CamelCase do
def map_source(field) do
splitted =
field
|> to_string()
|> String.split("_", parts: 2)
case splitted do
[key] -> key
[head, rest] -> head <> Macro.camelize(rest)
end
end
end
defmodule User do
defstruct [:first_name, :last_name]
use Starchoice.Decoder, source_mapper: CamelCase
defdecoder do
field(:first_name)
field(:last_name)
end
end
iex> map = %{"firstName" => "Bobby", "lastName" => "Hill"}
iex> Starchoice.decode(map, User)
%User{first_name: "Bobby", last_name: "Hill"}
iex> map = %{"first_name" => "Bobby", "last_name" => "Hill"}
iex> Starchoice.decode(map, User)
%User{first_name: nil, last_name: nil}
Doing so is the same as adding individual :source
option to each field.
Manually
You could also build decoder manually like the following:
defmodule User do
defstruct email: nil, password: nil
def mask_password(_), do: "MASKED"
end
User
|> Decoder.new()
|> Decoder.put_field(:email)
|> Decoder.put_field(:password, with: &User.mask_password/1)
# or
Decoder.new(User, [
{:email, []},
{:password, with: &User.mask_password/1}
])
To see available options, take a look at put_field/3
's documentation
Link to this section Summary
Functions
Initiates a new Decoder, you can pass a list of fields with options.
Puts a field decoding in the decoder. Available options are
Puts source option casted from source mapper
Link to this section Types
Specs
decoder_struct() :: module() | :map
Specs
field_definition() :: {atom(), [field_option()]}
Specs
field_option() :: {:required, boolean()} | {:with, Starchoice.decoder()} | {:default, any()} | {:sanitize, function()} | {:source, String.t()}
Specs
t() :: %Starchoice.Decoder{fields: [field_definition()], struct: module()}
Specs
using_option() :: {:source_mapper, module()}
Link to this section Functions
Specs
Specs
field(atom(), [field_option()]) :: Macro.t()
Specs
new(decoder_struct(), [field_definition()]) :: t()
Initiates a new Decoder, you can pass a list of fields with options.
To see available options, take a look at put_field/3
's documentation
Specs
put_field(t(), atom(), [field_option()]) :: t()
Puts a field decoding in the decoder. Available options are:
:required
: Defines if a field is required, will caused a raise (or{:error, _}
tuple) when the required field isn't present:default
: Specifies a fallback value in case the field is missing (can't be used withrequired: true
):with
: Specifies a decoder the decode the given field. Like theStarchoice.decode/3
call, it can support any valid decoder inModule
,{Module, :decoder}
and a function.:sanitize
: Specifies a sanitizer. By default, the value is sanitized by trimming the value and casting to nil if the value is "". Can either be a boolean or a function.:source
: Specifies the source key in the decoding item
Specs
put_source_option(atom(), [field_option()], module() | nil) :: String.t() | atom()
Puts source option casted from source mapper