diff --git a/.gitignore b/.gitignore
index fa62c33..19fe3da 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,24 @@
-public
-node_modules
-npm-debug.log.*
+# App artifacts
+/_build
+/db
+/deps
+/*.ez
+
+# Generated on crash by the VM
+erl_crash.dump
+
+# Static artifacts
+/node_modules
+
+# Since we are building assets from web/static,
+# we ignore priv/static. You may want to comment
+# this depending on your deployment strategy.
+/priv/static/
+
+# The config/prod.secret.exs file by default contains sensitive
+# data and you should not commit it into version control.
+#
+# Alternatively, you may comment the line below and commit the
+# secrets file as long as you replace its contents by environment
+# variables.
+/config/prod.secret.exs
diff --git a/README.md b/README.md
index dbbd570..b15ebd9 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,20 @@
# Chess
-A multiplayer chess game using Vue, VueX.
+To start your Phoenix app:
-## Instructions
+ * Install dependencies with `mix deps.get`
+ * Create and migrate your database with `mix ecto.create && mix ecto.migrate`
+ * Install Node.js dependencies with `npm install`
+ * Start Phoenix endpoint with `mix phoenix.server`
-To get started, simply run the following commands in your terminal...
+Now you can visit [`localhost:4000`](http://localhost:4000) from your browser.
-```bash
-yarn install
-brunch watch -s
-```
+Ready to run in production? Please [check our deployment guides](http://www.phoenixframework.org/docs/deployment).
-## License
+## Learn more
-This code is licensed under MIT, see [license.md](LICENSE.md) for details.
+ * Official website: http://www.phoenixframework.org/
+ * Guides: http://phoenixframework.org/docs/overview
+ * Docs: https://hexdocs.pm/phoenix
+ * Mailing list: http://groups.google.com/group/phoenix-talk
+ * Source: https://github.com/phoenixframework/phoenix
diff --git a/app/assets/index.html b/app/assets/index.html
deleted file mode 100644
index 5123a62..0000000
--- a/app/assets/index.html
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
- Chess
-
-
-
-
-
-
-
-
-
-
-
diff --git a/brunch-config.js b/brunch-config.js
index d38a592..74d9091 100644
--- a/brunch-config.js
+++ b/brunch-config.js
@@ -1,18 +1,58 @@
-module.exports = {
+exports.config = {
+ // See http://brunch.io/#documentation for docs.
files: {
javascripts: {
joinTo: {
- 'vendor.js': /^(?!app\/)/,
- 'app.js': /^app\//
+ "js/app.js": /^(web\/static\/js\/)/,
+ "js/vendor.js": /(node_modules)|^(web\/static\/vendor)|(deps)/
}
},
stylesheets: {
- joinTo: 'app.css'
+ joinTo: "css/app.css",
+ order: {
+ after: ["web/static/css/app.css"] // concat app.css last
+ }
+ },
+ templates: {
+ joinTo: "js/app.js"
}
},
+
+ conventions: {
+ // This option sets where we should place non-css and non-js assets in.
+ // By default, we set this to "/web/static/assets". Files in this directory
+ // will be copied to `paths.public`, which is "priv/static" by default.
+ assets: /^(web\/static\/assets)/
+ },
+
+ // Phoenix paths configuration
+ paths: {
+ // Dependencies and current project directories to watch
+ watched: [
+ "web/static",
+ "test/static"
+ ],
+
+ // Where to compile files to
+ public: "priv/static"
+ },
+
+ // Configure your plugins
plugins: {
babel: {
- presets: ['es2015', 'es2016', 'react']
+ // Do not use ES6 compiler in vendor code
+ presets: ["es2015", "es2016", "react"],
+ ignore: [/web\/static\/vendor/]
}
+ },
+
+ modules: {
+ autoRequire: {
+ "js/app.js": ["web/static/js/application"]
+ }
+ },
+
+ npm: {
+ enabled: true
}
-}
+};
diff --git a/config/config.exs b/config/config.exs
new file mode 100644
index 0000000..1f53aa4
--- /dev/null
+++ b/config/config.exs
@@ -0,0 +1,27 @@
+# This file is responsible for configuring your application
+# and its dependencies with the aid of the Mix.Config module.
+#
+# This configuration file is loaded before any dependency and
+# is restricted to this project.
+use Mix.Config
+
+# General application configuration
+config :chess_phoenix,
+ ecto_repos: [Chess.Repo]
+
+# Configures the endpoint
+config :chess_phoenix, Chess.Endpoint,
+ url: [host: "localhost"],
+ secret_key_base: "iiTDTKorCWTFoeBgAkr35XZp22cNIM2RsmnHiHdzKAuSHXUGXx42z7lawAwiu1B1",
+ render_errors: [view: Chess.ErrorView, accepts: ~w(html json)],
+ pubsub: [name: Chess.PubSub,
+ adapter: Phoenix.PubSub.PG2]
+
+# Configures Elixir's Logger
+config :logger, :console,
+ format: "$time $metadata[$level] $message\n",
+ metadata: [:request_id]
+
+# Import environment specific config. This must remain at the bottom
+# of this file so it overrides the configuration defined above.
+import_config "#{Mix.env}.exs"
diff --git a/config/dev.exs b/config/dev.exs
new file mode 100644
index 0000000..2b34c64
--- /dev/null
+++ b/config/dev.exs
@@ -0,0 +1,41 @@
+use Mix.Config
+
+# For development, we disable any cache and enable
+# debugging and code reloading.
+#
+# The watchers configuration can be used to run external
+# watchers to your application. For example, we use it
+# with brunch.io to recompile .js and .css sources.
+config :chess_phoenix, Chess.Endpoint,
+ http: [port: 4000],
+ debug_errors: true,
+ code_reloader: true,
+ check_origin: false,
+ watchers: [node: ["node_modules/brunch/bin/brunch", "watch", "--stdin",
+ cd: Path.expand("../", __DIR__)]]
+
+
+# Watch static and templates for browser reloading.
+config :chess_phoenix, Chess.Endpoint,
+ live_reload: [
+ patterns: [
+ ~r{priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$},
+ ~r{priv/gettext/.*(po)$},
+ ~r{web/views/.*(ex)$},
+ ~r{web/templates/.*(eex)$}
+ ]
+ ]
+
+# Do not include metadata nor timestamps in development logs
+config :logger, :console, format: "[$level] $message\n"
+
+# Set a higher stacktrace during development. Avoid configuring such
+# in production as building large stacktraces may be expensive.
+config :phoenix, :stacktrace_depth, 20
+
+# Configure your database
+config :chess_phoenix, Chess.Repo,
+ adapter: Ecto.Adapters.Postgres,
+ database: "chess_phoenix_dev",
+ hostname: "localhost",
+ pool_size: 10
diff --git a/config/prod.exs b/config/prod.exs
new file mode 100644
index 0000000..e6d5732
--- /dev/null
+++ b/config/prod.exs
@@ -0,0 +1,65 @@
+use Mix.Config
+
+# For production, we configure the host to read the PORT
+# from the system environment. Therefore, you will need
+# to set PORT=80 before running your server.
+#
+# You should also configure the url host to something
+# meaningful, we use this information when generating URLs.
+#
+# Finally, we also include the path to a manifest
+# containing the digested version of static files. This
+# manifest is generated by the mix phoenix.digest task
+# which you typically run after static files are built.
+config :chess_phoenix, Chess.Endpoint,
+ http: [port: {:system, "PORT"}],
+ url: [host: "example.com", port: 80],
+ cache_static_manifest: "priv/static/manifest.json"
+
+# Do not print debug messages in production
+config :logger, level: :info
+
+# ## SSL Support
+#
+# To get SSL working, you will need to add the `https` key
+# to the previous section and set your `:url` port to 443:
+#
+# config :chess_phoenix, Chess.Endpoint,
+# ...
+# url: [host: "example.com", port: 443],
+# https: [port: 443,
+# keyfile: System.get_env("SOME_APP_SSL_KEY_PATH"),
+# certfile: System.get_env("SOME_APP_SSL_CERT_PATH")]
+#
+# Where those two env variables return an absolute path to
+# the key and cert in disk or a relative path inside priv,
+# for example "priv/ssl/server.key".
+#
+# We also recommend setting `force_ssl`, ensuring no data is
+# ever sent via http, always redirecting to https:
+#
+# config :chess_phoenix, Chess.Endpoint,
+# force_ssl: [hsts: true]
+#
+# Check `Plug.SSL` for all available options in `force_ssl`.
+
+# ## Using releases
+#
+# If you are doing OTP releases, you need to instruct Phoenix
+# to start the server for all endpoints:
+#
+# config :phoenix, :serve_endpoints, true
+#
+# Alternatively, you can configure exactly which server to
+# start per endpoint:
+#
+# config :chess_phoenix, Chess.Endpoint, server: true
+#
+# You will also need to set the application root to `.` in order
+# for the new static assets to be served after a hot upgrade:
+#
+# config :chess_phoenix, Chess.Endpoint, root: "."
+
+# Finally import the config/prod.secret.exs
+# which should be versioned separately.
+import_config "prod.secret.exs"
diff --git a/config/test.exs b/config/test.exs
new file mode 100644
index 0000000..b71e353
--- /dev/null
+++ b/config/test.exs
@@ -0,0 +1,19 @@
+use Mix.Config
+
+# We don't run a server during test. If one is required,
+# you can enable the server option below.
+config :chess_phoenix, Chess.Endpoint,
+ http: [port: 4001],
+ server: false
+
+# Print only warnings and errors during test
+config :logger, level: :warn
+
+# Configure your database
+config :chess_phoenix, Chess.Repo,
+ adapter: Ecto.Adapters.Postgres,
+ username: "postgres",
+ password: "postgres",
+ database: "chess_phoenix_test",
+ hostname: "localhost",
+ pool: Ecto.Adapters.SQL.Sandbox
diff --git a/lib/chess_phoenix.ex b/lib/chess_phoenix.ex
new file mode 100644
index 0000000..01c243f
--- /dev/null
+++ b/lib/chess_phoenix.ex
@@ -0,0 +1,31 @@
+defmodule Chess do
+ use Application
+
+ # See http://elixir-lang.org/docs/stable/elixir/Application.html
+ # for more information on OTP Applications
+ def start(_type, _args) do
+ import Supervisor.Spec
+
+ # Define workers and child supervisors to be supervised
+ children = [
+ # Start the Ecto repository
+ supervisor(Chess.Repo, []),
+ # Start the endpoint when the application starts
+ supervisor(Chess.Endpoint, []),
+ # Start your own worker by calling: Chess.Worker.start_link(arg1, arg2, arg3)
+ # worker(Chess.Worker, [arg1, arg2, arg3]),
+ ]
+
+ # See http://elixir-lang.org/docs/stable/elixir/Supervisor.html
+ # for other strategies and supported options
+ opts = [strategy: :one_for_one, name: Chess.Supervisor]
+ Supervisor.start_link(children, opts)
+ end
+
+ # Tell Phoenix to update the endpoint configuration
+ # whenever the application is updated.
+ def config_change(changed, _new, removed) do
+ Chess.Endpoint.config_change(changed, removed)
+ :ok
+ end
+end
diff --git a/lib/chess_phoenix/endpoint.ex b/lib/chess_phoenix/endpoint.ex
new file mode 100644
index 0000000..4524d47
--- /dev/null
+++ b/lib/chess_phoenix/endpoint.ex
@@ -0,0 +1,42 @@
+defmodule Chess.Endpoint do
+ use Phoenix.Endpoint, otp_app: :chess_phoenix
+
+ socket "/socket", Chess.UserSocket
+
+ # Serve at "/" the static files from "priv/static" directory.
+ #
+ # You should set gzip to true if you are running phoenix.digest
+ # when deploying your static files in production.
+ plug Plug.Static,
+ at: "/", from: :chess_phoenix, gzip: false,
+ only: ~w(css fonts images js favicon.ico robots.txt)
+
+ # Code reloading can be explicitly enabled under the
+ # :code_reloader configuration of your endpoint.
+ if code_reloading? do
+ socket "/phoenix/live_reload/socket", Phoenix.LiveReloader.Socket
+ plug Phoenix.LiveReloader
+ plug Phoenix.CodeReloader
+ end
+
+ plug Plug.RequestId
+ plug Plug.Logger
+
+ plug Plug.Parsers,
+ parsers: [:urlencoded, :multipart, :json],
+ pass: ["*/*"],
+ json_decoder: Poison
+
+ plug Plug.MethodOverride
+ plug Plug.Head
+
+ # The session will be stored in the cookie and signed,
+ # this means its contents can be read but not tampered with.
+ # Set :encryption_salt if you would also like to encrypt it.
+ plug Plug.Session,
+ store: :cookie,
+ key: "_chess_phoenix_key",
+ signing_salt: "9LqUhZTU"
+
+ plug Chess.Router
+end
diff --git a/lib/chess_phoenix/repo.ex b/lib/chess_phoenix/repo.ex
new file mode 100644
index 0000000..5d3b4a6
--- /dev/null
+++ b/lib/chess_phoenix/repo.ex
@@ -0,0 +1,3 @@
+defmodule Chess.Repo do
+ use Ecto.Repo, otp_app: :chess_phoenix
+end
diff --git a/mix.exs b/mix.exs
new file mode 100644
index 0000000..fda7830
--- /dev/null
+++ b/mix.exs
@@ -0,0 +1,54 @@
+defmodule Chess.Mixfile do
+ use Mix.Project
+
+ def project do
+ [app: :chess_phoenix,
+ version: "0.0.1",
+ elixir: "~> 1.2",
+ elixirc_paths: elixirc_paths(Mix.env),
+ compilers: [:phoenix, :gettext] ++ Mix.compilers,
+ build_embedded: Mix.env == :prod,
+ start_permanent: Mix.env == :prod,
+ aliases: aliases(),
+ deps: deps()]
+ end
+
+ # Configuration for the OTP application.
+ #
+ # Type `mix help compile.app` for more information.
+ def application do
+ [mod: {Chess, []},
+ applications: [:phoenix, :phoenix_pubsub, :phoenix_html, :cowboy, :logger, :gettext,
+ :phoenix_ecto, :postgrex]]
+ end
+
+ # Specifies which paths to compile per environment.
+ defp elixirc_paths(:test), do: ["lib", "web", "test/support"]
+ defp elixirc_paths(_), do: ["lib", "web"]
+
+ # Specifies your project dependencies.
+ #
+ # Type `mix help deps` for examples and options.
+ defp deps do
+ [{:phoenix, "~> 1.2.0"},
+ {:phoenix_pubsub, "~> 1.0"},
+ {:phoenix_ecto, "~> 3.0"},
+ {:postgrex, ">= 0.0.0"},
+ {:phoenix_html, "~> 2.6"},
+ {:phoenix_live_reload, "~> 1.0", only: :dev},
+ {:gettext, "~> 0.11"},
+ {:cowboy, "~> 1.0"}]
+ end
+
+ # Aliases are shortcuts or tasks specific to the current project.
+ # For example, to create, migrate and run the seeds file at once:
+ #
+ # $ mix ecto.setup
+ #
+ # See the documentation for `Mix` for more info on aliases.
+ defp aliases do
+ ["ecto.setup": ["ecto.create", "ecto.migrate", "run priv/repo/seeds.exs"],
+ "ecto.reset": ["ecto.drop", "ecto.setup"],
+ "test": ["ecto.create --quiet", "ecto.migrate", "test"]]
+ end
+end
diff --git a/mix.lock b/mix.lock
new file mode 100644
index 0000000..72f4497
--- /dev/null
+++ b/mix.lock
@@ -0,0 +1,19 @@
+%{"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], []},
+ "cowboy": {:hex, :cowboy, "1.0.4", "a324a8df9f2316c833a470d918aaf73ae894278b8aa6226ce7a9bf699388f878", [:rebar, :make], [{:cowlib, "~> 1.0.0", [hex: :cowlib, optional: false]}, {:ranch, "~> 1.0", [hex: :ranch, optional: false]}]},
+ "cowlib": {:hex, :cowlib, "1.0.2", "9d769a1d062c9c3ac753096f868ca121e2730b9a377de23dec0f7e08b1df84ee", [:make], []},
+ "db_connection": {:hex, :db_connection, "1.1.0", "b2b88db6d7d12f99997b584d09fad98e560b817a20dab6a526830e339f54cdb3", [:mix], [{:connection, "~> 1.0.2", [hex: :connection, optional: false]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: true]}, {:sbroker, "~> 1.0", [hex: :sbroker, optional: true]}]},
+ "decimal": {:hex, :decimal, "1.3.1", "157b3cedb2bfcb5359372a7766dd7a41091ad34578296e951f58a946fcab49c6", [:mix], []},
+ "ecto": {:hex, :ecto, "2.0.6", "9dcbf819c2a77f67a66b83739b7fcc00b71aaf6c100016db4f798930fa4cfd47", [:mix], [{:db_connection, "~> 1.0", [hex: :db_connection, optional: true]}, {:decimal, "~> 1.1.2 or ~> 1.2", [hex: :decimal, optional: false]}, {:mariaex, "~> 0.8.0", [hex: :mariaex, optional: true]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: true]}, {:poolboy, "~> 1.5", [hex: :poolboy, optional: false]}, {:postgrex, "~> 0.12.0", [hex: :postgrex, optional: true]}, {:sbroker, "~> 1.0-beta", [hex: :sbroker, optional: true]}]},
+ "fs": {:hex, :fs, "0.9.2", "ed17036c26c3f70ac49781ed9220a50c36775c6ca2cf8182d123b6566e49ec59", [:rebar], []},
+ "gettext": {:hex, :gettext, "0.12.2", "4f71a0df7b0bf5693419da593b4ddde558cc28e8f56a991fbfc2b51be1d5c848", [:mix], []},
+ "mime": {:hex, :mime, "1.0.1", "05c393850524767d13a53627df71beeebb016205eb43bfbd92d14d24ec7a1b51", [:mix], []},
+ "phoenix": {:hex, :phoenix, "1.2.1", "6dc592249ab73c67575769765b66ad164ad25d83defa3492dc6ae269bd2a68ab", [:mix], [{:cowboy, "~> 1.0", [hex: :cowboy, optional: true]}, {:phoenix_pubsub, "~> 1.0", [hex: :phoenix_pubsub, optional: false]}, {:plug, "~> 1.1", [hex: :plug, optional: false]}, {:poison, "~> 1.5 or ~> 2.0", [hex: :poison, optional: false]}]},
+ "phoenix_ecto": {:hex, :phoenix_ecto, "3.0.1", "42eb486ef732cf209d0a353e791806721f33ff40beab0a86f02070a5649ed00a", [:mix], [{:ecto, "~> 2.0", [hex: :ecto, optional: false]}, {:phoenix_html, "~> 2.6", [hex: :phoenix_html, optional: true]}, {:plug, "~> 1.0", [hex: :plug, optional: false]}]},
+ "phoenix_html": {:hex, :phoenix_html, "2.8.0", "777598a4b6609ad6ab8b180f7b25c9af2904644e488922bb9b9b03ce988d20b1", [:mix], [{:plug, "~> 1.0", [hex: :plug, optional: false]}]},
+ "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.0.6", "4490d588c4f60248b1c5f1f0dc0a7271e1aed4bddbd8b1542630f7bf6bc7b012", [:mix], [{:fs, "~> 0.9.1", [hex: :fs, optional: false]}, {:phoenix, "~> 1.0 or ~> 1.2-rc", [hex: :phoenix, optional: false]}]},
+ "phoenix_pubsub": {:hex, :phoenix_pubsub, "1.0.1", "c10ddf6237007c804bf2b8f3c4d5b99009b42eca3a0dfac04ea2d8001186056a", [:mix], []},
+ "plug": {:hex, :plug, "1.3.0", "6e2b01afc5db3fd011ca4a16efd9cb424528c157c30a44a0186bcc92c7b2e8f3", [:mix], [{:cowboy, "~> 1.0.1 or ~> 1.1", [hex: :cowboy, optional: true]}, {:mime, "~> 1.0", [hex: :mime, optional: false]}]},
+ "poison": {:hex, :poison, "2.2.0", "4763b69a8a77bd77d26f477d196428b741261a761257ff1cf92753a0d4d24a63", [:mix], []},
+ "poolboy": {:hex, :poolboy, "1.5.1", "6b46163901cfd0a1b43d692657ed9d7e599853b3b21b95ae5ae0a777cf9b6ca8", [:rebar], []},
+ "postgrex": {:hex, :postgrex, "0.12.1", "2f8b46cb3a44dcd42f42938abedbfffe7e103ba4ce810ccbeee8dcf27ca0fb06", [:mix], [{:connection, "~> 1.0", [hex: :connection, optional: false]}, {:db_connection, "~> 1.0-rc.4", [hex: :db_connection, optional: false]}, {:decimal, "~> 1.0", [hex: :decimal, optional: false]}]},
+ "ranch": {:hex, :ranch, "1.2.1", "a6fb992c10f2187b46ffd17ce398ddf8a54f691b81768f9ef5f461ea7e28c762", [:make], []}}
diff --git a/package.json b/package.json
index 85017ce..e39198a 100644
--- a/package.json
+++ b/package.json
@@ -1,22 +1,30 @@
{
- "name": "simple-brunch",
- "version": "0.1.0",
- "private": true,
- "devDependencies": {
- "babel-brunch": "^6.0.6",
- "babel-plugin-transform-runtime": "^6.15.0",
- "babel-preset-react": "^6.16.0",
- "brunch": "^2.9.1",
- "javascript-brunch": "^2.0.0",
- "sass-brunch": "^2.7.0"
+ "repository": {},
+ "license": "MIT",
+ "scripts": {
+ "deploy": "brunch build --production",
+ "watch": "brunch watch --stdin"
},
"dependencies": {
"classnames": "^2.2.5",
"immutable": "^3.8.1",
"lodash": "^4.17.2",
+ "phoenix": "file:deps/phoenix",
+ "phoenix_html": "file:deps/phoenix_html",
"react": "^15.4.1",
"react-dom": "^15.4.1",
"react-redux": "^4.4.6",
"redux": "^3.6.0"
+ },
+ "devDependencies": {
+ "babel-brunch": "^6.0.6",
+ "babel-plugin-transform-runtime": "^6.15.0",
+ "babel-preset-react": "^6.16.0",
+ "brunch": "~2.6.0",
+ "clean-css-brunch": "~2.0.0",
+ "css-brunch": "~2.0.0",
+ "javascript-brunch": "~2.0.0",
+ "sass-brunch": "^2.7.0",
+ "uglify-js-brunch": "~2.0.1"
}
}
diff --git a/priv/gettext/en/LC_MESSAGES/errors.po b/priv/gettext/en/LC_MESSAGES/errors.po
new file mode 100644
index 0000000..087d374
--- /dev/null
+++ b/priv/gettext/en/LC_MESSAGES/errors.po
@@ -0,0 +1,93 @@
+## `msgid`s in this file come from POT (.pot) files.
+##
+## Do not add, change, or remove `msgid`s manually here as
+## they're tied to the ones in the corresponding POT file
+## (with the same domain).
+##
+## Use `mix gettext.extract --merge` or `mix gettext.merge`
+## to merge POT files into PO files.
+msgid ""
+msgstr ""
+"Language: en\n"
+
+## From Ecto.Changeset.cast/4
+msgid "can't be blank"
+msgstr ""
+
+## From Ecto.Changeset.unique_constraint/3
+msgid "has already been taken"
+msgstr ""
+
+## From Ecto.Changeset.put_change/3
+msgid "is invalid"
+msgstr ""
+
+## From Ecto.Changeset.validate_format/3
+msgid "has invalid format"
+msgstr ""
+
+## From Ecto.Changeset.validate_subset/3
+msgid "has an invalid entry"
+msgstr ""
+
+## From Ecto.Changeset.validate_exclusion/3
+msgid "is reserved"
+msgstr ""
+
+## From Ecto.Changeset.validate_confirmation/3
+msgid "does not match confirmation"
+msgstr ""
+
+## From Ecto.Changeset.no_assoc_constraint/3
+msgid "is still associated to this entry"
+msgstr ""
+
+msgid "are still associated to this entry"
+msgstr ""
+
+## From Ecto.Changeset.validate_length/3
+msgid "should be %{count} character(s)"
+msgid_plural "should be %{count} character(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should have %{count} item(s)"
+msgid_plural "should have %{count} item(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should be at least %{count} character(s)"
+msgid_plural "should be at least %{count} character(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should have at least %{count} item(s)"
+msgid_plural "should have at least %{count} item(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should be at most %{count} character(s)"
+msgid_plural "should be at most %{count} character(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should have at most %{count} item(s)"
+msgid_plural "should have at most %{count} item(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+## From Ecto.Changeset.validate_number/3
+msgid "must be less than %{number}"
+msgstr ""
+
+msgid "must be greater than %{number}"
+msgstr ""
+
+msgid "must be less than or equal to %{number}"
+msgstr ""
+
+msgid "must be greater than or equal to %{number}"
+msgstr ""
+
+msgid "must be equal to %{number}"
+msgstr ""
diff --git a/priv/gettext/errors.pot b/priv/gettext/errors.pot
new file mode 100644
index 0000000..a228957
--- /dev/null
+++ b/priv/gettext/errors.pot
@@ -0,0 +1,91 @@
+## This file is a PO Template file.
+##
+## `msgid`s here are often extracted from source code.
+## Add new translations manually only if they're dynamic
+## translations that can't be statically extracted.
+##
+## Run `mix gettext.extract` to bring this file up to
+## date. Leave `msgstr`s empty as changing them here as no
+## effect: edit them in PO (`.po`) files instead.
+
+## From Ecto.Changeset.cast/4
+msgid "can't be blank"
+msgstr ""
+
+## From Ecto.Changeset.unique_constraint/3
+msgid "has already been taken"
+msgstr ""
+
+## From Ecto.Changeset.put_change/3
+msgid "is invalid"
+msgstr ""
+
+## From Ecto.Changeset.validate_format/3
+msgid "has invalid format"
+msgstr ""
+
+## From Ecto.Changeset.validate_subset/3
+msgid "has an invalid entry"
+msgstr ""
+
+## From Ecto.Changeset.validate_exclusion/3
+msgid "is reserved"
+msgstr ""
+
+## From Ecto.Changeset.validate_confirmation/3
+msgid "does not match confirmation"
+msgstr ""
+
+## From Ecto.Changeset.no_assoc_constraint/3
+msgid "is still associated to this entry"
+msgstr ""
+
+msgid "are still associated to this entry"
+msgstr ""
+
+## From Ecto.Changeset.validate_length/3
+msgid "should be %{count} character(s)"
+msgid_plural "should be %{count} character(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should have %{count} item(s)"
+msgid_plural "should have %{count} item(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should be at least %{count} character(s)"
+msgid_plural "should be at least %{count} character(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should have at least %{count} item(s)"
+msgid_plural "should have at least %{count} item(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should be at most %{count} character(s)"
+msgid_plural "should be at most %{count} character(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+msgid "should have at most %{count} item(s)"
+msgid_plural "should have at most %{count} item(s)"
+msgstr[0] ""
+msgstr[1] ""
+
+## From Ecto.Changeset.validate_number/3
+msgid "must be less than %{number}"
+msgstr ""
+
+msgid "must be greater than %{number}"
+msgstr ""
+
+msgid "must be less than or equal to %{number}"
+msgstr ""
+
+msgid "must be greater than or equal to %{number}"
+msgstr ""
+
+msgid "must be equal to %{number}"
+msgstr ""
diff --git a/priv/repo/seeds.exs b/priv/repo/seeds.exs
new file mode 100644
index 0000000..929d834
--- /dev/null
+++ b/priv/repo/seeds.exs
@@ -0,0 +1,11 @@
+# Script for populating the database. You can run it as:
+#
+# mix run priv/repo/seeds.exs
+#
+# Inside the script, you can read and write to any of your
+# repositories directly:
+#
+# Chess.Repo.insert!(%Chess.SomeModel{})
+#
+# We recommend using the bang functions (`insert!`, `update!`
+# and so on) as they will fail if something goes wrong.
diff --git a/test/controllers/page_controller_test.exs b/test/controllers/page_controller_test.exs
new file mode 100644
index 0000000..c855bab
--- /dev/null
+++ b/test/controllers/page_controller_test.exs
@@ -0,0 +1,8 @@
+defmodule Chess.PageControllerTest do
+ use Chess.ConnCase
+
+ test "GET /", %{conn: conn} do
+ conn = get conn, "/"
+ assert html_response(conn, 200) =~ "Welcome to Phoenix!"
+ end
+end
diff --git a/test/support/channel_case.ex b/test/support/channel_case.ex
new file mode 100644
index 0000000..4f135bb
--- /dev/null
+++ b/test/support/channel_case.ex
@@ -0,0 +1,43 @@
+defmodule Chess.ChannelCase do
+ @moduledoc """
+ This module defines the test case to be used by
+ channel tests.
+
+ Such tests rely on `Phoenix.ChannelTest` and also
+ import other functionality to make it easier
+ to build and query models.
+
+ Finally, if the test case interacts with the database,
+ it cannot be async. For this reason, every test runs
+ inside a transaction which is reset at the beginning
+ of the test unless the test case is marked as async.
+ """
+
+ use ExUnit.CaseTemplate
+
+ using do
+ quote do
+ # Import conveniences for testing with channels
+ use Phoenix.ChannelTest
+
+ alias Chess.Repo
+ import Ecto
+ import Ecto.Changeset
+ import Ecto.Query
+
+
+ # The default endpoint for testing
+ @endpoint Chess.Endpoint
+ end
+ end
+
+ setup tags do
+ :ok = Ecto.Adapters.SQL.Sandbox.checkout(Chess.Repo)
+
+ unless tags[:async] do
+ Ecto.Adapters.SQL.Sandbox.mode(Chess.Repo, {:shared, self()})
+ end
+
+ :ok
+ end
+end
diff --git a/test/support/conn_case.ex b/test/support/conn_case.ex
new file mode 100644
index 0000000..7565f85
--- /dev/null
+++ b/test/support/conn_case.ex
@@ -0,0 +1,44 @@
+defmodule Chess.ConnCase do
+ @moduledoc """
+ This module defines the test case to be used by
+ tests that require setting up a connection.
+
+ Such tests rely on `Phoenix.ConnTest` and also
+ import other functionality to make it easier
+ to build and query models.
+
+ Finally, if the test case interacts with the database,
+ it cannot be async. For this reason, every test runs
+ inside a transaction which is reset at the beginning
+ of the test unless the test case is marked as async.
+ """
+
+ use ExUnit.CaseTemplate
+
+ using do
+ quote do
+ # Import conveniences for testing with connections
+ use Phoenix.ConnTest
+
+ alias Chess.Repo
+ import Ecto
+ import Ecto.Changeset
+ import Ecto.Query
+
+ import Chess.Router.Helpers
+
+ # The default endpoint for testing
+ @endpoint Chess.Endpoint
+ end
+ end
+
+ setup tags do
+ :ok = Ecto.Adapters.SQL.Sandbox.checkout(Chess.Repo)
+
+ unless tags[:async] do
+ Ecto.Adapters.SQL.Sandbox.mode(Chess.Repo, {:shared, self()})
+ end
+
+ {:ok, conn: Phoenix.ConnTest.build_conn()}
+ end
+end
diff --git a/test/support/model_case.ex b/test/support/model_case.ex
new file mode 100644
index 0000000..5296891
--- /dev/null
+++ b/test/support/model_case.ex
@@ -0,0 +1,65 @@
+defmodule Chess.ModelCase do
+ @moduledoc """
+ This module defines the test case to be used by
+ model tests.
+
+ You may define functions here to be used as helpers in
+ your model tests. See `errors_on/2`'s definition as reference.
+
+ Finally, if the test case interacts with the database,
+ it cannot be async. For this reason, every test runs
+ inside a transaction which is reset at the beginning
+ of the test unless the test case is marked as async.
+ """
+
+ use ExUnit.CaseTemplate
+
+ using do
+ quote do
+ alias Chess.Repo
+
+ import Ecto
+ import Ecto.Changeset
+ import Ecto.Query
+ import Chess.ModelCase
+ end
+ end
+
+ setup tags do
+ :ok = Ecto.Adapters.SQL.Sandbox.checkout(Chess.Repo)
+
+ unless tags[:async] do
+ Ecto.Adapters.SQL.Sandbox.mode(Chess.Repo, {:shared, self()})
+ end
+
+ :ok
+ end
+
+ @doc """
+ Helper for returning list of errors in a struct when given certain data.
+
+ ## Examples
+
+ Given a User schema that lists `:name` as a required field and validates
+ `:password` to be safe, it would return:
+
+ iex> errors_on(%User{}, %{password: "password"})
+ [password: "is unsafe", name: "is blank"]
+
+ You could then write your assertion like:
+
+ assert {:password, "is unsafe"} in errors_on(%User{}, %{password: "password"})
+
+ You can also create the changeset manually and retrieve the errors
+ field directly:
+
+ iex> changeset = User.changeset(%User{}, password: "password")
+ iex> {:password, "is unsafe"} in changeset.errors
+ true
+ """
+ def errors_on(struct, data) do
+ struct.__struct__.changeset(struct, data)
+ |> Ecto.Changeset.traverse_errors(&Chess.ErrorHelpers.translate_error/1)
+ |> Enum.flat_map(fn {key, errors} -> for msg <- errors, do: {key, msg} end)
+ end
+end
diff --git a/test/test_helper.exs b/test/test_helper.exs
new file mode 100644
index 0000000..881ee85
--- /dev/null
+++ b/test/test_helper.exs
@@ -0,0 +1,4 @@
+ExUnit.start
+
+Ecto.Adapters.SQL.Sandbox.mode(Chess.Repo, :manual)
+
diff --git a/test/views/error_view_test.exs b/test/views/error_view_test.exs
new file mode 100644
index 0000000..90417f9
--- /dev/null
+++ b/test/views/error_view_test.exs
@@ -0,0 +1,21 @@
+defmodule Chess.ErrorViewTest do
+ use Chess.ConnCase, async: true
+
+ # Bring render/3 and render_to_string/3 for testing custom views
+ import Phoenix.View
+
+ test "renders 404.html" do
+ assert render_to_string(Chess.ErrorView, "404.html", []) ==
+ "Page not found"
+ end
+
+ test "render 500.html" do
+ assert render_to_string(Chess.ErrorView, "500.html", []) ==
+ "Internal server error"
+ end
+
+ test "render any other" do
+ assert render_to_string(Chess.ErrorView, "505.html", []) ==
+ "Internal server error"
+ end
+end
diff --git a/test/views/layout_view_test.exs b/test/views/layout_view_test.exs
new file mode 100644
index 0000000..cad9dab
--- /dev/null
+++ b/test/views/layout_view_test.exs
@@ -0,0 +1,3 @@
+defmodule Chess.LayoutViewTest do
+ use Chess.ConnCase, async: true
+end
diff --git a/test/views/page_view_test.exs b/test/views/page_view_test.exs
new file mode 100644
index 0000000..2642373
--- /dev/null
+++ b/test/views/page_view_test.exs
@@ -0,0 +1,3 @@
+defmodule Chess.PageViewTest do
+ use Chess.ConnCase, async: true
+end
diff --git a/web/channels/user_socket.ex b/web/channels/user_socket.ex
new file mode 100644
index 0000000..380fb3e
--- /dev/null
+++ b/web/channels/user_socket.ex
@@ -0,0 +1,37 @@
+defmodule Chess.UserSocket do
+ use Phoenix.Socket
+
+ ## Channels
+ # channel "room:*", Chess.RoomChannel
+
+ ## Transports
+ transport :websocket, Phoenix.Transports.WebSocket
+ # transport :longpoll, Phoenix.Transports.LongPoll
+
+ # Socket params are passed from the client and can
+ # be used to verify and authenticate a user. After
+ # verification, you can put default assigns into
+ # the socket that will be set for all channels, ie
+ #
+ # {:ok, assign(socket, :user_id, verified_user_id)}
+ #
+ # To deny connection, return `:error`.
+ #
+ # See `Phoenix.Token` documentation for examples in
+ # performing token verification on connect.
+ def connect(_params, socket) do
+ {:ok, socket}
+ end
+
+ # Socket id's are topics that allow you to identify all sockets for a given user:
+ #
+ # def id(socket), do: "users_socket:#{socket.assigns.user_id}"
+ #
+ # Would allow you to broadcast a "disconnect" event and terminate
+ # all active sockets and channels for a given user:
+ #
+ # Chess.Endpoint.broadcast("users_socket:#{user.id}", "disconnect", %{})
+ #
+ # Returning `nil` makes this socket anonymous.
+ def id(_socket), do: nil
+end
diff --git a/web/controllers/page_controller.ex b/web/controllers/page_controller.ex
new file mode 100644
index 0000000..2a2c7f8
--- /dev/null
+++ b/web/controllers/page_controller.ex
@@ -0,0 +1,7 @@
+defmodule Chess.PageController do
+ use Chess.Web, :controller
+
+ def index(conn, _params) do
+ render conn, "index.html"
+ end
+end
diff --git a/web/gettext.ex b/web/gettext.ex
new file mode 100644
index 0000000..f77877e
--- /dev/null
+++ b/web/gettext.ex
@@ -0,0 +1,24 @@
+defmodule Chess.Gettext do
+ @moduledoc """
+ A module providing Internationalization with a gettext-based API.
+
+ By using [Gettext](https://hexdocs.pm/gettext),
+ your module gains a set of macros for translations, for example:
+
+ import Chess.Gettext
+
+ # Simple translation
+ gettext "Here is the string to translate"
+
+ # Plural translation
+ ngettext "Here is the string to translate",
+ "Here are the strings to translate",
+ 3
+
+ # Domain-based translation
+ dgettext "errors", "Here is the error message to translate"
+
+ See the [Gettext Docs](https://hexdocs.pm/gettext) for detailed usage.
+ """
+ use Gettext, otp_app: :chess_phoenix
+end
diff --git a/web/router.ex b/web/router.ex
new file mode 100644
index 0000000..6765403
--- /dev/null
+++ b/web/router.ex
@@ -0,0 +1,26 @@
+defmodule Chess.Router do
+ use Chess.Web, :router
+
+ pipeline :browser do
+ plug :accepts, ["html"]
+ plug :fetch_session
+ plug :fetch_flash
+ plug :protect_from_forgery
+ plug :put_secure_browser_headers
+ end
+
+ pipeline :api do
+ plug :accepts, ["json"]
+ end
+
+ scope "/", Chess do
+ pipe_through :browser # Use the default browser stack
+
+ get "/", PageController, :index
+ end
+
+ # Other scopes may use custom stacks.
+ # scope "/api", Chess do
+ # pipe_through :api
+ # end
+end
diff --git a/web/static/assets/favicon.ico b/web/static/assets/favicon.ico
new file mode 100644
index 0000000..73de524
Binary files /dev/null and b/web/static/assets/favicon.ico differ
diff --git a/app/assets/images/bishop_black.svg b/web/static/assets/images/bishop_black.svg
similarity index 100%
rename from app/assets/images/bishop_black.svg
rename to web/static/assets/images/bishop_black.svg
diff --git a/app/assets/images/bishop_white.svg b/web/static/assets/images/bishop_white.svg
similarity index 100%
rename from app/assets/images/bishop_white.svg
rename to web/static/assets/images/bishop_white.svg
diff --git a/app/assets/images/king_black.svg b/web/static/assets/images/king_black.svg
similarity index 100%
rename from app/assets/images/king_black.svg
rename to web/static/assets/images/king_black.svg
diff --git a/app/assets/images/king_white.svg b/web/static/assets/images/king_white.svg
similarity index 100%
rename from app/assets/images/king_white.svg
rename to web/static/assets/images/king_white.svg
diff --git a/app/assets/images/knight_black.svg b/web/static/assets/images/knight_black.svg
similarity index 100%
rename from app/assets/images/knight_black.svg
rename to web/static/assets/images/knight_black.svg
diff --git a/app/assets/images/knight_white.svg b/web/static/assets/images/knight_white.svg
similarity index 100%
rename from app/assets/images/knight_white.svg
rename to web/static/assets/images/knight_white.svg
diff --git a/app/assets/images/pawn_black.svg b/web/static/assets/images/pawn_black.svg
similarity index 100%
rename from app/assets/images/pawn_black.svg
rename to web/static/assets/images/pawn_black.svg
diff --git a/app/assets/images/pawn_white.svg b/web/static/assets/images/pawn_white.svg
similarity index 100%
rename from app/assets/images/pawn_white.svg
rename to web/static/assets/images/pawn_white.svg
diff --git a/app/assets/images/queen_black.svg b/web/static/assets/images/queen_black.svg
similarity index 100%
rename from app/assets/images/queen_black.svg
rename to web/static/assets/images/queen_black.svg
diff --git a/app/assets/images/queen_white.svg b/web/static/assets/images/queen_white.svg
similarity index 100%
rename from app/assets/images/queen_white.svg
rename to web/static/assets/images/queen_white.svg
diff --git a/app/assets/images/rook_black.svg b/web/static/assets/images/rook_black.svg
similarity index 100%
rename from app/assets/images/rook_black.svg
rename to web/static/assets/images/rook_black.svg
diff --git a/app/assets/images/rook_white.svg b/web/static/assets/images/rook_white.svg
similarity index 100%
rename from app/assets/images/rook_white.svg
rename to web/static/assets/images/rook_white.svg
diff --git a/web/static/assets/robots.txt b/web/static/assets/robots.txt
new file mode 100644
index 0000000..3c9c7c0
--- /dev/null
+++ b/web/static/assets/robots.txt
@@ -0,0 +1,5 @@
+# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
+#
+# To ban all spiders from the entire site uncomment the next two lines:
+# User-agent: *
+# Disallow: /
diff --git a/app/styles/main.scss b/web/static/css/app.scss
similarity index 100%
rename from app/styles/main.scss
rename to web/static/css/app.scss
diff --git a/app/application.js b/web/static/js/application.js
similarity index 79%
rename from app/application.js
rename to web/static/js/application.js
index bbfee64..fd5f611 100644
--- a/app/application.js
+++ b/web/static/js/application.js
@@ -5,11 +5,11 @@ import ReactDOM from "react-dom";
import { createStore } from "redux";
import { Provider } from "react-redux";
-import chessBoardReducer from "reducers/chess-board";
+import chessBoardReducer from "./reducers/chess-board";
const store = createStore(chessBoardReducer);
-import ChessBoard from "components/chess-board";
+import ChessBoard from "./components/chess-board";
class App extends React.Component {
render() {
diff --git a/app/components/chess-board-square.js b/web/static/js/components/chess-board-square.js
similarity index 95%
rename from app/components/chess-board-square.js
rename to web/static/js/components/chess-board-square.js
index b095c4a..4680b7e 100644
--- a/app/components/chess-board-square.js
+++ b/web/static/js/components/chess-board-square.js
@@ -1,7 +1,7 @@
import React from "react";
import classNames from "classnames";
-import { movePiece, selectPiece } from "store/actions";
+import { movePiece, selectPiece } from "../store/actions";
class ChessBoardSquare extends React.Component {
constructor(props) {
diff --git a/app/components/chess-board.js b/web/static/js/components/chess-board.js
similarity index 94%
rename from app/components/chess-board.js
rename to web/static/js/components/chess-board.js
index 2efb4a5..e2e4b1a 100644
--- a/app/components/chess-board.js
+++ b/web/static/js/components/chess-board.js
@@ -1,7 +1,7 @@
import React from "react";
import { connect } from "react-redux";
-import ChessBoardSquare from "components/chess-board-square";
+import ChessBoardSquare from "./chess-board-square";
class ChessBoard extends React.Component {
getBoard() {
diff --git a/app/reducers/chess-board.js b/web/static/js/reducers/chess-board.js
similarity index 83%
rename from app/reducers/chess-board.js
rename to web/static/js/reducers/chess-board.js
index 14fc298..d91962b 100644
--- a/app/reducers/chess-board.js
+++ b/web/static/js/reducers/chess-board.js
@@ -1,5 +1,5 @@
-import defaultState from "store/default-state";
-import movePiece from "reducers/move-piece";
+import defaultState from "../store/default-state";
+import movePiece from "./move-piece";
const chessBoardReducer = (state = defaultState, action) => {
switch (action.type) {
diff --git a/app/reducers/move-piece.js b/web/static/js/reducers/move-piece.js
similarity index 100%
rename from app/reducers/move-piece.js
rename to web/static/js/reducers/move-piece.js
index a830aa3..bd97c71 100644
--- a/app/reducers/move-piece.js
+++ b/web/static/js/reducers/move-piece.js
@@ -6,9 +6,9 @@ const movePiece = (board, from, to) => {
const piece = board[from.rank][from.file];
const boardChange = Map([
- [to.rank, Map([[to.file, piece]])]
- ]).mergeDeep(Map([
[from.rank, Map([[from.file, null]])]
+ ]).mergeDeep(Map([
+ [to.rank, Map([[to.file, piece]])]
]));
return newBoard.mergeDeep(boardChange).toJS();
diff --git a/web/static/js/socket.js b/web/static/js/socket.js
new file mode 100644
index 0000000..0f8d461
--- /dev/null
+++ b/web/static/js/socket.js
@@ -0,0 +1,62 @@
+// NOTE: The contents of this file will only be executed if
+// you uncomment its entry in "web/static/js/app.js".
+
+// To use Phoenix channels, the first step is to import Socket
+// and connect at the socket path in "lib/my_app/endpoint.ex":
+import {Socket} from "phoenix"
+
+let socket = new Socket("/socket", {params: {token: window.userToken}})
+
+// When you connect, you'll often need to authenticate the client.
+// For example, imagine you have an authentication plug, `MyAuth`,
+// which authenticates the session and assigns a `:current_user`.
+// If the current user exists you can assign the user's token in
+// the connection for use in the layout.
+//
+// In your "web/router.ex":
+//
+// pipeline :browser do
+// ...
+// plug MyAuth
+// plug :put_user_token
+// end
+//
+// defp put_user_token(conn, _) do
+// if current_user = conn.assigns[:current_user] do
+// token = Phoenix.Token.sign(conn, "user socket", current_user.id)
+// assign(conn, :user_token, token)
+// else
+// conn
+// end
+// end
+//
+// Now you need to pass this token to JavaScript. You can do so
+// inside a script tag in "web/templates/layout/app.html.eex":
+//
+//
+//
+// You will need to verify the user token in the "connect/2" function
+// in "web/channels/user_socket.ex":
+//
+// def connect(%{"token" => token}, socket) do
+// # max_age: 1209600 is equivalent to two weeks in seconds
+// case Phoenix.Token.verify(socket, "user socket", token, max_age: 1209600) do
+// {:ok, user_id} ->
+// {:ok, assign(socket, :user, user_id)}
+// {:error, reason} ->
+// :error
+// end
+// end
+//
+// Finally, pass the token on connect as below. Or remove it
+// from connect if you don't care about authentication.
+
+socket.connect()
+
+// Now that you are connected, you can join channels with a topic:
+let channel = socket.channel("topic:subtopic", {})
+channel.join()
+ .receive("ok", resp => { console.log("Joined successfully", resp) })
+ .receive("error", resp => { console.log("Unable to join", resp) })
+
+export default socket
diff --git a/app/store/actions.js b/web/static/js/store/actions.js
similarity index 100%
rename from app/store/actions.js
rename to web/static/js/store/actions.js
diff --git a/app/store/default-state.js b/web/static/js/store/default-state.js
similarity index 100%
rename from app/store/default-state.js
rename to web/static/js/store/default-state.js
diff --git a/web/templates/layout/app.html.eex b/web/templates/layout/app.html.eex
new file mode 100644
index 0000000..0f02954
--- /dev/null
+++ b/web/templates/layout/app.html.eex
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+ Hello Chess!
+ ">
+
+
+
+