Skip to content

lizhaochao/Argx

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Argx Hex Version docs

DSLs for checking args.

Installation

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

defp deps do
  [{:argx, "~> 1.1.4"}]
end

Install via mix deps.get and the happy check your args as described in Usage and Advanced.

Example

This Example Project is the basis for Argx, help you use well. Download via Gitee or Github.

Usage

Quick Start

Here’s a commented example.

# Use Argx like this in Your Project.
iex> defmodule YourProject do
...>   # step 1: introduce check function by Argx module
...>   use Argx
...>
...>   # step 2: define rule
...>   defconfig(Rule, id(:string))
...>
...>   def get(args) do
...>     # step 3: use check function to check args
...>     check(args, [Rule])
...>   end
...> end

# Return errors.
iex> YourProject.get(%{id: 1})
{:error, ["error type: id"]}

# If passed, return original args.
iex> YourProject.get(id: "a")
[id: "a"]

Check Via DSL

# step 1: define your validator
defmodule YourProject.Validator do
  use Argx.WithCheck
end

defmodule YourProject do
  # step 2: import your validator
  import YourProject.Validator

  # step 3: use with_check macro to wrap your function(s)
  with_check configs(id(:string)) do
    def get(id) do
      {id}
    end
  end
end

Advanced

1. How to share arg configs?

step 1: create a module for define shared arg configs.

defmodule YourProject.ArgConfigs do
  use Argx.Defconfig
  defconfig(NumberRule, number(:string, :empty))
  defconfig(PageSizeRule, page_size(:integer, :autoconvert, 1..100) || 10)
end

step 2 : config share module to the following positions.

use Argx, share: YourProject.ArgConfigs
# or
use Argx.WithCheck, share: YourProject.ArgConfigs

step 3 : use arg config by name.

def get(args) do
  check(args, [NumberRule, PageSizeRule])
  |> case do
    {:error, _} -> :error
    _ -> :ok
  end
end
# or
with_check configs(NumberRule, PageSizeRule) do
  def get(id) do
    {id}
  end
end

2. Format errors

just implement callback fmt_errors/1, Argx invoke your custom format errors function, when check done.

There are 3 places to put it.

Highest priority: in the current module.

defmodule YourProject do
  use Argx
  def fmt_errors({:error, _errors}), do: :error
  def fmt_errors(_new_args_or_result), do: :ok
  ...
end
# or
defmodule YourProject do
  import YourProject.Validator
  def fmt_errors({:error, _errors}), do: :error
  def fmt_errors(_new_args_or_result), do: :ok
  ...
end

Second priority: in the share arg configs module.

defmodule YourProject.ArgConfigs do
  use Argx.Defconfig
  def fmt_errors({:error, _errors}), do: :error
  def fmt_errors(_new_args_or_result), do: :ok
  ...
end

Lowest priority: if you use argx via with_check, also implement it in the definition module.

defmodule YourProject.Validator do
  use Argx.WithCheck
  def fmt_errors({:error, _errors}), do: :error
  def fmt_errors(_new_args_or_result), do: :ok
  ...
end

Features

  • set default value if arg is nil or empty.
  • convert arg's value automatically, if arg's value is compatible, such as: "1" to 1.
  • check whether arg is lacked or empty.
  • check whether arg's type is error.
  • check whether arg's length/value is out of range.
  • support nested data checking.
  • similar checkbox functionality, required at least one arg is not nil in group. usage
  • similar radio functionality, required only one arg is not nil in group. usage

Support Data Type

  • :boolean
  • :integer
  • :float
  • :string
  • :list
  • :map

check/2 function

  • meaning of function's arg:
    • first arg only accept map or keyword data type as checking args.
    • second arg must be a list that only contains one or more rule names.
    check(data, [RuleA, :RuleB, "RuleC"])
  • return value:
    • return new args, if success.
    • return errors, if failure.

DSL

defconfig

Reuse arg configs by name.

  • config name, arg name and type are necessary. all types
    defconfig(Rule, id(:integer))
    • Rule is config name. :Rule, Rule or "Rule" are acceptable.
    • id is arg name.
    • :string is type.
  • :optional declare arg's value that can be nil.
    defconfig(Rule, id(:integer, :optional))
  • :checkbox declare this arg has checkbox functionality, :optional was set by default.
    defconfig(Rule, [weight(:integer, :checkbox), height(:integer, :checkbox, :optional)])
  • :radio declare this arg has radio functionality, :optional was set by default also.
    defconfig(Rule, [weight(:integer, :radio), height(:integer, :radio, :optional)])
  • :autoconvert declare that argx convert it to expected type automatically if it is compatible.
    • "1" to 1
    • "1.2" to 1.2
    • 1 to 1.0
    • 1 to true
    • 0 to false
    • "1" to true
    • "0" to false
    defconfig(Rule, id(:integer, :autoconvert))
  • :empty empty value the same as nil, the following values are empty.
    • 0
    • 0.0
    • ""
    • %{}
    • []
    defconfig(Rule, id(:integer, :empty))
  • range: there are 2 ways to set value's range.
    • 10..20, between 10 and 20, also include begin value and end value.
    • 20, equal to 20.
    defconfig(Rule, id(:integer, 10..20))
    :list, :map and :string value calculate it's length or size. :integer and :float value compare it's value directly. :boolean value will be ignored.
  • default: there are 3 ways to set value's default value.
    • a value, such as: 1.
    • local function.
    • remote function, module name should be fully-qualified name, such as: YourProject.Helper.
    defconfig(Rule, id(:integer) || 0)
    defconfig(Rule, id(:integer) || get_default_id())
    defconfig(Rule, id(:integer) || YourProject.Helper.get_default_id())
  • multi configs: define them in one rule.
    defconfig(Rule, [id(:integer), name(:string)])

with_check

  • configs keyword is necessary and it's content is not empty.
  • define configs directly or reuse rules by name.
  • wrap multi functions that have different guards.
    defmodule YourProject do
      import YourProject.Validator
    
      with_check configs(
                     Rule,
                     id(:integer, :optional, :autoconvert, :empty, 1..99) || get_default_id()
                 ) do
        def create(id) when is_integer(id) do
          {:ok, id}
        end
        def create(id) when is_bitstring(id) do
          {:ok, String.to_integer(id)}
        end
      end
    end
  • getting all arg configs.
    • format: __get_[function_name]_configs__.
    • such as: configs = YourProject.__get_create_configs__().
    • configs' data type is keyword, sorted by function arg_names.

Errors

There are 5 types.

  1. lacked some fields.
  2. some fields' type is error.
  3. some field's range/length/size is out of range.
  4. checkbox functionality error.
  5. radio functionality error.

As shown below:

{
  :error,
  [
    error_type: ["cargoes:1:number", "cargoes:2:name"], # report nested data's error
    lacked: [:mobile],
    out_of_range: [:weight],
    checkbox_error: [:id, :number],
    radio_error: [:ip, :addr]
  ]
}

If you want to convert meta errors to readable message, just implement fmt_errors/1.

Configuration

config Argx or Argx.WithCheck module.

  1. set shared arg configs module.
  2. set warn flag.
use Argx.WithCheck, share: YourProject.ArgConfigs, warn: false

Benchee Report

Name ips average deviation median 99th % Recommand
without Argx 3090.90 K 0.32 μs ±13466.34% 0 μs 0.90 μs -
with_check DSL 55.57 K 17.99 μs ±124.26% 15.90 μs 56.90 μs YES
check 22.64 K 44.18 μs ±94.15% 36.90 μs 153.90 μs NO

Benchmark

mix bench
## ArgxBench
benchmark name                  iterations   average time 
deep match (4 nested level)     50000        44.65 µs/op

Contributing

Contributions to Argx are very welcome!

Bug reports, documentation, spelling corrections... all of those (and probably more) are much appreciated contributions!

About

DSLs for checking args.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages