vic / params Goto Github PK
View Code? Open in Web Editor NEWEasy parameters validation/casting with Ecto.Schema, akin to Rails' strong parameters.
Home Page: http://hex.pm/packages/params
License: Apache License 2.0
Easy parameters validation/casting with Ecto.Schema, akin to Rails' strong parameters.
Home Page: http://hex.pm/packages/params
License: Apache License 2.0
Consider this example:
defmodule MyParams do
use Params.Schema, %{
nested: %{
foo: :integer
}
}
end
It will define a nested schema in a module called MyParams.Nested
.
However, it seems that it will be defined in a "wrong" context. It's source
is set to "nofile"
, which I have had a few problems with:
nofile
file).I have tried to fix it in Params.Def
but was not able to get something working.
I think the main problem is that the macro chain is "broken" by some normal functions, which means that the module is actually created on runtime and not compiletime (because the macro expansion stopped).
I see different solutions but was not able to implement them without messing with the current code more than I wanted to:
__CALLER__
in the original __using__
macro and pass that on to Params.Def. Then create module with something like Module.create(name, quoted_content, Macro.Env.location(caller))
Hi,
i'd like to use #30 in our codebase. For this it would be great to have a new release on hex.
The last one there is 2.1.1 which is from january 2019.
Thanks in advance.
Is there any way to specify a default value for a field of type time
? e..g Set ~T[16:30:00]
as a default value?
I tried:
[field: :time, default: ~T[16:30:00]]
But it throws an error: ** (CompileError) nofile: invalid quoted expression: ~T[16:30:00]
Hi i've uncovered a bug relating to booleans.
String booleans do not get converted (although normal behaviour of Ecto.Changeset is to convert "true" into true)
# in MyController
defmodule MyController do
defparams(
index_params(%{
page: [field: :integer, default: 1],
per_page: [field: :integer, default: 15],
has_errors: :boolean,
is_approved: [field: :boolean, default: false]
})
)
end
# check params received by controller
iex> IO.inspect(params)
%{"is_approved" => "true"}
# check changeset data
iex> IO.inspect(changeset.data)
%Params.MyController.IndexParams{
__meta__: #Ecto.Schema.Metadata<:built, "params Elixir.Params.MyController.IndexParams">,
_id: nil,
has_errors: nil,
is_approved: false, <--- this should be true
page: 1,
per_page: 15
}
I've checked params_test.exs and there is no test suite that coveres booleans
Hi, big fan of this library for typecasting.
I notice that the atom type is not provided and raises an invalid type error, is this expected behaviour? Was hoping to cast strings to atoms automatically.
(ArgumentError) invalid or unknown type :atom for field :status
Stacktrace:
│ (ecto 3.7.1) lib/ecto/schema.ex:2201: Ecto.Schema.check_field_type!/4
│ (ecto 3.7.1) lib/ecto/schema.ex:1881: Ecto.Schema.__field__/4
│ (stdlib 3.16.1) erl_eval.erl:685: :erl_eval.do_apply/6
│ (stdlib 3.16.1) erl_eval.erl:123: :erl_eval.exprs/5
│ (stdlib 3.16.1) erl_eval.erl:919: :erl_eval.try_clauses/8
│ (stdlib 3.16.1) erl_eval.erl:123: :erl_eval.exprs/5Elixir
Phoenix.Component
overrides the def
and defp
macros to annotate function heads with some pattern matching.
I believe because defparams
creates a new method by calling Module.eval_quoted/2
this is somehow evaluating the ast returned by Phoenix.Component's def
macro prematurely(? - tbh, I don't grok this perfectly yet)
But the error can be seen with code like the following:
defmodule MyMod do
use Phoenix.Component
use Params
defparams fleeno %{some: :string}
def hi do
IO.puts("Hello")
end
end
error: cannot invoke remote function Phoenix.Component.Declarative.__pattern__!/2 inside a match
nofile:1: MyMod.util_2/1
Is there a reason I'm not understanding that the defparams
macro is using Module.eval_quoted/2
rather than just defining the method directly?
In other words, can't
Module.eval_quoted(__MODULE__, quote do
def unquote(name)(params) do
unquote(module_name).from(params)
end
end)
Just be written as
def unquote(name)(params) do
unquote(module_name).from(params)
end
In my own experiments it seems to work fine, but I'm not sure if I'm missing something.
(Of course, I could just call the macro before I use Phoenix.Component, but perhaps there's a more comprehensive fix)
Hey there,
The breaking changes introduced in 2.0-beta have caused params to break (thankfully its tests also break in the same way). I'm working on a PR to fix this right now, but in the meantime, here's the sort of errors I'm experiencing:
Compiled lib/params/def.ex
** (Protocol.UndefinedError) protocol Enumerable not implemented for :id
(elixir) lib/enum.ex:1: Enumerable.impl_for!/1
(elixir) lib/enum.ex:116: Enumerable.reduce/3
(elixir) lib/enum.ex:1486: Enum.reduce/3
test/params_test.exs:10: (module)
test/params_test.exs:8: (module)
== Compilation error on file lib/params.ex ==
** (CompileError) lib/params.ex:77: unknown key :model for struct Ecto.Changeset
(elixir) src/elixir_map.erl:185: :elixir_map."-assert_struct_keys/5-lc$^0/1-0-"/5
(elixir) src/elixir_map.erl:62: :elixir_map.translate_struct/4
(stdlib) lists.erl:1353: :lists.mapfoldl/3
Just as suggestion or maybe I missing something and this can be already achieved:
defmodule SomeIdentityParams do
use Params.Schema
import App.Params.Helpers
@fields ~w(id code)
schema do
field :id, :id
field :code, :string
# more here
end
def changeset(changeset, params) do
cast(changeset, params, @fields)
|> at_least_one_should_be_present([:id, :code])
end
end
# this one looks fine
defmodule SomeControllerParams do
use Params.Schema
@fields ~w(tarif_id some_identity)a
schema do
field :tarif_id, :id
embeds_one :some_identity, SomeIdentityParams
end
def changeset(cs, params) do
cast(cs, params, @fields)
|> validate_required(@fields)
|> cast_embed(:passenger, required: true)
end
end
# but it can be much shorter
defmodule SomeControllerParams do
use Params.Schema, %{
tarif_id: :id,
some_identity: SomeIdentityParams
}
end
Both to_map
and data
have the behaviour that it's not possible to see if nil
was submitted for a field or if that field was not submitted at all.
It makes sense that data
behaves that way, since it returns a struct. But with to_map
, I would expect it to use the schema as whitelist and not add extra fields unless default
is used.
Maybe this should be a different function than to_map
?
My usecase is an API endpoint that can update a resource. Something like this:
defmodule MyAPIEndpoint do
defmodule MyParams do
use Params.Schema, %{
id!: :integer,
name: string,
owner_id: :integer
}
end
def call(params) do
changeset = MyParams.from(params)
if changeset.valid? do
attributes = Params.to_map(changeset)
MyRepo.update_attributes(attributes)
end
end
end
So I want to update only the submitted keys. In this case it should be possible to update owner_id
to the value nil
.
Again, I will be happy to help with the implementation. Just wanted your oppinion before getting started ;)
Thanks for a great library. I'm just learning Elixir, so I might miss something obvious here.
Is it possible to somehow specify default values in a schema?
I have something like this:
defparams index_params %{
filter: %{
foo_id: :integer,
bar_id: :integer
},
page: %{
number: :integer,
size: :integer
},
sort: :string
}
filter
is not required, but it should default to %{}
.
page
is not required, but it should default to e.g. %{number: 1, size: 20}
.
sort
is not required, and should default to e.g. "id"
I could of course write a function that adds the default values, but since Ecto.Schema
supports this it could be nice to somehow specify it directly in the schema.
I love this library, but I almost never build my own changeset validation functions because it's too cumbersome. I usually just use with
after using params validation for types.
However, I think that it would be awesome if we could put validation functions in the schema. Could we somehow detect those and build a changeset validation out of functions, as long as they have the right call signature?
defparams asdf %{
x!: fn :x, x -> is_integer(x) and x > 0 end,
y!: :string,
}
This would satisfy the other 20% of api validation that I need. I'm happy to take a stab at this, if you guys think that it's feasible and desirable.
How can I validate a param type? Do I need to run the elixir Regex module separately for this or it's possible to check for existence and also against a format regex?
Hello, here is my issue:
defmodule MyParams do
use Params.Schema
schema do
field :count, :integer, default: 1
end
end
changeset = MyParams.from(%{})
assert %{count: 1} = Params.to_map(changeset) # but it is %{}
In my opinion, the README contains too much back story that you have to wade through before getting to the basic usage of the package. Everybody is interested in the basic usage, but only some people are interested in the back story. I suggest moving the "Usage" section to near the top of the document.
I'd be happy to make this into a PR.
Hi, vic.
I'm interested in to be a maintainer of params
.
I:
Know more about me at my website.
If there are any other requirements, let me know. ;)
When compiling this library using Elixir v 1.11 you get the following warning:
warning: Ecto.Changeset.validate_required/2 defined in application :ecto is used by the current application but the current application does not depend on :ecto. To fix this, you must do one of:
1. If :ecto is part of Erlang/Elixir, you must include it under :extra_applications inside "def application" in your mix.exs
2. If :ecto is a dependency, make sure it is listed under "def deps" in your mix.exs
3. In case you don't want to add a requirement to :ecto, you may optionally skip this warning by adding [xref: [exclude: [Ecto.Changeset]]] to your "def project" in mix.exs
lib/params.ex:145: Params.changeset/2
This can be solved by changing
def application do
[applications: [:logger]]
end
to
def application do
[extra_applications: [:logger]]
end
in mix.exs. Please see here for more info: https://hexdocs.pm/mix/1.11.4/Mix.Tasks.Compile.App.html
I will submit a pull request to fix this.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.