ExConstructor

repository·main·Indexed 43 days ago

https://github.com/appcues/exconstructor

An Elixir library that simplifies the instantiation of structs from external data sources like JSON. It automatically handles key type mismatches between strings and atoms, as well as casing differences between camelCase, PascalCase, and snake_case.

Tokens
1.4K
Snippets
7
Records
8
Agent score
5%

What's inside ExConstructor

  1. Inject a constructor into a module with use ExConstructor

    main

    To enable ExConstructor in your module, place use ExConstructor immediately after your defstruct statement. This injects a new/1 function into your module that accepts a map or keyword list and returns a populated struct.

    defmodule MyModule do
      use ExConstructor
      defstruct field_name: nil
    end
  2. Use ExConstructor to instantiate structs from external data

    main

    ExConstructor is an Elixir library designed to simplify instantiating structs from external data sources (like JSON parsers). By adding use ExConstructor to a module, you inject a constructor function (defaulting to new/1) that automatically resolves common data format mismatches, including:

    • Map vs. Keyword List: Handles both input types.
    • String vs. Atom Keys: Automatically converts string keys to atoms.
    • camelCase vs. under_score: Normalizes camelCase keys to match the struct's under_scored field names.

    This allows you to pass messy external maps directly into your struct constructor.

    defmodule TestStruct do
      use ExConstructor
      defstruct field_one: nil,
                field_two: nil,
                field_three: nil,
                field_four: nil
    end
    
    # The constructor handles mixed key types and casing automatically
    TestStruct.new(%{"field_one" => "a", "fieldTwo" => "b", :field_three => "c", :FieldFour => "d"})
    # => %TestStruct{field_one: "a", field_two: "b", field_three: "c", field_four: "d"}
  3. How ExConstructor handles different key formats

    main

    The generated constructor (and populate_struct/3) automatically attempts to match keys in the input data to the struct's fields using several transformations. By default, it supports:

    • Strings vs Atoms: Keys can be strings or atoms.
    • Case Styles: It checks for camelCase, UpperCamelCase (PascalCase), and under_score formats.

    This allows you to pass data directly from JSON parsers (which often use camelCase strings) into Elixir structs (which use under_score atoms) without manual transformation.

  4. Use ExConstructor to inject a constructor into a module

    main

    Add use ExConstructor after a defstruct statement to inject a constructor function into your module. By default, the function is named new/2.

    defmodule TestStruct do
      use ExConstructor
      defstruct field_one: 1, field_two: 2
    end
    
    # The constructor handles various key formats automatically:
    TestStruct.new(%{"field_one" => "a", "fieldTwo" => "b"})
    # => %TestStruct{field_one: "a", field_two: "b"}
  5. Configure the constructor name and default options

    main

    You can customize the name of the generated constructor and set default behavior by passing an atom or a keyword list to use ExConstructor or define_constructor/1.

    # Using a custom name via an atom
    defmodule User do
      use ExConstructor, :from_map
    end
    
    # Using a custom name and options via a keyword list
    defmodule User do
      use ExConstructor, name: :from_map, camelcase: false
    end
    
    # Using the explicit macro
    defmodule User do
      define_constructor :from_map
    end
  6. Configure key matching behavior with populate_struct/3

    main

    The populate_struct/3 function allows you to apply values from a map or keyword list to a struct while explicitly enabling or disabling specific key formats via an options keyword list.

    # Options keys: :strings, :atoms, :camelcase, :uppercamelcase, :underscore
    # Set any to `false` to disable that specific check.
    
    ExConstructor.populate_struct(my_struct, %{"field_one" => "val"}, %{strings: true, atoms: false})
  7. Reference: populate_struct/3 options

    main

    When calling populate_struct/3, you can pass a keyword list to control which key formats are attempted. All options default to true.

    # Available options:
    # :strings       - Check for string keys
    # :atoms         - Check for atom keys
    # :camelcase     - Check for camelCase
    # :uppercamelcase - Check for UpperCamelCase
    # :underscore     - Check for under_score