Book Changeset
Mix.install([
{:jason, "~> 1.4"},
{:kino, "~> 0.8.0", override: true},
{:youtube, github: "brooklinjazz/youtube"},
{:hidden_cell, github: "brooklinjazz/hidden_cell"},
{:ecto, "~> 3.9"}
])
Navigation
Book Changeset
You’re going to create a Book schemaless changeset struct. A book should have:
-
A required
:titlefield between3and100characters. -
A required
:contentstring field. -
A
:published_ondate field. -
A
:categoryfield which must be either"action","fiction", or"mystery"(you may choose to add more categories if you wish) -
A
:books_soldinteger which must be0or above. -
A
:publisher_emailfield which must be in the formatname@domain.extension -
An
:authorstring field. -
An
:has_licensefield which must always be true. -
A
:priceinteger field which must be above0.
For example, creating a book with the following invalid fields would return an {:error, changeset} tuple similar to the following.
Book.new(%{category: "invalid", email: "invalid", books_sold: -1, price: -1})
{:error,
#Ecto.Changeset<
action: :update,
changes: %{books_sold: -1, category: "invalid", price: -1},
errors: [
price: {"must be greater than %{number}",
[validation: :number, kind: :greater_than, number: 0]},
has_license: {"must be accepted", [validation: :acceptance]},
books_sold: {"must be greater than or equal to %{number}",
[validation: :number, kind: :greater_than_or_equal_to, number: 0]},
category: {"is invalid", [validation: :inclusion, enum: ["action", "fiction", "mystery"]]},
title: {"can't be blank", [validation: :required]},
content: {"can't be blank", [validation: :required]}
],
data: #Book<>,
valid?: false
>}
Hint
Read the Ecto.Changeset documentation.
There, you’ll find all of the validate* functions necessary for each field. For example, Ecto.Changeset.validate_change/3 allows you to create custom validation.
You can also refer to the primitive types documentation for the list of allowed field types.
Example Solution
defmodule Book do
@types %{
title: :string,
content: :string,
published_on: :date,
category: :string,
books_sold: :integer,
publisher_email: :string,
author: :string,
has_license: :boolean,
price: :integer
}
@keys Map.keys(@types)
defstruct @keys
def changeset(%__MODULE__{} = user, params \\ %{}) do
{user, @types}
|> Ecto.Changeset.cast(params, @keys)
|> Ecto.Changeset.validate_required([:title, :content])
|> Ecto.Changeset.validate_length(:title, min: 3, max: 100)
|> Ecto.Changeset.validate_inclusion(:category, ["action", "fiction", "mystery"])
|> Ecto.Changeset.validate_number(:books_sold, greater_than_or_equal_to: 0)
|> Ecto.Changeset.validate_change(:publisher_email, fn :publisher_email, publisher_email ->
if Regex.match?(~r/\w+@\w+\.\w+/, publisher_email) do
[]
else
[email: "invalid email"]
end
end)
|> Ecto.Changeset.validate_acceptance(:has_license)
|> Ecto.Changeset.validate_number(:price, greater_than: 0)
end
def new(params) do
%__MODULE__{}
|> changeset(params)
|> Ecto.Changeset.apply_action(:update)
end
end
You should rely on Ecto Changesets and your own custom validation to validate the book information. Enter your solution below.
defmodule Book do
end
Mark As Completed
file_name = Path.basename(Regex.replace(~r/#.+/, __ENV__.file, ""), ".livemd")
save_name =
case Path.basename(__DIR__) do
"reading" -> "book_changeset_reading"
"exercises" -> "book_changeset_exercise"
end
progress_path = __DIR__ <> "/../progress.json"
existing_progress = File.read!(progress_path) |> Jason.decode!()
default = Map.get(existing_progress, save_name, false)
form =
Kino.Control.form(
[
completed: input = Kino.Input.checkbox("Mark As Completed", default: default)
],
report_changes: true
)
Task.async(fn ->
for %{data: %{completed: completed}} <- Kino.Control.stream(form) do
File.write!(
progress_path,
Jason.encode!(Map.put(existing_progress, save_name, completed), pretty: true)
)
end
end)
form
Commit Your Progress
Run the following in your command line from the curriculum folder to track and save your progress in a Git commit.
Ensure that you do not already have undesired or unrelated changes by running git status or by checking the source control tab in Visual Studio Code.
$ git checkout -b book-changeset-exercise
$ git add .
$ git commit -m "finish book changeset exercise"
$ git push origin book-changeset-exercise
Create a pull request from your book-changeset-exercise branch to your solutions branch.
Please do not create a pull request to the DockYard Academy repository as this will spam our PR tracker.
DockYard Academy Students Only:
Notify your teacher by including @BrooklinJazz in your PR description to get feedback.
You (or your teacher) may merge your PR into your solutions branch after review.
If you are interested in joining the next academy cohort, sign up here to receive more news when it is available.
Up Next
| Previous | Next |
|---|---|
| Sign Up Form | Group Project: Blog |