From 759c7f4109abce6862a0b8ccdd89d22769c5b449 Mon Sep 17 00:00:00 2001 From: k-asm Date: Thu, 13 Jun 2024 19:20:20 +0900 Subject: [PATCH 01/12] refactor: mix format --- lib/params.ex | 50 +++++---- lib/params/behaviour.ex | 7 +- lib/params/def.ex | 106 +++++++++++------- lib/params/schema.ex | 9 +- test/params_test.exs | 237 +++++++++++++++++++++------------------- 5 files changed, 229 insertions(+), 180 deletions(-) diff --git a/lib/params.ex b/lib/params.ex index 925ffd3..8ca66c9 100644 --- a/lib/params.ex +++ b/lib/params.ex @@ -41,7 +41,7 @@ defmodule Params do Recursively traverses and transforms embedded changesets and skips keys that was not part of params given to changeset """ - @spec to_map(Changeset.t) :: map + @spec to_map(Changeset.t()) :: map def to_map(%Changeset{data: %{__struct__: module}} = ch) do ecto_defaults = module |> plain_defaults_defined_by_ecto_schema params_defaults = module |> schema |> defaults @@ -72,13 +72,14 @@ defmodule Params do data.login # => "foo" ``` """ - @spec data(Changeset.t) :: struct + @spec data(Changeset.t()) :: struct def data(%Changeset{data: data = %{__struct__: module}} = ch) do default_embeds = default_embeds_from_schema(module) - default = Enum.reduce(default_embeds, data, fn {k, v}, m -> - Map.put(m, k, Map.get(m, k) || v) - end) + default = + Enum.reduce(default_embeds, data, fn {k, v}, m -> + Map.put(m, k, Map.get(m, k) || v) + end) Enum.reduce(ch.changes, default, fn {k, v}, m -> case v do @@ -103,12 +104,14 @@ defmodule Params do end case schema(module) do - nil -> %{} + nil -> + %{} + schema -> schema |> Stream.filter(is_embed_default) |> Stream.map(default_embed) - |> Enum.into(struct(module) |> Map.from_struct) + |> Enum.into(struct(module) |> Map.from_struct()) end end @@ -143,7 +146,7 @@ defmodule Params do changeset |> Changeset.cast(params, required ++ optional) |> Changeset.validate_required(required) - |> cast_relations(required_relations, [required: true]) + |> cast_relations(required_relations, required: true) |> cast_relations(optional_relations, []) end @@ -158,11 +161,11 @@ defmodule Params do end defp change(%{__struct__: _} = model) do - model |> Changeset.change + model |> Changeset.change() end defp change(module) when is_atom(module) do - module |> struct |> Changeset.change + module |> struct |> Changeset.change() end defp relation_partition(module, names) do @@ -174,6 +177,7 @@ defmodule Params do case Map.get(types, name) do {type, _} when type in @relations -> {fields, [{name, type} | relations]} + _ -> {[name | fields], relations} end @@ -194,33 +198,39 @@ defmodule Params do defp deep_merge_conflict(_k, %{} = m1, %{} = m2) do deep_merge(m1, m2) end + defp deep_merge_conflict(_k, _v1, v2), do: v2 defp defaults(params), do: defaults(params, %{}, []) defp defaults(params, acc, path) defp defaults([], acc, _path), do: acc defp defaults(nil, _acc, _path), do: %{} + defp defaults([opts | rest], acc, path) when is_list(opts) do defaults([Enum.into(opts, %{}) | rest], acc, path) end + defp defaults([%{name: name, embeds: embeds} | rest], acc, path) do acc = defaults(embeds, acc, [name | path]) defaults(rest, acc, path) end + defp defaults([%{name: name, default: value} | rest], acc, path) do - funs = [name | path] - |> Enum.reverse - |> Enum.map(fn nested_name -> - fn :get_and_update, data, next -> - with {nil, inner_data} <- next.(data[nested_name] || %{}), - data = Map.put(data, nested_name, inner_data), - do: {nil, data} - end - end) + funs = + [name | path] + |> Enum.reverse() + |> Enum.map(fn nested_name -> + fn :get_and_update, data, next -> + with {nil, inner_data} <- next.(data[nested_name] || %{}), + data = Map.put(data, nested_name, inner_data), + do: {nil, data} + end + end) acc = put_in(acc, funs, value) defaults(rest, acc, path) end + defp defaults([%{} | rest], acc, path) do defaults(rest, acc, path) end @@ -238,7 +248,7 @@ defmodule Params do defp plain_defaults_defined_by_ecto_schema(module) do module |> struct - |> Map.from_struct + |> Map.from_struct() |> Map.delete(:__meta__) |> Enum.reject(fn {_, v} -> is_nil(v) end) |> Enum.into(%{}) diff --git a/lib/params/behaviour.ex b/lib/params/behaviour.ex index fc6b7d2..0ddfcf7 100644 --- a/lib/params/behaviour.ex +++ b/lib/params/behaviour.ex @@ -1,8 +1,7 @@ defmodule Params.Behaviour do @moduledoc false - @callback from(map, Keyword.t) :: Ecto.Changeset.t - @callback data(map, Keyword.t) :: {:ok, struct} | {:error, Ecto.Changeset.t} - @callback changeset(Ecto.Changeset.t, map) :: Ecto.Changeset.t - + @callback from(map, Keyword.t()) :: Ecto.Changeset.t() + @callback data(map, Keyword.t()) :: {:ok, struct} | {:error, Ecto.Changeset.t()} + @callback changeset(Ecto.Changeset.t(), map) :: Ecto.Changeset.t() end diff --git a/lib/params/def.ex b/lib/params/def.ex index fcb4ba7..24f9d31 100644 --- a/lib/params/def.ex +++ b/lib/params/def.ex @@ -2,8 +2,9 @@ defmodule Params.Def do @moduledoc false @doc false - defmacro defparams({name, _, [schema]}, [do: block]) do + defmacro defparams({name, _, [schema]}, do: block) do block = Macro.escape(block) + quote bind_quoted: [name: name, schema: schema, block: block] do module_name = Params.Def.module_concat(Params, __MODULE__, name) @@ -12,11 +13,14 @@ defmodule Params.Def do Code.eval_quoted(block, [], __ENV__) end - Module.eval_quoted(__MODULE__, quote do - def unquote(name)(params, options \\ []) do - unquote(module_name).from(params, options) - end - end) + Module.eval_quoted( + __MODULE__, + quote do + def unquote(name)(params, options \\ []) do + unquote(module_name).from(params, options) + end + end + ) end end @@ -29,11 +33,14 @@ defmodule Params.Def do Params.Def.defschema(schema) end - Module.eval_quoted(__MODULE__, quote do - def unquote(name)(params) do - unquote(module_name).from(params) - end - end) + Module.eval_quoted( + __MODULE__, + quote do + def unquote(name)(params) do + unquote(module_name).from(params) + end + end + ) end end @@ -44,7 +51,7 @@ defmodule Params.Def do Module.eval_quoted(__MODULE__, Params.Def.gen_root_schema(normalized_schema)) normalized_schema - |> Params.Def.build_nested_schemas + |> Params.Def.build_nested_schemas() |> Enum.each(fn {name, content} -> Module.create(name, content, Macro.Env.location(__ENV__)) @@ -54,41 +61,46 @@ defmodule Params.Def do def build_nested_schemas(schemas, acc \\ []) def build_nested_schemas([], acc), do: acc + def build_nested_schemas([schema | rest], acc) do embedded = Keyword.has_key?(schema, :embeds) - acc = if embedded do - sub_schema = Keyword.get(schema, :embeds) - - module_def = { - sub_schema |> List.first |> Keyword.get(:module), - Params.Def.gen_root_schema(sub_schema) - } - new_acc = [module_def | acc] - build_nested_schemas(sub_schema, new_acc) - else - acc - end + + acc = + if embedded do + sub_schema = Keyword.get(schema, :embeds) + + module_def = { + sub_schema |> List.first() |> Keyword.get(:module), + Params.Def.gen_root_schema(sub_schema) + } + + new_acc = [module_def | acc] + build_nested_schemas(sub_schema, new_acc) + else + acc + end + build_nested_schemas(rest, acc) end def module_concat(parent, scope, name) do - Module.concat [parent, scope, Macro.camelize("#{name}")] + Module.concat([parent, scope, Macro.camelize("#{name}")]) end def module_concat(parent, name) do - Module.concat [parent, Macro.camelize("#{name}")] + Module.concat([parent, Macro.camelize("#{name}")]) end def gen_root_schema(schema) do quote do use Params.Schema - @schema unquote(schema) + @schema unquote(schema) @required unquote(field_names(schema, &is_required?/1)) @optional unquote(field_names(schema, &is_optional?/1)) schema do - unquote_splicing(schema_fields(schema)) + (unquote_splicing(schema_fields(schema))) end end end @@ -118,6 +130,7 @@ defmodule Params.Def do field_type(meta), field_options(meta) } + quote do unquote(call)(unquote(name), unquote(type), unquote(opts)) end @@ -125,17 +138,24 @@ defmodule Params.Def do defp field_call(meta) do cond do - Keyword.get(meta, :field) -> :field - Keyword.get(meta, :embeds_one) -> :embeds_one - Keyword.get(meta, :embeds_many) -> :embeds_many + Keyword.get(meta, :field) -> + :field + + Keyword.get(meta, :embeds_one) -> + :embeds_one + + Keyword.get(meta, :embeds_many) -> + :embeds_many + Keyword.get(meta, :embeds) -> - "embeds_#{Keyword.get(meta, :cardinality, :one)}" |> String.to_atom + "embeds_#{Keyword.get(meta, :cardinality, :one)}" |> String.to_atom() end end defp field_type(meta) do module = Keyword.get(meta, :module) - name = Keyword.get(meta, :name) + name = Keyword.get(meta, :name) + cond do Keyword.get(meta, :field) -> Keyword.get(meta, :field) Keyword.get(meta, :embeds) -> module_concat(module, name) @@ -145,19 +165,28 @@ defmodule Params.Def do end defp field_options(meta) do - Keyword.drop(meta, [:module, :name, :field, :embeds, :embeds_one, :embeds_many, :required, :cardinality]) + Keyword.drop(meta, [ + :module, + :name, + :field, + :embeds, + :embeds_one, + :embeds_many, + :required, + :cardinality + ]) end def normalize_schema(dict, module) do - Enum.reduce(dict, [], fn {k,v}, list -> + Enum.reduce(dict, [], fn {k, v}, list -> [normalize_field({module, k, v}) | list] end) end defp normalize_field({module, k, v}) do required = String.ends_with?("#{k}", "!") - name = String.replace_trailing("#{k}", "!", "") |> String.to_atom - normalize_field(v, [name: name, required: required, module: module]) + name = String.replace_trailing("#{k}", "!", "") |> String.to_atom() + normalize_field(v, name: name, required: required, module: module) end defp normalize_field({:embeds_one, embed_module}, options) do @@ -169,7 +198,7 @@ defmodule Params.Def do end defp normalize_field(schema = %{}, options) do - module = module_concat Keyword.get(options, :module), Keyword.get(options, :name) + module = module_concat(Keyword.get(options, :module), Keyword.get(options, :name)) [embeds: normalize_schema(schema, module)] ++ options end @@ -192,5 +221,4 @@ defmodule Params.Def do defp normalize_field([value], options) do [field: {:array, value}] ++ options end - end diff --git a/lib/params/schema.ex b/lib/params/schema.ex index e13b8c7..8f48f41 100644 --- a/lib/params/schema.ex +++ b/lib/params/schema.ex @@ -47,7 +47,7 @@ defmodule Params.Schema do end @doc false - defmacro schema([do: definition]) do + defmacro schema(do: definition) do quote do Ecto.Schema.schema "params #{__MODULE__}" do unquote(definition) @@ -67,13 +67,13 @@ defmodule Params.Schema do quote do Module.register_attribute(__MODULE__, :required, persist: true) Module.register_attribute(__MODULE__, :optional, persist: true) - Module.register_attribute(__MODULE__, :schema, persist: true) + Module.register_attribute(__MODULE__, :schema, persist: true) @behaviour Params.Behaviour def from(params, options \\ []) when is_list(options) do on_cast = Keyword.get(options, :with, &__MODULE__.changeset(&1, &2)) - __MODULE__ |> struct |> Ecto.Changeset.change |> on_cast.(params) + __MODULE__ |> struct |> Ecto.Changeset.change() |> on_cast.(params) end def data(params, options \\ []) when is_list(options) do @@ -87,8 +87,7 @@ defmodule Params.Schema do Params.changeset(changeset, params) end - defoverridable [changeset: 2] + defoverridable changeset: 2 end end - end diff --git a/test/params_test.exs b/test/params_test.exs index 5c22be0..12b30e6 100644 --- a/test/params_test.exs +++ b/test/params_test.exs @@ -2,30 +2,29 @@ defmodule ParamsTest do use ExUnit.Case use Params - alias Ecto.Changeset + alias Ecto.Changeset import Ecto.Changeset defmodule PetParams do use Params.Schema + schema do - field :name - field :age, :integer + field(:name) + field(:age, :integer) end end test "module has schema types" do - assert %{age: :integer, - name: :string, - _id: :binary_id} == - PetParams.__changeset__ + assert %{age: :integer, name: :string, _id: :binary_id} == + PetParams.__changeset__() end test "defaults to no required fields" do - assert [] == Params.required PetParams + assert [] == Params.required(PetParams) end test "defaults to all optional fields" do - assert [:_id, :age, :name] == Params.optional PetParams + assert [:_id, :age, :name] == Params.optional(PetParams) end test "from returns a changeset" do @@ -42,8 +41,8 @@ defmodule ParamsTest do use Params.Schema @required ~w(latitude longitude) schema do - field :latitude, :float - field :longitude, :float + field(:latitude, :float) + field(:longitude, :float) end end @@ -51,8 +50,8 @@ defmodule ParamsTest do use Params.Schema @required ~w(origin destination) schema do - embeds_one :origin, LocationParams - embeds_one :destination, LocationParams + embeds_one(:origin, LocationParams) + embeds_one(:destination, LocationParams) end end @@ -78,7 +77,7 @@ defmodule ParamsTest do test "invalid if nested required missing" do params = %{ "origin" => %{ - "latitude" => 12.2, + "latitude" => 12.2 }, "destination" => %{ "longitude" => 13.3 @@ -88,28 +87,31 @@ defmodule ParamsTest do assert %{valid?: false} = BusParams.from(params) end - test "to_map gets map of struct except for _id" do params = %{ "latitude" => 12.2, "longitude" => 13.3 } - result = params - |> LocationParams.from - |> Params.to_map + + result = + params + |> LocationParams.from() + |> Params.to_map() assert result == %{latitude: 12.2, longitude: 13.3} end - defparams kitten %{ - breed!: :string, - age_min: :integer, - age_max: :integer, - near_location!: %{ - latitude: :float, - longitude: :float - } - } + defparams( + kitten(%{ + breed!: :string, + age_min: :integer, + age_max: :integer, + near_location!: %{ + latitude: :float, + longitude: :float + } + }) + ) test "kitten module has list of required fields" do assert [:near_location, :breed] = Params.required(Params.ParamsTest.Kitten) @@ -133,15 +135,18 @@ defmodule ParamsTest do "longitude" => "-90.0" } } + assert %Changeset{valid?: true} = kitten(params) end - defparams puppy %{ - breed!: :string, - age_min: :integer, - age_max: :integer, - near_location!: {:embeds_one, LocationParams} - } + defparams( + puppy(%{ + breed!: :string, + age_min: :integer, + age_max: :integer, + near_location!: {:embeds_one, LocationParams} + }) + ) test "puppy module has list of required fields" do assert [:near_location, :breed] = Params.required(Params.ParamsTest.Puppy) @@ -165,15 +170,18 @@ defmodule ParamsTest do "longitude" => "-90.0" } } + assert %Changeset{valid?: true} = puppy(params) end - defparams dragon %{ - breed!: :string, - age_min: :integer, - age_max: :integer, - near_locations!: {:embeds_many, LocationParams} - } + defparams( + dragon(%{ + breed!: :string, + age_min: :integer, + age_max: :integer, + near_locations!: {:embeds_many, LocationParams} + }) + ) test "dragon module has list of required fields" do assert [:near_locations, :breed] = Params.required(Params.ParamsTest.Dragon) @@ -203,15 +211,14 @@ defmodule ParamsTest do } ] } + assert %Changeset{valid?: true} = dragon(params) end - defparams kid( - %{ - name: :string, - age: :integer - }) do - + defparams kid(%{ + name: :string, + age: :integer + }) do def custom(ch, params) do cast(ch, params, ~w(name age)a) |> validate_required([:name]) @@ -233,7 +240,7 @@ defmodule ParamsTest do end test "can obtain data from changeset" do - m = Params.data kid(%{name: "hugo", age: "5"}) + m = Params.data(kid(%{name: "hugo", age: "5"})) assert "hugo" == m.name assert 5 == m.age assert nil == m._id @@ -243,7 +250,7 @@ defmodule ParamsTest do @schema %{ name: :string, near: %{ - latitude: :float, + latitude: :float, longitude: :float } } @@ -271,16 +278,17 @@ defmodule ParamsTest do end defmodule ManyNames do - use Params.Schema, %{names!: [%{name!: :string}]} + use Params.Schema, %{names!: [%{name!: :string}]} end test "can have array of embedded schemas" do assert %{valid?: true} = ch = ManyNames.from(%{names: [%{name: "Julio"}, %{name: "Cesar"}]}) - assert ["Julio", "Cesar"] = ch |> Params.data |> Map.get(:names) |> Enum.map(&(&1.name)) + assert ["Julio", "Cesar"] = ch |> Params.data() |> Map.get(:names) |> Enum.map(& &1.name) end defmodule Vowel do use Params.Schema, %{x: :string} + def changeset(ch, params) do cast(ch, params, [:x]) |> validate_required([:x]) @@ -296,9 +304,11 @@ defmodule ParamsTest do assert {:error, %Changeset{valid?: false}} = Vowel.data(%{"x" => "x"}) end - defparams schema_options %{ - foo: [field: :string, default: "FOO"] - } + defparams( + schema_options(%{ + foo: [field: :string, default: "FOO"] + }) + ) test "can specify raw Ecto.Schema options like default using a keyword list" do ch = schema_options(%{}) @@ -313,19 +323,21 @@ defmodule ParamsTest do assert map == %{foo: "FOO"} end - defparams default_nested %{ - foo: %{ - bar: :string, - baz: :string - }, - bat: %{ - man: [field: :string, default: "BATMAN"], - wo: %{ - man: [field: :string, default: "BATWOMAN"] + defparams( + default_nested(%{ + foo: %{ + bar: :string, + baz: :string }, - mo: %{ vil: :string } - } - } + bat: %{ + man: [field: :string, default: "BATMAN"], + wo: %{ + man: [field: :string, default: "BATWOMAN"] + }, + mo: %{vil: :string} + } + }) + ) test "embeds with defaults are not nil" do ch = default_nested(%{}) @@ -344,34 +356,35 @@ defmodule ParamsTest do result = Params.to_map(changeset) assert result == %{ - bat: %{ - man: "BATMAN", - wo: %{ - man: "BATWOMAN" - } - } - } + bat: %{ + man: "BATMAN", + wo: %{ + man: "BATWOMAN" + } + } + } end test "to_map works on nested schemas with default values" do - changeset = %{ - bat: %{ - man: "Bruce" + changeset = + %{ + bat: %{ + man: "Bruce" + } } - } - |> default_nested + |> default_nested assert changeset.valid? result = Params.to_map(changeset) assert result == %{ - bat: %{ - man: "Bruce", - wo: %{ - man: "BATWOMAN" - } - } - } + bat: %{ + man: "Bruce", + wo: %{ + man: "BATWOMAN" + } + } + } end defmodule DefaultNested do @@ -382,12 +395,12 @@ defmodule ParamsTest do d: %{ e: :string, f: :string, - g: [field: :string, default: "G"], + g: [field: :string, default: "G"] }, h: %{ i: :string, j: :string, - k: [field: :string, default: "K"], + k: [field: :string, default: "K"] }, l: %{ m: :string @@ -397,49 +410,49 @@ defmodule ParamsTest do p: [field: :string, default: "P"] } } - } end test "to_map only returns submitted fields" do - result = %{ - a: "A", - d: %{ - e: "E", - g: "g" + result = + %{ + a: "A", + d: %{ + e: "E", + g: "g" + } } - } - |> DefaultNested.from - |> Params.to_map + |> DefaultNested.from() + |> Params.to_map() assert result == %{ - a: "A", - c: "C", - d: %{ - e: "E", - g: "g" - }, - h: %{ - k: "K" - }, - n: %{ - o: %{ - p: "P" - } - } - } + a: "A", + c: "C", + d: %{ + e: "E", + g: "g" + }, + h: %{ + k: "K" + }, + n: %{ + o: %{ + p: "P" + } + } + } end defmodule DefaultCountParams do use Params.Schema schema do - field :count, :integer, default: 1 + field(:count, :integer, default: 1) end end test "use Params.Schema respects defaults" do - changeset = DefaultCountParams.from(%{}) - assert %{count: 1} = Params.to_map(changeset) + changeset = DefaultCountParams.from(%{}) + assert %{count: 1} = Params.to_map(changeset) end end From f3463eecd4ed5454dfbd1b8efa79d475c32a0162 Mon Sep 17 00:00:00 2001 From: k-asm Date: Thu, 13 Jun 2024 19:20:56 +0900 Subject: [PATCH 02/12] fix: suppress warning, use Mix.Config is deprecated --- config/config.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/config.exs b/config/config.exs index daddc8c..f5c345c 100644 --- a/config/config.exs +++ b/config/config.exs @@ -1,6 +1,6 @@ # This file is responsible for configuring your application # and its dependencies with the aid of the Mix.Config module. -use Mix.Config +import Config # This configuration is loaded before any dependency and is restricted # to this project. If another project depends on this project, this From 9d46d454178ea686643b7cdb39a400b38396b989 Mon Sep 17 00:00:00 2001 From: k-asm Date: Thu, 13 Jun 2024 19:23:09 +0900 Subject: [PATCH 03/12] fix: suppress warning about parentheses warning: using map.field notation (without parentheses) to invoke function ParamsTest.StringArray.__changeset__() is deprecated, you must add parentheses instead: remote.function() --- lib/params.ex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/params.ex b/lib/params.ex index 8ca66c9..4ba39c4 100644 --- a/lib/params.ex +++ b/lib/params.ex @@ -169,7 +169,7 @@ defmodule Params do end defp relation_partition(module, names) do - types = module.__changeset__ + types = module.__changeset__() names |> Stream.map(fn x -> String.to_atom("#{x}") end) From cb83163e8023bd27eef0b1c728ede3d8ee2dc042 Mon Sep 17 00:00:00 2001 From: k-asm Date: Thu, 13 Jun 2024 19:28:19 +0900 Subject: [PATCH 04/12] test: sort fields in order to support OTP 26+ --- test/params_test.exs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/test/params_test.exs b/test/params_test.exs index 12b30e6..c1916c6 100644 --- a/test/params_test.exs +++ b/test/params_test.exs @@ -24,7 +24,9 @@ defmodule ParamsTest do end test "defaults to all optional fields" do - assert [:_id, :age, :name] == Params.optional(PetParams) + # the order of the map fields is not guaranteed in OTP 26+ + # https://www.erlang.org/news/164#maps + assert [:_id, :age, :name] == Params.optional(PetParams) |> Enum.sort() end test "from returns a changeset" do @@ -118,7 +120,9 @@ defmodule ParamsTest do end test "kitten module has list of optional fields" do - assert [:age_min, :age_max] = Params.optional(Params.ParamsTest.Kitten) + # the order of the map fields is not guaranteed in OTP 26+ + # https://www.erlang.org/news/164#maps + assert [:age_max, :age_min] = Params.optional(Params.ParamsTest.Kitten) |> Enum.sort() end test "kitten method returns changeset" do @@ -153,7 +157,9 @@ defmodule ParamsTest do end test "puppy module has list of optional fields" do - assert [:age_min, :age_max] = Params.optional(Params.ParamsTest.Puppy) + # the order of the map fields is not guaranteed in OTP 26+ + # https://www.erlang.org/news/164#maps + assert [:age_max, :age_min] = Params.optional(Params.ParamsTest.Puppy) |> Enum.sort() end test "puppy method returns changeset" do @@ -188,7 +194,9 @@ defmodule ParamsTest do end test "dragon module has list of optional fields" do - assert [:age_min, :age_max] = Params.optional(Params.ParamsTest.Dragon) + # the order of the map fields is not guaranteed in OTP 26+ + # https://www.erlang.org/news/164#maps + assert [:age_max, :age_min] = Params.optional(Params.ParamsTest.Dragon) |> Enum.sort() end test "dragon method returns changeset" do From d3e54979ef64dcba63790ceb2ccf0d39e01ae47b Mon Sep 17 00:00:00 2001 From: k-asm Date: Thu, 13 Jun 2024 19:38:21 +0900 Subject: [PATCH 05/12] dep: mix deps.update --all --- mix.lock | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/mix.lock b/mix.lock index d5c8945..6ff2da6 100644 --- a/mix.lock +++ b/mix.lock @@ -1,12 +1,13 @@ %{ - "decimal": {:hex, :decimal, "1.5.0", "b0433a36d0e2430e3d50291b1c65f53c37d56f83665b43d79963684865beab68", [:mix], [], "hexpm", "130926580655f34d759dd25f5d723fd233c9bbe0399cde57e2a1adea9ed92e08"}, - "dialyxir": {:hex, :dialyxir, "0.5.0", "5bc543f9c28ecd51b99cc1a685a3c2a1a93216990347f259406a910cf048d1d7", [:mix], [], "hexpm", "7c5c6c1eceb93e26a06c36148cb6f8021ae6f4f9a07bb1ae95f588e0a01ea8e1"}, + "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, + "dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm", "6c32a70ed5d452c6650916555b1f96c79af5fc4bf286997f8b15f213de786f73"}, "earmark": {:hex, :earmark, "1.3.1", "73812f447f7a42358d3ba79283cfa3075a7580a3a2ed457616d6517ac3738cb9", [:mix], [], "hexpm", "000aaeff08919e95e7aea13e4af7b2b9734577b3e6a7c50ee31ee88cab6ec4fb"}, - "earmark_parser": {:hex, :earmark_parser, "1.4.18", "e1b2be73eb08a49fb032a0208bf647380682374a725dfb5b9e510def8397f6f2", [:mix], [], "hexpm", "114a0e85ec3cf9e04b811009e73c206394ffecfcc313e0b346de0d557774ee97"}, - "ecto": {:hex, :ecto, "3.0.1", "a26605ee7b243a754e6609d1c23da27bcb22823659b07bf03f9020da92a8e4f4", [:mix], [{:decimal, "~> 1.5", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:poison, "~> 2.2 or ~> 3.0", [hex: :poison, repo: "hexpm", optional: true]}], "hexpm", "8a04eb11fd4a1e75443fe5f7e4ea38f77c36b7325ea478a5e7a301f52b236504"}, - "ex_doc": {:hex, :ex_doc, "0.26.0", "1922164bac0b18b02f84d6f69cab1b93bc3e870e2ad18d5dacb50a9e06b542a3", [:mix], [{:earmark_parser, "~> 1.4.0", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "2775d66e494a9a48355db7867478ffd997864c61c65a47d31c4949459281c78d"}, - "makeup": {:hex, :makeup, "1.0.5", "d5a830bc42c9800ce07dd97fa94669dfb93d3bf5fcf6ea7a0c67b2e0e4a7f26c", [:mix], [{:nimble_parsec, "~> 0.5 or ~> 1.0", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cfa158c02d3f5c0c665d0af11512fed3fba0144cf1aadee0f2ce17747fba2ca9"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.15.2", "dc72dfe17eb240552857465cc00cce390960d9a0c055c4ccd38b70629227e97c", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.1", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "fd23ae48d09b32eff49d4ced2b43c9f086d402ee4fd4fcb2d7fad97fa8823e75"}, - "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.2.0", "b44d75e2a6542dcb6acf5d71c32c74ca88960421b6874777f79153bbbbd7dccc", [:mix], [], "hexpm", "52b2871a7515a5ac49b00f214e4165a40724cf99798d8e4a65e4fd64ebd002c1"}, + "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, + "ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"}, + "ex_doc": {:hex, :ex_doc, "0.34.0", "ab95e0775db3df71d30cf8d78728dd9261c355c81382bcd4cefdc74610bef13e", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "60734fb4c1353f270c3286df4a0d51e65a2c1d9fba66af3940847cc65a8066d7"}, + "makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, + "makeup_erlang": {:hex, :makeup_erlang, "1.0.0", "6f0eff9c9c489f26b69b61440bf1b238d95badae49adac77973cbacae87e3c2e", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "ea7a9307de9d1548d2a72d299058d1fd2339e3d398560a0e46c27dab4891e4d2"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, + "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, } From c9f98c5ae1d4a854b7cab3b6c66f0797fe8495de Mon Sep 17 00:00:00 2001 From: k-asm Date: Thu, 13 Jun 2024 19:38:39 +0900 Subject: [PATCH 06/12] dep: mix deps.clean --unlock --unused --- mix.lock | 1 - 1 file changed, 1 deletion(-) diff --git a/mix.lock b/mix.lock index 6ff2da6..2d06fca 100644 --- a/mix.lock +++ b/mix.lock @@ -1,7 +1,6 @@ %{ "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "dialyxir": {:hex, :dialyxir, "0.5.1", "b331b091720fd93e878137add264bac4f644e1ddae07a70bf7062c7862c4b952", [:mix], [], "hexpm", "6c32a70ed5d452c6650916555b1f96c79af5fc4bf286997f8b15f213de786f73"}, - "earmark": {:hex, :earmark, "1.3.1", "73812f447f7a42358d3ba79283cfa3075a7580a3a2ed457616d6517ac3738cb9", [:mix], [], "hexpm", "000aaeff08919e95e7aea13e4af7b2b9734577b3e6a7c50ee31ee88cab6ec4fb"}, "earmark_parser": {:hex, :earmark_parser, "1.4.39", "424642f8335b05bb9eb611aa1564c148a8ee35c9c8a8bba6e129d51a3e3c6769", [:mix], [], "hexpm", "06553a88d1f1846da9ef066b87b57c6f605552cfbe40d20bd8d59cc6bde41944"}, "ecto": {:hex, :ecto, "3.11.2", "e1d26be989db350a633667c5cda9c3d115ae779b66da567c68c80cfb26a8c9ee", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "3c38bca2c6f8d8023f2145326cc8a80100c3ffe4dcbd9842ff867f7fc6156c65"}, "ex_doc": {:hex, :ex_doc, "0.34.0", "ab95e0775db3df71d30cf8d78728dd9261c355c81382bcd4cefdc74610bef13e", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "60734fb4c1353f270c3286df4a0d51e65a2c1d9fba66af3940847cc65a8066d7"}, From e363cdb07acab2acebc12ffd4d53ec911f4ea351 Mon Sep 17 00:00:00 2001 From: k-asm Date: Thu, 13 Jun 2024 20:15:18 +0900 Subject: [PATCH 07/12] ci: exec tests on github actions --- .github/workflows/ci.yml | 58 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..db26cc9 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,58 @@ +name: CI +on: [push, pull_request] + +jobs: + build: + name: Build and test + runs-on: ubuntu-latest + env: + MIX_ENV: test + # see https://hexdocs.pm/elixir/compatibility-and-deprecations.html#between-elixir-and-erlang-otp + strategy: + fail-fast: false + matrix: + include: + - elixir: 1.17.x + otp: 27.x + lint: true + - elixir: 1.17.x + otp: 25.x + - elixir: 1.16.x + otp: 26.x + - elixir: 1.16.x + otp: 24.x + - elixir: 1.15.x + otp: 26.x + - elixir: 1.15.x + otp: 24.x + - elixir: 1.14.x + otp: 25.x + - elixir: 1.14.x + otp: 23.x + - elixir: 1.13.x + otp: 24.x + - elixir: 1.13.x + otp: 22.x + - elixir: 1.12.x + otp: 24.x + - elixir: 1.12.x + otp: 22.x + - elixir: 1.11.x + otp: 23.x + - elixir: 1.11.x + otp: 21.x + steps: + - uses: actions/checkout@v4 + - uses: erlef/setup-beam@v1 + with: + elixir-version: ${{ matrix.elixir }} + otp-version: ${{ matrix.otp }} + - run: mix deps.get + - run: mix deps.compile + - run: mix compile --warnings-as-errors + if: ${{ matrix.lint }} + - run: mix format --check-formatted + if: ${{ matrix.lint }} + - run: mix deps.unlock --check-unused + if: ${{ matrix.lint }} + - run: mix test From d13a686db0a952ab00d0e1bee9bb7505622ce0c7 Mon Sep 17 00:00:00 2001 From: k-asm Date: Thu, 13 Jun 2024 20:15:40 +0900 Subject: [PATCH 08/12] ci: remove .travis.yml --- .travis.yml | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6ed6484..0000000 --- a/.travis.yml +++ /dev/null @@ -1,12 +0,0 @@ -language: elixir -elixir: - - 1.3.4 - - 1.4.5 - - 1.5.2 -otp_release: - - 19.3 - - 20.0 -matrix: - exclude: - - elixir: 1.3.4 - otp_release: 20.0 From 5db0aab2b7ecf3407e00cd8ad83e86a9db754722 Mon Sep 17 00:00:00 2001 From: k-asm Date: Thu, 13 Jun 2024 20:21:16 +0900 Subject: [PATCH 09/12] ci: use ubuntu-20.04 --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index db26cc9..8f5c71d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ on: [push, pull_request] jobs: build: name: Build and test - runs-on: ubuntu-latest + runs-on: ubuntu-20.04 env: MIX_ENV: test # see https://hexdocs.pm/elixir/compatibility-and-deprecations.html#between-elixir-and-erlang-otp From 68adabcc3f0fa9c816a01e09cd2403eb98b1ba51 Mon Sep 17 00:00:00 2001 From: k-asm Date: Thu, 13 Jun 2024 20:29:44 +0900 Subject: [PATCH 10/12] doc: remove status badge for Travis CI --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index e6e7d56..377c3c1 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,6 @@ [Looking for maintainer](https://github.com/vic/params/issues/new?title=Becoming%20a%20maintainer) -[![Build Status](https://travis-ci.org/vic/params.svg?branch=master)](https://travis-ci.org/vic/params) [![Hex Version](https://img.shields.io/hexpm/v/params.svg)](https://hex.pm/packages/params) [![Hex Docs](https://img.shields.io/badge/hex-docs-lightgreen.svg)](https://hexdocs.pm/params/) [![Total Download](https://img.shields.io/hexpm/dt/params.svg)](https://hex.pm/packages/params) From bc3614112c0bcb58cf90bea41bd97fa775fafe82 Mon Sep 17 00:00:00 2001 From: k-asm Date: Wed, 11 Dec 2024 11:25:54 +0900 Subject: [PATCH 11/12] fix: suppress warning, Module.eval_quoted/2 is deprecated --- lib/params/def.ex | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/lib/params/def.ex b/lib/params/def.ex index 24f9d31..f969af2 100644 --- a/lib/params/def.ex +++ b/lib/params/def.ex @@ -13,13 +13,14 @@ defmodule Params.Def do Code.eval_quoted(block, [], __ENV__) end - Module.eval_quoted( - __MODULE__, + Code.eval_quoted( quote do def unquote(name)(params, options \\ []) do unquote(module_name).from(params, options) end - end + end, + [], + module: __MODULE__ ) end end @@ -33,13 +34,14 @@ defmodule Params.Def do Params.Def.defschema(schema) end - Module.eval_quoted( - __MODULE__, + Code.eval_quoted( quote do def unquote(name)(params) do unquote(module_name).from(params) end - end + end, + [], + module: __MODULE__ ) end end @@ -48,7 +50,7 @@ defmodule Params.Def do defmacro defschema(schema) do quote bind_quoted: [schema: schema] do normalized_schema = Params.Def.normalize_schema(schema, __MODULE__) - Module.eval_quoted(__MODULE__, Params.Def.gen_root_schema(normalized_schema)) + Code.eval_quoted(Params.Def.gen_root_schema(normalized_schema), [], module: __MODULE__) normalized_schema |> Params.Def.build_nested_schemas() From 191b0514914ba7e7beb77d7313152c73a8ea2286 Mon Sep 17 00:00:00 2001 From: k-asm Date: Wed, 11 Dec 2024 11:26:51 +0900 Subject: [PATCH 12/12] ci: exec tests on elixir 1.18 --- .github/workflows/ci.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f5c71d..49e285c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,9 +12,11 @@ jobs: fail-fast: false matrix: include: - - elixir: 1.17.x + - elixir: 1.18.x otp: 27.x lint: true + - elixir: 1.17.x + otp: 27.x - elixir: 1.17.x otp: 25.x - elixir: 1.16.x