StructyRecord (StructyRecord v0.2.0) View Source

StructyRecord provides a Struct-like interface for your Records.

  • Use your record's macros in the same module where it is defined!
  • Access and update fields in your record through named macro calls.
  • Create and update records at runtime (not limited to compile time).
  • Calculate 1-based indexes to access record fields in :ets tables.

Link to this section Summary

Functions

Defines a module named alias that is also a Record composed of fields.

Link to this section Functions

Link to this macro

defrecord(alias, fields, do_block \\ [])

View Source (macro)

Defines a module named alias that is also a Record composed of fields.

Parameters

  • alias is the name of the module being defined. It also serves as the tag parameter of Record.defrecord/3, which helps identify the record.

  • fields specifies the shape of the record being defined. It is either:

    • a list of Atom field names whose default values are always nil
    • a Keyword list of field names along with their own default values
  • do_block is an optional block of code that is passed into defmodule/2. It allows you to extend the module being defined with your own custom code, which has compile-time access to all the guards and macros described below.

Results

The defined module contains the following guards, macros, and functions.

Guards (available at compile time):

  • is_record/1 to check if argument loosely matches this record's shape

Macros (available at compile time):

  • {}/0 to create a new record with default values for all fields
  • {}/1 to create a new record with the given fields and values
  • {}/1 to get the zero-based index of the given field in a record
  • {{}}/1 to convert a record into a list of its fields and values
  • {{}}/2 to get the value of a given field in a given record
  • {{}}/2 to assign the given fields and values in a given record
  • record?/1 to check if argument strictly matches this record's shape
  • record/0 to create a new record with default values for all fields
  • record/1 to create a new record with the given fields and values
  • record/1 to get the zero-based index of the given field in a record
  • record/1 to convert a record into a list of its fields and values
  • record/2 to get the value of a given field in a given record
  • record/2 to assign the given fields and values in a given record
  • get/2 to fetch the value of a given field in a given record
  • put/2 to assign the given fields and values inside a given record
  • get_${field}/1 to fetch the value of a specific field in a given record
  • put_${field}/2 to assign the value of a specific field in a given record
  • index/1 to get the zero-based index of the given field in a record
  • keypos/1 to get the 1-based index of the given field in a record
  • to_list/0 to get a template of fields and default values for this record
  • to_list/1 to convert a record into a list of its fields and values

Functions (available at runtime only):

  • from_list/1 to create a new record with the given fields and values
  • merge/2 to assign the given fields and values inside a given record
  • inspect/2 to inspect the contents of a record using Kernel.inspect/2

Examples

Activate this macro in your environment:

require StructyRecord

Define a structy record for a rectangle:

StructyRecord.defrecord Rectangle, [:width, :height] do
  def area(r = record()) do
    get_width(r) * get_height(r)
  end

  def perimeter(record(width: w, height: h)) do
    2 * (w + h)
  end

  def square?(record(width: same, height: same)), do: true
  def square?(_), do: false
end

Activate its macros in your environment:

use Rectangle

Create instances of your structy record:

rect = Rectangle.{}                        #-> {Rectangle, nil, nil}
rect = Rectangle.{[]}                      #-> {Rectangle, nil, nil}
no_h = Rectangle.{[width: 1]}              #-> {Rectangle, 1, nil}
no_w = Rectangle.{[height: 2]}             #-> {Rectangle, nil, 2}
wide = Rectangle.{[width: 10, height: 5]}  #-> {Rectangle, 10, 5}
tall = Rectangle.{[width: 4, height: 25]}  #-> {Rectangle, 4, 25}
even = Rectangle.{[width: 10, height: 10]} #-> {Rectangle, 10, 10}

Inspect the contents of those instances:

rect |> Rectangle.inspect() #-> "Rectangle.{[width: nil, height: nil]}"
no_h |> Rectangle.inspect() #-> "Rectangle.{[width: 1, height: nil]}"
no_w |> Rectangle.inspect() #-> "Rectangle.{[width: nil, height: 2]}"
wide |> Rectangle.inspect() #-> "Rectangle.{[width: 10, height: 5]}"
tall |> Rectangle.inspect() #-> "Rectangle.{[width: 4, height: 25]}"
even |> Rectangle.inspect() #-> "Rectangle.{[width: 10, height: 10]}"

Get values of fields in those instances:

Rectangle.{{tall, :height}}       #-> 25
Rectangle.{[height: h]} = tall; h #-> 25
tall |> Rectangle.get_height()    #-> 25

Set values of fields in those instances:

Rectangle.{{even, width: 1}}    #-> {Rectangle, 1, 10}
even |> Rectangle.put(width: 1) #-> {Rectangle, 1, 10}
even |> Rectangle.put_width(1)  #-> {Rectangle, 1, 10}

Rectangle.{{even, width: 1, height: 2}}                   #-> {Rectangle, 1, 2}
even |> Rectangle.put(width: 1, height: 2)                #-> {Rectangle, 1, 2}
even |> Rectangle.put_width(1) |> Rectangle.put_height(2) #-> {Rectangle, 1, 2}

Use your custom code on those instances:

rect |> Rectangle.area() #-> (ArithmeticError) bad argument in arithmetic expression: nil * nil
no_h |> Rectangle.area() #-> (ArithmeticError) bad argument in arithmetic expression: 1 * nil
no_w |> Rectangle.area() #-> (ArithmeticError) bad argument in arithmetic expression: nil * 2
wide |> Rectangle.area() #-> 50
tall |> Rectangle.area() #-> 100
even |> Rectangle.area() #-> 100

rect |> Rectangle.perimeter() #-> (ArithmeticError) bad argument in arithmetic expression: nil + nil
no_h |> Rectangle.perimeter() #-> (ArithmeticError) bad argument in arithmetic expression: 1 + nil
no_w |> Rectangle.perimeter() #-> (ArithmeticError) bad argument in arithmetic expression: nil + 2
wide |> Rectangle.perimeter() #-> 30
tall |> Rectangle.perimeter() #-> 58
even |> Rectangle.perimeter() #-> 40

rect |> Rectangle.square?() #-> true
no_h |> Rectangle.square?() #-> false
no_w |> Rectangle.square?() #-> false
wide |> Rectangle.square?() #-> false
tall |> Rectangle.square?() #-> false
even |> Rectangle.square?() #-> true