Skip to content

Commit

Permalink
adds update function
Browse files Browse the repository at this point in the history
  • Loading branch information
Danwhy committed Sep 16, 2018
1 parent dc9dbe1 commit fb344b5
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 0 deletions.
103 changes: 103 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -445,3 +445,106 @@ end
```
`mix test` again, and we should be all green.
#### 4.3 Update
Now we come to the update function. "But I thought we were only appending to the database?" I hear you ask. This is true, but we still need to relate our existing data to the new, updated data we add.
To do this, we need to be able to reference the previous entry somehow. The simplest (conceptually) way of doing this is to provide each entry a unique id. Note that the id will be used to represent unique entries, but it will not be unique in a table, as revisions of entries will have the same id. This is the simplest way we can link our entries, but there may be some disadvantages, which we'll look into later.
So, first we'll need to edit our schema to add a shared id to our address entries:
``` elixir
defmodule Append.Address do
...
schema "addresses" do
...
field(:entry_id, :integer)

...
end
...
end
```
And create a migration file:
```
mix ecto.gen.migration add_entry_id
```
``` elixir
defmodule Append.Repo.Migrations.AddEntryId do
use Ecto.Migration

def change do
alter table("addresses") do
add :entry_id, :integer
end

end
end
```
```
mix ecto.migrate
```
Now, we'll write a test for the update function.
``` elixir
defmodule Append.AddressTest do
...
test "update item in database" do
{:ok, item} = insert_address()
{:ok, updated_item} = Address.update(item, %{tel: "0123444444"})
assert updated_item.name == item.name
assert updated_item.tel != item.tel
end
...
end
```
Then we write the update function itself. We'll take the existing item, and a map of updated attributes as arguments.
Then we'll use the `insert` function to create a new entry in the database, rather than `update`, which would overwrite the old one.
``` elixir
defmodule Append.AppendOnlyLog do
...
defmacro __before_compile__(_env) do
quote do
...
def update(%__MODULE__{} = item, attrs) do
item
|> __MODULE__.changeset(attrs)
|> Repo.insert()
end
end
end
end
```
But now, if we try to run our tests:
```
1) test update item in database (Append.AddressTest)
test/append/address_test.exs:25
** (Ecto.ConstraintError) constraint error when attempting to insert struct:
* unique: addresses_pkey
```
We can't add the item again, because the unique id already exists in the database.
``` elixir
def update(%__MODULE__{} = item, attrs) do
item
|> Map.put(:id, nil)
|> __MODULE__.changeset(attrs)
|> Repo.insert()
end
```
So here we remove the original autogenerated id from the existing item, preventing us from duplicating it in the database.
1 change: 1 addition & 0 deletions lib/append/address.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ defmodule Append.Address do
field(:name, :string)
field(:postcode, :string)
field(:tel, :string)
field(:entry_id, :integer)

timestamps()
end
Expand Down
4 changes: 4 additions & 0 deletions lib/append/append_only_log.ex
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ defmodule Append.AppendOnlyLog do
end

def update(%__MODULE__{} = item, attrs) do
item
|> Map.put(:id, nil)
|> __MODULE__.changeset(attrs)
|> Repo.insert()
end
end
end
Expand Down
10 changes: 10 additions & 0 deletions priv/repo/migrations/20180914130516_add_entry_id.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
defmodule Append.Repo.Migrations.AddEntryId do
use Ecto.Migration

def change do
alter table("addresses") do
add :entry_id, :integer
end

end
end
9 changes: 9 additions & 0 deletions test/append/address_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,15 @@ defmodule Append.AddressTest do
end
end

test "update item in database" do
{:ok, item} = insert_address()

{:ok, updated_item} = Address.update(item, %{tel: "0123444444"})

assert updated_item.name == item.name
assert updated_item.tel != item.tel
end

def insert_address do
Address.insert(%{
name: "Thor",
Expand Down

0 comments on commit fb344b5

Please sign in to comment.