View Source Estructura (estructura v0.2.0)
Estructura
is a set of extensions for Elixir structures,
such as Access
implementation, Enumerable
and Collectable
implementations, validations and test data generation via StreamData
.
Estructura
simplifies the following
Access
implementation for structsEnumerable
implementation for structs (as maps)Collectable
implementation for one of struct’s fields (asMapSet
does)StreamData
generation of structs for property-based testing
Use Options
use Estructura
accepts four keyword arguments.
access: boolean()
whether to generate theAccess
implementation, defaulttrue
; whentrue
, it also producesput/3
andget/3
methods to be used withcoercion
andvalidation
coercion: boolean() | [key()]
whether to generate the bunch ofcoerce_×××/1
functions to be overwritten by implementations, defaultfalse
validation: boolean() | [key()]
whether to generate the bunch ofvalidate_×××/1
functions to be overwritten by implementations, defaultfalse
enumerable: boolean()
whether to generate theEnumerable
porotocol implementation, defaultfalse
collectable: false | key()
whether to generate theCollectable
protocol implementation, defaultfalse
; if non-falsey atom is given, it must point to a struct field whereCollectable
would collect. Should be one oflist()
,map()
,MapSet.t()
,bitstribg()
generator: %{optional(key()) => Estructura.Config.generator()}
the instructions for the__generate__/{0,1}
functions that would produce the target structure values suitable for usage inStreamData
property testing; the generated__generator__/1
function is overwritable.
Please note, that setting coercion
and/or validation
to truthy values has effect
if and only if access
has been also set to true
.
Typical example of usage would be:
defmodule MyStruct do
use Estructura,
access: true,
coercion: [:foo],
validation: true,
enumerable: true,
collectable: :bar,
generator: [
foo: {StreamData, :integer},
bar: {StreamData, :list_of, [{StreamData, :string, [:alphanumeric]}]},
baz: {StreamData, :fixed_map,
[[key1: {StreamData, :integer}, key2: {StreamData, :integer}]]}
]
defstruct foo: 42, bar: [], baz: %{}
end
The above would allow the following to be done with the structure:
s = %MyStruct{}
put_in s, [:foo], :forty_two
#⇒ %MyStruct{foo: :forty_two, bar: [], baz: %{}}
for i <- [1, 2, 3], into: s, do: i
#⇒ %MyStruct{foo: 42, bar: [1, 2, 3], baz: %{}}
Enum.map(s, &elem(&1, 1))
#⇒ [42, [], %{}]
MyStruct.__generator__() |> Enum.take(3)
#⇒ [
# %MyStruct{bar: [], baz: %{key1: 0, key2: 0}, foo: -1},
# %MyStruct{bar: ["g", "xO"], baz: %{key1: -1, key2: -2}, foo: 2},
# %MyStruct{bar: ["", "", ""], baz: %{key1: -3, key2: 1}, foo: -1}
# ]
Coercion
When coercion: true | [key()]
is passed as an argument to use Estructura
,
the ovewriteable stubs for coercion_×××/1
are generated for all the fields passed.
To make a coercion work with MyStruct.put/3
and put_in/3
provided
by Access
implementation, the consumer module should implement coercion_×××/1
functions for each field.
Validation
When validation: true | [key()]
is passed as an argument to use Estructura
,
the ovewriteable stubs for validation_×××/1
are generated for all the fields passed.
To make a validation work with MyStruct.put/3
and put_in/3
provided
by Access
implementation, the consumer module should implement validation_×××/1
functions for each field.
Generation
If generator
keyword argument has been passed, MyStruct.__generate__/{0,1}
can be
used to generate instances of this struct for StreamData
property based tests.
property "generation" do
check all %MyStruct{foo: foo, bar: bar, baz: baz} <- MyStruct.__generator__() do
assert match?(%{key1: v1, key2: v2} when is_integer(v1) and is_integer(v2), baz)
assert is_integer(foo)
assert is_binary(bar)
end
end