riptide v0.5.0-beta7 Riptide.Mutation View Source
A mutation represents a set of changes that can be applied to a Riptide.Store
. This module contains functions that make it easy to compose complex mutations and combine them together.
Mutations contains two types of operations:
:merge
- A map containing the values that will be merged in - creating them if they don't already exist:delete
- A map containing the paths that should be deleted from the store
Deleting
In a mutation, the deletes are always applied first. They are expressed as a map with a value of 1
for each path to be deleted.
iex> Mutation.put_delete(["todo:info", "001"])
%Riptide.Mutation{
delete: %{
"todo:info" => %{
"001" => 1
}
},
merge: %{}
}
This mutation will delete everything under ["todo:info", "001]
Merging
Merges are applied after deletes and are expressed as a map pointing to the values that should be set.
Mutation.put_merge(
["todo:info", "001"],
%{
"key" => "001",
"text" => "Document riptide!"
}
)
%Riptide.Mutation{
delete: %{},
merge: %{
"todo:info" => %{
"001" => %{
"key" => "001",
"text" => "Document riptide!"
}
}
}
}
This mutation will delete everything under ["todo:info", "001]
Composing
There are various functions in this module for composing sophisticated mutations. A good approach is to break down a complex mutation into atomic pieces for clarity and combine them together.
Here are some examples of how they can be helpful:
Mutation.new()
|> Mutation.put_merge(["user:info", "001", "name"], "jack")
|> Mutation.put_merge(["user:info", "002", "name"], "john")
|> Mutation.put_delete(["todo:info"])
%Riptde.Mutation{
delete: %{"todo:info" => 1},
merge: %{
"user:info" => %{
"001" => %{"name" => "jack"},
"002" => %{"name" => "john"}
}
}
}
def create_user_mut(key, email) do
Mutation.put_merge(["user:info", key], %{
"key" => key,
"email" => email
})
end
def set_password_mut(key, password) do
Mutation.put_merge(["user:passwords", key], Bcrypt.encrypt(password))
end
Mutation.combine(
create_user_mut("001", "user@example.com"),
set_password_mut("001", "mypassword")
)
%Riptde.Mutation{
merge: %{
"user:info" => %{
"001" => %{
"key" => "001",
"email" => "user@example.com",
}
},
"user:password" => %{
"001" => "$2a$10$kj5ZhhLWIwik8uK4RJrDA.ddOEIK5VO9f4Y5FwL5D3CvVafSVXcYe"
}
}
}
1..100
|> Stream.map(fn index -> Mutation.merge(["todo:info", to_string(index)], index) end)
|> Mutation.combine()
%Riptide.Mutation{
delete: %{},
merge: %{
"todo:info" => %{
"1" => 1,
"2" => 2,
"3" => 3,
...
}
}
Link to this section Summary
Types
A key-value pair representing a layer of the mutation. The key is a list of strings representing the path to the current layer. The value is a mutation, representing any deeper sub-mutations.
A map containing paths to be added (merge) and paths to be removed (delete).
Functions
Applies the entire mutation to the input map.
Takes a stream of mutations, combines them in batches of size count
. Useful when writing a lot of mutations that would be faster written as batches.
Takes a list or stream of Mutations and combines them in order to produce a single output mutation.
Combines the right mutation into the left mutation and returns a singular mutation
Takes two maps and returns a mutation that could be applied to turn the the first map into the second.
Accepts a list and mutation, and returns a new mutation with the given mutation nested at the given path.
Returns a mapping with an entry for every layer of the mutation. The keys represent a path and the value represents the full mutation that is being merged in at that path.
Creates a new mutation, optionally passing in a map for merges or deletes
Creates a new mutation and puts a path to be deleted
Adds a delete path to the input mutation
Creates a new mutation and puts a value to be merged
Adds a merge value to the input mutation
Link to this section Types
A key-value pair representing a layer of the mutation. The key is a list of strings representing the path to the current layer. The value is a mutation, representing any deeper sub-mutations.
A map containing paths to be added (merge) and paths to be removed (delete).
Link to this section Functions
Applies the entire mutation to the input map.
Example
iex> Riptide.Mutation.apply(
...> %{"b" => false},
...> %{delete: %{}, merge: %{"a" => true}}
...> )
%{"a" => true, "b" => false}
Takes a stream of mutations, combines them in batches of size count
. Useful when writing a lot of mutations that would be faster written as batches.
Examples
iex> 1..10
...> |> Stream.map(fn index -> Riptide.Mutation.put_merge(["data", to_string(index)], index) end)
...> |> Riptide.Mutation.chunk(5)
...> |> Enum.to_list()
[
%Riptide.Mutation{
delete: %{},
merge: %{"data" => %{"1" => 1, "2" => 2, "3" => 3, "4" => 4, "5" => 5}}
},
%Riptide.Mutation{
delete: %{},
merge: %{"data" => %{"10" => 10, "6" => 6, "7" => 7, "8" => 8, "9" => 9}}
}
]
Takes a list or stream of Mutations and combines them in order to produce a single output mutation.
Examples
iex> 0..3
...> |> Stream.map(fn index ->
...> Riptide.Mutation.put_merge(["todo:info", to_string(index)], index)
...> end)
...> |> Riptide.Mutation.combine()
%Riptide.Mutation{delete: %{}, merge: %{"todo:info" => %{"0" => 0, "1" => 1, "2" => 2, "3" => 3}}}
Combines the right mutation into the left mutation and returns a singular mutation
Examples
iex> Riptide.Mutation.combine(
...> %Riptide.Mutation{delete: %{}, merge: %{"a" => true}},
...> %Riptide.Mutation{delete: %{}, merge: %{"b" => false}}
...> )
%Riptide.Mutation{delete: %{}, merge: %{"a" => true, "b" => false}}
Takes two maps and returns a mutation that could be applied to turn the the first map into the second.
Examples
iex> Riptide.Mutation.diff(
...> %{"a" => 1},
...> %{"b" => 2}
...> )
%Riptide.Mutation{delete: %{"a" => 1}, merge: %{"b" => 2}}
Accepts a list and mutation, and returns a new mutation with the given mutation nested at the given path.
Example
iex> Riptide.Mutation.inflate(
...> ["a", "b"],
...> %{
...> delete: %{},
...> merge: %{
...> "a" => 1
...> }
...> }
...> )
%Riptide.Mutation{
delete: %{
"a" => %{
"b" => %{}
}
},
merge: %{
"a" => %{
"b" => %{
"a" => 1
}
}
}
}
Returns a mapping with an entry for every layer of the mutation. The keys represent a path and the value represents the full mutation that is being merged in at that path.
Examples
iex> Riptide.Mutation.put_merge(["a", "b"], true) |> Riptide.Mutation.layers
%{
[] => %Riptide.Mutation{
delete: %{},
merge: %{
"a" => %{
"b" => true
}
}
},
["a"] => %Riptide.Mutation{
delete: %{},
merge: %{
"b" => true
}
}
}
Creates a new mutation, optionally passing in a map for merges or deletes
Examples
iex> Riptide.Mutation.new
%Riptide.Mutation{delete: %{}, merge: %{}}
Creates a new mutation and puts a path to be deleted
Adds a delete path to the input mutation
Examples
iex> Riptide.Mutation.new()
...> |> Riptide.Mutation.put_delete(["c"])
%Riptide.Mutation{delete: %{"c" => 1}, merge: %{}}
Creates a new mutation and puts a value to be merged
Examples
iex> Riptide.Mutation.put_merge(["a", "b"], 1)
%Riptide.Mutation{delete: %{}, merge: %{"a" => %{"b" => 1}}}
Adds a merge value to the input mutation
Examples
iex> mutation = Riptide.Mutation.put_merge(["a", "b"], 1)
iex> Riptide.Mutation.put_merge(mutation, ["a", "c"], 2)
%Riptide.Mutation{delete: %{}, merge: %{"a" => %{"b" => 1, "c" => 2}}}