Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
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
32 changes: 30 additions & 2 deletions lib/agoneum/account/account.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ defmodule Agoneum.Account do

import Ecto.Query, warn: false
alias Agoneum.Repo

alias Agoneum.Account.User
alias Agoneum.{Account.User, Games.Game}

@doc """
Returns the list of users.
Expand Down Expand Up @@ -120,4 +119,33 @@ defmodule Agoneum.Account do
def change_user(%User{} = user) do
User.changeset(user, %{})
end

@doc """
Adds a new game (or games) to the user's collection.
"""
def add_games(%User{} = user, %Game{} = game), do: add_games(user, [game])
def add_games(%User{} = user, games) do
game_changesets = games ++ user.games
|> Enum.map(&Ecto.Changeset.change/1)
|> Enum.uniq()

user
|> change_user()
|> Ecto.Changeset.put_assoc(:games, game_changesets)
|> Repo.update()
end

@doc """
Removes a game (or games) from the user's collection.
"""
def remove_games(%User{} = user, %Game{} = game), do: remove_games(user, [game])
def remove_games(%User{} = user, games) do
remaining_games = Enum.reject(user.games, fn(game) -> Enum.member?(games, game) end)
game_changesets = Enum.map(remaining_games, &Ecto.Changeset.change/1)

user
|> change_user()
|> Ecto.Changeset.put_assoc(:games, game_changesets)
|> Repo.update()
end
end
2 changes: 2 additions & 0 deletions lib/agoneum/account/user.ex
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ defmodule Agoneum.Account.User do

field :admin, :boolean, default: false, null: false

many_to_many :games, Agoneum.Games.Game, join_through: "user_games", on_replace: :delete

timestamps()
end

Expand Down
41 changes: 41 additions & 0 deletions lib/agoneum/games/game.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
defmodule Agoneum.Games.Game do
@moduledoc ""
use Ecto.Schema
import Ecto.Changeset
alias Agoneum.Games.Game

@required_fields ~w(name description min_players max_players)a
@optional_fields ~w(year image)a
@all_fields @required_fields ++ @optional_fields

schema "games" do
field :description, :string
field :image, :string
field :max_players, :integer
field :min_players, :integer
field :name, :string
field :year, :integer

many_to_many :users, Agoneum.Account.User, join_through: "user_games", on_replace: :delete

timestamps()
end

@doc false
def changeset(%Game{} = game, attrs) do
game
|> cast(attrs, @all_fields)
|> validate_required(@required_fields)
|> validate_player_counts
|> unique_constraint(:name, name: :name_year)
end

defp validate_player_counts(changeset) do
# Pulled this out so that we can access the changes map
changeset
|> validate_number(:min_players, greater_than: 0)
|> validate_number(:min_players,
less_than_or_equal_to: Map.get(changeset.changes, :max_players),
message: "Min players must be less than or equal to the max players.")
end
end
112 changes: 112 additions & 0 deletions lib/agoneum/games/games.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
defmodule Agoneum.Games do
@moduledoc """
The Games context.
"""

import Ecto.Query, warn: false
alias Agoneum.Repo

alias Agoneum.Games.Game

@doc """
Returns the list of games.

## Examples

iex> list_games()
[%Game{}, ...]

"""
def list_games do
Repo.all(Game)
end

@doc """
Creates a query that orders games by name.
"""
def ordered(query) do
from g in query,
order_by: g.name
end

@doc """
Gets a single game.

Raises `Ecto.NoResultsError` if the Game does not exist.

## Examples

iex> get_game!(123)
%Game{}

iex> get_game!(456)
** (Ecto.NoResultsError)

"""
def get_game!(id), do: Repo.get!(Game, id)

@doc """
Creates a game.

## Examples

iex> create_game(%{field: value})
{:ok, %Game{}}

iex> create_game(%{field: bad_value})
{:error, %Ecto.Changeset{}}

"""
def create_game(attrs \\ %{}) do
%Game{}
|> Game.changeset(attrs)
|> Repo.insert()
end

@doc """
Updates a game.

## Examples

iex> update_game(game, %{field: new_value})
{:ok, %Game{}}

iex> update_game(game, %{field: bad_value})
{:error, %Ecto.Changeset{}}

"""
def update_game(%Game{} = game, attrs) do
game
|> Game.changeset(attrs)
|> Repo.update()
end

@doc """
Deletes a Game.

## Examples

iex> delete_game(game)
{:ok, %Game{}}

iex> delete_game(game)
{:error, %Ecto.Changeset{}}

"""
def delete_game(%Game{} = game) do
Repo.delete(game)
end

@doc """
Returns an `%Ecto.Changeset{}` for tracking game changes.

## Examples

iex> change_game(game)
%Ecto.Changeset{source: %Game{}}

"""
def change_game(%Game{} = game) do
Game.changeset(game, %{})
end
end
60 changes: 60 additions & 0 deletions lib/agoneum_web/controllers/game_controller.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
defmodule AgoneumWeb.GameController do
use AgoneumWeb, :controller

alias Agoneum.Games
alias Agoneum.Games.Game

def index(conn, _params) do
games = Games.list_games()
render(conn, "index.html", games: games)
end

def new(conn, _params) do
changeset = Games.change_game(%Game{})
render(conn, "new.html", changeset: changeset)
end

def create(conn, %{"game" => game_params}) do
case Games.create_game(game_params) do
{:ok, game} ->
conn
|> put_flash(:info, "Game created successfully.")
|> redirect(to: game_path(conn, :show, game))
{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "new.html", changeset: changeset)
end
end

def show(conn, %{"id" => id}) do
game = Games.get_game!(id)
render(conn, "show.html", game: game)
end

def edit(conn, %{"id" => id}) do
game = Games.get_game!(id)
changeset = Games.change_game(game)
render(conn, "edit.html", game: game, changeset: changeset)
end

def update(conn, %{"id" => id, "game" => game_params}) do
game = Games.get_game!(id)

case Games.update_game(game, game_params) do
{:ok, game} ->
conn
|> put_flash(:info, "Game updated successfully.")
|> redirect(to: game_path(conn, :show, game))
{:error, %Ecto.Changeset{} = changeset} ->
render(conn, "edit.html", game: game, changeset: changeset)
end
end

def delete(conn, %{"id" => id}) do
game = Games.get_game!(id)
{:ok, _game} = Games.delete_game(game)

conn
|> put_flash(:info, "Game deleted successfully.")
|> redirect(to: game_path(conn, :index))
end
end
1 change: 1 addition & 0 deletions lib/agoneum_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ defmodule AgoneumWeb.Router do
pipe_through [:browser, :browser_auth]

get "/", PageController, :index
resources "/games", GameController
end

scope "/api", AgoneumWeb do
Expand Down
5 changes: 5 additions & 0 deletions lib/agoneum_web/templates/game/edit.html.eex
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<h2>Edit Game</h2>

<%= render "form.html", Map.put(assigns, :action, game_path(@conn, :update, @game)) %>

<span><%= link "Back", to: game_path(@conn, :index) %></span>
47 changes: 47 additions & 0 deletions lib/agoneum_web/templates/game/form.html.eex
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<%= form_for @changeset, @action, fn f -> %>
<%= if @changeset.action do %>
<div class="alert alert-danger">
<p>Oops, something went wrong! Please check the errors below.</p>
</div>
<% end %>

<div class="form-group">
<%= label f, :name, class: "control-label" %>
<%= text_input f, :name, class: "form-control" %>
<%= error_tag f, :name %>
</div>

<div class="form-group">
<%= label f, :year, class: "control-label" %>
<%= number_input f, :year, class: "form-control" %>
<%= error_tag f, :year %>
</div>

<div class="form-group">
<%= label f, :description, class: "control-label" %>
<%= text_input f, :description, class: "form-control" %>
<%= error_tag f, :description %>
</div>

<div class="form-group">
<%= label f, :min_players, class: "control-label" %>
<%= number_input f, :min_players, class: "form-control" %>
<%= error_tag f, :min_players %>
</div>

<div class="form-group">
<%= label f, :max_players, class: "control-label" %>
<%= number_input f, :max_players, class: "form-control" %>
<%= error_tag f, :max_players %>
</div>

<div class="form-group">
<%= label f, :image, class: "control-label" %>
<%= text_input f, :image, class: "form-control" %>
<%= error_tag f, :image %>
</div>

<div class="form-group">
<%= submit "Submit", class: "btn btn-primary" %>
</div>
<% end %>
36 changes: 36 additions & 0 deletions lib/agoneum_web/templates/game/index.html.eex
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<h2>Listing Games</h2>

<table class="table">
<thead>
<tr>
<th>Name</th>
<th>Year</th>
<th>Description</th>
<th>Min players</th>
<th>Max players</th>
<th>Image</th>

<th></th>
</tr>
</thead>
<tbody>
<%= for game <- @games do %>
<tr>
<td><%= game.name %></td>
<td><%= game.year %></td>
<td><%= game.description %></td>
<td><%= game.min_players %></td>
<td><%= game.max_players %></td>
<td><%= game.image %></td>

<td class="text-right">
<span><%= link "Show", to: game_path(@conn, :show, game), class: "btn btn-default btn-xs" %></span>
<span><%= link "Edit", to: game_path(@conn, :edit, game), class: "btn btn-default btn-xs" %></span>
<span><%= link "Delete", to: game_path(@conn, :delete, game), method: :delete, data: [confirm: "Are you sure?"], class: "btn btn-danger btn-xs" %></span>
</td>
</tr>
<% end %>
</tbody>
</table>

<span><%= link "New Game", to: game_path(@conn, :new) %></span>
5 changes: 5 additions & 0 deletions lib/agoneum_web/templates/game/new.html.eex
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<h2>New Game</h2>

<%= render "form.html", Map.put(assigns, :action, game_path(@conn, :create)) %>

<span><%= link "Back", to: game_path(@conn, :index) %></span>
Loading