• Stars
    star
    356
  • Rank 119,446 (Top 3 %)
  • Language
    Elixir
  • License
    Apache License 2.0
  • Created almost 9 years ago
  • Updated 5 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Easy parameters validation/casting with Ecto.Schema, akin to Rails' strong parameters.

Params

Looking for maintainer

Build Status Hex Version Hex Docs Total Download License Last Updated

Easily define parameter structure and validate/cast with Ecto.Schema

Installation

Available in Hex, the package can be installed as:

Add params to your list of dependencies in mix.exs:

def deps do
  [
    {:params, "~> 2.0"}
  ]
end

About

If you've been doing Ecto based applications lately, you know Ecto provides a very easy way to populate structs with data coming from request parameters, validating and casting their values along the way.

All this thanks to the Ecto.Schema and Ecto.Changeset modules. The first specifies the fields your model has (typically the same as your db table) and the later provides an easy way to convert potentially unsafe data and validate stuff via changesets.

So for example, in a typical Phoenix application, a User model would look like:

defmodule MyApp.User do
   use MyApp.Web, :model

   schema "users" do
     field :name, :string
     field :age,  :integer
   end

   @required [:name]
   @optional [:age]

   def changeset(changeset_or_model, params) do
     cast(changeset_or_model, params, @required ++ @optional)
     |> validate_required(@required)
   end
end

Normally, changesets are related to some data that will be persisted into a database, and your controller would use the User.changeset method like:

# UserController.ex
def create(conn, params) do
  ch = User.changeset(%User{}, params)
  if ch.valid? do
    ...
end

However, you can use Ecto.Schema for validating/casting data that won't necessarily be persisted into a database. All you need is just specify a module and define your schema, Ecto.Changeset will be happy to work with it.

This comes handy when you have certain parameter structure you want to enforce for example when creating a REST API.

Some Rails developers might be right now wondering where their strong parameters can be defined. On Elixir land, there's no need for such a thing, as we will see, just using an Ecto.Schema with Ecto.Changeset can be much more flexible. Using schemas allows not only specifying which fields we want, but changesets let use type cast, perform validations on values, etc.

So, for example, suppose your Phoenix based API performs a search for kittens looking for a home and expects something like:

{
  "breed": "Russian Blue",
  "age_min": 0,
  "age_max": 5,
  "near_location": {
     "latitude": 92.1,
     "longitude": -82.1
  }
}

You'd like to validate that your controller has received the correct params structure, all you need to do is create a couple of modules:

defmodule MyApi.Params.Location
  use Ecto.Schema
  import Ecto.Changeset

  @required ~w(latitude longitude)
  @optional ~w()

  schema "location params" do
    field :latitude, :float
    field :longitude, :float
  end

  def changeset(ch, params) do
    cast(ch, params, @required ++ @optional)
    |> validate_required(@required)
  end
end

defmodule MyAPI.Params.KittenSearch
  use Ecto.Schema
  import Ecto.Changeset

  @required ~w(breed)
  @optional ~w(age_min age_max)

  schema "params for kitten search" do
    field :breed, :string
    field :age_min, :integer
    field :age_max, :integer
    field :age_max, :integer
    field :color, Ecto.Enum, values: [:brown, :black, :grey, :unknown],
    embeds_one :near_location, Location
  end

  def changeset(ch, params) do
    cast(ch, params, @required ++ @optional)
    |> validate_required(@required)
    |> cast_embed(:near_location, required: true)
  end
end

# On your controller:
def search(conn, params) do
  alias MyAPI.Params.KittenSearch
  changeset = KittenSearch.changeset(%KittenSearch{}, params)
  if changeset.valid? do
    ...
end

That would allow you to take only valid params as you'd normally have with any other Ecto.Schema module.

However it's still a lot of code, most of it defining the the changeset, specifying the optional and required fields, etc.

Params is just a simple Ecto.Schema wrapper for reducing all this boilerplate, while still leting you create custom changesets for parameter processing.

Usage

The previous example could be written like:

defmodule MyAPI.KittenController do

  use Params

  defparams kitten_search %{
    breed!: :string,
    age_max: :integer,
    age_min: [field: :integer, default: 1],
    color: [field: Ecto.Enum, values: [:brown, :black, :grey, :unknown]],
    near_location!: %{
      latitude!: :float, longitude!: :float
    },
    tags: [:string]
  }

  def index(conn, params) do
    changeset = kitten_search(params)
    if changeset.valid? do
      search = Params.data changeset
      IO.puts search.near_location.latitude
    ...
  end
end

The defparams macro generates a module for processing a params schema

By default all fields are optional. You can mark required fields by ending them with a !, of course the bang is removed from the field definition and is only used to mark which fields are required by default.

You can also create a module and define your schema or custom changesets in it:

defmodule UserSearch do
  use Params.Schema, %{name: :string, age: :integer}
  import Ecto.Changeset, only: [cast: 3, validate_inclusion: 3]

  def child(ch, params) do
    cast(ch, params, ~w(name age))
    |> validate_inclusion(:age, 1..6)
  end
end

defmodule MyApp.UserController do

  def index(conn, params) do
    changeset = UserSearch.from(params, with: &UserSearch.child/2)
    if changeset.valid? do
      # age in 1..6
  end

end

The Params.data and Params.to_map can be useful for obtaining a struct or map from a changeset.

Note that Params.data and Params.to_map have different behaviour: data returns a struct which will include all valid params. to_map returns a map that only includes the submitted keys and keys with default values:

defmodule UserUpdateParams do
  use Params.Schema, %{
    name: :string,
    age: :integer,
    auditlog: [field: :boolean, default: true]
  }
end

changeset = UserUpdateParams.from(%{name: "John"})

Params.data(changeset) # => %UserUpdateParams{name: "John", age: nil, auditlog: true}
Params.to_map(changeset) # => %{name: "John", auditlog: true}

API Documentation

API Documentation

Contributors

Here's a list of awesome people who have contributed code to this project.

If you find a bug or want to improve something, please send a pull-request. Thank you!

Copyright and License

Copyright (c) 2016 Victor Hugo Borja

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

More Repositories

1

expat

Reusable, composable patterns across Elixir libraries
Elixir
175
star
2

ok_jose

Pipe elixir functions that match ok/error tuples or custom patterns.
Elixir
96
star
3

apollo-phoenix-websocket

An Apollo networkInterface for executing GraphQL queries via Phoenix Channels
JavaScript
91
star
4

typhon

Snakes on rbx-head. A Python implementation for the Rubinius VM
Ruby
83
star
5

color-theme-buffer-local

Set emacs color themes by buffer.
Emacs Lisp
78
star
6

spec

Data specification conformance and generation for Elixir
Elixir
77
star
7

mk-darwin-system

Small Nix utility to create an M1 aarch64-darwin (nixFlakes + nix-darwin + home-manager) system.
Nix
69
star
8

happy

the alchemist's happy path with elixir
Elixir
44
star
9

comeonin_ecto_password

Ecto type for saving encrypted passwords using Comeonin
Elixir
35
star
10

pipe_here

An Elixir macro for easily piping arguments at any position.
Elixir
33
star
11

happy_with

Avoid commas on Elixir's with special form.
Elixir
30
star
12

pit

Elixir macro for extracting or transforming values inside a pipe flow.
Elixir
27
star
13

pond

State aware Elixir functions without spawning processes
Elixir
27
star
14

asdf-elm

elm version manager plugin for asdf.
Shell
26
star
15

laminar_cycle

A cycle.js style user-computer model in Laminar
Scala
24
star
16

prefix-css

Prefix all rules from a css-file with a namespace selector.
CoffeeScript
23
star
17

deco

Minimalist Function Decorators for Elixir
Elixir
21
star
18

heroku-buildpack-nim

Deploy nim applications to heroku.
Shell
21
star
19

NoR

There's No Return. A javascript reactive programming engine.
JavaScript
20
star
20

indifferent

Elixir Indifferent access on maps/lists/tuples with custom key transforms.
Elixir
20
star
21

mix_under

Execute mix tasks under Elixir umbrella applications
Elixir
19
star
22

silabas.js

Spanish syllable separator in javascript
JavaScript
17
star
23

sube

Web-based Subtitle Editor.
JavaScript
17
star
24

mill-docker

Build minimalist distroless docker images for your java applications using Mill
Scala
15
star
25

phoenix_now

Example Phoenix deployment to Zeist Now.
Elixir
14
star
26

asdf-ocaml

OCaml plugin for ASDF version manager
Shell
13
star
27

ido-better-flex

A better fuzzy matching algorithm for emacs ido-mode
Emacs Lisp
12
star
28

cyclone

Cyclic Airstream-based stateful components for functional-reactive interfaces on Laminar and Scala.js
Scala
12
star
29

vix

Vic's *Nix config.
Emacs Lisp
12
star
30

gooby

An experimental rubinius bytecode interpreter in Go.
Go
12
star
31

gleam-nix

Build Gleam with Nix.
Nix
10
star
32

rouge

Ruby + Clojure = Rouge
Ruby
10
star
33

elm-facebook

Use Facebook Javascript API from inside ELM
Elm
9
star
34

swagger-elixir

Generate client and (plug) server code in elixir from a swagger api spec.
Elixir
9
star
35

verily

Demo application for APW subscriptions via websockets
Elixir
9
star
36

lispy

Lispy is sexpy Ruby
Ruby
9
star
37

mill-buildr

An small Mill module that lets you define big project structures as a regular scala project itself.
Scala
8
star
38

akin

An Akin programming language optimized for fun.
Ruby
8
star
39

having

Haskell like `where` sugar for Elixir. A pipe-able `with` special form.
Elixir
7
star
40

color-theme-select

Web based version of emacs' (color-theme-select)
JavaScript
7
star
41

tintan

Titanium development with style.
CoffeeScript
6
star
42

pinocchio

Git based server provisioning.
Shell
6
star
43

macpato

Simple pattern matching on Elixir quoted expressions.
Elixir
6
star
44

elmo

A cyclic elm architectured framework
JavaScript
6
star
45

discordex

Discord API for Elixir
Elixir
5
star
46

.hammerspoon

absolute power
Lua
5
star
47

test_async

Make tests inside your ExUnit case to run async.
Elixir
5
star
48

rex

Concatenative Elixir macro language.
Elixir
5
star
49

rojo

Rubinius bytecode interpreter in JavaScript.
5
star
50

fastparse_ext

FastParse extensions
Scala
5
star
51

vee

Vic's Emacs Environment.
Emacs Lisp
4
star
52

vinerb

Full featured Vine API client for ruby.
Ruby
4
star
53

mill-dotenv

A Mill module for twelve-factor apps loading environment variables from a local file.
Scala
4
star
54

docker-neo4j-spatial

Neo4J with spatial plugin on small alpine linux.
4
star
55

nim-heroku-example

Example nim app using heroku buildpack.
Nim
4
star
56

elaxtic

ElasticSearch client for Elixir and Ecto driver.
Elixir
4
star
57

redux-observable-adapter-xstream

Use xstream with redux-observable
JavaScript
4
star
58

redleaf

A Ruby parser producing s-exps using TreeTop.
Ruby
3
star
59

jakesmine

Add Coffee, Jake and Jasmine to your Titanium mobile development.
JavaScript
3
star
60

silabas4j

Spanish syllable separator for java
Java
3
star
61

...

start fresh.
Shell
3
star
62

knockout-jqueryBindingProvider

A binding provider for knockoutjs bind data without using data-bind on views.
JavaScript
3
star
63

clap-nix

Command line argument parser in pure Nix. Supports sub-commands, typed positional arguments, value coercion and resolution via Nix Modules.
Nix
3
star
64

jwm-tt-js-ninja

JWM TechTalk. Becomming a JavaScript Ninja.
JavaScript
3
star
65

silabas.rb

Spanish syllabe separator in ruby
Ruby
3
star
66

infancy

Programming In Fancy. An OpenSource Book to teach the Fancy Programming Language.
3
star
67

mill-scalaxb

Generate Scala from wsdl and xsd files on mill builds
Scala
3
star
68

mill-test-junit-report

Generate JUnit xml reports from Mill's test output.
Scala
3
star
69

mrT

Inverse of shell command, find file first and act accordingly on it. A fast file finder with curses interface.
Ruby
3
star
70

poops

Social real time tamagotchi game.
JavaScript
3
star
71

tito

Quickly create Titanium Mobile UI prototypes on an HTML5 canvas and export them to javascript and jss
2
star
72

elixir_idris

Idris compiler on Elixir
Elixir
2
star
73

ioke-outdated

ioke is a new language for the JVM, based on Io and other languages.
Java
2
star
74

wat.rb

Wat VM implemented in Ruby.
Ruby
2
star
75

SPC

Send keyboard macros to Spacemacs or DOOM Emacs via emacsclient.
Shell
2
star
76

blue

Blue Velvet - Block Lisp Underneath Elixir.
Elixir
2
star
77

promised

Convert back and forth from javascript Promises to Callbacks
2
star
78

jwmscript

Manipulate browser DOM and interact with Javascript libraries using your favourite JVM language
Java
2
star
79

setup_tag

Use tags to mix and match your exunit test context
Elixir
2
star
80

patuit

Twitter for ducks using polymer, neo4j and couchdb
Ruby
2
star
81

fap-fap-fap

Javascript *fap fap fap* Concatenative *fap fap fap* Combinators *fap fap fap*
2
star
82

elmx-webpack-boilerplate

Get up to speed with webpack elm + sass + elmx
JavaScript
2
star
83

knockout-routes

Routes for client-side apps powered by Knockout and History.js
CoffeeScript
2
star
84

im2fansi

Scala library to convert any image into true-color ansi text for display at terminal.
2
star
85

elmx-webpack-preloader

Compile elmx to elm files before using elm-webpack-loader
JavaScript
2
star
86

vico

vic personal homepage
JavaScript
2
star
87

redy

If javascript mews as ruby, it's a ruby.
JavaScript
2
star
88

bc-timer

A basecamp time tracking tool intended to be embedded in gmail, firefox sidebar, and kde's web plasma.
JavaScript
2
star
89

teclado-malvado

an evil keyboard for android
Java
1
star
90

exfmt

Elixir code formatting tool
1
star
91

typeset

An Scala type-indexed set, checked at compile time.
Scala
1
star
92

require.js

A simple node like synchronous require made just for the browser.
JavaScript
1
star
93

schmalz

Automatically exported from code.google.com/p/schmalz
Erlang
1
star
94

pegi

A minimalist parser expression grammar as ruby dsl
Ruby
1
star
95

eniv.co

vine's evil tween
Shell
1
star
96

ikka

Akka experiments in ioke
1
star
97

ice

ICE - IBE Control Examples
JavaScript
1
star
98

cabezon

A prototype using RaphaelJS
JavaScript
1
star
99

viento

No camines que despierta el viento
1
star
100

vic

vic readme
1
star