sunaku

sunaku

StructyRecord - provides a Struct-like interface for Records

Hey folks,

I’m pleased to announce StructyRecord, my very first Elixir library! :birthday:

I created this library to improve the usability of Records (which are clunky to use in comparison to the first-class language support for Structs) because my performance-sensitive Elixir applications make heavy use of ETS tables that rely on Records for data representation.

I hope that it’s also useful to you, enjoy! :gift:

Cheers.

Readme

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.

To get started, see the documentation for StructyRecord.defrecord/3:

iex> h StructyRecord.defrecord

Features

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

Guards:

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

Macros:

  • 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 the given record into a Keyword list
  • record/2 to get the value of a given field in a given record
  • record/2 to update an existing record with the given fields and values
  • ${field}/1 to get the value of a specific field in a given record
  • ${field}/2 to set the value of a specific field in a given record
  • keypos/1 to get the 1-based index of the given field in a record

Functions:

  • record!/1 to create a new record at runtime with the given fields and values
  • record!/2 to update an existing record with the given fields and values

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
    width(r) * 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.record()                      #-> {Rectangle, nil, nil}
no_h = Rectangle.record(width: 1)              #-> {Rectangle, 1, nil}
no_w = Rectangle.record(height: 2)             #-> {Rectangle, nil, 2}
wide = Rectangle.record(width: 10, height: 5)  #-> {Rectangle, 10, 5}
tall = Rectangle.record(width:  4, height: 25) #-> {Rectangle, 4, 25}
even = Rectangle.record(width: 10, height: 10) #-> {Rectangle, 10, 10}

Get values of fields in those instances:

tall |> Rectangle.height()            #-> 25
tall |> Rectangle.record(:height)     #-> 25
Rectangle.record(height: h) = tall; h #-> 25

Set values of fields in those instances:

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

even |> Rectangle.width(1) |> Rectangle.height(2) #-> {Rectangle, 1, 2}
even |> Rectangle.record(width: 1, 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

Most Liked

OvermindDL1

OvermindDL1

Now if only the Elixir built-in Protocol’s supported dispatch based on the first tuple element. ^.^;

Love records though!

sunaku

sunaku

Version 0.2.0 (2021-02-18)

This release adds a convenient new shorthand syntax for the record() macro,
renames field accessors to prevent name collisions, clarifies docs, and more.

Incompatible:

  • Rename record!/1 function to from_list/1.

  • Rename record!/2 function to merge/2.

  • Add get_ and put_ prefix to field accessors.

    Field names can no longer conflict with defined macros & functions.

  • Don’t check argument types in Elixiry interface.

    It broke simple macro expansion when used in case/function clauses.

Enhancements:

  • Add Module.{_} syntax for Module.record(_).

    https://stackoverflow.com/a/51313720/120075

  • Add inspect/2 for friendlier inspection.

  • Add to_list/0 to get record’s template.

  • Add to_list/1 alias for record/1 macro.

  • Add index/1 to get field index in tuple.

  • Add get/2 macro as an alias to record/2.

  • Add put/2 macro as an alias to record/2.

  • Define documentation for all macros and functions.

Housekeeping:

  • record?/1 macro: only use pattern matching check.

  • keypos/1 macro: don’t call module being defined.

  • mix.exs: drop application(); use runtime: false.

mudasobwa

mudasobwa

Creator of Cure

You might want to check this blog for the insight of how to handle it differently in guards.

sunaku

sunaku

Version 0.2.1 (2021-02-20)

Housekeeping:

  • README: runtime: false suggestion broke releases.

    The runtime: false flag in the user’s mix.exs file prevents the
    structy_record application (which bundles module StructyRecord)
    from being included in app releases. This breaks defined macros and
    functions that delegate to the StructyRecord.from_list/3 function
    because the StructyRecord module won’t be included in app releases!

Where Next?

Popular in Libraries Top

mhanberg
I just released the first version of Temple: an HTML DSL for Elixir and Phoenix! You can read this blog post or the docs for more info...
New
zoltanszogyenyi
Hey everyone :wave: Excited to join this forum - I am one of the founders and current project maintainers of a popular and open-source U...
New
Crowdhailer
The latest release of Ace (0.10.0) includes serving content over HTTP/2. I have started writing a webserver to teach my self more about...
New
sasajuric
I’d like to announce a small library called boundaries. This is an experimental project which explores the idea of enforcing boundaries ...
New
pkrawat1
Presenting Aviacommerce, open source e-commerce platform in Elixir Aviacommerce is an open source e-commerce platform in Elixir. We at...
New
New
zorbash
I created Kitto a framework for dashboards inspired by Dashing. [demo] The distributed characteristics of Elixir and the low memory foo...
New
handnot2
Samly can be used to enable SAML 2.0 Single Sign On in a Plug/Phoenix application. This library uses Erlang esaml to provide plug enabl...
New
mischov
import Meeseeks.CSS html = HTTPoison.get!("https://news.ycombinator.com/").body for story <- Meeseeks.all(html, css("tr.athing")) do...
New
versilov
Could not wait for the missing Elixir ML libraries to appear, so, I wrote one myself, taking https://github.com/sdwolfz/exlearn as a foun...
New

Other popular topics Top

lessless
I believe there are people here who are dealing with CSV files import on the daily basis, and since Excel is a really popular tool there ...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
sergio
I couldn’t find any guides that worked well with Phoenix 1.6.0 and esbuild. I hope this helps people test the waters and eases you into t...
New
KronicDeth
Elixir plugin for JetBrain’s IntelliJ Platform (including Rubymine) This is a plugin that adds support for Elixir to JetBrains IntelliJ...
289 35421 110
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
chensan
I have a User schema with a :from_id field set to type :string: defmodule TweetBot.Repo.Migrations.CreateUsers do use Ecto.Migration ...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New

Sub Categories:

We're in Beta

About us Mission Statement