Sorcery.Mutation (sorcery v0.3.2)
This is the interface for creating changes in Sorcery. Mutations have some similarities to Ecto Changesets in the sense that we are building a struct that defines all the changes without actually applying them. But there are many differences, as you will soon see.
You start by creating the initial mutation with a portal.
Then you apply all the changes you want to the data in that portal.
At first nothing happens! This is because you must send the mutation up to the parent PortalServer.
After it applies the changes, it will dispatch them back down to any child PortalServers that care about any of the entities involved. This is where the magic of Sorcery really comes in. Under the hood, we are doing some pretty efficient reverse queries, in parallel, to figure out where to send the data.
All the PortalServers will update their Portals accordingly.
iex> alias Sorcery.Mutation, as: M
iex> m = M.init(socket.assigns.sorcery, :my_portal)
iex> m = M.update(m, [:player, 1, :age], fn _original_age, latest_age -> latest_age + 1 end)
iex> m = M.put(m, [:player, 1, :health], 100)
iex> ### We can also use placeholder ids so that new entities (which haven't even been created yet) can be referenced by other entities.
iex> m = M.create_entity(m, :team, "?my_new_team", %{name: "My New Team"})
iex> m = M.put(m, [:player, 1, :team_id], "?my_new_team.id")
iex> # This funny string is of course, the placeholder. But after we actually run the mutation, the team is created with a normal integer id.
iex> # The parent PortalServer creates the new team entity, it will automatically replace all calls to "?my_new_team" with that entity.
iex> Sorcery.Mutation.send_mutation(m)
iex> # ... a few milliseconds later, after receiving the update
iex> player = portal_view(@sorcery, :my_portal, "?all_players")[1]
iex> new_team_id = player.team_id
iex> is_integer(new_team_id)
true
iex> team = portal_view(@sorcery, :my_portal, "?all_teams")[new_team_id]
iex> team.name
"My New Team"
Summary
Functions
One of the benefits of a Mutation is that you can create entities using placeholder ids.
Sometimes we want to delete an entity entirely. Be careful, after the mutation is run, this cannot be undone
The first step of every mutation. You must have an existing portal in order to do any of this.
Just like put_in, but for mutations.
Sends the mutation to the corresponding Portal Server The Portal Server will then update its own data store. All portals that care about the changes will automatically update accordingly (including the one calling this function)
Kind of like a fusion between Kernel.update_in/3 and Map.update/4
Functions
create_entity(mutation, tk, lvar, body)
One of the benefits of a Mutation is that you can create entities using placeholder ids.
Examples
iex> m = M.create_entity(m, :team, "?my_new_team", %{name: "My New Team"})
iex> m = M.put(m, [:player, 1, :team_id], "?my_new_team")
iex> # It always defaults to using the :id, but you can specify another field
iex> m = M.put(m, [:player, 1, :team_id], "?my_new_team.my_field")
delete_entity(mutation, tk, id)
Sometimes we want to delete an entity entirely. Be careful, after the mutation is run, this cannot be undone
init(state, portal_name)
The first step of every mutation. You must have an existing portal in order to do any of this.
Examples
iex> m = Sorcery.Mutation.init(socket.assigns.sorcery, :my_portal)
iex> is_struct(m)
true
put(mutation, path, value)
Just like put_in, but for mutations.
Examples
iex> m = Sorcery.Mutation.put(m, [:player, 1, :health], 100)
send_mutation(mutation, state)
Sends the mutation to the corresponding Portal Server The Portal Server will then update its own data store. All portals that care about the changes will automatically update accordingly (including the one calling this function)
Returns the mutation passed in.
To be clear, this does not return the new data you are waiting for. You probably won't need the return value.
All the updates happen automatically, through some magic sorcery behind the scenes.
update(mutation, path, cb)
Kind of like a fusion between Kernel.update_in/3 and Map.update/4
You need to pass in both a path list, and a default value in case the original value is nil.
Examples
iex> Sorcery.Mutation.update(m, [:player, 1, :age], 100, fn _original_age, latest_age -> latest_age + 1 end)