Add games to seats and implement tables
This commit is contained in:
parent
32194b11b9
commit
92f2806e31
160
Gemfile.lock
160
Gemfile.lock
@ -34,12 +34,10 @@ GEM
|
||||
minitest (~> 5.1)
|
||||
thread_safe (~> 0.1)
|
||||
tzinfo (~> 1.1)
|
||||
addressable (2.3.8)
|
||||
addressable (2.4.0)
|
||||
arel (5.0.1.20140414130214)
|
||||
ast (2.1.0)
|
||||
astrolabe (1.3.1)
|
||||
parser (~> 2.2)
|
||||
babel-source (5.8.26)
|
||||
ast (2.2.0)
|
||||
babel-source (5.8.35)
|
||||
babel-transpiler (0.7.0)
|
||||
babel-source (>= 4.0, < 6)
|
||||
execjs (~> 2.0)
|
||||
@ -62,25 +60,26 @@ GEM
|
||||
capistrano (>= 3.0.0)
|
||||
capistrano-postgresql (4.2.1)
|
||||
capistrano (>= 3.0)
|
||||
capistrano-rails (1.1.5)
|
||||
capistrano-rails (1.1.6)
|
||||
capistrano (~> 3.1)
|
||||
capistrano-bundler (~> 1.1)
|
||||
capistrano-rails-console (1.0.0)
|
||||
capistrano-rails-console (1.0.2)
|
||||
capistrano (>= 3.1.0, < 4.0.0)
|
||||
capistrano-rbenv (2.0.3)
|
||||
capistrano-rbenv (2.0.4)
|
||||
capistrano (~> 3.1)
|
||||
sshkit (~> 1.3)
|
||||
capistrano3-puma (1.2.1)
|
||||
capistrano (~> 3.0)
|
||||
puma (>= 2.6)
|
||||
capybara (2.5.0)
|
||||
capybara (2.6.2)
|
||||
addressable
|
||||
mime-types (>= 1.16)
|
||||
nokogiri (>= 1.3.3)
|
||||
rack (>= 1.0.0)
|
||||
rack-test (>= 0.5.4)
|
||||
xpath (~> 2.0)
|
||||
chartkick (1.4.1)
|
||||
childprocess (0.5.6)
|
||||
childprocess (0.5.9)
|
||||
ffi (~> 1.0, >= 1.0.11)
|
||||
cliver (0.3.2)
|
||||
coderay (1.1.0)
|
||||
@ -90,38 +89,37 @@ GEM
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.9.1.1)
|
||||
colorize (0.7.7)
|
||||
coffee-script-source (1.10.0)
|
||||
connection_pool (2.2.0)
|
||||
database_cleaner (1.5.1)
|
||||
debug_inspector (0.0.2)
|
||||
devise (3.5.2)
|
||||
devise (3.5.6)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 3.2.6, < 5)
|
||||
responders
|
||||
thread_safe (~> 0.1)
|
||||
warden (~> 1.2.3)
|
||||
devise_invitable (1.5.3)
|
||||
devise_invitable (1.5.5)
|
||||
actionmailer (>= 3.2.6, < 5)
|
||||
devise (>= 3.2.0)
|
||||
diff-lcs (1.2.5)
|
||||
dotenv (2.0.2)
|
||||
dotenv-rails (2.0.2)
|
||||
dotenv (= 2.0.2)
|
||||
railties (~> 4.0)
|
||||
dotenv (2.1.0)
|
||||
dotenv-rails (2.1.0)
|
||||
dotenv (= 2.1.0)
|
||||
railties (>= 4.0, < 5.1)
|
||||
em-websocket (0.5.1)
|
||||
eventmachine (>= 0.12.9)
|
||||
http_parser.rb (~> 0.6.0)
|
||||
erubis (2.7.0)
|
||||
eventmachine (1.0.8)
|
||||
eventmachine (1.0.9.1)
|
||||
execjs (2.6.0)
|
||||
factory_girl (4.5.0)
|
||||
activesupport (>= 3.0.0)
|
||||
factory_girl_rails (4.5.0)
|
||||
factory_girl_rails (4.6.0)
|
||||
factory_girl (~> 4.5.0)
|
||||
railties (>= 3.0.0)
|
||||
faker (1.5.0)
|
||||
faker (1.6.1)
|
||||
i18n (~> 0.5)
|
||||
ffi (1.9.10)
|
||||
formatador (0.2.5)
|
||||
@ -142,7 +140,7 @@ GEM
|
||||
guard-ctags-bundler (1.4.0)
|
||||
guard (>= 2.0)
|
||||
guard-compat (>= 0.1.0)
|
||||
guard-livereload (2.5.1)
|
||||
guard-livereload (2.5.2)
|
||||
em-websocket (~> 0.5)
|
||||
guard (~> 2.8)
|
||||
guard-compat (~> 1.0)
|
||||
@ -177,8 +175,8 @@ GEM
|
||||
hub (1.12.4)
|
||||
i18n (0.7.0)
|
||||
interception (0.5)
|
||||
jbuilder (2.3.2)
|
||||
activesupport (>= 3.0.0, < 5)
|
||||
jbuilder (2.4.1)
|
||||
activesupport (>= 3.0.0, < 5.1)
|
||||
multi_json (~> 1.2)
|
||||
jquery-rails (3.1.4)
|
||||
railties (>= 3.0, < 5.0)
|
||||
@ -189,37 +187,37 @@ GEM
|
||||
activesupport (>= 3.0.0)
|
||||
launchy (2.4.3)
|
||||
addressable (~> 2.3)
|
||||
listen (3.0.3)
|
||||
listen (3.0.6)
|
||||
rb-fsevent (>= 0.9.3)
|
||||
rb-inotify (>= 0.9)
|
||||
lumberjack (1.0.9)
|
||||
rb-inotify (>= 0.9.7)
|
||||
lumberjack (1.0.10)
|
||||
mail (2.6.3)
|
||||
mime-types (>= 1.16, < 3)
|
||||
method_source (0.8.2)
|
||||
mime-types (2.6.2)
|
||||
mini_portile (0.6.2)
|
||||
minitest (5.8.1)
|
||||
mime-types (2.99)
|
||||
mini_portile2 (2.0.0)
|
||||
minitest (5.8.4)
|
||||
multi_json (1.11.2)
|
||||
nenv (0.2.0)
|
||||
nenv (0.3.0)
|
||||
net-scp (1.2.1)
|
||||
net-ssh (>= 2.6.5)
|
||||
net-ssh (3.0.1)
|
||||
newrelic_rpm (3.14.1.311)
|
||||
nokogiri (1.6.6.2)
|
||||
mini_portile (~> 0.6.0)
|
||||
net-ssh (3.0.2)
|
||||
newrelic_rpm (3.14.3.313)
|
||||
nokogiri (1.6.7.2)
|
||||
mini_portile2 (~> 2.0.0.rc2)
|
||||
notiffany (0.0.8)
|
||||
nenv (~> 0.1)
|
||||
shellany (~> 0.0)
|
||||
orm_adapter (0.5.0)
|
||||
parser (2.2.3.0)
|
||||
ast (>= 1.1, < 3.0)
|
||||
pg (0.18.3)
|
||||
poltergeist (1.7.0)
|
||||
parser (2.3.0.5)
|
||||
ast (~> 2.2)
|
||||
pg (0.18.4)
|
||||
poltergeist (1.9.0)
|
||||
capybara (~> 2.1)
|
||||
cliver (~> 0.3.1)
|
||||
multi_json (~> 1.0)
|
||||
websocket-driver (>= 0.2.0)
|
||||
polyamorous (1.2.0)
|
||||
polyamorous (1.3.0)
|
||||
activerecord (>= 3.0)
|
||||
powerpack (0.1.1)
|
||||
pry (0.10.3)
|
||||
@ -231,8 +229,8 @@ GEM
|
||||
pry-rescue (1.4.2)
|
||||
interception (>= 0.5)
|
||||
pry
|
||||
puma (2.14.0)
|
||||
pundit (1.0.1)
|
||||
puma (2.16.0)
|
||||
pundit (1.1.0)
|
||||
activesupport (>= 3.0.0)
|
||||
quiet_assets (1.1.0)
|
||||
railties (>= 3.1, < 5.0)
|
||||
@ -251,14 +249,14 @@ GEM
|
||||
bundler (>= 1.3.0, < 2.0)
|
||||
railties (= 4.1.8)
|
||||
sprockets-rails (~> 2.0)
|
||||
rails_layout (1.0.26)
|
||||
rails_layout (1.0.29)
|
||||
railties (4.1.8)
|
||||
actionpack (= 4.1.8)
|
||||
activesupport (= 4.1.8)
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
rainbow (2.0.0)
|
||||
rake (10.4.2)
|
||||
rainbow (2.1.0)
|
||||
rake (10.5.0)
|
||||
ransack (1.7.0)
|
||||
actionpack (>= 3.0)
|
||||
activerecord (>= 3.0)
|
||||
@ -267,53 +265,53 @@ GEM
|
||||
polyamorous (~> 1.2)
|
||||
rb-fchange (0.0.6)
|
||||
ffi
|
||||
rb-fsevent (0.9.6)
|
||||
rb-inotify (0.9.5)
|
||||
rb-fsevent (0.9.7)
|
||||
rb-inotify (0.9.7)
|
||||
ffi (>= 0.5.0)
|
||||
rdoc (4.2.0)
|
||||
rdoc (4.2.2)
|
||||
json (~> 1.4)
|
||||
react-rails (1.3.2)
|
||||
react-rails (1.6.0)
|
||||
babel-transpiler (>= 0.7.0)
|
||||
coffee-script-source (~> 1.8)
|
||||
connection_pool
|
||||
execjs
|
||||
rails (>= 3.2)
|
||||
tilt
|
||||
react-rails-hot-loader (0.4.0)
|
||||
react-rails-hot-loader (0.5.0)
|
||||
em-websocket
|
||||
rails
|
||||
react-rails
|
||||
responders (1.1.2)
|
||||
railties (>= 3.2, < 4.2)
|
||||
rspec (3.3.0)
|
||||
rspec-core (~> 3.3.0)
|
||||
rspec-expectations (~> 3.3.0)
|
||||
rspec-mocks (~> 3.3.0)
|
||||
rspec-core (3.3.2)
|
||||
rspec-support (~> 3.3.0)
|
||||
rspec-expectations (3.3.1)
|
||||
rspec (3.4.0)
|
||||
rspec-core (~> 3.4.0)
|
||||
rspec-expectations (~> 3.4.0)
|
||||
rspec-mocks (~> 3.4.0)
|
||||
rspec-core (3.4.2)
|
||||
rspec-support (~> 3.4.0)
|
||||
rspec-expectations (3.4.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.3.0)
|
||||
rspec-mocks (3.3.2)
|
||||
rspec-support (~> 3.4.0)
|
||||
rspec-mocks (3.4.1)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.3.0)
|
||||
rspec-rails (3.3.3)
|
||||
rspec-support (~> 3.4.0)
|
||||
rspec-rails (3.4.2)
|
||||
actionpack (>= 3.0, < 4.3)
|
||||
activesupport (>= 3.0, < 4.3)
|
||||
railties (>= 3.0, < 4.3)
|
||||
rspec-core (~> 3.3.0)
|
||||
rspec-expectations (~> 3.3.0)
|
||||
rspec-mocks (~> 3.3.0)
|
||||
rspec-support (~> 3.3.0)
|
||||
rspec-support (3.3.0)
|
||||
rubocop (0.34.2)
|
||||
astrolabe (~> 1.3)
|
||||
parser (>= 2.2.2.5, < 3.0)
|
||||
rspec-core (~> 3.4.0)
|
||||
rspec-expectations (~> 3.4.0)
|
||||
rspec-mocks (~> 3.4.0)
|
||||
rspec-support (~> 3.4.0)
|
||||
rspec-support (3.4.1)
|
||||
rubocop (0.37.2)
|
||||
parser (>= 2.3.0.4, < 3.0)
|
||||
powerpack (~> 0.1)
|
||||
rainbow (>= 1.99.1, < 3.0)
|
||||
ruby-progressbar (~> 1.4)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (~> 0.3)
|
||||
ruby-progressbar (1.7.5)
|
||||
ruby_parser (3.7.1)
|
||||
ruby_parser (3.7.3)
|
||||
sexp_processor (~> 4.1)
|
||||
rubyzip (1.1.7)
|
||||
sass (3.2.19)
|
||||
@ -325,18 +323,18 @@ GEM
|
||||
sdoc (0.4.1)
|
||||
json (~> 1.7, >= 1.7.7)
|
||||
rdoc (~> 4.0)
|
||||
selenium-webdriver (2.48.1)
|
||||
selenium-webdriver (2.52.0)
|
||||
childprocess (~> 0.5)
|
||||
multi_json (~> 1.0)
|
||||
rubyzip (~> 1.0)
|
||||
websocket (~> 1.0)
|
||||
sexp_processor (4.6.0)
|
||||
sexp_processor (4.6.1)
|
||||
shellany (0.0.1)
|
||||
simple_form (3.2.0)
|
||||
actionpack (~> 4.0)
|
||||
activemodel (~> 4.0)
|
||||
simple_form (3.2.1)
|
||||
actionpack (> 4, < 5.1)
|
||||
activemodel (> 4, < 5.1)
|
||||
slop (3.6.0)
|
||||
spring (1.4.0)
|
||||
spring (1.6.3)
|
||||
spring-commands-rspec (1.0.4)
|
||||
spring (>= 0.9.1)
|
||||
sprockets (2.12.4)
|
||||
@ -348,8 +346,7 @@ GEM
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
sprockets (>= 2.8, < 4.0)
|
||||
sshkit (1.7.1)
|
||||
colorize (>= 0.7.0)
|
||||
sshkit (1.8.1)
|
||||
net-scp (>= 1.1.2)
|
||||
net-ssh (>= 2.8.0)
|
||||
sysexits (1.2.0)
|
||||
@ -363,6 +360,7 @@ GEM
|
||||
uglifier (2.7.2)
|
||||
execjs (>= 0.3.0)
|
||||
json (>= 1.8.0)
|
||||
unicode-display_width (0.3.1)
|
||||
upmin-admin (0.1.3)
|
||||
chartkick
|
||||
haml (>= 3.0.0)
|
||||
@ -371,10 +369,10 @@ GEM
|
||||
rails
|
||||
ransack
|
||||
sass-rails
|
||||
warden (1.2.3)
|
||||
warden (1.2.6)
|
||||
rack (>= 1.0)
|
||||
websocket (1.2.2)
|
||||
websocket-driver (0.6.2)
|
||||
websocket-driver (0.6.3)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.2)
|
||||
xpath (2.0.0)
|
||||
|
||||
@ -50,9 +50,10 @@ end
|
||||
# * zeus: 'zeus rspec' (requires the server to be started separately)
|
||||
# * 'just' rspec: 'rspec'
|
||||
|
||||
guard :rspec, cmd: "bundle exec spring rspec", all_after_pass: false, failed_mode: :focus do #
|
||||
guard :rspec, cmd: "bundle exec spring rspec", all_after_pass: false do#, failed_mode: :focus
|
||||
require "guard/rspec/dsl"
|
||||
dsl = Guard::RSpec::Dsl.new(self)
|
||||
notification :off
|
||||
|
||||
# Feel free to open issues for suggestions and improvements
|
||||
|
||||
|
||||
@ -38,6 +38,7 @@ end
|
||||
guard 'rails', host: '0.0.0.0' do
|
||||
watch('Gemfile.lock')
|
||||
watch(%r{^(config|lib)/.*})
|
||||
notification :off
|
||||
end
|
||||
|
||||
|
||||
|
||||
@ -4,6 +4,8 @@ Thirteen Tien Len
|
||||
TODO
|
||||
----
|
||||
|
||||
- Refactor seat.jsx buttons in to a <form>.
|
||||
|
||||
- 1st 2nd 3rd place messages. End game message.
|
||||
|
||||
- Tables to contain the games
|
||||
@ -36,6 +38,12 @@ first play
|
||||
|
||||
- Review attributions and license compliance
|
||||
|
||||
- TODO: Refactor guardfiles down to one file using `groups`
|
||||
https://github.com/guard/guard/wiki/Guardfile-examples
|
||||
|
||||
- TODO: Move game logic out of game model / controller to module in
|
||||
/lib/game_logic.rb e.g. https://github.com/adamof/rails-poker/tree/master/poker
|
||||
|
||||
## Proposed Schema
|
||||
|
||||
Table
|
||||
|
||||
@ -16,5 +16,5 @@
|
||||
//= require react
|
||||
//= require react_ujs
|
||||
//= require components
|
||||
//= require react-rails-hot-loader
|
||||
// require react-rails-hot-loader
|
||||
//= require_tree .
|
||||
|
||||
@ -57,7 +57,7 @@ function handleFlashMessagesHeader(node, xhr) {
|
||||
|
||||
$(document).ready(function() {
|
||||
var dummy = new Array();
|
||||
var flashDiv = React.render(<FlashMessages messages={dummy} />, $('#flash_messages')[0]);
|
||||
var flashDiv = ReactDOM.render(<FlashMessages messages={dummy} />, $('#flash_messages')[0]);
|
||||
|
||||
$(document).ajaxComplete(function(event, xhr, settings) {
|
||||
handleFlashMessagesHeader(flashDiv, xhr);
|
||||
|
||||
@ -5,10 +5,14 @@ var Game = React.createClass({
|
||||
title: React.PropTypes.string,
|
||||
players: React.PropTypes.array,
|
||||
id: React.PropTypes.number,
|
||||
url: React.PropTypes.string,
|
||||
play_to_beat_string: React.PropTypes.string
|
||||
},
|
||||
|
||||
url: function() {
|
||||
var url = "/games/" + this.props.id;
|
||||
return url;
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {
|
||||
data: null,
|
||||
@ -22,7 +26,7 @@ var Game = React.createClass({
|
||||
},
|
||||
|
||||
loadGameFromServer: function() {
|
||||
$.getJSON(this.props.url, function(result) {
|
||||
$.getJSON(this.url(), function(result) {
|
||||
if (this.isMounted()) {
|
||||
this.setState({ data: result });
|
||||
}
|
||||
@ -35,8 +39,9 @@ var Game = React.createClass({
|
||||
},
|
||||
|
||||
handlePlayHandSubmit: function(card_ids) {
|
||||
console.log("Hello from handlePlayHandSubmit()");
|
||||
$.ajax({
|
||||
url: this.props.url + '/play_hand',
|
||||
url: this.url() + '/play_hand',
|
||||
type: 'PATCH',
|
||||
dataType: 'json',
|
||||
data: card_ids,
|
||||
@ -45,14 +50,14 @@ var Game = React.createClass({
|
||||
this.loadGameFromServer();
|
||||
}.bind(this),
|
||||
error: function(xhr, status, err) {
|
||||
console.error(this.props.url, status, err.toString());
|
||||
console.error(this.url(), status, err.toString());
|
||||
}.bind(this)
|
||||
});
|
||||
},
|
||||
|
||||
handleStart: function() {
|
||||
$.ajax({
|
||||
url: this.props.url + '/start',
|
||||
url: this.url() + '/start',
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
complete: function (jqXHR, textStatus) {
|
||||
@ -69,7 +74,7 @@ var Game = React.createClass({
|
||||
|
||||
handleJoin: function() {
|
||||
$.ajax({
|
||||
url: this.props.url + '/join',
|
||||
url: this.url() + '/join',
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
complete: function (jqXHR, textStatus) {
|
||||
@ -98,7 +103,7 @@ var Game = React.createClass({
|
||||
:
|
||||
<section id='game-controls'>
|
||||
{/* TODO: If not joinable, show why. i.e. game full*/ }
|
||||
{this.state.data.is_joinable ? <button onClick={this.handleJoin}>Join</button> : null }
|
||||
{/* TODO: DEPRECATED use seat sit instead this.state.data.is_joinable ? <button onClick={this.handleJoin}>Join</button> : null */}
|
||||
{/* TODO: If not startable, show why. i.e. need more players */}
|
||||
<div id='start'>
|
||||
{this.state.data.is_started ? null : <button onClick={this.handleStart} disabled={this.state.data.is_startable ? null : "disabled" }>Start</button>}
|
||||
@ -108,6 +113,8 @@ var Game = React.createClass({
|
||||
|
||||
<PlayerList
|
||||
game={this.state.data}
|
||||
current_user={this.props.current_user}
|
||||
onSeatSubmit={this.props.onSeatSubmit}
|
||||
onPlayHandSubmit={this.handlePlayHandSubmit} />
|
||||
|
||||
{this.state.data.winner_player_id ? "Winner: " + this.state.data.winner_player_id : null}
|
||||
|
||||
@ -8,10 +8,10 @@ var Player = React.createClass({
|
||||
return (
|
||||
<div id={"player-" + this.props.id} className={"player " + (this.props.game.controlling_player_id == this.props.id ? 'has-control' : '')}>
|
||||
<div className='user-name'>{this.props.email || "Guest" + this.props.soft_token.substr(0,6)}</div>
|
||||
{this.props.game.is_started
|
||||
?
|
||||
{this.props.game.is_started ?
|
||||
<div>Inventory count: {this.props.inventory_count}</div>
|
||||
: null }
|
||||
: null }
|
||||
|
||||
|
||||
{/* Show player controls if player is owned by current_user */}
|
||||
{(this.props.soft_token == this.props.game.current_user_soft_token) && (this.props.game.is_started == true)
|
||||
|
||||
@ -4,6 +4,7 @@ var PlayerList = React.createClass({
|
||||
game_id: React.PropTypes.number
|
||||
},
|
||||
|
||||
/* TODO: DEPRECATED playerNodes */
|
||||
playerNodes: function() {
|
||||
var playerList = this;
|
||||
var playerNodes = this.props.game.players.map(function (player) {
|
||||
@ -23,15 +24,45 @@ var PlayerList = React.createClass({
|
||||
return (
|
||||
<div id="playerList">
|
||||
<div id="top">
|
||||
{this.playerNodes()[0]}
|
||||
<div className="game-seat position-1">
|
||||
{console.log("Hello from player_list.jsx")}
|
||||
{console.log(this.props.onPlayHandSubmit)}
|
||||
<Seat
|
||||
{...this.props.game.seats[0]}
|
||||
game={this.props.game}
|
||||
onPlayHandSubmit={this.props.onPlayHandSubmit}
|
||||
current_user={this.props.current_user}
|
||||
onSeatSubmit={this.props.onSeatSubmit} />
|
||||
</div>
|
||||
</div>
|
||||
<div id="mid">
|
||||
{this.playerNodes()[1]}
|
||||
<div className="game-seat position-2">
|
||||
<Seat
|
||||
{...this.props.game.seats[1]}
|
||||
game={this.props.game}
|
||||
onPlayHandSubmit={this.props.onPlayHandSubmit}
|
||||
current_user={this.props.current_user}
|
||||
onSeatSubmit={this.props.onSeatSubmit} />
|
||||
</div>
|
||||
<div id="play-to-beat">Hand to beat: {this.props.game.play_to_beat_string}</div>
|
||||
{this.playerNodes()[2]}
|
||||
<div className="game-seat position-3">
|
||||
<Seat
|
||||
{...this.props.game.seats[2]}
|
||||
game={this.props.game}
|
||||
onPlayHandSubmit={this.props.onPlayHandSubmit}
|
||||
current_user={this.props.current_user}
|
||||
onSeatSubmit={this.props.onSeatSubmit} />
|
||||
</div>
|
||||
</div>
|
||||
<div id="bot">
|
||||
{this.playerNodes()[3]}
|
||||
<div className="game-seat position-4">
|
||||
<Seat
|
||||
{...this.props.game.seats[3]}
|
||||
game={this.props.game}
|
||||
onPlayHandSubmit={this.props.onPlayHandSubmit}
|
||||
current_user={this.props.current_user}
|
||||
onSeatSubmit={this.props.onSeatSubmit} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -1,29 +1,68 @@
|
||||
var Seat = React.createClass({
|
||||
|
||||
onClick: function(e) {
|
||||
// TODO: Sit at table
|
||||
$.ajax({
|
||||
url: window.location.pathname + '/sit',
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
complete: function (jqXHR, textStatus) {
|
||||
// callback
|
||||
},
|
||||
success: function (data, textStatus, jqXHR) {
|
||||
// success callback
|
||||
},
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
// error callback
|
||||
}
|
||||
});
|
||||
isOccupied: function() {
|
||||
//TODO: create isOccupied logic
|
||||
return false;
|
||||
},
|
||||
|
||||
url: function() {
|
||||
var url = "/seats/" + this.props.id + ".json"
|
||||
return url;
|
||||
},
|
||||
|
||||
sitClick: function(e) {
|
||||
this.props.onSeatSubmit(this.props.id, 'sit');
|
||||
},
|
||||
|
||||
standClick: function(e) {
|
||||
this.props.onSeatSubmit(this.props.id, 'stand');
|
||||
},
|
||||
|
||||
isAvailable: function() {
|
||||
return this.props.user_display_name == null
|
||||
},
|
||||
|
||||
sitButtonStyle: function() {
|
||||
var style = {};
|
||||
if(null != this.props.user_id == this.props.current_user.id)
|
||||
style = { display: 'none'};
|
||||
if(this.props.user_soft_token == this.props.current_user.soft_token)
|
||||
style = { display: 'none'};
|
||||
return style;
|
||||
},
|
||||
|
||||
standButtonStyle: function() {
|
||||
var style = { display: 'none'};
|
||||
if(null != this.props.user_id == this.props.current_user.id)
|
||||
style = {};
|
||||
if(this.props.user_soft_token == this.props.current_user.soft_token)
|
||||
style = {};
|
||||
return style;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<button onClick={this.onClick}>
|
||||
Sit
|
||||
</button>
|
||||
<div className='seat'>
|
||||
<div className='seat-controls'>
|
||||
<button
|
||||
onClick={this.sitClick}
|
||||
style={ this.sitButtonStyle() }
|
||||
disabled={!this.isAvailable()} >
|
||||
Sit
|
||||
</button>
|
||||
<button
|
||||
onClick={this.standClick}
|
||||
style={ this.standButtonStyle() }>
|
||||
Stand
|
||||
</button>
|
||||
</div>
|
||||
{this.props.player ?
|
||||
<Player
|
||||
game={this.props.game}
|
||||
onPlayHandSubmit={this.props.onPlayHandSubmit}
|
||||
{...this.props.player} />
|
||||
: "Empty" }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
35
app/assets/javascripts/components/seat_list.js.jsx
Normal file
35
app/assets/javascripts/components/seat_list.js.jsx
Normal file
@ -0,0 +1,35 @@
|
||||
var SeatList = React.createClass({
|
||||
propTypes: {
|
||||
seats: React.PropTypes.array
|
||||
},
|
||||
|
||||
seatNodes: function() {
|
||||
var onSeatSubmit = this.props.onSeatSubmit;
|
||||
var current_user = this.props.current_user;
|
||||
var seatNodes = this.props.seats.map(function(seat){
|
||||
return (
|
||||
<Seat
|
||||
key={seat.id}
|
||||
id={seat.id}
|
||||
user_id={seat.user_id}
|
||||
user_soft_token={seat.user_soft_token}
|
||||
user_display_name={seat.user_display_name}
|
||||
current_user={current_user}
|
||||
onSeatSubmit={onSeatSubmit} />
|
||||
)
|
||||
})
|
||||
return seatNodes;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return (
|
||||
<div id="seatList">
|
||||
{this.seatNodes()[0]}
|
||||
{this.seatNodes()[1]}
|
||||
{this.seatNodes()[2]}
|
||||
{this.seatNodes()[3]}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
});
|
||||
@ -14,7 +14,7 @@ var Table = React.createClass({
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
loadTableFromServer: function() {
|
||||
$.getJSON(this.props.url, function(result) {
|
||||
if (this.isMounted()) {
|
||||
this.setState({ data: result });
|
||||
@ -22,6 +22,29 @@ var Table = React.createClass({
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
|
||||
componentDidMount: function() {
|
||||
this.loadTableFromServer();
|
||||
},
|
||||
|
||||
handleSeatSubmit: function(seat_id, action) {
|
||||
console.log("handleSeatSubmit");
|
||||
$.ajax({
|
||||
url: window.location.pathname + '/seats/' + seat_id + '/' + action,
|
||||
type: 'PATCH',
|
||||
dataType: 'json',
|
||||
success: function (data, textStatus, jqXHR) {
|
||||
// success callback
|
||||
console.log(data.message);
|
||||
this.loadTableFromServer();
|
||||
}.bind(this),
|
||||
error: function (jqXHR, textStatus, errorThrown) {
|
||||
// error callback
|
||||
console.log(data.message);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (!this.state.data) {
|
||||
return <div>Loading...</div>;
|
||||
@ -29,10 +52,8 @@ var Table = React.createClass({
|
||||
return (
|
||||
<div id={"table-" + this.state.data.id}>
|
||||
<h1>{this.state.data.title}</h1>
|
||||
<Seat />
|
||||
<Seat />
|
||||
<Seat />
|
||||
<Seat />
|
||||
{/* TODO: DEPRECATED seats are now rendered within games <SeatList current_user={this.state.data.current_user} seats={this.state.data.seats} onSeatSubmit={this.handleSeatSubmit} /> */}
|
||||
<Game id={this.state.data.current_game_id} current_user={this.state.data.current_user} onSeatSubmit={this.handleSeatSubmit} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
3
app/assets/javascripts/seat.js.coffee
Normal file
3
app/assets/javascripts/seat.js.coffee
Normal file
@ -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/
|
||||
3
app/assets/stylesheets/seat.css.scss
Normal file
3
app/assets/stylesheets/seat.css.scss
Normal file
@ -0,0 +1,3 @@
|
||||
// Place all the styles related to the Seat controller here.
|
||||
// They will automatically be included in application.css.
|
||||
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||
@ -4,11 +4,18 @@ class GamesController < ApplicationController
|
||||
respond_to :html, :json
|
||||
|
||||
def index
|
||||
@games = Game.all.order :id
|
||||
if params[:table_id]
|
||||
@table = Table.find(params[:table_id])
|
||||
@games = @table.games
|
||||
else
|
||||
@games = Game.all.order :id
|
||||
end
|
||||
respond_with(@games)
|
||||
end
|
||||
|
||||
def show
|
||||
#TODO: Implement standalone games, without a table?
|
||||
#redirect_to @game.table
|
||||
respond_with(@game)
|
||||
end
|
||||
|
||||
@ -98,7 +105,8 @@ class GamesController < ApplicationController
|
||||
def play_now
|
||||
# First joinable game or create new game
|
||||
@game = Game.play_now(current_user)
|
||||
@game.add_player_from_user current_user
|
||||
@seats = @game.seats.order(:position).map {|s| s.occupied? ? nil : s}
|
||||
@seats.compact.last.sit(current_user)
|
||||
redirect_to @game
|
||||
end
|
||||
|
||||
|
||||
85
app/controllers/seats_controller.rb
Normal file
85
app/controllers/seats_controller.rb
Normal file
@ -0,0 +1,85 @@
|
||||
class SeatsController < ApplicationController
|
||||
before_action :set_seat, only: [:show, :edit, :update, :destroy, :sit, :stand]
|
||||
|
||||
respond_to :html, :json
|
||||
|
||||
# GET /seats
|
||||
# GET /seats.json
|
||||
def index
|
||||
@seats = Seat.all
|
||||
end
|
||||
|
||||
# GET /seats/1
|
||||
# GET /seats/1.json
|
||||
def show
|
||||
end
|
||||
|
||||
# GET /seats/new
|
||||
def new
|
||||
@seat = Seat.new
|
||||
end
|
||||
|
||||
# GET /seats/1/edit
|
||||
def edit
|
||||
end
|
||||
|
||||
# POST /seats
|
||||
# POST /seats.json
|
||||
def create
|
||||
@seat = Seat.new(seat_params)
|
||||
|
||||
respond_to do |format|
|
||||
if @seat.save
|
||||
format.html { redirect_to @seat, notice: 'Seat was successfully created.' }
|
||||
format.json { render :show, status: :created, location: @seat }
|
||||
else
|
||||
format.html { render :new }
|
||||
format.json { render json: @seat.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# PATCH/PUT /seats/1
|
||||
# PATCH/PUT /seats/1.json
|
||||
def update
|
||||
respond_to do |format|
|
||||
if @seat.update(seat_params)
|
||||
format.html { redirect_to @seat, notice: 'Seat was successfully updated.' }
|
||||
format.json { render :show, status: :ok, location: @seat }
|
||||
else
|
||||
format.html { render :edit }
|
||||
format.json { render json: @seat.errors, status: :unprocessable_entity }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# DELETE /seats/1
|
||||
# DELETE /seats/1.json
|
||||
def destroy
|
||||
@seat.destroy
|
||||
respond_to do |format|
|
||||
format.html { redirect_to seats_url, notice: 'Seat was successfully destroyed.' }
|
||||
format.json { head :no_content }
|
||||
end
|
||||
end
|
||||
|
||||
# POST /seats/1
|
||||
def sit
|
||||
@message = @seat.sit current_user
|
||||
end
|
||||
|
||||
def stand
|
||||
@message = @seat.stand current_user
|
||||
end
|
||||
|
||||
private
|
||||
# Use callbacks to share common setup or constraints between actions.
|
||||
def set_seat
|
||||
@seat = Seat.find(params[:id])
|
||||
end
|
||||
|
||||
# Never trust parameters from the scary internet, only allow the white list through.
|
||||
def seat_params
|
||||
params.require(:seat).permit(:position, :table_id, :user_id)
|
||||
end
|
||||
end
|
||||
2
app/helpers/seat_helper.rb
Normal file
2
app/helpers/seat_helper.rb
Normal file
@ -0,0 +1,2 @@
|
||||
module SeatHelper
|
||||
end
|
||||
@ -1,6 +1,7 @@
|
||||
# The game class
|
||||
class Game < ActiveRecord::Base
|
||||
belongs_to :table
|
||||
has_many :seats
|
||||
has_many :players, dependent: :destroy
|
||||
has_many :users, through: :players
|
||||
has_many :player_cards, through: :players
|
||||
@ -8,10 +9,12 @@ class Game < ActiveRecord::Base
|
||||
has_one :controlling_player
|
||||
belongs_to :play_to_beat
|
||||
accepts_nested_attributes_for :players
|
||||
validates :title, presence: true
|
||||
#validates :title, presence: true
|
||||
after_create :add_table
|
||||
|
||||
# Returns the newest joinable game or creates a new game if none are found
|
||||
scope :play_now, lambda { |user|
|
||||
where(status:nil).joins("LEFT OUTER JOIN players ON players.game_id = games.id").group("games.id").having("count(players) < 4").first || Game.create(title: "Game " + user.soft_token[0..4])
|
||||
where(status:nil).joins("LEFT OUTER JOIN players ON players.game_id = games.id").group("games.id").having("count(players) < 4").last || Game.create(title: "Game " + user.soft_token[0..4])
|
||||
}
|
||||
|
||||
def start
|
||||
@ -72,14 +75,37 @@ class Game < ActiveRecord::Base
|
||||
new_player = self.players.create(user: user, soft_token: user.soft_token)
|
||||
end
|
||||
|
||||
def remove_player_from_user(user)
|
||||
return false unless self.has_user? user
|
||||
|
||||
if self.players.exists?(user_id: user.id)
|
||||
@player = self.players.find_by(user_id: user.id)
|
||||
elsif self.players.exists?(soft_token: user.soft_token)
|
||||
@player = self.players.find_by(soft_token: user.soft_token)
|
||||
end
|
||||
|
||||
if @player
|
||||
@player.destroy
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def can_accomodate(user)
|
||||
!(already_has?(user) || full?)
|
||||
end
|
||||
|
||||
#TODO: Which has_user? method is better? Probably has_user?.
|
||||
def already_has?(user)
|
||||
self.players.pluck(:user_id, :soft_token).flatten.compact.to_set.intersect? ([user.id, user.soft_token].to_set)
|
||||
end
|
||||
|
||||
def has_user? user
|
||||
return true if self.players.exists?(user_id: user.id)
|
||||
return true if self.players.exists?(soft_token: user.soft_token)
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
def full?
|
||||
players.count >= 4
|
||||
end
|
||||
@ -130,4 +156,12 @@ class Game < ActiveRecord::Base
|
||||
@inventory_counts.include? 0
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_table
|
||||
self.table ||= Table.create current_game_id: self.id
|
||||
self.seats = self.table.seats
|
||||
self.save
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@ -1,4 +1,52 @@
|
||||
class Seat < ActiveRecord::Base
|
||||
belongs_to :table
|
||||
belongs_to :game
|
||||
belongs_to :user
|
||||
|
||||
def sit(current_user)
|
||||
if self.occupied?
|
||||
return "Error. Seat is occupied."
|
||||
else
|
||||
# Stand up from any other seats at this table
|
||||
current_user.stand_from(self.table)
|
||||
|
||||
self.user_id = current_user.id
|
||||
self.user_soft_token = current_user.soft_token
|
||||
self.save
|
||||
|
||||
self.table.current_game.add_player_from_user(current_user)
|
||||
return "Successfully sat."
|
||||
end
|
||||
end
|
||||
|
||||
def stand(current_user)
|
||||
unless self.occupied_by?(current_user)
|
||||
return "Error. Seat not occupied by you."
|
||||
else
|
||||
self.user_id = nil
|
||||
self.user_soft_token = nil
|
||||
self.save
|
||||
|
||||
self.table.current_game.remove_player_from_user(current_user)
|
||||
return "Successfully stood up."
|
||||
end
|
||||
end
|
||||
|
||||
def occupied?
|
||||
self.user_id.present? || self.user_soft_token.present?
|
||||
end
|
||||
|
||||
def occupied_by? user
|
||||
if user.id.present?
|
||||
self.user_id == user.id
|
||||
end
|
||||
if user.soft_token.present?
|
||||
self.user_soft_token == user.soft_token
|
||||
end
|
||||
end
|
||||
|
||||
def user_display_name
|
||||
return nil unless self.occupied?
|
||||
self.user.try(:email) || "Guest" + self.user_soft_token.to_s[0..4]
|
||||
end
|
||||
end
|
||||
|
||||
@ -3,5 +3,28 @@ class Table < ActiveRecord::Base
|
||||
has_many :seats
|
||||
has_many :users, through: :seats
|
||||
has_many :games
|
||||
has_one :current_game, :class_name => "Game", :primary_key => "current_game_id"
|
||||
#TODO: Deprecated current_game_id from database.
|
||||
# Instead calculate with self.current_game
|
||||
#
|
||||
#has_one :current_game, :class_name => "Game", :primary_key => "current_game_id"
|
||||
|
||||
#TODO: Should these be part of Game initialize instead of callback?
|
||||
after_create :add_seats
|
||||
after_create :add_game
|
||||
|
||||
def current_game
|
||||
self.games.last
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def add_seats
|
||||
1.upto(4) do |i|
|
||||
self.seats.create(position: i)
|
||||
end
|
||||
end
|
||||
|
||||
def add_game
|
||||
self.games.create if self.current_game_id.blank?
|
||||
end
|
||||
end
|
||||
|
||||
@ -16,6 +16,25 @@ class User < ActiveRecord::Base
|
||||
!soft_user?
|
||||
end
|
||||
|
||||
def display_name
|
||||
self.email || "Guest#{self.soft_token[0..5]}"
|
||||
end
|
||||
|
||||
def stand_from(table)
|
||||
@seats = []
|
||||
if self.id
|
||||
@seats << table.seats.where(user_id: self.id)
|
||||
end
|
||||
if self.soft_token
|
||||
@seats << table.seats.where(user_soft_token: self.soft_token)
|
||||
end
|
||||
@seats.flatten.each do |seat|
|
||||
seat.user_id = nil
|
||||
seat.user_soft_token = nil
|
||||
seat.save
|
||||
end
|
||||
end
|
||||
|
||||
# Include default devise modules. Others available are:
|
||||
# :confirmable, :lockable, :timeoutable and :omniauthable
|
||||
devise :invitable, :database_authenticatable, :registerable, # :confirmable,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
#game-component-container-container= react_component 'Game', props = {url: "#{game_path @game}"}, html_options = {id: "game-component-container"}
|
||||
#game-component-container-container= react_component 'Game', props = {id: @game.id, current_user: current_user}, html_options = {id: "game-component-container"}
|
||||
|
||||
-# TODO: Remove deprecated commented out code
|
||||
-#%p
|
||||
|
||||
@ -13,14 +13,40 @@ json.is_startable @game.startable?
|
||||
json.current_user_id current_user.try(:id)
|
||||
json.current_user_soft_token current_user.try(:soft_token)
|
||||
|
||||
json.players @game.players do |player|
|
||||
json.id player.id
|
||||
json.email player.user.try(:email)
|
||||
json.inventory_count player.inventory.count
|
||||
json.user_id player.user.try(:id)
|
||||
json.soft_token player.soft_token
|
||||
#json.players @game.players do |player|
|
||||
# json.id player.id
|
||||
# json.email player.user.try(:email)
|
||||
# json.inventory_count player.inventory.count
|
||||
# json.user_id player.user.try(:id)
|
||||
# json.soft_token player.soft_token
|
||||
#
|
||||
# if player.soft_token == current_user.soft_token
|
||||
# json.inventory player.inventory
|
||||
# end
|
||||
#end
|
||||
|
||||
if player.soft_token == current_user.soft_token
|
||||
json.inventory player.inventory
|
||||
json.seats @game.seats.order(:position) do |seat|
|
||||
json.id seat.id
|
||||
json.table_id seat.table_id
|
||||
json.position seat.position
|
||||
json.user_id seat.user_id
|
||||
json.user_soft_token seat.user_soft_token
|
||||
json.user_display_name seat.user_display_name
|
||||
|
||||
json.set! :player do
|
||||
@player = nil
|
||||
if !seat.user_id.blank?
|
||||
@player = @game.players.find_by(user_id: seat.user_id)
|
||||
elsif !seat.user_soft_token.blank?
|
||||
@player = @game.players.find_by(soft_token: seat.user_soft_token)
|
||||
end
|
||||
if @player
|
||||
json.id @player.id
|
||||
json.email @player.user.try(:email)
|
||||
json.inventory_count @player.inventory.count
|
||||
json.user_id @player.user_id
|
||||
json.soft_token @player.soft_token
|
||||
json.inventory @player.inventory if @player.soft_token == current_user.soft_token
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
1
app/views/seats/show.json.jbuilder
Normal file
1
app/views/seats/show.json.jbuilder
Normal file
@ -0,0 +1 @@
|
||||
json.extract! @seat, :id, :table_id, :user_id, :user_soft_token
|
||||
1
app/views/seats/sit.json.jbuilder
Normal file
1
app/views/seats/sit.json.jbuilder
Normal file
@ -0,0 +1 @@
|
||||
json.message @message
|
||||
1
app/views/seats/stand.json.jbuilder
Normal file
1
app/views/seats/stand.json.jbuilder
Normal file
@ -0,0 +1 @@
|
||||
json.message @message
|
||||
@ -1 +1,13 @@
|
||||
json.extract! @table, :id, :title, :created_at, :updated_at
|
||||
|
||||
json.current_game_id @table.current_game.id
|
||||
json.current_user current_user
|
||||
|
||||
json.seats @table.seats.order(:position) do |seat|
|
||||
json.id seat.id
|
||||
json.table_id seat.table_id
|
||||
json.position seat.position
|
||||
json.user_id seat.user_id
|
||||
json.user_soft_token seat.user_soft_token
|
||||
json.user_display_name seat.user_display_name
|
||||
end
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
if Rails.env.development?
|
||||
React::Rails::HotLoader.start()
|
||||
#React::Rails::HotLoader.start()
|
||||
end
|
||||
|
||||
@ -1,7 +1,12 @@
|
||||
Rails.application.routes.draw do
|
||||
resources :players
|
||||
resources :tables do
|
||||
resources :seats
|
||||
resources :seats do
|
||||
member do
|
||||
patch 'sit'
|
||||
patch 'stand'
|
||||
end
|
||||
end
|
||||
resources :games
|
||||
end
|
||||
|
||||
|
||||
5
db/migrate/20160209120734_add_position_to_seats.rb
Normal file
5
db/migrate/20160209120734_add_position_to_seats.rb
Normal file
@ -0,0 +1,5 @@
|
||||
class AddPositionToSeats < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :seats, :position, :integer
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,5 @@
|
||||
class AddUserSoftTokenToSeats < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :seats, :user_soft_token, :string
|
||||
end
|
||||
end
|
||||
5
db/migrate/20160219012611_add_game_id_to_seats.rb
Normal file
5
db/migrate/20160219012611_add_game_id_to_seats.rb
Normal file
@ -0,0 +1,5 @@
|
||||
class AddGameIdToSeats < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :seats, :game_id, :integer
|
||||
end
|
||||
end
|
||||
@ -11,7 +11,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20151231035240) do
|
||||
ActiveRecord::Schema.define(version: 20160219012611) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
@ -76,6 +76,9 @@ ActiveRecord::Schema.define(version: 20151231035240) do
|
||||
t.integer "user_id"
|
||||
t.datetime "created_at"
|
||||
t.datetime "updated_at"
|
||||
t.integer "position"
|
||||
t.string "user_soft_token"
|
||||
t.integer "game_id"
|
||||
end
|
||||
|
||||
add_index "seats", ["table_id"], name: "index_seats_on_table_id", using: :btree
|
||||
|
||||
@ -1,7 +1,10 @@
|
||||
FactoryGirl.define do
|
||||
factory :seat do
|
||||
table { FactoryGirl.create(:table) }
|
||||
user { FactoryGirl.create(:user) }
|
||||
end
|
||||
|
||||
trait :occupied do
|
||||
user { FactoryGirl.create(:user) }
|
||||
user_soft_token { user.soft_token }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
FactoryGirl.define do
|
||||
factory :table do
|
||||
sequence(:title) { |n| "Test Table #{n}" }
|
||||
current_game { FactoryGirl.create(:game) }
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@ -3,7 +3,7 @@ FactoryGirl.define do
|
||||
confirmed_at Time.now
|
||||
name 'Test User'
|
||||
sequence(:email) { |n| "test#{n}@example.com" }
|
||||
password 'please123'
|
||||
password 'password'
|
||||
sequence(:soft_token) { |n| "soft_token#{n}" }
|
||||
|
||||
trait :admin do
|
||||
|
||||
@ -2,31 +2,68 @@
|
||||
# As a visitor
|
||||
# I want to quickly join a game
|
||||
# So I can play
|
||||
feature 'Play now', :devise do
|
||||
before { @game = FactoryGirl.create :game }
|
||||
feature 'Play now', :devise, js: true do
|
||||
# Scenario: Visitor can play now
|
||||
# Given I am not signed in
|
||||
# When I click Play Now
|
||||
# Then I join a game
|
||||
scenario 'visitor can join a game', js: true do
|
||||
visit root_path
|
||||
click_link 'Play Now'
|
||||
expect(page).to have_content "Test Game"
|
||||
expect(current_path).to eq game_path(@game)
|
||||
@player = @game.players.last
|
||||
@playerComponent = page.find("#player-1")
|
||||
expect(@playerComponent.text).to have_content("Guest#{@player.soft_token[0..5]}")
|
||||
describe 'visitor can Play Now' do
|
||||
context 'there are no joinable games' do
|
||||
before do
|
||||
expect(Game.all.count).to eq 0
|
||||
visit root_path
|
||||
click_link 'Play Now'
|
||||
end
|
||||
it 'creates and joins a new game' do
|
||||
#TODO: Fix race condition
|
||||
expect(page).to have_content "Hand to beat"
|
||||
#Game title derived from visitor soft_token
|
||||
expect(page).to have_content "#{Game.last.players.first.soft_token[0..4]}"
|
||||
#Game count increased by 1
|
||||
expect(Game.all.count).to eq 1
|
||||
end
|
||||
end
|
||||
|
||||
# Add another player
|
||||
@game.add_player_from_user FactoryGirl.create :user
|
||||
#TODO: Remove race condition
|
||||
visit current_path
|
||||
click_button 'Start'
|
||||
#TODO: Remove race condition
|
||||
visit current_path
|
||||
visit current_path
|
||||
visit current_path
|
||||
visit current_path
|
||||
expect(page).to have_content 'Inventory'
|
||||
context 'visitor joins an existing game' do
|
||||
before do
|
||||
FactoryGirl.create :game
|
||||
@game = Game.last
|
||||
visit root_path
|
||||
expect(Game.all.count).to eq 1
|
||||
click_link 'Play Now'
|
||||
end
|
||||
|
||||
it 'shows and starts the game' do
|
||||
#TODO: Deprecated it shows the game title
|
||||
#expect(page).to have_content "Test Game"
|
||||
expect(page).to have_content "Hand to beat"
|
||||
#it 'is located at the correct url' do
|
||||
expect(current_path).to eq game_path(@game)
|
||||
|
||||
#it 'shows the visitor as a player' do
|
||||
@player = @game.players.last
|
||||
@playerComponent = page.find("#player-1")
|
||||
#TODO: have_content @player.display_name
|
||||
expect(@playerComponent.text).to have_content("Guest#{@player.soft_token[0..5]}")
|
||||
|
||||
#it 'shows other players' do
|
||||
# Add another player
|
||||
@user2 = FactoryGirl.create :user
|
||||
@seats = @game.table.seats.map { |s| s.occupied? ? nil : s}
|
||||
@seats.compact.first.sit(@user2)
|
||||
#TODO: Remove race condition
|
||||
visit current_path
|
||||
expect(page).to have_content @user2.display_name
|
||||
#it 'the game is startable' do
|
||||
click_button 'Start'
|
||||
#TODO: Remove race condition
|
||||
visit current_path
|
||||
visit current_path
|
||||
visit current_path
|
||||
visit current_path
|
||||
expect(page).to have_content 'Inventory'
|
||||
expect(Game.all.count).to eq 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -7,7 +7,7 @@ feature 'Play a hand', type: :feature, js: true do
|
||||
@game = setup_game
|
||||
@game.reload
|
||||
@player = @game.controlling_player
|
||||
signin(@player.user.email,'please123')
|
||||
signin(@player.user.email,'password')
|
||||
#login_as(@player.user, scope: :user)
|
||||
visit game_path @game
|
||||
end
|
||||
@ -228,4 +228,17 @@ feature 'Play a hand', type: :feature, js: true do
|
||||
expect(page).to have_content 'Hand to beat: None'
|
||||
end
|
||||
|
||||
# Scenario: Player wins the game
|
||||
# Given I am the active player
|
||||
# And I play my last card(s)
|
||||
# And I am the first player to do so
|
||||
# Then I see a 1st place winner message
|
||||
scenario 'first place winner' do
|
||||
# play last card
|
||||
pending "TBD"
|
||||
expect(@player.inventory.count).to eq 0
|
||||
expect(page).to have_content "Winner: #{@player.display_name}"
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
108
spec/features/seats/seats_sit_spec.rb
Normal file
108
spec/features/seats/seats_sit_spec.rb
Normal file
@ -0,0 +1,108 @@
|
||||
# Feature: Seats
|
||||
# As a visitor
|
||||
# I want to sit in a seat
|
||||
# So I can be at a table and play a game
|
||||
feature 'Seats', :devise, js: true do
|
||||
context "As a visitor" do
|
||||
before(:each) do
|
||||
@table = FactoryGirl.create :table
|
||||
visit table_path @table
|
||||
end
|
||||
|
||||
it 'visitor can sit in an empty seat' do
|
||||
click_button 'Sit', match: :first
|
||||
expect(page).to have_content "Guest"
|
||||
end
|
||||
|
||||
it 'sit button is disabled for unavailable seats' do
|
||||
@button = page.find_button('Sit', match: :first)
|
||||
@button.click
|
||||
expect(page).to have_content "Guest"
|
||||
expect(@button.disabled?).to eq true
|
||||
end
|
||||
|
||||
it 'has a stand button if current user is seated' do
|
||||
@button = page.find_button('Sit', match: :first)
|
||||
@button.click
|
||||
expect(page).to have_content "Guest"
|
||||
@buttons = page.find_all('button', text: 'Stand')
|
||||
expect(@buttons.length).to eq 1
|
||||
end
|
||||
|
||||
scenario 'table seats match up with game seats' do
|
||||
click_button 'Sit', match: :first
|
||||
expect(page.find(:css, ".game-seat.position-2")).to have_content "Guest"
|
||||
end
|
||||
|
||||
#TODO: User can only occupy one seat per table
|
||||
scenario 'visitor changes seats' do
|
||||
click_button 'Sit', match: :first
|
||||
expect(page).to have_content "Guest"
|
||||
#TODO: Fix race condition
|
||||
sleep 3
|
||||
click_button 'Sit', match: :first
|
||||
expect(page).to have_content "Guest"
|
||||
expect(page.text.scan('Guest').count).to eq 2
|
||||
end
|
||||
|
||||
scenario 'visitor can join the current game' do
|
||||
click_button 'Sit', match: :first
|
||||
#TODO: Fix race condition. Trigger game react component reload when player joins a table
|
||||
sleep 4
|
||||
expect(page).to have_content "Guest"
|
||||
expect(page.text.scan('Guest').count).to eq 2
|
||||
end
|
||||
|
||||
scenario 'visitor can leave the current game' do
|
||||
click_button 'Sit', match: :first
|
||||
#TODO: Fix race condition
|
||||
sleep 4
|
||||
expect(page.text.scan('Guest').count).to eq 2
|
||||
click_button 'Stand', match: :first
|
||||
expect(page).to have_content "Sit"
|
||||
#TODO: Fix race condition
|
||||
sleep 4
|
||||
expect(page.text.scan('Guest').count).to eq 0
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "As a user" do
|
||||
before(:each) do
|
||||
@table = FactoryGirl.create :table
|
||||
@user = FactoryGirl.create :user
|
||||
signin(@user.email, @user.password)
|
||||
visit table_path @table
|
||||
end
|
||||
|
||||
scenario 'user can sit at the table' do
|
||||
#TODO: Fix race condition
|
||||
expect(page).to have_content "Sit"
|
||||
click_button 'Sit', match: :first
|
||||
expect(page).to have_content @user.email
|
||||
end
|
||||
|
||||
scenario 'user can join the current game' do
|
||||
expect(page).to have_content "Sit"
|
||||
click_button 'Sit', match: :first
|
||||
#TODO: Fix race condition. Trigger game react component reload when player joins a table
|
||||
sleep 4
|
||||
expect(page).to have_content @user.email
|
||||
expect(page.text.scan(@user.email).count).to eq 2
|
||||
end
|
||||
|
||||
scenario 'user can leave the current game' do
|
||||
expect(page).to have_content "Sit"
|
||||
click_button 'Sit', match: :first
|
||||
#TODO: Fix race condition
|
||||
sleep 4
|
||||
expect(page.text.scan(@user.email).count).to eq 2
|
||||
click_button 'Stand', match: :first
|
||||
expect(page).to have_content "Sit"
|
||||
#TODO: Fix race condition
|
||||
sleep 4
|
||||
expect(page.text.scan(@user.email).count).to eq 0
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@ -26,7 +26,7 @@ feature 'Table index', :devise do
|
||||
# Then I see an index of tables
|
||||
scenario 'I can see all the tables', js: true do
|
||||
@user = FactoryGirl.create :user
|
||||
signin(@user.email,'please123')
|
||||
signin(@user.email,'password')
|
||||
|
||||
5.times { FactoryGirl.create :table }
|
||||
visit root_path
|
||||
|
||||
@ -9,33 +9,70 @@ feature 'Table show', :devise do
|
||||
visit tables_path
|
||||
click_link @table.title
|
||||
end
|
||||
# Scenario: Visitor can view a table
|
||||
# Given I am not signed in
|
||||
# When I visit a table
|
||||
# Then I see the table attributes
|
||||
scenario 'visitor can see the table', js: true do
|
||||
expect(page).to have_selector "#table-#{@table.id} > h1", @table.title
|
||||
end
|
||||
|
||||
scenario 'visitor can sit at the table', js: true do
|
||||
click_button 'Sit'
|
||||
click_button 'Sit', match: :first
|
||||
expect(page).to have_content "Guest"
|
||||
end
|
||||
|
||||
scenario 'visitor can join the current game', js: true do
|
||||
click_button 'Sit', match: :first
|
||||
#TODO: Fix race condition. Trigger game react component reload when player joins a table
|
||||
sleep 4
|
||||
expect(page).to have_content "Guest"
|
||||
expect(page.text.scan('Guest').count).to eq 2
|
||||
end
|
||||
|
||||
scenario 'visitor can leave the current game', js: true do
|
||||
click_button 'Sit', match: :first
|
||||
#TODO: Fix race condition
|
||||
sleep 4
|
||||
expect(page.text.scan('Guest').count).to eq 2
|
||||
click_button 'Stand', match: :first
|
||||
expect(page).to have_content "Sit"
|
||||
#TODO: Fix race condition
|
||||
sleep 4
|
||||
expect(page.text.scan('Guest').count).to eq 0
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context "As a user" do
|
||||
# Scenario: User can view a table
|
||||
# Given I am signed in
|
||||
# When I click a table
|
||||
# Then I see an index of tables
|
||||
scenario 'visitor can see the table', js: true do
|
||||
before(:each) do
|
||||
@table = FactoryGirl.create :table
|
||||
# TODO: sign in
|
||||
visit tables_path
|
||||
click_link @table.title
|
||||
expect(page).to have_button "Sit"
|
||||
expect(page).to have_selector "#table-#{@table.id} > .title", @table.title
|
||||
expect(current_path).to eq table_path(@table)
|
||||
@user = FactoryGirl.create :user
|
||||
signin(@user.email, @user.password)
|
||||
visit table_path @table
|
||||
end
|
||||
|
||||
scenario 'user can sit at the table', js: true do
|
||||
#TODO: Fix race condition
|
||||
expect(page).to have_content "Sit"
|
||||
click_button 'Sit', match: :first
|
||||
expect(page).to have_content @user.email
|
||||
end
|
||||
|
||||
scenario 'user can join the current game', js: true do
|
||||
expect(page).to have_content "Sit"
|
||||
click_button 'Sit', match: :first
|
||||
#TODO: Fix race condition. Trigger game react component reload when player joins a table
|
||||
sleep 4
|
||||
expect(page).to have_content @user.email
|
||||
expect(page.text.scan(@user.email).count).to eq 2
|
||||
end
|
||||
|
||||
scenario 'user can leave the current game', js: true do
|
||||
expect(page).to have_content "Sit"
|
||||
click_button 'Sit', match: :first
|
||||
#TODO: Fix race condition
|
||||
sleep 4
|
||||
expect(page.text.scan(@user.email).count).to eq 2
|
||||
click_button 'Stand', match: :first
|
||||
expect(page).to have_content "Sit"
|
||||
#TODO: Fix race condition
|
||||
sleep 4
|
||||
expect(page.text.scan(@user.email).count).to eq 0
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
60
spec/features/tables/table_show_spec.rb
Normal file
60
spec/features/tables/table_show_spec.rb
Normal file
@ -0,0 +1,60 @@
|
||||
# Feature: Table show
|
||||
# As a visitor
|
||||
# I want to view a table
|
||||
# So I can sit and play a game
|
||||
feature 'Table show', :devise do
|
||||
context "As a visitor" do
|
||||
before(:each) do
|
||||
@table = FactoryGirl.create :table
|
||||
visit tables_path
|
||||
click_link @table.title
|
||||
end
|
||||
# Scenario: Visitor can view a table
|
||||
# Given I am not signed in
|
||||
# When I visit a table
|
||||
# Then I see the table attributes
|
||||
scenario 'visitor can see the table', js: true do
|
||||
expect(page).to have_selector "#table-#{@table.id} > h1", @table.title
|
||||
end
|
||||
|
||||
scenario 'visitor can see the seats', js: true do
|
||||
#TODO: Race condition exists without have_content
|
||||
expect(page).to have_content "Sit"
|
||||
@seat_count = page.find_all(".seat").length
|
||||
expect(@seat_count).to eq @table.seats.count
|
||||
end
|
||||
|
||||
scenario 'visitor can see the current game', js: true do
|
||||
#TODO: Write a better expectation
|
||||
expect(page).to have_content "Hand to beat"
|
||||
end
|
||||
end
|
||||
|
||||
context "As a user" do
|
||||
before(:each) do
|
||||
@table = FactoryGirl.create :table
|
||||
@user = FactoryGirl.create :user
|
||||
signin(@user.email, @user.password)
|
||||
end
|
||||
|
||||
# Scenario: User can view a table
|
||||
# Given I am signed in
|
||||
# When I click a table
|
||||
# Then I see the table attributes
|
||||
scenario 'user can see the table', js: true do
|
||||
visit tables_path
|
||||
click_link @table.title
|
||||
expect(page).to have_content "Sign out"
|
||||
expect(page).to have_selector "#table-#{@table.id} > h1", @table.title
|
||||
expect(current_path).to eq table_path(@table)
|
||||
end
|
||||
|
||||
scenario 'user can see the current game', js: true do
|
||||
#TODO: Fix race condition
|
||||
visit table_path @table
|
||||
#TODO: Write a better expectation
|
||||
expect(page).to have_content "Hand to beat"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@ -8,7 +8,7 @@ feature 'Sign in', :devise do
|
||||
# When I sign in with valid credentials
|
||||
# Then I see an invalid credentials message
|
||||
scenario 'user cannot sign in if not registered' do
|
||||
signin('test@example.com', 'please123')
|
||||
signin('test@example.com', 'password')
|
||||
expect(page).to have_content
|
||||
I18n.t 'devise.failure.not_found_in_database', authentication_keys: 'email'
|
||||
end
|
||||
|
||||
@ -20,4 +20,9 @@ feature 'Join game', :devise do
|
||||
@playerComponent = page.find("#player-1")
|
||||
expect(@playerComponent.text).to have_content("Guest#{@player.soft_token[0..5]}")
|
||||
end
|
||||
|
||||
scenario 'visitor can join a game in progress' do
|
||||
pending "To Be Implemented"
|
||||
expect(page).to have_content "Waiting for next game."
|
||||
end
|
||||
end
|
||||
|
||||
@ -9,13 +9,31 @@ RSpec.describe Seat, type: :model do
|
||||
|
||||
it { should respond_to :table }
|
||||
it { should respond_to :user }
|
||||
it { should respond_to :user_id }
|
||||
it { should respond_to :user_soft_token }
|
||||
|
||||
it '#table returns a Table' do
|
||||
expect(@seat.table.class).to match Table
|
||||
end
|
||||
|
||||
it '#user returns a User' do
|
||||
@seat = FactoryGirl.create(:seat, :occupied)
|
||||
expect(@seat.user.class).to match User
|
||||
end
|
||||
|
||||
it '#user_id returns a Fixnum' do
|
||||
@seat = FactoryGirl.create(:seat, :occupied)
|
||||
expect(@seat.user_id.class).to match Fixnum
|
||||
end
|
||||
|
||||
it '#user_soft_token returns a String' do
|
||||
@seat = FactoryGirl.create(:seat, :occupied)
|
||||
expect(@seat.user_soft_token.class).to match String
|
||||
end
|
||||
|
||||
it "#occupied? returns a Boolean" do
|
||||
expect(@seat.occupied?).to eq false
|
||||
@seat.user_id = 1
|
||||
expect(@seat.occupied?).to eq true
|
||||
end
|
||||
end
|
||||
|
||||
@ -33,4 +33,8 @@ RSpec.describe Table, type: :model do
|
||||
expect(@table.current_game.class).to match Game
|
||||
end
|
||||
|
||||
it "#seats returns 4 seats by default" do
|
||||
expect(@table.seats.count).to eq 4
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@ -7,10 +7,10 @@ module Features
|
||||
user2 = FactoryGirl.create :user
|
||||
user3 = FactoryGirl.create :user
|
||||
user4 = FactoryGirl.create :user
|
||||
game.add_player_from_user user1
|
||||
game.add_player_from_user user2
|
||||
game.add_player_from_user user3
|
||||
game.add_player_from_user user4
|
||||
game.seats[0].sit user1
|
||||
game.seats[1].sit user2
|
||||
game.seats[2].sit user3
|
||||
game.seats[3].sit user4
|
||||
game.start
|
||||
return game
|
||||
end
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user