Giter Club home page Giter Club logo

forma's People

Contributors

fredr avatar frekw avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Forkers

gorbak25

forma's Issues

Struct fields are not enforced

Hi, thanks for this library. ๐Ÿ™‚

It works well but it does not enforce fields that are declared in the struct to be enforced.

Imagine this struct:

defmodule Person do
  @enforce_keys [:name]
  defstruct name: nil,
            age: nil,
            happy?: true,
            phone: nil

  @type t() :: %__MODULE__{
          name: String.t(),
          age: non_neg_integer() | nil,
          happy?: boolean(),
          phone: String.t() | nil
        }
end

Now if you evaluate

data = %{"age" => 42, "happy?" => true, "phone" => "(206) 342-8631"}
Forma.parse(data, Person)

you get

{:ok, %Person{age: 42, happy?: true, name: nil, phone: "(206) 342-8631"}}

Notice that the call was successful and :name is set to nil even though it is in @enforced_keys and its type is not nullable.

Instead, if you evaluate %Person{age: 42, happy?: true, phone: "(206) 342-8631"}, you get this error: (ArgumentError) the following keys must also be given when building struct Person: [:name].

I think Forma should enforce :name being present in data as well. What do you think?

I think it could be achieved by changing the struct clause for Forma.Parser.parse!/3 from this:

  def parse!(input, parsers, {:struct, name, fields}) do
    case input do
      input when is_map(input) -> map_exact_fields!(input, parsers, struct(name), fields)
      _ -> raise "not a map #{inspect(input)}"
    end
  end

to something like this:

  def parse!(input, parsers, {:struct, name, fields}) do
    case input do
      input when is_map(input) -> map_exact_fields!(input, parsers, %{}, fields) |> then(&struct!(name, &1))
      _ -> raise "not a map #{inspect(input)}"
    end
  end

Production code is missing typespecs

Expected behavior:
Forma works in self-contained releases.

Actual behavior:
Forma is working well in the dev environment. Forma isn't working at all in compiled releases as the typespecs the library is using are stripped away during compilation. Trying to use forma in production code results in the following error:

        ** (Protocol.UndefinedError) protocol Enumerable not implemented for nil of type Atom. This protocol is implemented for the following type(s): Ecto.Adapters.SQL.Stream, Postgrex.Stream, DBConnection.Stream, DBConnection.PrepareStream, GenEvent.Stream, HashSet, Date.Range, IO.Stream, Map, File.Stream, Stream, MapSet, Range, HashDict, List, Function
            (elixir) lib/enum.ex:1: Enumerable.impl_for!/1
            (elixir) lib/enum.ex:141: Enumerable.reduce/3
            (elixir) lib/enum.ex:3023: Enum.map/2
            (forma) lib/forma/typespecs.ex:6: Forma.Typespecs.compile/1
            (forma) lib/forma/types.ex:32: Forma.Types.compile/1
            (forma) lib/forma/types.ex:23: Forma.Types.handle_call/3
            (stdlib) gen_server.erl:661: :gen_server.try_handle_call/4
            (stdlib) gen_server.erl:690: :gen_server.handle_msg/6
    (elixir) lib/gen_server.ex:1009: GenServer.call/3
    (forma) lib/forma.ex:96: Forma.parse/3

Handle custom typespec rewrites

For the struct definition:

  defstruct accounts: [], item: nil, numbers: [], request_id: nil

  @type t :: %__MODULE__{
          accounts: [Plaid.Accounts.Account.t()],
          item: Plaid.Item.t(),
          numbers: %{
            ach: [Plaid.Auth.Numbers.ACH.t()],
            eft: [Plaid.Auth.Numbers.EFT.t()],
            international: [Plaid.Auth.Numbers.International.t()],
            bacs: [Plaid.Auth.Numbers.BACS.t()]
          },
          request_id: String.t()
        }

Getting this error from Form.Typespecs.compile(Plaid.Auth)

     ** (FunctionClauseError) no function clause matching in Forma.Typespecs.map/3

     The following arguments were given to Forma.Typespecs.map/3:
     
         # 1
         [{:type, 26, :map_field_assoc, [{:atom, 0, :options}, {:type, 26, :map, [{:type, 27, :map_field_assoc, [{:atom, 0, :account_ids}, {:type, 0, :list, [{:remote_type, 27, [{:atom, 0, String}, {:atom, 0, :t}, []]}]}]}]}]}]
     
         # 2
         Plaid.Auth
     
         # 3
         %{"access_token" => {:access_token, {{String, :t}, []}}}
     
     Attempted function clauses (showing 2 out of 2):
     
         def map([{:type, _, :map_field_exact, [{field_type, _, name}, typ]} | rest], module, acc)
         def map([], _module, acc)

Would be helpful to either allow a custom rewriters prop to be added to Form.Typespecs.compile/1 as in:

def compile(module, rewriters \\ %{}) do
    module
    |> Kernel.Typespec.beam_types()
    |> rewrite(module, rewriters)
    |> Enum.map(fn {t, d} -> {{module, t}, d} end)
    |> Enum.into(%{})
  end

  ...

  # Add rewriters arg on final pattern match
  def rewrite([], _, _module, rewriters) do
    ...
  end
end

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.