Rolodex v0.4.0 Rolodex.Schema

Exposes functions and macros for working with request and response parameters.

It includes two macros. Used together, they will setup reuseable schemas for things like API responses.

It also exposes the following functions:

  • is_schema_module?/1 - determines if the provided item is a module that has defined a reuseable schema
  • to_map/1 - serializes a schema module into a map for use by a Rolodex.Processor behaviour
  • new_field/1 - parses a schema field into a map of metadata. The field/3 macro uses this function to parse the metadata passed in. This function is also called when parsing all controller action @doc parameter annotations
  • get_refs/1 - takes a schema or map and traverses it, looking for any nested references to schemas within

Link to this section Summary

Functions

Adds a new field to the schema. Will generate a method __field__/1 where the one argument is the field identifier. This can be used to fetch the field metadata later

Returns a unique list of all nested Rolodex.Schema refs within the current field map or schema module

Determines if an arbitrary item is a module that has defined a reusable schema via Rolodex.Schema macros

Parses data for schema fields and controller action parameter annotations. Resolves references to any nested Rolodex.Schema modules within. Generates a new map representing the field in a standardized format

Opens up the schema definition for the current module. Will name the schema and generate metadata for the schema based on subsequent calls to field/3

Serializes the Rolodex.Schema metadata defined for the given module into an object, using the new_field/1 helper

Link to this section Functions

Link to this macro

field(identifier, type, opts \\ []) (macro)

Adds a new field to the schema. Will generate a method __field__/1 where the one argument is the field identifier. This can be used to fetch the field metadata later.

Accepts

  • identifier - field name
  • type - either an atom or another Rolodex.Schema module
  • opts - a keyword list of options, looks for desc and of (for array types)

Example

defmodule MySchema do
  use Rolodex.Schema

  schema "MySchema", desc: "Example schema" do
    # Atomic field with no description
    field :id, :uuid

    # Atomic field with a description
    field :name, :string, desc: "The object's name"

    # A field that refers to another, nested object
    field :other, OtherSchema

    # A field that is an array of items of one-or-more types
    field :multi, :list, of: [:string, OtherSchema]

    # A field that is one of the possible provided types
    field :any, :one_of, of: [:string, OtherSchema]
  end
end
Link to this function

get_refs(field)
get_refs(module() | map()) :: [module()]

Returns a unique list of all nested Rolodex.Schema refs within the current field map or schema module.

Examples

iex> defmodule NestedSchema do
...>   use Rolodex.Schema
...>
...>   schema "NestedSchema" do
...>     field :id, :uuid
...>   end
...> end
iex>
iex> defmodule TopSchema do
...>   use Rolodex.Schema
...>
...>   schema "TopSchema", desc: "An example" do
...>     # Atomic field with no description
...>     field :id, :uuid
...>
...>     # Atomic field with a description
...>     field :name, :string, desc: "The schema's name"
...>
...>     # A field that refers to another, nested object
...>     field :other, NestedSchema
...>
...>     # A field that is an array of items of one-or-more types
...>     field :multi, :list, of: [:string, NestedSchema]
...>
...>     # A field that is one of the possible provided types
...>     field :any, :one_of, of: [:string, NestedSchema]
...>   end
...> end
iex>
iex> # Searching for refs in a formatted map
iex> Rolodex.Schema.new_field(type: :list, of: [TopSchema, NestedSchema])
...> |> Rolodex.Schema.get_refs()
[Rolodex.SchemaTest.NestedSchema, Rolodex.SchemaTest.TopSchema]
iex>
iex> # Searching for refs in an arbitrary map
iex> Rolodex.Schema.get_refs(%{id: :uuid, nested: TopSchema})
[Rolodex.SchemaTest.NestedSchema]
iex>
iex> # Search for refs in a schema
iex> Rolodex.Schema.get_refs(TopSchema)
[Rolodex.SchemaTest.NestedSchema]
Link to this function

is_schema_module?(item)
is_schema_module?(any()) :: boolean()

Determines if an arbitrary item is a module that has defined a reusable schema via Rolodex.Schema macros

Example

iex> defmodule SimpleSchema do
...>   use Rolodex.Schema
...>   schema "SimpleSchema", desc: "Demo schema" do
...>     field :id, :uuid
...>   end
...> end
iex>
iex> # Validating a schema module
iex> Rolodex.Schema.is_schema_module?(SimpleSchema)
true
iex> # Validating some other module
iex> Rolodex.Schema.is_schema_module?(OtherModule)
false
Link to this function

new_field(opts)
new_field(atom() | module() | list() | map()) :: map()

Parses data for schema fields and controller action parameter annotations. Resolves references to any nested Rolodex.Schema modules within. Generates a new map representing the field in a standardized format.

Every field within the map returned will have a type. Some fields, like lists and objects, have other data nested within. Other fields hold references (called refs) to Rolodex.Schema modules.

You can think of the output as an AST of parameter data that a Rolodex.Processor behaviour can accept for writing out to a destination.

Examples

Parsing primitive data types (e.g. string)

# Creating a simple field with a primitive type
iex> Rolodex.Schema.new_field(:string)
%{type: :string}

# With additional options
iex> Rolodex.Schema.new_field(type: :string, desc: "My string")
%{type: :string, desc: "My string"}

Parsing collections: objects and lists

# Create an object
iex> Rolodex.Schema.new_field(type: :object, properties: %{id: :uuid, name: :string})
%{
  type: :object,
  properties: %{
    id: %{type: :uuid},
    name: %{type: :string}
  }
}

# Shorthand for creating an object: a top-level map or keyword list
iex> Rolodex.Schema.new_field(%{id: :uuid, name: :string})
%{
  type: :object,
  properties: %{
    id: %{type: :uuid},
    name: %{type: :string}
  }
}

# Create a list
iex> Rolodex.Schema.new_field(type: :list, of: [:string, :uuid])
%{
  type: :list,
  of: [
    %{type: :string},
    %{type: :uuid}
  ]
}

# Shorthand for creating a list: a list of types
iex> Rolodex.Schema.new_field([:string, :uuid])
%{
  type: :list,
  of: [
    %{type: :string},
    %{type: :uuid}
  ]
}

Arbitrary collections

Use the one_of type to describe a field that can be one of the provided types

iex> Rolodex.Schema.new_field(type: :one_of, of: [:string, :uuid])
%{
  type: :one_of,
  of: [
    %{type: :string},
    %{type: :uuid}
  ]
}

Working with schemas

iex> defmodule DemoSchema do
...>   use Rolodex.Schema
...>
...>   schema "DemoSchema" do
...>     field :id, :uuid
...>   end
...> end
iex>
iex> # Creating a field with a [`Rolodex.Schema`](#content) as the top-level type
iex> Rolodex.Schema.new_field(DemoSchema)
%{type: :ref, ref: Rolodex.SchemaTest.DemoSchema}
iex>
iex> # Creating a collection field with various members, including a nested schema
iex> Rolodex.Schema.new_field(type: :list, of: [:string, DemoSchema])
%{
  type: :list,
  of: [
    %{type: :string},
    %{type: :ref, ref: Rolodex.SchemaTest.DemoSchema}
  ]
}
Link to this macro

schema(name, opts \\ [], list) (macro)

Opens up the schema definition for the current module. Will name the schema and generate metadata for the schema based on subsequent calls to field/3

Accepts

  • name - the schema name
  • opts - a keyword list of options (currently, only looks for a desc key)
  • block - the inner schema definition with one or more calls to field/3

Example

defmodule MySchema do
  use Rolodex.Schema

  schema "MySchema", desc: "Example schema" do
    # Atomic field with no description
    field :id, :uuid

    # Atomic field with a description
    field :name, :string, desc: "The object's name"

    # A field that refers to another, nested object
    field :other, OtherSchema

    # A field that is an array of items of one-or-more types
    field :multi, :list, of: [:string, OtherSchema]

    # A field that is one of the possible provided types
    field :any, :one_of, of: [:string, OtherSchema]
  end
end
Link to this function

to_map(schema)
to_map(module()) :: map()

Serializes the Rolodex.Schema metadata defined for the given module into an object, using the new_field/1 helper.

Example

iex> defmodule OtherSchema do
...>   use Rolodex.Schema
...>
...>   schema "OtherSchema" do
...>     field :id, :uuid
...>   end
...> end
iex>
iex> defmodule MySchema do
...>   use Rolodex.Schema
...>
...>   schema "MySchema", desc: "An example" do
...>     # Atomic field with no description
...>     field :id, :uuid
...>
...>     # Atomic field with a description
...>     field :name, :string, desc: "The schema's name"
...>
...>     # A field that refers to another, nested object
...>     field :other, OtherSchema
...>
...>     # A field that is an array of items of one-or-more types
...>     field :multi, :list, of: [:string, OtherSchema]
...>
...>     # A field that is one of the possible provided types
...>     field :any, :one_of, of: [:string, OtherSchema]
...>   end
...> end
iex>
iex> Rolodex.Schema.to_map(MySchema)
%{
  type: :object,
  desc: "An example",
  properties: %{
    id: %{type: :uuid},
    name: %{desc: "The schema's name", type: :string},
    other: %{type: :ref, ref: Rolodex.SchemaTest.OtherSchema},
    multi: %{
      type: :list,
      of: [
        %{type: :string},
        %{type: :ref, ref: Rolodex.SchemaTest.OtherSchema}
      ]
    },
    any: %{
      type: :one_of,
      of: [
        %{type: :string},
        %{type: :ref, ref: Rolodex.SchemaTest.OtherSchema}
      ]
    }
  }
}