MassAssignable
MassAssignable brings mass assignment to your classes! Especially useful for filling immutable structs on initialization. Handles arbitrarily deep nesting and common types such as Arrays, Hashes, Tuples, NamedTuples, etc.
Installation
-
Add the dependency to your
shard.yml
:dependencies: mass_assignable: gitlab: repomaa/mass_assignable
-
Run
shards install
Usage
Include MassAssignable
in your class/struct. It will generate an initializer,
which accepts named arguments for all of your instance variables and a
#to_attribute
method which will return a NamedTuple
with the values of the
instance variables.
Both work recursively so you can use builtin types and all the way down in
nested structures as input to the initializer. #to_attribute
will also
coerce nested structures into a named tuple of builtin types.
Annotations
ignore
To exclude an instance variables use the @[MassAssignable::Field(ignore: true)]
annotation.
converter
To specify how a value should be coerced you can set
@[MassAssignable::Field(converter: ConverterClass)]
where ConverterClass
is
a module/class with a class method from_attribute
that takes an attribute of
whatever type is used in the named args and returns an instance of the type
defined for the instance variable. To use MassAssignable#to_attribute
the
converter also has to implement a to_attribute
class method which gets the
value of the instance variable and should return a value of the type expected
for the key in the resulting NamedTuple
.
root
Parses the value of the given attribute in a named tuple with the key defined
with the @[MassAssignable::Field(root: "foo")
annotation. In this case it
would look for the value to coerce inside a nested structure under the key
foo
.
Example
require "mass_assignable"
struct Inner
include MassAssignable
getter foobar : Int32
end
module CSVConverter
def from_attribute(string : String)
string.split(',').map(&.strip)
end
def to_attribute(values)
values.join(',')
end
extend self
end
struct Test
include MassAssignable
@[MassAssignable::Field(converter: CSVConverter)]
getter foo : Array(String)
@[MassAssignable::Field(ignore: true)]
getter bar : Int32?
getter baz : Tuple(Inner, Inner)
@[MassAssignable::Field(key: "fooBar")]
getter foobar : Hash(Int32, Tuple(Inner, Inner))
@[MassAssignable::Field(key: "bar_foo")]
getter barfoo : StaticArray(Int32, 3)
end
pp test = Test.new(
foo: "foo,bar",
bar: 2,
baz: [
Inner.new(foobar: 1),
{ foobar: 2 }
],
fooBar: {
2 => [
{foobar: 1},
Inner.new(foobar: 2)
]
},
bar_foo: { 1, 2, 3 }
) # =>
# Test(
# @bar=nil,
# @barfoo=StaticArray[Inner(@foobar=1), Inner(@foobar=2), Inner(@foobar=3)],
# @baz={Inner(@foobar=1), Inner(@foobar=2)},
# @foo=["foo", "bar"],
# @foobar={2 => {Inner(@foobar=1), Inner(@foobar=2)}}
# )
pp test.to_attribute # =>
# {
# foo: "foo,bar",
# baz: {{foobar: 1}, {foobar: 2}},
# fooBar: {2 => {{foobar: 1}, {foobar: 2}}},
# bar_foo: {{foobar: 1}, {foobar: 2}, {foobar: 3}}
# }
Contributing
- Fork it (https://gitlab.com/repomaa/mass_assignable/forks/new)
- Create your feature branch (
git checkout -b my-new-feature
) - Write a spec
- Implement your feature
- Check that specs are green
- Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create a new Merge Request
Contributors
- Joakim Repomaa - creator and maintainer