mirror of
https://github.com/danbee/chess
synced 2025-03-04 08:39:06 +00:00
Create new games and display them
This commit is contained in:
parent
1b37e5f38a
commit
f6c38661c7
12
priv/repo/migrations/20161209105511_create_game.exs
Normal file
12
priv/repo/migrations/20161209105511_create_game.exs
Normal file
@ -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
|
||||||
66
test/controllers/game_controller_test.exs
Normal file
66
test/controllers/game_controller_test.exs
Normal file
@ -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
|
||||||
18
test/models/game_test.exs
Normal file
18
test/models/game_test.exs
Normal file
@ -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
|
||||||
@ -3,7 +3,8 @@ defmodule Chess.Api.GameController do
|
|||||||
|
|
||||||
alias Chess.Game
|
alias Chess.Game
|
||||||
|
|
||||||
def show(conn, _params) do
|
def show(conn, %{"id" => id}) do
|
||||||
render conn, "show.json", id: 1
|
game = Repo.get!(Game, id)
|
||||||
|
render conn, "show.json", game: game
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
66
web/controllers/game_controller.ex
Normal file
66
web/controllers/game_controller.ex
Normal file
@ -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
|
||||||
73
web/models/game.ex
Normal file
73
web/models/game.ex
Normal file
@ -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
|
||||||
@ -13,15 +13,21 @@ import ChessBoard from "./components/chess-board";
|
|||||||
|
|
||||||
class App extends React.Component {
|
class App extends React.Component {
|
||||||
render() {
|
render() {
|
||||||
const { store } = this.props;
|
const { store, gameId } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ChessBoard gameId={1} store={store} />
|
<ChessBoard gameId={gameId} store={store} />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const container = document.getElementById("app");
|
||||||
|
|
||||||
|
if (container != undefined) {
|
||||||
|
const gameId = container.getAttribute("data-game-id");
|
||||||
|
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<App store={store} />,
|
<App store={store} gameId={gameId} />,
|
||||||
document.getElementById('app')
|
container
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|||||||
6
web/templates/game/edit.html.eex
Normal file
6
web/templates/game/edit.html.eex
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<h2>Edit game</h2>
|
||||||
|
|
||||||
|
<%= render "form.html", changeset: @changeset,
|
||||||
|
action: game_path(@conn, :update, @game) %>
|
||||||
|
|
||||||
|
<%= link "Back", to: game_path(@conn, :index) %>
|
||||||
17
web/templates/game/form.html.eex
Normal file
17
web/templates/game/form.html.eex
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<%= 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, :board, class: "control-label" %>
|
||||||
|
<%= text_input f, :board, class: "form-control" %>
|
||||||
|
<%= error_tag f, :board %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<%= submit "Submit", class: "btn btn-primary" %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
29
web/templates/game/index.html.eex
Normal file
29
web/templates/game/index.html.eex
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<h2>Listing games</h2>
|
||||||
|
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Id</th>
|
||||||
|
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<%= for game <- @games do %>
|
||||||
|
<tr>
|
||||||
|
<td><%= game.id %></td>
|
||||||
|
|
||||||
|
<td class="text-right">
|
||||||
|
<%= 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" %>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<% end %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<%= form_for @changeset, game_path(@conn, :create), fn f -> %>
|
||||||
|
<div class="form-group">
|
||||||
|
<%= submit "Create game", class: "btn btn-primary" %>
|
||||||
|
</div>
|
||||||
|
<% end %>
|
||||||
6
web/templates/game/new.html.eex
Normal file
6
web/templates/game/new.html.eex
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<h2>New game</h2>
|
||||||
|
|
||||||
|
<%= render "form.html", changeset: @changeset,
|
||||||
|
action: game_path(@conn, :create) %>
|
||||||
|
|
||||||
|
<%= link "Back", to: game_path(@conn, :index) %>
|
||||||
2
web/templates/game/show.html.eex
Normal file
2
web/templates/game/show.html.eex
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
<div id="app" data-game-id="<%= @game.id %>">
|
||||||
|
</div>
|
||||||
@ -1,52 +1,7 @@
|
|||||||
defmodule Chess.Api.GameView do
|
defmodule Chess.Api.GameView do
|
||||||
use Chess.Web, :view
|
use Chess.Web, :view
|
||||||
|
|
||||||
def render("show.json", %{ id: 1 }) do
|
def render("show.json", %{game: game}) do
|
||||||
%{
|
game.board
|
||||||
"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
|
||||||
end
|
end
|
||||||
|
|||||||
3
web/views/game_view.ex
Normal file
3
web/views/game_view.ex
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
defmodule Chess.GameView do
|
||||||
|
use Chess.Web, :view
|
||||||
|
end
|
||||||
Loading…
Reference in New Issue
Block a user