diff --git a/app/assets/javascripts/games.js.coffee b/app/assets/javascripts/games.js.coffee new file mode 100644 index 00000000..24f83d18 --- /dev/null +++ b/app/assets/javascripts/games.js.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/players.js.coffee b/app/assets/javascripts/players.js.coffee new file mode 100644 index 00000000..24f83d18 --- /dev/null +++ b/app/assets/javascripts/players.js.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/games.css.scss b/app/assets/stylesheets/games.css.scss new file mode 100644 index 00000000..3e6a7ecc --- /dev/null +++ b/app/assets/stylesheets/games.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Games controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/games_controller.rb b/app/controllers/games_controller.rb new file mode 100644 index 00000000..8e355b36 --- /dev/null +++ b/app/controllers/games_controller.rb @@ -0,0 +1,66 @@ +class GamesController < ApplicationController + before_action :set_game, only: [:show, :edit, :update, :destroy] + + respond_to :html + + def index + @games = Game.all + respond_with(@games) + end + + def show + respond_with(@game) + end + + def new + @game = Game.new + respond_with(@game) + end + + def edit + end + + def create + @game = Game.new(title: game_params[:title]) + if game_params[:player_ids] + game_params[:player_ids].each do |user_id| + @game.players.new(user_id: user_id) + end + end + @game.save + respond_with(@game) + end + + def update + @game.update(game_params) + respond_with(@game) + end + + def destroy + @game.destroy + respond_with(@game) + end + + def join + @game = Game.find(params[:game_id]) + @game.add_player_from_user(current_user) + flash[:notice] = "Successfully joined game..." + redirect_to(@game) + end + + def start + @game = Game.find(params[:game_id]) + @status = 'Game Started' if @game.is_startable? + redirect_to(@game) + end + + private + + def set_game + @game = Game.find(params[:id]) + end + + def game_params + params.require(:game).permit(:title, player_ids: []) + end +end diff --git a/app/controllers/players_controller.rb b/app/controllers/players_controller.rb new file mode 100644 index 00000000..56195a65 --- /dev/null +++ b/app/controllers/players_controller.rb @@ -0,0 +1,47 @@ +class PlayersController < ApplicationController + before_action :set_player, only: [:show, :edit, :update, :destroy] + + respond_to :html + + def index + @players = Player.all + respond_with(@players) + end + + def show + respond_with(@player) + end + + def new + @player = Player.new + respond_with(@player) + end + + def edit + end + + def create + @player = Player.new(player_params) + @player.save + respond_with(@player) + end + + def update + @player.update(player_params) + respond_with(@player) + end + + def destroy + @player.destroy + respond_with(@player) + end + + private + def set_player + @player = Player.find(params[:id]) + end + + def player_params + params.require(:player).permit(:game_id, :user_id) + end +end diff --git a/app/helpers/games_helper.rb b/app/helpers/games_helper.rb new file mode 100644 index 00000000..2ef8c1f4 --- /dev/null +++ b/app/helpers/games_helper.rb @@ -0,0 +1,2 @@ +module GamesHelper +end diff --git a/app/helpers/players_helper.rb b/app/helpers/players_helper.rb new file mode 100644 index 00000000..e8f775c0 --- /dev/null +++ b/app/helpers/players_helper.rb @@ -0,0 +1,2 @@ +module PlayersHelper +end diff --git a/app/models/game.rb b/app/models/game.rb new file mode 100644 index 00000000..2e286cdf --- /dev/null +++ b/app/models/game.rb @@ -0,0 +1,42 @@ +# The game class +class Game < ActiveRecord::Base + has_many :players, :dependent => :destroy + has_many :users, through: :players + accepts_nested_attributes_for :players + validates :title, presence: true + + def start + # Save the game to the database + save + # Start the game TODO + end + + def validate_startable + @player_count = players.size + errors[:notice] = 'Must be at least 2 players.' if @player_count < 2 + errors[:players] = 'Must be less than 5 players.' if @player_count > 4 + end + + def is_startable? + @player_count = players.size + @player_count.between?(2, 4) + end + + def add_player_from_user(user) + if can_accomodate(user) + Player.create(user: user, game: self) + end + end + + def can_accomodate(user) + !(already_has?(user) || full?) + end + + def already_has?(user) + users.include?(user) + end + + def full? + players.size >= 4 + end +end diff --git a/app/models/player.rb b/app/models/player.rb new file mode 100644 index 00000000..41a1f43d --- /dev/null +++ b/app/models/player.rb @@ -0,0 +1,10 @@ +# Associates users to games +class Player < ActiveRecord::Base + belongs_to :game + belongs_to :user + + has_many :player_cards + + validates_presence_of :user_id + validates_presence_of :game_id +end diff --git a/app/models/player_card.rb b/app/models/player_card.rb new file mode 100644 index 00000000..9aa2e152 --- /dev/null +++ b/app/models/player_card.rb @@ -0,0 +1,4 @@ +class PlayerCard < ActiveRecord::Base + belongs_to :player + belongs_to :card +end diff --git a/app/views/games/_form.html.haml b/app/views/games/_form.html.haml new file mode 100644 index 00000000..fa6aa96e --- /dev/null +++ b/app/views/games/_form.html.haml @@ -0,0 +1,9 @@ += simple_form_for(@game) do |f| + = f.error_notification + + .form-inputs + = f.input :title + = f.association :players, as: :select, collection: User.all.map {|u| u.id}, include_blank: false, include_hidden: false + + .form-actions + = f.button :submit diff --git a/app/views/games/edit.html.haml b/app/views/games/edit.html.haml new file mode 100644 index 00000000..3000f15c --- /dev/null +++ b/app/views/games/edit.html.haml @@ -0,0 +1,7 @@ +%h1 Editing game + += render 'form' + += link_to 'Show', @game +\| += link_to 'Back', games_path diff --git a/app/views/games/index.html.haml b/app/views/games/index.html.haml new file mode 100644 index 00000000..6a76c3b7 --- /dev/null +++ b/app/views/games/index.html.haml @@ -0,0 +1,21 @@ +%h1 Listing games + +%table + %thead + %tr + %th Title + %th + %th + %th + + %tbody + - @games.each do |game| + %tr + %td= game.title + %td= link_to 'Show', game + %td= link_to 'Edit', edit_game_path(game) + %td= link_to 'Destroy', game, :method => :delete, :data => { :confirm => 'Are you sure?' } + +%br + += link_to 'New Game', new_game_path diff --git a/app/views/games/index.json.jbuilder b/app/views/games/index.json.jbuilder new file mode 100644 index 00000000..e9ef30ab --- /dev/null +++ b/app/views/games/index.json.jbuilder @@ -0,0 +1,4 @@ +json.array!(@games) do |game| + json.extract! game, :id, :title + json.url game_url(game, format: :json) +end diff --git a/app/views/games/new.html.haml b/app/views/games/new.html.haml new file mode 100644 index 00000000..c47db8ac --- /dev/null +++ b/app/views/games/new.html.haml @@ -0,0 +1,5 @@ +%h1 New game + += render 'form' + += link_to 'Back', games_path diff --git a/app/views/games/show.html.haml b/app/views/games/show.html.haml new file mode 100644 index 00000000..887c95a3 --- /dev/null +++ b/app/views/games/show.html.haml @@ -0,0 +1,23 @@ +%p + %b Status: + = @status + +%p + %b Title: + = @game.title + +- if @game.can_accomodate(current_user) + = link_to 'Join', game_join_path(@game) + +- if @game.is_startable? && @game.already_has?(current_user) + = link_to 'Start', game_start_path(@game) + +%p + %b Players: + %ol + - @game.players.each do |player| + %li= player + += link_to 'Edit', edit_game_path(@game) +\| += link_to 'Back', games_path diff --git a/app/views/games/show.json.jbuilder b/app/views/games/show.json.jbuilder new file mode 100644 index 00000000..8eceebb7 --- /dev/null +++ b/app/views/games/show.json.jbuilder @@ -0,0 +1 @@ +json.extract! @game, :id, :title, :created_at, :updated_at diff --git a/app/views/layouts/_navigation_links.html.erb b/app/views/layouts/_navigation_links.html.erb index 24c35e5b..02f3bb9f 100644 --- a/app/views/layouts/_navigation_links.html.erb +++ b/app/views/layouts/_navigation_links.html.erb @@ -1,4 +1,5 @@ -<%# add navigation links to this file %> +
  • <%= link_to 'Home', root_path %>
  • +
  • <%= link_to 'Quick Play', quick_play_path %>
  • <% if user_signed_in? %>
  • <%= link_to 'Edit account', edit_user_registration_path %>
  • <%= link_to 'Sign out', destroy_user_session_path, :method=>'delete' %>
  • diff --git a/app/views/players/_form.html.haml b/app/views/players/_form.html.haml new file mode 100644 index 00000000..132b1b80 --- /dev/null +++ b/app/views/players/_form.html.haml @@ -0,0 +1,9 @@ += simple_form_for(@player) do |f| + = f.error_notification + + .form-inputs + = f.association :game + = f.association :user + + .form-actions + = f.button :submit diff --git a/app/views/players/edit.html.haml b/app/views/players/edit.html.haml new file mode 100644 index 00000000..02e155c0 --- /dev/null +++ b/app/views/players/edit.html.haml @@ -0,0 +1,7 @@ +%h1 Editing player + += render 'form' + += link_to 'Show', @player +\| += link_to 'Back', players_path diff --git a/app/views/players/index.html.haml b/app/views/players/index.html.haml new file mode 100644 index 00000000..d452b1ea --- /dev/null +++ b/app/views/players/index.html.haml @@ -0,0 +1,23 @@ +%h1 Listing players + +%table + %thead + %tr + %th Game + %th User + %th + %th + %th + + %tbody + - @players.each do |player| + %tr + %td= player.game + %td= player.user + %td= link_to 'Show', player + %td= link_to 'Edit', edit_player_path(player) + %td= link_to 'Destroy', player, :method => :delete, :data => { :confirm => 'Are you sure?' } + +%br + += link_to 'New Player', new_player_path diff --git a/app/views/players/index.json.jbuilder b/app/views/players/index.json.jbuilder new file mode 100644 index 00000000..25c840c6 --- /dev/null +++ b/app/views/players/index.json.jbuilder @@ -0,0 +1,4 @@ +json.array!(@players) do |player| + json.extract! player, :id, :game_id, :user_id + json.url player_url(player, format: :json) +end diff --git a/app/views/players/new.html.haml b/app/views/players/new.html.haml new file mode 100644 index 00000000..17c238b2 --- /dev/null +++ b/app/views/players/new.html.haml @@ -0,0 +1,5 @@ +%h1 New player + += render 'form' + += link_to 'Back', players_path diff --git a/app/views/players/show.html.haml b/app/views/players/show.html.haml new file mode 100644 index 00000000..80707d96 --- /dev/null +++ b/app/views/players/show.html.haml @@ -0,0 +1,12 @@ +%p#notice= notice + +%p + %b Game: + = @player.game +%p + %b User: + = @player.user + += link_to 'Edit', edit_player_path(@player) +\| += link_to 'Back', players_path diff --git a/app/views/players/show.json.jbuilder b/app/views/players/show.json.jbuilder new file mode 100644 index 00000000..6c93eff7 --- /dev/null +++ b/app/views/players/show.json.jbuilder @@ -0,0 +1 @@ +json.extract! @player, :id, :game_id, :user_id, :created_at, :updated_at diff --git a/app/views/visitors/quick-play.html.haml b/app/views/visitors/quick-play.html.haml new file mode 100644 index 00000000..4cd27870 --- /dev/null +++ b/app/views/visitors/quick-play.html.haml @@ -0,0 +1 @@ +%h1 Joining a game diff --git a/config/routes.rb b/config/routes.rb index 3b42a77a..c7f6d6a9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,14 @@ Rails.application.routes.draw do + resources :players + + resources :games do + get 'start' + get 'join' + end + mount Upmin::Engine => '/admin' root to: 'visitors#index' + get '/quick-play', to: 'visitors#quick-play' devise_for :users resources :users end diff --git a/db/migrate/20150104065001_create_games.rb b/db/migrate/20150104065001_create_games.rb new file mode 100644 index 00000000..ec7beb9e --- /dev/null +++ b/db/migrate/20150104065001_create_games.rb @@ -0,0 +1,9 @@ +class CreateGames < ActiveRecord::Migration + def change + create_table :games do |t| + t.string :title + + t.timestamps + end + end +end diff --git a/db/migrate/20150104203256_create_players.rb b/db/migrate/20150104203256_create_players.rb new file mode 100644 index 00000000..9d017d5b --- /dev/null +++ b/db/migrate/20150104203256_create_players.rb @@ -0,0 +1,10 @@ +class CreatePlayers < ActiveRecord::Migration + def change + create_table :players do |t| + t.references :game, index: true + t.references :user, index: true + + t.timestamps + end + end +end diff --git a/db/migrate/20150104203850_create_player_cards.rb b/db/migrate/20150104203850_create_player_cards.rb new file mode 100644 index 00000000..3478e459 --- /dev/null +++ b/db/migrate/20150104203850_create_player_cards.rb @@ -0,0 +1,10 @@ +class CreatePlayerCards < ActiveRecord::Migration + def change + create_table :player_cards do |t| + t.references :player, index: true + t.references :card, index: true + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index f0dbaa80..366b7519 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,11 +11,37 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150102000743) do +ActiveRecord::Schema.define(version: 20150104203850) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" + create_table "games", force: true do |t| + t.string "title" + t.datetime "created_at" + t.datetime "updated_at" + end + + create_table "player_cards", force: true do |t| + t.integer "player_id" + t.integer "card_id" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "player_cards", ["card_id"], name: "index_player_cards_on_card_id", using: :btree + add_index "player_cards", ["player_id"], name: "index_player_cards_on_player_id", using: :btree + + create_table "players", force: true do |t| + t.integer "game_id" + t.integer "user_id" + t.datetime "created_at" + t.datetime "updated_at" + end + + add_index "players", ["game_id"], name: "index_players_on_game_id", using: :btree + add_index "players", ["user_id"], name: "index_players_on_user_id", using: :btree + create_table "users", force: true do |t| t.string "email", default: "", null: false t.string "encrypted_password", default: "" diff --git a/spec/factories/games.rb b/spec/factories/games.rb new file mode 100644 index 00000000..e13770a2 --- /dev/null +++ b/spec/factories/games.rb @@ -0,0 +1,6 @@ +FactoryGirl.define do + factory :game do + title "MyString" + end + +end diff --git a/spec/factories/player_cards.rb b/spec/factories/player_cards.rb new file mode 100644 index 00000000..0260e24f --- /dev/null +++ b/spec/factories/player_cards.rb @@ -0,0 +1,7 @@ +FactoryGirl.define do + factory :player_card do + player nil +card nil + end + +end diff --git a/spec/factories/players.rb b/spec/factories/players.rb new file mode 100644 index 00000000..714788b4 --- /dev/null +++ b/spec/factories/players.rb @@ -0,0 +1,7 @@ +FactoryGirl.define do + factory :player do + game nil +user nil + end + +end diff --git a/spec/factories/users.rb b/spec/factories/users.rb index 250e115b..8c1b90d6 100644 --- a/spec/factories/users.rb +++ b/spec/factories/users.rb @@ -2,7 +2,7 @@ FactoryGirl.define do factory :user do confirmed_at Time.now name "Test User" - email "test@example.com" + sequence(:email) { |n| "test#{n}@example.com" } password "please123" trait :admin do diff --git a/spec/features/users/user_quick_play_spec.rb b/spec/features/users/user_quick_play_spec.rb new file mode 100644 index 00000000..cc90f952 --- /dev/null +++ b/spec/features/users/user_quick_play_spec.rb @@ -0,0 +1,23 @@ +include Warden::Test::Helpers +Warden.test_mode! + +# Feature: Quick play +# As a user +# I want to play a quick game +# So I can have fun as fast as possible +feature 'Quick play', :devise do + + # Scenario: User can join a game using the quick play link + # Given I am signed in + # When I click the Quick Play link + # Then I join a game + scenario 'user can join a quick play game' do + pending("#EXPECT USER TO JOIN FIRST OPEN QUICK PLAY GAME") + user = FactoryGirl.create(:user) + signin(user.email, user.password) + click_link 'Quick Play' + #EXPECT VISITOR TO JOIN FIRST OPEN QUICK PLAY TABLE + expect(fail) + end + +end diff --git a/spec/features/visitors/quick_play_spec.rb b/spec/features/visitors/quick_play_spec.rb new file mode 100644 index 00000000..1389235b --- /dev/null +++ b/spec/features/visitors/quick_play_spec.rb @@ -0,0 +1,19 @@ +# Feature: Quick play +# As a visitor +# I want to play a game +# So I can have fun without signing up +feature 'Quick play', :devise do + + # Scenario: Visitor can join a game using the quick play link + # Given I am not signed in + # When I click the Quick Play link + # Then I join a game + scenario 'visitor can join a quick play game' do + pending("#EXPECT VISITOR TO JOIN FIRST OPEN QUICK PLAY GAME") + visit root_path + click_link 'Quick Play' + #EXPECT VISITOR TO JOIN FIRST OPEN QUICK PLAY TABLE + expect(fail) + end + +end diff --git a/spec/models/game_spec.rb b/spec/models/game_spec.rb new file mode 100644 index 00000000..a98654aa --- /dev/null +++ b/spec/models/game_spec.rb @@ -0,0 +1,47 @@ +require 'rails_helper' + +RSpec.describe Game, type: :model do + before(:each) { @game = Game.new(title: 'Test Game') } + + subject { @game } + + it { should respond_to(:title) } + + it '#title returns a string' do + expect(@game.title).to match 'Test Game' + end + + describe 'validations' do + describe 'player_count' do + context 'when there are between 2 and 4 players' do + it 'is valid' do + @game.players.new + @game.players.new + expect(@game).to be_valid + end + end + + context 'when there are less than 2 players' do + it 'fails validation with too few players' do + expect(@game).to_not be_valid + expect(@game.errors[:players]) + .to include 'Must be at least 2 players.' + end + end + context 'when there are more than 4 players' do + it 'fails validation with too many players' do + 5.times do + @game.players.new + end + expect(@game).to_not be_valid + expect(@game.errors[:players]) + .to include 'Must be less than 5 players.' + end + end + end + end + + it 'Can not be started if game is invalid' do + expect(@game.start).to eq false + end +end diff --git a/spec/models/player_card_spec.rb b/spec/models/player_card_spec.rb new file mode 100644 index 00000000..40e6573e --- /dev/null +++ b/spec/models/player_card_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe PlayerCard, :type => :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/player_spec.rb b/spec/models/player_spec.rb new file mode 100644 index 00000000..b5778bca --- /dev/null +++ b/spec/models/player_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Player, :type => :model do + pending "add some examples to (or delete) #{__FILE__}" +end