View Source Pathex (Pathex v2.6.1)
This module contains main functions and macros used to create, use and manipulate paths.
Usage
To use Pathex just insert to your context. You can import Pathex in module body or even in function body.
require Pathex
import Pathex, only: [path: 1, path: 2, "~>": 2, ...]Or you can just use Pathex.
defmodule MyModule do
# `default_mod` option is optional
# when no mod is specified, `:naive` is selected
use Pathex, default_mod: :json
...
endThis will import all operatiors and path macro
Available macros
Any macro here belongs to one of three categories:
Summary
Types
Function which is passed to path-closure as second element in args tuple
This depends on the modifier
Value returned by non-bang path call
Also known as path-closure
Also known as path-closure
Functions
Creates composition of two paths which has some inspiration from logical and.
This means that a &&& b path-closure tries to apply a and only if it returns {:ok, something}, tries
apply b and if b returns exactly the same as a does, the a &&& b returns {:ok, something}
Easy and convenient way to add pathex to your module.
This macro creates compositions of paths which work along with each other
Applies func under path in struct and returns result of this func.
Applies func under path in struct and returns result of this func.
Raises if path is not found.
The same as Pathex.~>/2 for those who do not like operators
Deletes value under path in struct.
Deletes value under path in struct or raises if value is not found.
Gets the value under path in struct or returns default value if not found.
Applies func under path of struct.
Applies func under path of struct.
Sets the value under path in struct.
Sets the value under path in struct.
Gets the value under path in struct or returns default when path is not present.
Note that the default value is always lazily evaluted.
Inspect the given path-closure and returns string which corresponds to given path-closure
Applies func to the item under the path in struct
and returns modified structure. Works like Map.update!/3 but doesn't raise.
Applies the func to the item under path in struct and returns modified structure.
Works like Map.update!/3.
Creates path from quoted ast. Paths look like unix filesystems paths and consist of
elements separated from each other with /. Each element defines the key or index
in the collection.
This macro converts path (which can be matched upon) into pattern.
Macro which gets value in the structure and deletes it.
Gets value under path in struct and then deletes it.
Sets value under path in structure. Think of it like Map.put/3.
Sets the value under path in struct. Think of it like Map.put/3.
Gets the value under path in struct.
Gets the value under path in struct. Raises if path not found.
Deletes value under path in struct if it is present.
Otherwise, returns a struct unmodified
Creates composition of two paths which has some inspiration from logical or.
This means that a ||| b path-closure tries to apply a and only if it returns :error, tries
apply b
Creates composition of two paths similar to concatenating them together.
This means that a ~> b path-closure applies a and only if it returns {:ok, something}
it applies b to something
Types
@type force_update_args(input, output) :: {input, inner_func(output), any()}
Function which is passed to path-closure as second element in args tuple
@type inspect_args() :: any()
@type mod() :: :map | :json | :naive
More about modifiers
This depends on the modifier
@type result(inner) :: {:ok, inner} | :error | :delete_me
Value returned by non-bang path call
@type t() :: t(pathex_compatible_structure(), any())
Also known as path-closure
@type t(input, output) :: (op_name(), force_update_args(input, output) | update_args(input, output) | inspect_args() -> result(output | input) | Macro.t())
Also known as path-closure
@type update_args(input, output) :: {input, inner_func(output)}
Functions
Creates composition of two paths which has some inspiration from logical and.
This means that a &&& b path-closure tries to apply a and only if it returns {:ok, something}, tries
apply b and if b returns exactly the same as a does, the a &&& b returns {:ok, something}
Example
iex> p1 = path :x / :y
iex> p2 = path :a / :b
iex> ap = p1 &&& p2
iex> {:ok, 1} = view %{x: %{y: 1}, a: [b: 1]}, ap
iex> :error = view %{x: %{y: 1}, a: [b: 2]}, ap
iex> {:ok, %{x: %{y: 2}, a: [b: 2]}} = set %{x: %{y: 1}, a: [b: 1]}, ap, 2
iex> {:ok, %{x: %{y: 2}, a: %{b: 2}}} = force_set %{}, ap, 2
Easy and convenient way to add pathex to your module.
You can specify modifier
use Pathex, default_mod: :jsonOr just use it with default :naive modifier
use Pathex
use PathexWhen you
use Pathex, the Pathex module will requirePathexand importPathex's operators,path/2andalongside/1macros. Plus it will set special module attribute withdefault_modvalue in it.
This macro creates compositions of paths which work along with each other
Think of alongside([path1, path2, path3]) as path1 &&& path2 &&& path3
The only difference is that for viewing alongside returns list of variables
Example
iex> pa = alongside [path(:x), path(:y)]
iex> {:ok, [1, 2]} = view(%{x: 1, y: 2}, pa)
iex> {:ok, %{x: 3, y: 3}} = set(%{x: 1, y: 2}, pa, 3)
iex> :error = set(%{x: 1}, pa, 3)
iex> {:ok, %{x: 1, y: 1}} = force_set(%{}, pa, 1)
Applies func under path in struct and returns result of this func.
Example
iex> x = 1
iex> {:ok, 9} = at [0, %{x: 8}], path(x / :x), fn x -> x + 1 end
iex> p = path "hey" / 0
iex> {:ok, {:here, 9}} = at(%{"hey" => {9, -9}}, p, & {:here, &1})
Applies func under path in struct and returns result of this func.
Raises if path is not found.
Example
iex> x = 1
iex> 9 = at! [0, %{x: 8}], path(x / :x), fn x -> x + 1 end
iex> p = path "hey" / 0
iex> {:here, 9} = at!(%{"hey" => {9, -9}}, p, & {:here, &1})
The same as Pathex.~>/2 for those who do not like operators
Example
iex> p1 = path :x / :y
iex> p2 = path :a / :b
iex> composed_path = concat(p1, p2)
iex> {:ok, 1} = view %{x: [y: [a: [a: 0, b: 1]]]}, composed_path
Deletes value under path in struct.
Example
iex> x = 1
iex> {:ok, [0, %{}]} = delete([0, %{x: 8}], path(x / :x))
iex> :error = delete([0, %{x: 8}], path(1 / :y))
Deletes value under path in struct or raises if value is not found.
Example
iex> x = 1
iex> [0, %{}] = delete!([0, %{x: 8}], path(x / :x))
Gets the value under path in struct or returns default value if not found.
Example
iex> x = 1
iex> true = exists?([0, %{x: 8}], path(x / :x))
iex> p = path "hey" / "you"
iex> false = exists?(%{"hey" => [x: 1]}, p)
Applies func under path of struct.
If the path does not exist it creates the path favouring maps when structure is unknown and inserts default value.
Example
iex> x = 1
iex> {:ok, [0, %{x: {:xxx, 8}}]} = force_over([0, %{x: 8}], path(x / :x), & {:xxx, &1}, 123)
iex> p = path "hey" / 0
iex> {:ok, %{"hey" => %{0 => 1}}} = force_over(%{}, p, fn x -> x + 1 end, 1)If the item in path doesn't have the right type, it returns :error.
Example
iex> p = path "hey" / "you"
iex> :error = force_over %{"hey" => {1, 2}}, p, fn x -> x end, "value"
Applies func under path of struct.
If the path does not exist it creates the path favouring maps when structure is unknown and inserts default value.
Example
iex> x = 1
iex> [0, %{x: {:xxx, 8}}] = force_over!([0, %{x: 8}], path(x / :x), & {:xxx, &1}, 123)
iex> p = path "hey" / 0
iex> %{"hey" => %{0 => 1}} = force_over!(%{}, p, fn x -> x + 1 end, 1)If the item in path doesn't have the right type, it raises.
Example
iex> p = path "hey" / "you"
iex> force_over! %{"hey" => {1, 2}}, p, fn x -> x end, "value"
** (Pathex.Error) Type mismatch in structure
Sets the value under path in struct.
Creates path if it is not present
If the path does not exist it creates the path favouring maps when structure is unknown.
iex> x = 1
iex> {:ok, [0, %{x: 123}]} = force_set [0, %{x: 8}], path(x / :x), 123
iex> p = path "hey" / 0
iex> {:ok, %{"hey" => %{0 => 1}}} = force_set %{}, p, 1Incorrect types may be detected during call
If the item in path doesn't have the right type, it returns :error.
iex> p = path "hey" / "you"
iex> :error = force_set %{"hey" => {1, 2}}, p, "value"Empty space is filled with nil
Note that for paths created with Pathex.path/2 list and tuple indexes
which are out of bounds fill the empty space with nil.
iex> p = path 4
iex> {:ok, [1, 2, 3, nil, 5]} = force_set [1, 2, 3], p, 5
iex> {:ok, {1, 2, 3, nil, 5}} = force_set {1, 2, 3}, p, 5Negative indexes
This is also true for negative indexes (except -1 for lists which always prepends)
iex> p = path -5
iex> {:ok, [0, nil, 1, 2, 3]} = force_set [1, 2, 3], p, 0
iex> {:ok, {0, nil, 1, 2, 3}} = force_set {1, 2, 3}, p, 0
Sets the value under path in struct.
Creates path if it is not present
If the path does not exist it creates the path favouring maps when structure is unknown.
iex> x = 1
iex> [0, %{x: 123}] = force_set! [0, %{x: 8}], path(x / :x), 123
iex> p = path "hey" / 0
iex> %{"hey" => %{0 => 1}} = force_set! %{}, p, 1Incorrect types may be detected during call
If the item in path doesn't have the right type, it raises.
iex> p = path "hey" / "you"
iex> force_set! %{"hey" => {1, 2}}, p, "value"
** (Pathex.Error) Type mismatch in structureEmpty space is filled with nil
Note that for paths created with Pathex.path/2 list and tuple indexes
which are out of bounds fill the empty space with nil.
iex> p = path 4
iex> [1, 2, 3, nil, 5] = force_set! [1, 2, 3], p, 5
iex> {1, 2, 3, nil, 5} = force_set! {1, 2, 3}, p, 5Negative indexes
This is also true for negative indexes (except -1 for lists which always prepends)
iex> p = path -5
iex> [0, nil, 1, 2, 3] = force_set! [1, 2, 3], p, 0
iex> {0, nil, 1, 2, 3} = force_set! {1, 2, 3}, p, 0
Gets the value under path in struct or returns default when path is not present.
Note that the default value is always lazily evaluted.
Example
iex> x = 1
iex> 8 = get([0, %{x: 8}], path(x / :x))
iex> p = path "hey" / "you"
iex> nil = get(%{"hey" => [x: 1]}, p)
iex> :default = get(%{"hey" => [x: 1]}, p, :default)
Inspect the given path-closure and returns string which corresponds to given path-closure
Example
iex> index = 1
iex> p = path(:x) ~> path(:y / index) &&& path(-1)
iex> Pathex.inspect(p)
"path(:x) ~> path(:y / 1) &&& path(-1)"
Applies func to the item under the path in struct
and returns modified structure. Works like Map.update!/3 but doesn't raise.
Example
iex> index = 1
iex> inc = fn x -> x + 1 end
iex> {:ok, [0, %{x: 9}]} = over [0, %{x: 8}], path(index / :x), inc
iex> p = path "hey" / 0
iex> {:ok, %{"hey" => [2, [2]]}} = over %{"hey" => [1, [2]]}, p, incNote: Exceptions from passed function left unhandled
iex> over(%{1 => "x"}, path(1), fn x -> x + 1 end)
** (ArithmeticError) bad argument in arithmetic expression
Applies the func to the item under path in struct and returns modified structure.
Works like Map.update!/3.
Example
iex> x = 1
iex> inc = fn x -> x + 1 end
iex> [0, %{x: 9}] = over! [0, %{x: 8}], path(x / :x), inc
iex> p = path "hey" / 0
iex> %{"hey" => [2, [2]]} = over! %{"hey" => [1, [2]]}, p, inc
Creates path from quoted ast. Paths look like unix filesystems paths and consist of
elements separated from each other with /. Each element defines the key or index
in the collection.
Example
iex> x = 1
iex> mypath = path 1 / :atom / "string" / {"tuple?"} / x
iex> structure = [0, [atom: %{"string" => %{{"tuple?"} => %{1 => 2}}}]]
iex> {:ok, 2} = view structure, mypathPaths can be used with one of the verbs in Pathex module (for example, Pathex.view/2).
Paths can be customized with modifiers, composed using one of
composition operators (Pathex.concat/2, Pathex.~>/2, Pathex.|||/2, Pathex.&&&/2 or
Pathex.alongside/1).
Note: Each element in path can have collection type annotated using
::operator. Available collection types are:list,:keyword,:tupleand:map. Multiple collections can be annotated using list It must comply with the limits set with modifier.
Example
iex> p = path( (0 :: [:list, :map]) / (:x :: :keyword) )
iex> {:ok, :hit} = view %{0 => [x: :hit]}, p
iex> {:ok, :hit} = view [[x: :hit]], p
iex> :error = view [%{x: :hit}], p
This macro converts path (which can be matched upon) into pattern.
These requirements must be satisfied in order for this macro to work correctly:
- Path must be inlined into this macro. This means that path must be defined in a argument of this macro
- Defined paths must contain constants only
- Path must result only in case with one clause
Example
iex> import Pathex
iex> structure = %{users: %{1 => %{fname: "Jose", lname: "Valim"}}}
iex> case structure do
...> pattern(fname, path(:users / 1 / :fname, :map)) ->
...> {:ok, fname}
...>
...> _ ->
...> :error
...> end
{:ok, "Jose"}
Macro which gets value in the structure and deletes it.
Note:
Current implementation of this function performs double lookup. Which is still more efficient than
pop_in
Example
iex> {:ok, {1, [2, 3]}} = pop([1, 2, 3], path(0))
Gets value under path in struct and then deletes it.
Note:
Current implementation of this function performs double lookup. Which is still more efficient than
pop_in
Example
iex> {1, [2, 3]} = pop!([1, 2, 3], path(0))
Sets value under path in structure. Think of it like Map.put/3.
Example
iex> x = 1
iex> {:ok, [0, %{x: 123}]} = set [0, %{x: 8}], path(x / :x), 123
iex> p = path "hey" / 0
iex> {:ok, %{"hey" => [123, [2]]}} = set %{"hey" => [1, [2]]}, p, 123
Sets the value under path in struct. Think of it like Map.put/3.
Example
iex> x = 1
iex> [0, %{x: 123}] = set! [0, %{x: 8}], path(x / :x), 123
iex> p = path "hey" / 0
iex> %{"hey" => [123, [2]]} = set! %{"hey" => [1, [2]]}, p, 123
Gets the value under path in struct.
Example
iex> x = 1
iex> {:ok, 8} = view [0, %{x: 8}], path(x / :x)
iex> p = path "hey" / 0
iex> {:ok, 9} = view %{"hey" => {9, -9}}, p
Gets the value under path in struct. Raises if path not found.
Example
iex> x = 1
iex> 8 = view! [0, %{x: 8}], path(x / :x)
iex> p = path "hey" / 0
iex> 9 = view! %{"hey" => {9, -9}}, p
Deletes value under path in struct if it is present.
Otherwise, returns a struct unmodified
Example
iex> [0, %{}] = without([0, %{x: 8}], path(0 / :x))
iex> [0, %{x: 8}] = without([0, %{x: 8}], path(1 / :y))
Creates composition of two paths which has some inspiration from logical or.
This means that a ||| b path-closure tries to apply a and only if it returns :error, tries
apply b
Example
iex> p1 = path :x / :y
iex> p2 = path :a / :b
iex> op = p1 ||| p2
iex> {:ok, 1} = view %{x: %{y: 1}, a: [b: 2]}, op
iex> {:ok, 2} = view %{x: 1, a: [b: 2]}, op
iex> {:ok, %{x: %{y: 2}, a: [b: 1]}} = set %{x: %{y: 1}, a: [b: 1]}, op, 2
iex> {:ok, %{x: %{y: 2}}} = force_set %{}, op, 2
iex> {:ok, %{x: %{}, a: [b: 1]}} = force_set %{x: %{y: 1}, a: [b: 1]}, op, 2
Creates composition of two paths similar to concatenating them together.
This means that a ~> b path-closure applies a and only if it returns {:ok, something}
it applies b to something
Example
iex> p1 = path :x / :y
iex> p2 = path :a / :b
iex> composed_path = p1 ~> p2
iex> {:ok, 1} = view %{x: [y: [a: [a: 0, b: 1]]]}, composed_path