Skip to content
This repository was archived by the owner on Feb 9, 2021. It is now read-only.
Merged
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
20 changes: 20 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Elixir CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-elixir/ for more details
version: 2
jobs:
build:
docker:
- image: elixir:1.5.2
- image: postgres:9.4.1
environment:
POSTGRES_USER: postgres
steps:
- checkout

# specify any bash command here prefixed with `run: `
- run: mix local.hex --force
- run: mix local.rebar
- run: mix deps.get
- run: mix ecto.create
- run: mix test
131 changes: 131 additions & 0 deletions .credo.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# This file contains the configuration for Credo and you are probably reading
# this after creating it with `mix credo.gen.config`.
#
# If you find anything wrong or unclear in this file, please report an
# issue on GitHub: https://github.com/rrrene/credo/issues
#
%{
#
# You can have as many configs as you like in the `configs:` field.
configs: [
%{
#
# Run any exec using `mix credo -C <name>`. If no exec name is given
# "default" is used.
#
name: "default",
#
# These are the files included in the analysis:
files: %{
#
# You can give explicit globs or simply directories.
# In the latter case `**/*.{ex,exs}` will be used.
#
included: ["lib/", "src/", "web/", "apps/"],
excluded: [~r"/_build/", ~r"/deps/"]
},
#
# If you create your own checks, you must specify the source files for
# them here, so they can be loaded by Credo before running the analysis.
#
requires: [],
#
# If you want to enforce a style guide and need a more traditional linting
# experience, you can change `strict` to `true` below:
#
strict: false,
#
# If you want to use uncolored output by default, you can change `color`
# to `false` below:
#
color: true,
#
# You can customize the parameters of any check by adding a second element
# to the tuple.
#
# To disable a check put `false` as second element:
#
# {Credo.Check.Design.DuplicatedCode, false}
#
checks: [
{Credo.Check.Consistency.ExceptionNames},
{Credo.Check.Consistency.LineEndings},
{Credo.Check.Consistency.ParameterPatternMatching},
{Credo.Check.Consistency.SpaceAroundOperators},
{Credo.Check.Consistency.SpaceInParentheses},
{Credo.Check.Consistency.TabsOrSpaces},

{Credo.Check.Design.AliasUsage, false},
{Credo.Check.Design.DuplicatedCode, excluded_macros: []},
{Credo.Check.Design.TagTODO, exit_status: 2},
{Credo.Check.Design.TagFIXME},

{Credo.Check.Readability.FunctionNames},
{Credo.Check.Readability.LargeNumbers},
{Credo.Check.Readability.MaxLineLength, false},
{Credo.Check.Readability.ModuleAttributeNames},
{Credo.Check.Readability.ModuleDoc},
{Credo.Check.Readability.ModuleNames},
{Credo.Check.Readability.ParenthesesOnZeroArityDefs},
{Credo.Check.Readability.ParenthesesInCondition},
{Credo.Check.Readability.PredicateFunctionNames},
{Credo.Check.Readability.PreferImplicitTry},
{Credo.Check.Readability.RedundantBlankLines},
{Credo.Check.Readability.StringSigils},
{Credo.Check.Readability.TrailingBlankLine},
{Credo.Check.Readability.TrailingWhiteSpace},
{Credo.Check.Readability.VariableNames},
{Credo.Check.Readability.Semicolons},
{Credo.Check.Readability.SpaceAfterCommas},

{Credo.Check.Refactor.DoubleBooleanNegation},
{Credo.Check.Refactor.CondStatements},
{Credo.Check.Refactor.CyclomaticComplexity},
{Credo.Check.Refactor.FunctionArity},
{Credo.Check.Refactor.LongQuoteBlocks},
{Credo.Check.Refactor.MatchInCondition},
{Credo.Check.Refactor.NegatedConditionsInUnless},
{Credo.Check.Refactor.NegatedConditionsWithElse},
{Credo.Check.Refactor.Nesting},
{Credo.Check.Refactor.PipeChainStart},
{Credo.Check.Refactor.UnlessWithElse},

{Credo.Check.Warning.BoolOperationOnSameValues},
{Credo.Check.Warning.ExpensiveEmptyEnumCheck},
{Credo.Check.Warning.IExPry},
{Credo.Check.Warning.IoInspect},
{Credo.Check.Warning.LazyLogging},
{Credo.Check.Warning.OperationOnSameValues},
{Credo.Check.Warning.OperationWithConstantResult},
{Credo.Check.Warning.UnusedEnumOperation},
{Credo.Check.Warning.UnusedFileOperation},
{Credo.Check.Warning.UnusedKeywordOperation},
{Credo.Check.Warning.UnusedListOperation},
{Credo.Check.Warning.UnusedPathOperation},
{Credo.Check.Warning.UnusedRegexOperation},
{Credo.Check.Warning.UnusedStringOperation},
{Credo.Check.Warning.UnusedTupleOperation},
{Credo.Check.Warning.RaiseInsideRescue},

# Controversial and experimental checks (opt-in, just remove `, false`)
#
{Credo.Check.Refactor.ABCSize, false},
{Credo.Check.Refactor.AppendSingleItem, false},
{Credo.Check.Refactor.VariableRebinding, false},
{Credo.Check.Warning.MapGetUnsafePass, false},
{Credo.Check.Consistency.MultiAliasImportRequireUse, false},

# Deprecated checks (these will be deleted after a grace period)
#
{Credo.Check.Readability.Specs, false},
{Credo.Check.Warning.NameRedeclarationByAssignment, false},
{Credo.Check.Warning.NameRedeclarationByCase, false},
{Credo.Check.Warning.NameRedeclarationByDef, false},
{Credo.Check.Warning.NameRedeclarationByFn, false},

# Custom checks can be created using `mix credo.gen.check`.
#
]
}
]
}
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Platform

[![CircleCI](https://circleci.com/gh/elixir-elm-tutorial/platform.svg?style=svg)](https://circleci.com/gh/elixir-elm-tutorial/platform)

This repository contains the source code for
[elixir-elm-tutorial.herokuapp.com](https://elixir-elm-tutorial.herokuapp.com),
which is the demo application from the
Expand All @@ -9,6 +11,7 @@ which is the demo application from the

- Elixir 1.5
- Phoenix 1.3
- Elm 0.18

## Setup

Expand All @@ -24,6 +27,7 @@ which is the demo application from the

- Run the test suite with `mix test`.
- Check for outdated dependencies with `mix hex.outdated`.
- Run static analysis with `mix credo --strict`.

## Deployment

Expand All @@ -33,4 +37,3 @@ This app is deployed to Heroku at https://elixir-elm-tutorial.herokuapp.com.

- Open a [GitHub Issue](https://github.com/elixir-elm-tutorial/platform/issues).
- Email me at `bijanbwb@gmail.com`.

9 changes: 9 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ config :platform,

# Configures the endpoint
config :platform, PlatformWeb.Endpoint,
instrumenters: [Appsignal.Phoenix.Instrumenter],
url: [host: "localhost"],
secret_key_base: "5WY2LoKih9wBLcSs1KJdSumLNMFDzgDYvhqwdZjFvSVgDDVIYjx+dHmikCho8/lm",
render_errors: [view: PlatformWeb.ErrorView, accepts: ~w(html json)],
Expand All @@ -22,6 +23,14 @@ config :logger, :console,
format: "$time $metadata[$level] $message\n",
metadata: [:request_id]

# AppSignal
config :phoenix, :template_engines,
eex: Appsignal.Phoenix.Template.EExEngine,
exs: Appsignal.Phoenix.Template.ExsEngine

config :platform, Platform.Repo,
loggers: [Appsignal.Ecto, Ecto.LogEntry]

# 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"
8 changes: 7 additions & 1 deletion lib/platform/accounts/player.ex
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
defmodule Platform.Accounts.Player do
@moduledoc """
Players equate to users on the platform. The player module allows for
creating player accounts that are usable for authentication, and once
players are authenticated they can play through the Elm front-end games
and track scores.
"""

use Ecto.Schema
import Ecto.Changeset
alias Platform.Accounts.Player
alias Platform.Products.Game
alias Platform.Products.Gameplay


schema "players" do
many_to_many :games, Game, join_through: Gameplay

Expand Down
4 changes: 4 additions & 0 deletions lib/platform/application.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
defmodule Platform.Application do
@moduledoc """
Phoenix OTP Application
"""

use Application

# See https://hexdocs.pm/elixir/Application.html
Expand Down
7 changes: 6 additions & 1 deletion lib/platform/products/game.ex
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
defmodule Platform.Products.Game do
@moduledoc """
This module allows for creating game metadata and records on the
platform's back-end, but all game development actually occurs in
the Elm front-end.
"""

use Ecto.Schema
import Ecto.Changeset
alias Platform.Products.Game
alias Platform.Products.Gameplay
alias Platform.Accounts.Player


schema "games" do
many_to_many :players, Player, join_through: Gameplay

Expand Down
8 changes: 8 additions & 0 deletions lib/platform/products/gameplay.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
defmodule Platform.Products.Gameplay do
@moduledoc """
Gameplays connect players and games. They allow for tracking individual
player score "attempts", and can be aggregated for a player's total score.

Player gameplay data should be accessible via `player.gameplays`, and game
data should be accessible via `game.gameplays`.
"""

use Ecto.Schema
import Ecto.Changeset
alias Platform.Products.Gameplay
Expand Down
18 changes: 15 additions & 3 deletions lib/platform_web/channels/score_channel.ex
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
defmodule PlatformWeb.ScoreChannel do
@moduledoc """
Track scores from Elm front-end games (via elm-phoenix-socket).

Channel joins can use each game's slug so this should be reusable for
different games that send a `player_score` value.

The "save_score" message should allow players to both save their score to
the database and also broadcast it to any other users connected to the
socket.
"""

use PlatformWeb, :channel
use Appsignal.Instrumentation.Decorators

alias Platform.Accounts
alias Platform.Products

def join("score:" <> game_slug, payload, socket) do
def join("score:" <> game_slug, _payload, socket) do
game = Products.get_game_by_slug!(game_slug)
socket = assign(socket, :game_id, game.id)
{:ok, socket}
end

def handle_in("save_score", %{"player_score" => player_score} = payload, socket) do
@decorate channel_action()
def handle_in("save_score", %{"player_score" => player_score}, socket) do
payload = %{
player_score: player_score,
game_id: socket.assigns.game_id,
Expand Down
2 changes: 1 addition & 1 deletion lib/platform_web/channels/user_socket.ex
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ defmodule PlatformWeb.UserSocket do
# See `Phoenix.Token` documentation for examples in
# performing token verification on connect.
def connect(%{"token" => token}, socket) do
case Phoenix.Token.verify(socket, "user socket", token, max_age: 1209600) do
case Phoenix.Token.verify(socket, "user socket", token, max_age: 1_209_600) do
{:ok, current_user_id} ->
socket = assign(socket, :player_id, current_user_id)
{:ok, socket}
Expand Down
1 change: 1 addition & 0 deletions lib/platform_web/endpoint.ex
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ defmodule PlatformWeb.Endpoint do
key: "_platform_key",
signing_salt: "ZlLKP/TE"

use Appsignal.Phoenix
plug PlatformWeb.Router

@doc """
Expand Down
29 changes: 29 additions & 0 deletions lib/platform_web/templates/layout/app.html.eex
Original file line number Diff line number Diff line change
Expand Up @@ -42,5 +42,34 @@
</div> <!-- /container -->
<script>window.userToken = encodeURIComponent("<%= assigns[:user_token] %>");</script>
<script src="<%= static_path(@conn, "/js/app.js") %>"></script>

<!-- Full Story Snippet -->
<script>
window['_fs_debug'] = false;
window['_fs_host'] = 'www.fullstory.com';
window['_fs_org'] = '3TRP3';
window['_fs_namespace'] = 'FS';
(function(m,n,e,t,l,o,g,y){
if (e in m && m.console && m.console.log) { m.console.log('FullStory namespace conflict. Please set window["_fs_namespace"].'); return;}
g=m[e]=function(a,b){g.q?g.q.push([a,b]):g._api(a,b);};g.q=[];
o=n.createElement(t);o.async=1;o.src='https://'+_fs_host+'/s/fs.js';
y=n.getElementsByTagName(t)[0];y.parentNode.insertBefore(o,y);
g.identify=function(i,v){g(l,{uid:i});if(v)g(l,v)};g.setUserVars=function(v){g(l,v)};
g.identifyAccount=function(i,v){o='account';v=v||{};v.acctId=i;g(o,v)};
g.clearUserCookie=function(c,d,i){if(!c || document.cookie.match('fs_uid=[`;`]*`[`;`]*`[`;`]*`')){
d=n.domain;while(1){n.cookie='fs_uid=;domain='+d+
';path=/;expires='+new Date(0).toUTCString();i=d.indexOf('.');if(i<0)break;d=d.slice(i+1)}}};
})(window,document,window['_fs_namespace'],'script','user');
</script>

<!-- Google Analytics -->
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-96476785-1', 'auto');
ga('send', 'pageview');
</script>
</body>
</html>
19 changes: 11 additions & 8 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ defmodule Platform.Mixfile do
[
app: :platform,
version: "0.0.1",
elixir: "~> 1.4",
elixir: "~> 1.5",
elixirc_paths: elixirc_paths(Mix.env),
compilers: [:phoenix, :gettext] ++ Mix.compilers,
start_permanent: Mix.env == :prod,
Expand All @@ -20,7 +20,7 @@ defmodule Platform.Mixfile do
def application do
[
mod: {Platform.Application, []},
extra_applications: [:logger, :runtime_tools]
extra_applications: [:appsignal, :logger, :runtime_tools]
]
end

Expand All @@ -33,16 +33,19 @@ defmodule Platform.Mixfile do
# Type `mix help deps` for examples and options.
defp deps do
[
{:appsignal, "~> 1.0"},
{:bcrypt_elixir, "~> 1.0"},
{:comeonin, "~> 4.0"},
{:cowboy, "~> 1.0"},
{:credo, "~> 0.8.0", only: [:dev, :test], runtime: false},
{:ex_machina, "~> 2.1", only: :test},
{:gettext, "~> 0.11"},
{:phoenix, "~> 1.3.0"},
{:phoenix_pubsub, "~> 1.0"},
{:phoenix_ecto, "~> 3.2"},
{:postgrex, ">= 0.0.0"},
{:phoenix_html, "~> 2.10"},
{:phoenix_live_reload, "~> 1.0", only: :dev},
{:gettext, "~> 0.11"},
{:cowboy, "~> 1.0"},
{:comeonin, "~> 4.0"},
{:bcrypt_elixir, "~> 0.12"}
{:phoenix_pubsub, "~> 1.0"},
{:postgrex, ">= 0.0.0"},
]
end

Expand Down
Loading