Ruby style accessors for Elixir structs.
Provides AttrAccessor module provides attr_reader, attr_writer and attr_accessor macros to define "get" and/or "set" functions for reading/write struct field values.
- accessors are functions, so can be used in pipe chain
- accessors are functions, so can be passed to
Enumetc higher order functions - opaque structs can more easily keep implementation details separate
Say we've got an basic "todo" item struct define as:
defmodule Todo do
defstruct [:id, :title, :description, :created_at, :done_at]
def done?(todo) do
todo.done_at != nil
end
endWe can use AttrAccessor to create get/set functions.
defmodule Todo do
defstruct [:id, :title, :description, :created_at, :done_at]
import AttrAccessor
attr_accessor [:title, :description]
attr_reader [:id, :created_at]
attr_writer :done_at
def done?(todo) do
todo.done_at != nil
end
endThen we can use those accessor functions to read and write data on the struct value.
my_todo = %Todo{id: 123, created_at: DateTime.utc_now()}
Todo.id(my_todo)
# 123
my_todo = Todo.title("my todo item")
my_todo = Todo.description("do some stuff")
Todo.title(my_todo)
# "my todo item"
Todo.description(my_todo)
# "do some stuff"
Todo.done?(my_todo)
# false
my_todo = Todo.done_at(my_todo, DateTime.utc_now())
Todo.done?(my_todo)
# trueAttrAccessor also creates a "bang" method for each attr_writer defined on a struct to allow the current value to be passed to a function, and the resulting value set.
my_todo = %Todo{} |> Todo.title("My todo")
# %Todo{title: "My todo"}
my_todo = my_todo |> Todo.title!(&String.upcase/1)
# %Todo{title: "MY TODO"}On it's own AttrAccessor may not provide much over built in syntax for accessing struct fields, but because they are regular Elixir functions they can be used with pipe chains to incrementally build a struct.
%Todo{id: 123, created_at: DateTime.utc_now()}
|> Todo.title("my todo item")
|> Todo.description("do some stuff")
|> Todo.done_at(DateTime.utc_now())Because accessors are functions, they can also be used with Enum etc functions
todos = [
%Todo{title: "do thing 1"},
%Todo{title: "do thing 2"},
]
todo_titles = todos |> Enum.map(&Todo.title/1)
# ["do thing 1", "do thing 2"]
now = DateTime.utc_now()
completed_todos = todos |> Enum.map(&Todo.done_at(&1, now))
completed_todos |> Enum.map(&Todo.done?/1)
# [true, true]If available in Hex, the package can be installed
by adding attr_accessor to your list of dependencies in mix.exs:
def deps do
[
{:attr_accessor, "~> 0.1.0"}
]
endDocumentation can be generated with ExDoc and published on HexDocs. Once published, the docs can be found at https://hexdocs.pm/attr_accessor.