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
Accessimplementation for structsEnumerableimplementation for structs (as maps)Collectableimplementation for one of struct’s fields (asMapSetdoes)StreamDatageneration of structs for property-based testing
Use Options
use Estructura accepts four keyword arguments.
access: boolean()whether to generate theAccessimplementation, defaulttrue; whentrue, it also producesput/3andget/3methods to be used withcoercionandvalidationcoercion: boolean() | [key()]whether to generate the bunch ofcoerce_×××/1functions to be overwritten by implementations, defaultfalsevalidation: boolean() | [key()]whether to generate the bunch ofvalidate_×××/1functions to be overwritten by implementations, defaultfalseenumerable: boolean()whether to generate theEnumerableporotocol implementation, defaultfalsecollectable: false | key()whether to generate theCollectableprotocol implementation, defaultfalse; if non-falsey atom is given, it must point to a struct field whereCollectablewould 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 inStreamDataproperty testing; the generated__generator__/1function 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: %{}
endThe 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