Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 40 additions & 6 deletions lib/ex_poll/polls.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ defmodule ExPoll.Polls do
"""

import Ecto.Query, warn: false
alias Ecto.Multi
alias ExPoll.Repo

alias ExPoll.Polls.{Poll, Option}
alias ExPoll.Polls.{Poll, Option, Vote}

# POLL

Expand Down Expand Up @@ -43,6 +43,32 @@ defmodule ExPoll.Polls do
Repo.delete(poll)
end

def publish_poll(%Poll{} = poll) do
poll
|> Poll.publish_changeset()
|> Repo.update()
end

def unpublish_poll(%Poll{} = poll) do
poll_changeset = Poll.unpublish_changeset(poll)
option_ids = Enum.map(poll.options, fn option -> option.id end)
poll_votes_query = from(v in Vote, where: v.option_id in ^option_ids)

result =
Multi.new()
|> Multi.update(:update_poll, poll_changeset)
|> Multi.delete_all(:delete_votes, poll_votes_query)
|> Repo.transaction()

case result do
{:ok, %{update_poll: poll}} ->
{:ok, get_poll!(poll.id)}

{:error, _failed_operation, _failed_value, _changes_so_far} ->
{:error, "Something went wrong while unpublishing a poll"}
end
end

def change_poll(%Poll{} = poll, attrs \\ %{}) do
Poll.changeset(poll, attrs)
end
Expand All @@ -59,14 +85,15 @@ defmodule ExPoll.Polls do
def get_option!(id) do
query =
from o in options_query(),
where: o.id == ^id
where: o.id == ^id,
preload: [:poll]

Repo.one!(query)
end

def create_option(%Poll{} = poll, attrs \\ %{}) do
poll
|> Ecto.build_assoc(:options)
|> Ecto.build_assoc(:options, poll: poll)
|> Option.changeset(attrs)
|> Repo.insert()
end
Expand All @@ -78,7 +105,9 @@ defmodule ExPoll.Polls do
end

def delete_option(%Option{} = option) do
Repo.delete(option)
option
|> change_option()
|> Repo.delete()
end

def change_option(%Option{} = option, attrs \\ %{}) do
Expand All @@ -89,7 +118,12 @@ defmodule ExPoll.Polls do

def create_vote(%Option{} = option) do
option
|> Ecto.build_assoc(:votes)
|> Ecto.build_assoc(:votes, option: option)
|> change_vote()
|> Repo.insert()
end

def change_vote(%Vote{} = vote, attrs \\ %{}) do
Vote.changeset(vote, attrs)
end
end
10 changes: 10 additions & 0 deletions lib/ex_poll/polls/option.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,15 @@ defmodule ExPoll.Polls.Option do
|> cast(attrs, [:value])
|> validate_required([:value])
|> assoc_constraint(:poll)
|> validate_poll_is_not_published()
end

defp validate_poll_is_not_published(changeset) do
poll = get_field(changeset, :poll)

case poll.is_published do
true -> add_error(changeset, :is_published, "poll can't be modified when it's published")
false -> changeset
end
end
end
31 changes: 31 additions & 0 deletions lib/ex_poll/polls/poll.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ defmodule ExPoll.Polls.Poll do

schema "polls" do
field :question, :string
field :is_published, :boolean, default: false
timestamps()

has_many(:options, Option, on_replace: :delete)
Expand All @@ -16,5 +17,35 @@ defmodule ExPoll.Polls.Poll do
|> cast(attrs, [:question])
|> cast_assoc(:options)
|> validate_required([:question])
|> validate_poll_is_not_published()
end

def publish_changeset(poll) do
poll
|> cast(%{is_published: true}, [:is_published])
|> validate_min_options_count()
end

def unpublish_changeset(poll) do
poll
|> cast(%{is_published: false}, [:is_published])
end

defp validate_poll_is_not_published(changeset) do
is_published = get_field(changeset, :is_published)

case is_published do
true -> add_error(changeset, :is_published, "poll can't be modified when it's published")
false -> changeset
end
end

defp validate_min_options_count(changeset, count \\ 2) do
options = get_field(changeset, :options, [])

case length(options) >= count do
true -> changeset
false -> add_error(changeset, :options, "should have at least #{count} option(s)")
end
end
end
11 changes: 10 additions & 1 deletion lib/ex_poll/polls/vote.ex
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,15 @@ defmodule ExPoll.Polls.Vote do
vote
|> cast(attrs, [])
|> validate_required([])
|> assoc_constraint(:option)
|> validate_poll_is_published()
end

defp validate_poll_is_published(changeset) do
option = get_field(changeset, :option)

case option.poll.is_published do
true -> changeset
false -> add_error(changeset, :is_published, "poll can't be voted on when it's unpublished")
end
end
end
16 changes: 16 additions & 0 deletions lib/ex_poll_web/controllers/poll_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,20 @@ defmodule ExPollWeb.PollController do
send_resp(conn, :no_content, "")
end
end

def publish(conn, %{"id" => id}) do
poll = Polls.get_poll!(id)

with {:ok, %Poll{} = poll} <- Polls.publish_poll(poll) do
render(conn, "show.json", poll: poll)
end
end

def unpublish(conn, %{"id" => id}) do
poll = Polls.get_poll!(id)

with {:ok, %Poll{} = poll} <- Polls.unpublish_poll(poll) do
render(conn, "show.json", poll: poll)
end
end
end
2 changes: 2 additions & 0 deletions lib/ex_poll_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ defmodule ExPollWeb.Router do
end

post("/polls/:id/vote", VoteController, :create)
post("/polls/:id/publish", PollController, :publish)
post("/polls/:id/unpublish", PollController, :unpublish)
end

# Enables LiveDashboard only for development
Expand Down
4 changes: 3 additions & 1 deletion lib/ex_poll_web/views/poll_view.ex
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,16 @@ defmodule ExPollWeb.PollView do
def render("poll.json", %{poll: poll}) do
%{
id: poll.id,
question: poll.question
question: poll.question,
is_publised: poll.is_published
}
end

def render("poll_with_options.json", %{poll: poll}) do
%{
id: poll.id,
question: poll.question,
is_publised: poll.is_published,
options: render_many(poll.options, OptionView, "option.json")
}
end
Expand Down
9 changes: 9 additions & 0 deletions priv/repo/migrations/20200814130356_update_polls_table.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
defmodule ExPoll.Repo.Migrations.UpdatePollsTable do
use Ecto.Migration

def change do
alter table(:polls) do
add :is_published, :boolean, default: false
end
end
end