phst_transform v1.0.2 PhStTransform protocol

The PhStTransform protocol will convert any Elixir data structure using a given transform into a new data structure.

The transform/3 function takes the data structure and a map of transformation functions and a depth list. It then does a depth-first recursion through the structure, applying the tranformation functions for all data types found in the data structure.

The transform map has data types as keys and anonymous functions as values. The anonymous functions have the data item and optionally a recursion depth list as inputs and can return anything. These maps of transform functions are refered to as potions.

The transmogrify/3 function is similar except that it allows the functions to modify the potion map as the tranform is in progress and it returns a tuple consisting of the transformed data and potion.

Example: Convert all atoms to strings

atom_to_string_potion = %{ Atom => fn(atom) -> Atom.to_string(atom) end }
PhStTransform.transform(data, atom_to_string_potion)

The potion map should have Elixir Data types as keys and anonymous functions of either fn(x) or fn(x, depth) arity. You can supply nearly any kind of map as an argument however, since the PhStTransform.Potion.brewfunction will strip out any invalid values. The valid keys are all of the standard Protocol types:

[Atom, Integer, Float, BitString, Regexp, PID, Function, Reference, Port, Tuple, List, Map]

plus Keyword and the name of any defined Structs (e.g. Range)

There is also the special type Any, this is the default function applied when there is no function for the type listed in the potion. By default this is set to the identity function fn(x, _d) -> x end, but can be overridden in the initial map.

The depth argument should always be left at the default value when using this protocol. For the anonymous functions in the potion map, they can use the depth list to know which kind of data structure contains the current data type.

Example: Capitalize all strings in the UserName struct, normalize all other strings.

user_potion = %{ BitString => fn(str, depth) ->
  if(List.first(depth) == UserName), do: String.capitalize(str), else: String.downcase(str)) end}

PhStTransform.transform(data, user_potion)

Example: Parse a list of strings input from a CSV file, into a list of maps.

csv_potion = %{ BitString => fn(str, potion) ->
                                keys = String.split(str, ",")
                                new_potion = Map.put(potion, BitString, fn(str, potion) ->
                                 { String.split(str,",")
                                  |> Enum.zip(keys)
                                  |> Enum.reduce( %{}, fn(tuple, map) ->
                                    {v, k} = tuple
                                    Map.put(map,k,v) end),
                                  potion }
                                  end )
                                {keys, new_potion}
end }

 csv_strings = File.stream!("file.csv") |> Enum.into([])
 {[keys | maps ], new_potion } = PhStTranform.transmogrify(csv_strings, csv_potion)

Summary

Functions

uses the given function_map to transform any Elixir data structure

Works similarly to transform, but returns a tuple consisting of {result, potion} allowing self modifying potions

Types

t :: term

Functions

transform(data_structure, function_map, depth \\ [])

uses the given function_map to transform any Elixir data structure.

function_map should contain keys that correspond to the data types to be transformed. Each key must map to a function that takes that data type and optionally the depth list as arguments.

depth should always be left at the default value since it is meant for internal recursion.

Examples

iex> atom_to_string_potion = %{ Atom => fn(atom) -> Atom.to_string(atom) end }
iex> PhStTransform.transform([[:a], :b, {:c, :e}], atom_to_string_potion)
[["a"], "b", {"c", "e"}]
transmogrify(data_structure, function_map, depth \\ [])

Works similarly to transform, but returns a tuple consisting of {result, potion} allowing self modifying potions.

Examples

iex> atom_first = %{ Atom => fn(atom, potion) ->
       old = atom
      { atom, Map.put(potion, Atom, fn(atom, potion) ->
        {old, potion} end )} end }
iex> PhStTransform.transmorgrify([:a, :b, :c, :d], atom_first)
{[:a, :a, :a, :a], %{Atom => #Function<12.54118792/2 in :erl_eval.expr/5>} }