From f6c38661c79b41da99e436acabd562c5606fa524 Mon Sep 17 00:00:00 2001 From: Dan Barber Date: Fri, 9 Dec 2016 14:02:22 +0000 Subject: [PATCH] Create new games and display them --- .../migrations/20161209105511_create_game.exs | 12 +++ test/controllers/game_controller_test.exs | 66 +++++++++++++++++ test/models/game_test.exs | 18 +++++ web/controllers/api/game_controller.ex | 5 +- web/controllers/game_controller.ex | 66 +++++++++++++++++ web/models/game.ex | 73 +++++++++++++++++++ web/static/js/application.js | 18 +++-- web/templates/game/edit.html.eex | 6 ++ web/templates/game/form.html.eex | 17 +++++ web/templates/game/index.html.eex | 29 ++++++++ web/templates/game/new.html.eex | 6 ++ web/templates/game/show.html.eex | 2 + web/views/api/game_view.ex | 49 +------------ web/views/game_view.ex | 3 + 14 files changed, 315 insertions(+), 55 deletions(-) create mode 100644 priv/repo/migrations/20161209105511_create_game.exs create mode 100644 test/controllers/game_controller_test.exs create mode 100644 test/models/game_test.exs create mode 100644 web/controllers/game_controller.ex create mode 100644 web/models/game.ex create mode 100644 web/templates/game/edit.html.eex create mode 100644 web/templates/game/form.html.eex create mode 100644 web/templates/game/index.html.eex create mode 100644 web/templates/game/new.html.eex create mode 100644 web/templates/game/show.html.eex create mode 100644 web/views/game_view.ex diff --git a/priv/repo/migrations/20161209105511_create_game.exs b/priv/repo/migrations/20161209105511_create_game.exs new file mode 100644 index 0000000..f00f953 --- /dev/null +++ b/priv/repo/migrations/20161209105511_create_game.exs @@ -0,0 +1,12 @@ +defmodule Chess.Repo.Migrations.CreateGame do + use Ecto.Migration + + def change do + create table(:games) do + add :board, :map + + timestamps() + end + + end +end diff --git a/test/controllers/game_controller_test.exs b/test/controllers/game_controller_test.exs new file mode 100644 index 0000000..54e3aef --- /dev/null +++ b/test/controllers/game_controller_test.exs @@ -0,0 +1,66 @@ +defmodule Chess.GameControllerTest do + use Chess.ConnCase + + alias Chess.Game + @valid_attrs %{board: %{}} + @invalid_attrs %{} + + test "lists all entries on index", %{conn: conn} do + conn = get conn, game_path(conn, :index) + assert html_response(conn, 200) =~ "Listing games" + end + + test "renders form for new resources", %{conn: conn} do + conn = get conn, game_path(conn, :new) + assert html_response(conn, 200) =~ "New game" + end + + test "creates resource and redirects when data is valid", %{conn: conn} do + conn = post conn, game_path(conn, :create), game: @valid_attrs + assert redirected_to(conn) == game_path(conn, :index) + assert Repo.get_by(Game, @valid_attrs) + end + + test "does not create resource and renders errors when data is invalid", %{conn: conn} do + conn = post conn, game_path(conn, :create), game: @invalid_attrs + assert html_response(conn, 200) =~ "New game" + end + + test "shows chosen resource", %{conn: conn} do + game = Repo.insert! %Game{} + conn = get conn, game_path(conn, :show, game) + assert html_response(conn, 200) =~ "Show game" + end + + test "renders page not found when id is nonexistent", %{conn: conn} do + assert_error_sent 404, fn -> + get conn, game_path(conn, :show, -1) + end + end + + test "renders form for editing chosen resource", %{conn: conn} do + game = Repo.insert! %Game{} + conn = get conn, game_path(conn, :edit, game) + assert html_response(conn, 200) =~ "Edit game" + end + + test "updates chosen resource and redirects when data is valid", %{conn: conn} do + game = Repo.insert! %Game{} + conn = put conn, game_path(conn, :update, game), game: @valid_attrs + assert redirected_to(conn) == game_path(conn, :show, game) + assert Repo.get_by(Game, @valid_attrs) + end + + test "does not update chosen resource and renders errors when data is invalid", %{conn: conn} do + game = Repo.insert! %Game{} + conn = put conn, game_path(conn, :update, game), game: @invalid_attrs + assert html_response(conn, 200) =~ "Edit game" + end + + test "deletes chosen resource", %{conn: conn} do + game = Repo.insert! %Game{} + conn = delete conn, game_path(conn, :delete, game) + assert redirected_to(conn) == game_path(conn, :index) + refute Repo.get(Game, game.id) + end +end diff --git a/test/models/game_test.exs b/test/models/game_test.exs new file mode 100644 index 0000000..199e3ad --- /dev/null +++ b/test/models/game_test.exs @@ -0,0 +1,18 @@ +defmodule Chess.GameTest do + use Chess.ModelCase + + alias Chess.Game + + @valid_attrs %{board: %{}} + @invalid_attrs %{} + + test "changeset with valid attributes" do + changeset = Game.changeset(%Game{}, @valid_attrs) + assert changeset.valid? + end + + test "changeset with invalid attributes" do + changeset = Game.changeset(%Game{}, @invalid_attrs) + refute changeset.valid? + end +end diff --git a/web/controllers/api/game_controller.ex b/web/controllers/api/game_controller.ex index 0d74ce0..49a3952 100644 --- a/web/controllers/api/game_controller.ex +++ b/web/controllers/api/game_controller.ex @@ -3,7 +3,8 @@ defmodule Chess.Api.GameController do alias Chess.Game - def show(conn, _params) do - render conn, "show.json", id: 1 + def show(conn, %{"id" => id}) do + game = Repo.get!(Game, id) + render conn, "show.json", game: game end end diff --git a/web/controllers/game_controller.ex b/web/controllers/game_controller.ex new file mode 100644 index 0000000..3e8541e --- /dev/null +++ b/web/controllers/game_controller.ex @@ -0,0 +1,66 @@ +defmodule Chess.GameController do + use Chess.Web, :controller + + alias Chess.Game + + def index(conn, _params) do + changeset = Game.changeset(%Game{}) + games = Repo.all(Game) + render(conn, "index.html", games: games, changeset: changeset) + end + + def new(conn, _params) do + changeset = Game.changeset(%Game{}) + render(conn, "new.html", changeset: changeset) + end + + def create(conn, _params) do + changeset = Game.changeset(%Game{}) + + case Repo.insert(changeset) do + {:ok, _game} -> + conn + |> put_flash(:info, "Game created successfully.") + |> redirect(to: game_path(conn, :index)) + {:error, changeset} -> + render(conn, "new.html", changeset: changeset) + end + end + + def show(conn, %{"id" => id}) do + game = Repo.get!(Game, id) + render(conn, "show.html", game: game) + end + + def edit(conn, %{"id" => id}) do + game = Repo.get!(Game, id) + changeset = Game.changeset(game) + render(conn, "edit.html", game: game, changeset: changeset) + end + + def update(conn, %{"id" => id, "game" => game_params}) do + game = Repo.get!(Game, id) + changeset = Game.changeset(game, game_params) + + case Repo.update(changeset) do + {:ok, game} -> + conn + |> put_flash(:info, "Game updated successfully.") + |> redirect(to: game_path(conn, :show, game)) + {:error, changeset} -> + render(conn, "edit.html", game: game, changeset: changeset) + end + end + + def delete(conn, %{"id" => id}) do + game = Repo.get!(Game, id) + + # Here we use delete! (with a bang) because we expect + # it to always work (and if it does not, it will raise). + Repo.delete!(game) + + conn + |> put_flash(:info, "Game deleted successfully.") + |> redirect(to: game_path(conn, :index)) + end +end diff --git a/web/models/game.ex b/web/models/game.ex new file mode 100644 index 0000000..dbfd6b7 --- /dev/null +++ b/web/models/game.ex @@ -0,0 +1,73 @@ +defmodule Chess.Game do + use Chess.Web, :model + + schema "games" do + field :board, :map + + timestamps() + end + + @doc """ + Builds a changeset based on the `struct` and `params`. + """ + def changeset(struct, params \\ %{}) do + struct + |> cast(params, [:board]) + |> set_default_board + |> validate_required([:board]) + end + + def set_default_board(changeset) do + changeset + |> put_change(:board, default_board) + end + + def default_board do + %{ + "8" => %{ + a: %{ type: "rook", colour: "black" }, + b: %{ type: "knight", colour: "black" }, + c: %{ type: "bishop", colour: "black" }, + d: %{ type: "queen", colour: "black" }, + e: %{ type: "king", colour: "black" }, + f: %{ type: "bishop", colour: "black" }, + g: %{ type: "knight", colour: "black" }, + h: %{ type: "rook", colour: "black" } + }, + "7" => %{ + a: %{ type: "pawn", colour: "black" }, + b: %{ type: "pawn", colour: "black" }, + c: %{ type: "pawn", colour: "black" }, + d: %{ type: "pawn", colour: "black" }, + e: %{ type: "pawn", colour: "black" }, + f: %{ type: "pawn", colour: "black" }, + g: %{ type: "pawn", colour: "black" }, + h: %{ type: "pawn", colour: "black" } + }, + "6" => %{ a: nil, b: nil, c: nil, d: nil, e: nil, f: nil, g: nil, h: nil }, + "5" => %{ a: nil, b: nil, c: nil, d: nil, e: nil, f: nil, g: nil, h: nil }, + "4" => %{ a: nil, b: nil, c: nil, d: nil, e: nil, f: nil, g: nil, h: nil }, + "3" => %{ a: nil, b: nil, c: nil, d: nil, e: nil, f: nil, g: nil, h: nil }, + "2" => %{ + a: %{ type: "pawn", colour: "white" }, + b: %{ type: "pawn", colour: "white" }, + c: %{ type: "pawn", colour: "white" }, + d: %{ type: "pawn", colour: "white" }, + e: %{ type: "pawn", colour: "white" }, + f: %{ type: "pawn", colour: "white" }, + g: %{ type: "pawn", colour: "white" }, + h: %{ type: "pawn", colour: "white" } + }, + "1" => %{ + a: %{ type: "rook", colour: "white" }, + b: %{ type: "knight", colour: "white" }, + c: %{ type: "bishop", colour: "white" }, + d: %{ type: "queen", colour: "white" }, + e: %{ type: "king", colour: "white" }, + f: %{ type: "bishop", colour: "white" }, + g: %{ type: "knight", colour: "white" }, + h: %{ type: "rook", colour: "white" } + } + } + end +end diff --git a/web/static/js/application.js b/web/static/js/application.js index c745ab1..dadee85 100644 --- a/web/static/js/application.js +++ b/web/static/js/application.js @@ -13,15 +13,21 @@ import ChessBoard from "./components/chess-board"; class App extends React.Component { render() { - const { store } = this.props; + const { store, gameId } = this.props; return ( - + ); } } -ReactDOM.render( - , - document.getElementById('app') -); +const container = document.getElementById("app"); + +if (container != undefined) { + const gameId = container.getAttribute("data-game-id"); + + ReactDOM.render( + , + container + ); +} diff --git a/web/templates/game/edit.html.eex b/web/templates/game/edit.html.eex new file mode 100644 index 0000000..d9919d9 --- /dev/null +++ b/web/templates/game/edit.html.eex @@ -0,0 +1,6 @@ +

Edit game

+ +<%= render "form.html", changeset: @changeset, + action: game_path(@conn, :update, @game) %> + +<%= link "Back", to: game_path(@conn, :index) %> diff --git a/web/templates/game/form.html.eex b/web/templates/game/form.html.eex new file mode 100644 index 0000000..631176a --- /dev/null +++ b/web/templates/game/form.html.eex @@ -0,0 +1,17 @@ +<%= form_for @changeset, @action, fn f -> %> + <%= if @changeset.action do %> +
+

Oops, something went wrong! Please check the errors below.

+
+ <% end %> + +
+ <%= label f, :board, class: "control-label" %> + <%= text_input f, :board, class: "form-control" %> + <%= error_tag f, :board %> +
+ +
+ <%= submit "Submit", class: "btn btn-primary" %> +
+<% end %> diff --git a/web/templates/game/index.html.eex b/web/templates/game/index.html.eex new file mode 100644 index 0000000..53932a3 --- /dev/null +++ b/web/templates/game/index.html.eex @@ -0,0 +1,29 @@ +

Listing games

+ + + + + + + + + + +<%= for game <- @games do %> + + + + + +<% end %> + +
Id
<%= game.id %> + <%= link "Show", to: game_path(@conn, :show, game), class: "btn btn-default btn-xs" %> + <%= link "Delete", to: game_path(@conn, :delete, game), method: :delete, data: [confirm: "Are you sure?"], class: "btn btn-danger btn-xs" %> +
+ +<%= form_for @changeset, game_path(@conn, :create), fn f -> %> +
+ <%= submit "Create game", class: "btn btn-primary" %> +
+<% end %> diff --git a/web/templates/game/new.html.eex b/web/templates/game/new.html.eex new file mode 100644 index 0000000..169e331 --- /dev/null +++ b/web/templates/game/new.html.eex @@ -0,0 +1,6 @@ +

New game

+ +<%= render "form.html", changeset: @changeset, + action: game_path(@conn, :create) %> + +<%= link "Back", to: game_path(@conn, :index) %> diff --git a/web/templates/game/show.html.eex b/web/templates/game/show.html.eex new file mode 100644 index 0000000..9c68a44 --- /dev/null +++ b/web/templates/game/show.html.eex @@ -0,0 +1,2 @@ +
+
diff --git a/web/views/api/game_view.ex b/web/views/api/game_view.ex index 01b0c43..6a25272 100644 --- a/web/views/api/game_view.ex +++ b/web/views/api/game_view.ex @@ -1,52 +1,7 @@ defmodule Chess.Api.GameView do use Chess.Web, :view - def render("show.json", %{ id: 1 }) do - %{ - "8" => %{ - a: %{ type: "rook", colour: "black" }, - b: %{ type: "knight", colour: "black" }, - c: %{ type: "bishop", colour: "black" }, - d: %{ type: "queen", colour: "black" }, - e: %{ type: "king", colour: "black" }, - f: %{ type: "bishop", colour: "black" }, - g: %{ type: "knight", colour: "black" }, - h: %{ type: "rook", colour: "black" } - }, - "7" => %{ - a: %{ type: "pawn", colour: "black" }, - b: %{ type: "pawn", colour: "black" }, - c: %{ type: "pawn", colour: "black" }, - d: %{ type: "pawn", colour: "black" }, - e: %{ type: "pawn", colour: "black" }, - f: %{ type: "pawn", colour: "black" }, - g: %{ type: "pawn", colour: "black" }, - h: %{ type: "pawn", colour: "black" } - }, - "6" => %{ a: nil, b: nil, c: nil, d: nil, e: nil, f: nil, g: nil, h: nil }, - "5" => %{ a: nil, b: nil, c: nil, d: nil, e: nil, f: nil, g: nil, h: nil }, - "4" => %{ a: nil, b: nil, c: nil, d: nil, e: nil, f: nil, g: nil, h: nil }, - "3" => %{ a: nil, b: nil, c: nil, d: nil, e: nil, f: nil, g: nil, h: nil }, - "2" => %{ - a: %{ type: "pawn", colour: "white" }, - b: %{ type: "pawn", colour: "white" }, - c: %{ type: "pawn", colour: "white" }, - d: %{ type: "pawn", colour: "white" }, - e: %{ type: "pawn", colour: "white" }, - f: %{ type: "pawn", colour: "white" }, - g: %{ type: "pawn", colour: "white" }, - h: %{ type: "pawn", colour: "white" } - }, - "1" => %{ - a: %{ type: "rook", colour: "white" }, - b: %{ type: "knight", colour: "white" }, - c: %{ type: "bishop", colour: "white" }, - d: %{ type: "queen", colour: "white" }, - e: %{ type: "king", colour: "white" }, - f: %{ type: "bishop", colour: "white" }, - g: %{ type: "knight", colour: "white" }, - h: %{ type: "rook", colour: "white" } - } - } + def render("show.json", %{game: game}) do + game.board end end diff --git a/web/views/game_view.ex b/web/views/game_view.ex new file mode 100644 index 0000000..e8af9de --- /dev/null +++ b/web/views/game_view.ex @@ -0,0 +1,3 @@ +defmodule Chess.GameView do + use Chess.Web, :view +end