zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

parameterized_type.ex (5987B)


      1 defmodule Ecto.ParameterizedType do
      2   @moduledoc """
      3   Parameterized types are Ecto types that can be customized per field.
      4 
      5   Parameterized types allow a set of options to be specified in the schema
      6   which are initialized on compilation and passed to the callback functions
      7   as the last argument.
      8 
      9   For example, `field :foo, :string` behaves the same for every field.
     10   On the other hand, `field :foo, Ecto.Enum, values: [:foo, :bar, :baz]`
     11   will likely have a different set of values per field.
     12 
     13   Note that options are specified as a keyword, but it is idiomatic to
     14   convert them to maps inside `c:init/1` for easier pattern matching in
     15   other callbacks.
     16 
     17   Parameterized types are a superset of regular types. In other words,
     18   with parameterized types you can do everything a regular type does,
     19   and more. For example, parameterized types can handle `nil` values
     20   in both `load` and `dump` callbacks, they can customize `cast` behavior
     21   per query and per changeset, and also control how values are embedded.
     22 
     23   However, parameterized types are also more complex. Therefore, if
     24   everything you need to achieve can be done with basic types, they
     25   should be preferred to parameterized ones.
     26 
     27   ## Examples
     28 
     29   To create a parameterized type, create a module as shown below:
     30 
     31       defmodule MyApp.MyType do
     32         use Ecto.ParameterizedType
     33 
     34         def type(_params), do: :string
     35 
     36         def init(opts) do
     37           validate_opts(opts)
     38           Enum.into(opts, %{})
     39         end
     40 
     41         def cast(data, params) do
     42           ...
     43           {:ok, cast_data}
     44         end
     45 
     46         def load(data, _loader, params) do
     47           ...
     48           {:ok, loaded_data}
     49         end
     50 
     51         def dump(data, dumper, params) do
     52           ...
     53           {:ok, dumped_data}
     54         end
     55 
     56         def equal?(a, b, _params) do
     57           a == b
     58         end
     59       end
     60 
     61   To use this type in a schema field, specify the type and parameters like this:
     62 
     63       schema "foo" do
     64         field :bar, MyApp.MyType, opt1: :baz, opt2: :boo
     65       end
     66 
     67   To use this type in places where you need it to be initialized (for example,
     68   schemaless changesets), you can use `init/2`.
     69   """
     70 
     71   @typedoc """
     72   The keyword options passed from the Schema's field macro into `c:init/1`
     73   """
     74   @type opts :: keyword()
     75 
     76   @typedoc """
     77   The parameters for the ParameterizedType
     78 
     79   This is the value passed back from `c:init/1` and subsequently passed
     80   as the last argument to all callbacks. Idiomatically it is a map.
     81   """
     82   @type params :: term()
     83 
     84   @doc """
     85   Callback to convert the options specified in  the field macro into parameters
     86   to be used in other callbacks.
     87 
     88   This function is called at compile time, and should raise if invalid values are
     89   specified. It is idiomatic that the parameters returned from this are a map.
     90   `field` and `schema` will be injected into the options automatically.
     91 
     92   For example, this schema specification
     93 
     94       schema "my_table" do
     95         field :my_field, MyParameterizedType, opt1: :foo, opt2: nil
     96       end
     97 
     98   will result in the call:
     99 
    100       MyParameterizedType.init([schema: "my_table", field: :my_field, opt1: :foo, opt2: nil])
    101 
    102   """
    103   @callback init(opts :: opts()) :: params()
    104 
    105   @doc """
    106   Casts the given input to the ParameterizedType with the given parameters.
    107 
    108   If the parameterized type is also a composite type,
    109   the inner type can be cast by calling `Ecto.Type.cast/2`
    110   directly.
    111 
    112   For more information on casting, see `c:Ecto.Type.cast/1`.
    113   """
    114   @callback cast(data :: term, params()) ::
    115               {:ok, term} | :error | {:error, keyword()}
    116 
    117   @doc """
    118   Loads the given term into a ParameterizedType.
    119 
    120   It receives a `loader` function in case the parameterized
    121   type is also a composite type. In order to load the inner
    122   type, the `loader` must be called with the inner type and
    123   the inner value as argument.
    124 
    125   For more information on loading, see `c:Ecto.Type.load/1`.
    126   Note that this callback *will* be called when loading a `nil`
    127   value, unlike `c:Ecto.Type.load/1`.
    128   """
    129   @callback load(value :: any(), loader :: function(), params()) :: {:ok, value :: any()} | :error
    130 
    131   @doc """
    132   Dumps the given term into an Ecto native type.
    133 
    134   It receives a `dumper` function in case the parameterized
    135   type is also a composite type. In order to dump the inner
    136   type, the `dumper` must be called with the inner type and
    137   the inner value as argument.
    138 
    139   For more information on dumping, see `c:Ecto.Type.dump/1`.
    140   Note that this callback *will* be called when dumping a `nil`
    141   value, unlike `c:Ecto.Type.dump/1`.
    142   """
    143   @callback dump(value :: any(), dumper :: function(), params()) :: {:ok, value :: any()} | :error
    144 
    145   @doc """
    146   Returns the underlying schema type for the ParameterizedType.
    147 
    148   For more information on schema types, see `c:Ecto.Type.type/0`
    149   """
    150   @callback type(params()) :: Ecto.Type.t()
    151 
    152   @doc """
    153   Checks if two terms are semantically equal.
    154   """
    155   @callback equal?(value1 :: any(), value2 :: any(), params()) :: boolean()
    156 
    157   @doc """
    158   Dictates how the type should be treated inside embeds.
    159 
    160   For more information on embedding, see `c:Ecto.Type.embed_as/1`
    161   """
    162   @callback embed_as(format :: atom(), params()) :: :self | :dump
    163 
    164   @doc """
    165   Generates a loaded version of the data.
    166 
    167   This is callback is invoked when a parameterized type is given
    168   to `field` with the `:autogenerate` flag.
    169   """
    170   @callback autogenerate(params()) :: term()
    171 
    172   @optional_callbacks autogenerate: 1
    173 
    174   @doc """
    175   Inits a parameterized type given by `type` with `opts`.
    176 
    177   Useful when manually initializing a type for schemaless changesets.
    178   """
    179   def init(type, opts) do
    180     {:parameterized, type, type.init(opts)}
    181   end
    182 
    183   @doc false
    184   defmacro __using__(_) do
    185     quote location: :keep do
    186       @behaviour Ecto.ParameterizedType
    187 
    188       @doc false
    189       def embed_as(_, _), do: :self
    190 
    191       @doc false
    192       def equal?(term1, term2, _params), do: term1 == term2
    193 
    194       defoverridable embed_as: 2, equal?: 3
    195     end
    196   end
    197 end