summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorLuke Shumaker <shumakl@purdue.edu>2014-04-29 03:41:21 -0400
committerLuke Shumaker <shumakl@purdue.edu>2014-04-29 03:41:21 -0400
commitf9488b183bbd41d74d801a9cbdd41410be4ddba5 (patch)
treea485cfd89181c447a3f471dacdfdf6bc57c823dd /app
parentcd0c7d73be76feffbaaea9200c485b2316604469 (diff)
Validate everything that I can.
Diffstat (limited to 'app')
-rw-r--r--app/models/game.rb40
-rw-r--r--app/models/game_setting.rb11
-rw-r--r--app/models/match.rb32
-rw-r--r--app/models/tournament.rb85
-rw-r--r--app/models/tournament_setting.rb12
-rw-r--r--app/models/tournament_stage.rb10
-rw-r--r--app/models/user.rb94
7 files changed, 215 insertions, 69 deletions
diff --git a/app/models/game.rb b/app/models/game.rb
index c5cb32a..d5622af 100644
--- a/app/models/game.rb
+++ b/app/models/game.rb
@@ -1,7 +1,45 @@
class Game < ActiveRecord::Base
belongs_to :parent, class_name: "Game"
+
has_many :children, class_name: "Game"
- has_many :game_settings
+ has_many :game_settings
+ validates_associated :game_settings
alias_attribute :settings, :game_settings
+
+ validates(:name,
+ presence: true,
+ length: {minimum: 5},
+ uniqueness: {case_sensitive: true})
+
+ validates(:min_players_per_team,
+ presence: true,
+ numericality: {
+ only_integer: true,
+ less_than_or_equal_to: :max_players_per_team,
+ })
+ validates(:max_players_per_team,
+ presence: true,
+ numericality: {
+ only_integer: true,
+ greater_than_or_equal_to: :min_players_per_team,
+ })
+
+ validates(:min_teams_per_match,
+ presence: true,
+ numericality: {
+ only_integer: true,
+ less_than_or_equal_to: :max_teams_per_match,
+ })
+ validates(:max_teams_per_match,
+ presence: true,
+ numericality: {
+ only_integer: true,
+ greater_than_or_equal_to: :min_teams_per_match,
+ })
+
+ validate :validate_scoring_method
+ def validate_scoring_method
+ (not self.scoring_method.try(:empty?)) and (Tournament.scoring_methods.include? scoring_method)
+ end
end
diff --git a/app/models/game_setting.rb b/app/models/game_setting.rb
index e701cae..40ab32f 100644
--- a/app/models/game_setting.rb
+++ b/app/models/game_setting.rb
@@ -3,6 +3,17 @@ class GameSetting < ActiveRecord::Base
alias_attribute :value, :default
+ validates(:vartype, presence: true, numericality: {only_integer: true})
+ validates(:type_opt, presence: true, if: :needs_type_opt?)
+
+ def needs_type_opt?
+ [
+ GameSetting.types[:pick_one_radio],
+ GameSetting.types[:pick_one_dropdown],
+ GameSetting.types[:pick_several],
+ ].include? self.vartype
+ end
+
def self.types
return {
:text_short => 0,
diff --git a/app/models/match.rb b/app/models/match.rb
index 85084f5..7b36777 100644
--- a/app/models/match.rb
+++ b/app/models/match.rb
@@ -5,6 +5,18 @@ class Match < ActiveRecord::Base
belongs_to :winner, class_name: "Team"
+ # status:integer
+ before_save { self.status ||= 0 }
+
+ # tournament_stage:references
+ validates_presence_of :tournament_stage
+
+ # winner:references
+ # not validated
+
+ ##
+ # Returns whether or not all the statistics have been collected
+ # such that the match may be considered finished.
def finished?
ok = true
tournament_stage.scoring.stats_needed.each do |stat|
@@ -13,26 +25,35 @@ class Match < ActiveRecord::Base
ok
end
- def win?(player)
- winner.players.include? player
- end
-
+ ##
+ # Returns all players involved in this match (from all teams).
def users
ret = []
self.teams.each{|t| ret.concat(t.users)}
return ret
end
+ ##
+ # Given a sampling class (a class that implements the interface
+ # described in `/lib/sampling/README.md`), this returns which
+ # statistics (in an Array of Strings) an instance of the class
+ # should collect.
def stats_from(sampling_class)
figure_sampling_methods.map{|stat,klass| (sampling_class==klass) ? stat : nil}.select{|s| not s.nil?}
end
+ ##
+ # Delagates PUT/PATCH HTTP params to the appropriate sampling
+ # methods.
def handle_sampling(user, params)
method_classes.each do |klass|
klass.new(self).handle_user_interaction(user, params)
end
end
+ ##
+ # Delagates out rendering forms to the appropriate sampling
+ # methods.
def render_sampling(user)
require 'set'
html = ''
@@ -46,6 +67,9 @@ class Match < ActiveRecord::Base
return html.html_safe
end
+ ##
+ # Calls `Sampling#start` on every sampling method that this match
+ # uses.
def start_sampling
method_classes.each do |klass|
klass.new(self).start
diff --git a/app/models/tournament.rb b/app/models/tournament.rb
index b867716..8a96dcc 100644
--- a/app/models/tournament.rb
+++ b/app/models/tournament.rb
@@ -1,15 +1,60 @@
class Tournament < ActiveRecord::Base
belongs_to :game
+
has_many :tournament_stages
+ # Don't validate presence of stages; sadly, it seems to break things
+ #validates_presence_of :tournament_stages
+ alias_attribute :stages, :tournament_stages
+
has_many :brackets
+
has_many :tournament_settings
+
has_and_belongs_to_many :players, class_name: "User", association_foreign_key: "player_id", join_table: "players_tournaments"
+
has_and_belongs_to_many :hosts, class_name: "User", association_foreign_key: "host_id", join_table: "hosts_tournaments"
+ validates_presence_of :hosts
- alias_attribute :stages, :tournament_stages
+ validates_presence_of :game
before_save { self.status ||= 0 }
+ validates(:name,
+ presence: true,
+ length: {minimum: 5},
+ uniqueness: {case_sensitive: true})
+
+ validates(:min_players_per_team,
+ presence: true,
+ numericality: {
+ only_integer: true,
+ less_than_or_equal_to: :max_players_per_team,
+ })
+ validates(:max_players_per_team,
+ presence: true,
+ numericality: {
+ only_integer: true,
+ greater_than_or_equal_to: :min_players_per_team,
+ })
+
+ validates(:min_teams_per_match,
+ presence: true,
+ numericality: {
+ only_integer: true,
+ less_than_or_equal_to: :max_teams_per_match,
+ })
+ validates(:max_teams_per_match,
+ presence: true,
+ numericality: {
+ only_integer: true,
+ greater_than_or_equal_to: :min_teams_per_match,
+ })
+
+ validate :validate_scoring_method
+ def validate_scoring_method
+ (not self.scoring_method.try(:empty?)) and (scoring_methods.include? scoring_method)
+ end
+
# Settings #################################################################
def settings
@@ -69,16 +114,10 @@ class Tournament < ActiveRecord::Base
end
end
- # Misc. ####################################################################
-
- def open?
- return true
- end
-
# Joining/Leaving ##########################################################
def joinable_by?(user)
- return (open? and user.can?(:join_tournament) and !players.include?(user))
+ return (status==0 and user.can?(:join_tournament) and !players.include?(user))
end
def join(user)
@@ -100,32 +139,42 @@ class Tournament < ActiveRecord::Base
@scoring ||= "Scoring::#{self.scoring_method.camelcase}".constantize
end
- # YISSSSSS
+ # Options for configured methods/modules ###################################
+ # We're conflicted about whether these should be `self.` or not. ###########
- def scoring_methods
+ def self.scoring_methods
make_methods "scoring"
end
+ def scoring_methods
+ self.class.scoring_methods
+ end
def sampling_methods
- make_methods("sampling").select do |name|
+ self.class.make_methods("sampling").select do |name|
"Sampling::#{name.camelcase}".constantize.works_with?(self.game)
end
end
- def scheduling_methods
+ def self.scheduling_methods
make_methods "scheduling"
end
+ def scheduling_methods
+ self.class.scheduling_methods
+ end
- def seeding_methods
+ def self.seeding_methods
make_methods "seeding"
end
+ def seeding_methods
+ self.class.seeding_methods
+ end
private
- def make_methods(dir)
- @@methods ||= {}
- if @@methods[dir].nil? or Rails.env.development?
- @@methods[dir] = Dir.glob("#{Rails.root}/lib/#{dir}/*.rb").map{|filename| File.basename(filename, ".rb") }
+ def self.make_methods(dir)
+ @methods ||= {}
+ if @methods[dir].nil? or Rails.env.development?
+ @methods[dir] = Dir.glob("#{Rails.root}/lib/#{dir}/*.rb").map{|filename| File.basename(filename, ".rb") }
end
- return @@methods[dir]
+ return @methods[dir]
end
end
diff --git a/app/models/tournament_setting.rb b/app/models/tournament_setting.rb
index 9efaaea..20d9842 100644
--- a/app/models/tournament_setting.rb
+++ b/app/models/tournament_setting.rb
@@ -1,6 +1,18 @@
class TournamentSetting < ActiveRecord::Base
belongs_to :tournament
+ validates(:vartype, presence: true, numericality: {only_integer: true})
+ validates(:type_opt, presence: true, if: :needs_type_opt?)
+
+ def needs_type_opt?
+ [
+ GameSetting.types[:pick_one_radio],
+ GameSetting.types[:pick_one_dropdown],
+ GameSetting.types[:pick_several],
+ ].include? self.vartype
+ end
+
+
def self.types
GameSetting.types
end
diff --git a/app/models/tournament_stage.rb b/app/models/tournament_stage.rb
index 19b9c23..72aa14c 100644
--- a/app/models/tournament_stage.rb
+++ b/app/models/tournament_stage.rb
@@ -1,7 +1,17 @@
class TournamentStage < ActiveRecord::Base
belongs_to :tournament
+ validates_presence_of :tournament
+
has_many :matches
+ validates(:scheduling_method,
+ presence: true,
+ inclusion: {in: Tournament.new.scheduling_methods})
+
+ validates(:seeding_method,
+ presence: true,
+ inclusion: {in: Tournament.new.seeding_methods})
+
# A 1-indexed hash of matches
def matches_ordered
h = {}
diff --git a/app/models/user.rb b/app/models/user.rb
index b2c7862..a39037c 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,6 +1,7 @@
class User < ActiveRecord::Base
- before_save :default_values
-
+ ##################################################################
+ # Relationships #
+ ##################################################################
has_and_belongs_to_many :tournaments_played, class_name: "Tournament", foreign_key: "player_id", join_table: "players_tournaments"
has_and_belongs_to_many :tournaments_hosted, class_name: "Tournament", foreign_key: "host_id", join_table: "hosts_tournaments"
has_and_belongs_to_many :teams
@@ -8,20 +9,49 @@ class User < ActiveRecord::Base
has_many :statistics
has_many :remote_usernames
- apply_simple_captcha
+ ##################################################################
+ # Attributes #
+ ##################################################################
- acts_as_messageable
+ # name:string
+ validates(:name, presence: true, length: { maximum: 50 })
+ # email:string:uniq
+ before_save { self.email = email.downcase }
+ validates(:email,
+ presence: true,
+ format: {with: /\A\S+@\S+\.\S+\z/i},
+ uniqueness: { case_sensitive: false })
+
+ # user_name:string_uniq
+ validates(:user_name,
+ presence: true,
+ length:{maximum: 50},
+ format: {with: /\A[a-zA-Z0-9 _\-]+\z/},
+ uniqueness: {case_sensitive: false })
+
+ # password_digest:string
+ has_secure_password validations: false # maps :password and :password_confirmation to :password_digest
+ validates(:password,
+ length: { minimum: 6 },
+ confirmation: true,
+ unless: Proc.new { |u| u.password.try(:empty?) and not u.password_digest.try(:empty?) })
+
+ # permissions:integer
+ before_save { self.permissions ||= Server.first.default_user_permissions }
+
+ ##################################################################
+ # XXX: hard-coded-ish. It makes me feel dirty. #
+ ##################################################################
+ apply_simple_captcha
+ acts_as_messageable
def mailboxer_email(object)
return nil
end
- before_save { self.email = email.downcase }
- before_save { self.user_name = user_name }
-
- def default_values
- self.permissions ||= Server.first.default_user_permissions
- end
+ ##################################################################
+ # remote_usernames #
+ ##################################################################
def set_remote_username(game, data)
remote = self.remote_usernames.where(:game => game).first
@@ -46,6 +76,10 @@ class User < ActiveRecord::Base
end
end
+ ##################################################################
+ # Permissions #
+ ##################################################################
+
def self.permission_bits
return {
:create_tournament => (2**1),
@@ -104,7 +138,6 @@ class User < ActiveRecord::Base
end
end
-
# A representation of the permission bits as a mock-array.
def abilities
@abilities ||= Abilities.new(self)
@@ -122,7 +155,7 @@ class User < ActiveRecord::Base
# easy to modify them using a form.
class Abilities
def initialize(user)
- @user = user
+o @user = user
end
def [](ability)
return @user.can?(ability)
@@ -146,40 +179,9 @@ class User < ActiveRecord::Base
end
end
- # VAILD_EMAIL is the regex used to validate a user given email.
- VALID_EMAIL_REG = /\A\S+@\S+\.\S+\z/i
-
- # VALID_USER_NAME checks to make sure a user's user_name
- # is in the proper format.
- VALID_USER_NAME_REG = /\A[a-zA-Z0-9 _\-]+\z/
-
- # The following lines put a user account through a series of
- # validations in order to make sure all of their information
- # is in the proper format.
- #
- # validates :symbol_to_be_validated
- #
- # - presence: determines whether or not a symbol is filled or not
- # - length: ensures there is a length limit on the symbol
- # - format: checks the format of given information to ensure
- # validity
- validates(:name, presence: true, length: { maximum: 50 })
- validates(:email, presence: true, format: {with:
- VALID_EMAIL_REG},
- uniqueness: { case_sensitive: false })
- validates(:user_name, presence: true, length:{maximum: 50},
- format: {with: VALID_USER_NAME_REG },
- uniqueness: {case_sensitive: false })
-
- # Instead of adding password and password_confirmation
- # attributes, requiring the presence of a password,
- # requiring that pw and pw_com match, and add an authenticate
- # method to compare an encrypted password to the
- # password_digest to authenticate users, I can just add
- # has_secure_password which does all of this for me.
- has_secure_password
-
- validates :password, length: { minimum: 6 }
+ ##################################################################
+ # Null-object pattern #
+ ##################################################################
class NilUser
def nil?