diff --git a/.hound.yml b/.hound.yml index 6540cc8..cdf1016 100644 --- a/.hound.yml +++ b/.hound.yml @@ -1 +1,4 @@ fail_on_violations: true + +ruby: + config_file: .rubocop.yml diff --git a/.rubocop.yml b/.rubocop.yml index 5d48bb2..271ade4 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -30,3 +30,16 @@ Metrics/AbcSize: Exclude: - "db/migrate/*" Max: 20 + +Metrics/BlockLength: + ExcludedMethods: + - "namespace" + - "create_table" + Exclude: + - "config/**/*.rb" + - "spec/**/*.rb" + +# Disable documentation checks for now +# should be done with yard instead +Style/Documentation: + Enabled: false diff --git a/app/models/group.rb b/app/models/group.rb index 3738e97..39cba55 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true class Group < ApplicationRecord - belongs_to :matches - belongs_to :teams + belongs_to :stage + has_many :matches, dependent: :destroy + has_many :group_scores, dependent: :destroy end diff --git a/app/models/group_score.rb b/app/models/group_score.rb new file mode 100644 index 0000000..d7686d3 --- /dev/null +++ b/app/models/group_score.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +class GroupScore < ApplicationRecord + belongs_to :team + belongs_to :group +end diff --git a/app/models/group_stage.rb b/app/models/group_stage.rb deleted file mode 100644 index aac2290..0000000 --- a/app/models/group_stage.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true - -class GroupStage < ApplicationRecord -end diff --git a/app/models/match.rb b/app/models/match.rb index 5d8cb6e..54c1f66 100644 --- a/app/models/match.rb +++ b/app/models/match.rb @@ -1,4 +1,9 @@ # frozen_string_literal: true class Match < ApplicationRecord + belongs_to :stage + belongs_to :group + has_many :scores, dependent: :destroy + + validates :scores, length: { maximum: 2 } end diff --git a/app/models/playoff_stage.rb b/app/models/playoff_stage.rb deleted file mode 100644 index 682ef29..0000000 --- a/app/models/playoff_stage.rb +++ /dev/null @@ -1,4 +0,0 @@ -# frozen_string_literal: true - -class PlayoffStage < ApplicationRecord -end diff --git a/app/models/score.rb b/app/models/score.rb new file mode 100644 index 0000000..c24c0a9 --- /dev/null +++ b/app/models/score.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +class Score < ApplicationRecord + belongs_to :match + belongs_to :team +end diff --git a/app/models/stage.rb b/app/models/stage.rb new file mode 100644 index 0000000..b43d80c --- /dev/null +++ b/app/models/stage.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +class Stage < ApplicationRecord + belongs_to :tournament + has_many :matches, dependent: :destroy + has_many :groups, dependent: :destroy +end diff --git a/app/models/team.rb b/app/models/team.rb index ab022b6..023b9db 100644 --- a/app/models/team.rb +++ b/app/models/team.rb @@ -1,4 +1,9 @@ # frozen_string_literal: true class Team < ApplicationRecord + belongs_to :tournament + has_many :group_scores, dependent: :destroy + has_many :scores, dependent: :destroy + + validates :name, presence: true end diff --git a/app/models/tournament.rb b/app/models/tournament.rb index 46e28f4..68b8495 100644 --- a/app/models/tournament.rb +++ b/app/models/tournament.rb @@ -1,4 +1,18 @@ # frozen_string_literal: true +require 'securerandom' + class Tournament < ApplicationRecord + belongs_to :user + has_many :teams, dependent: :destroy + has_many :stages, dependent: :destroy + + validates :name, presence: true + validates :code, presence: true, uniqueness: true + + alias_attribute :owner, :user + + after_initialize do |tournament| + tournament.code ||= SecureRandom.hex 3 + end end diff --git a/app/models/user.rb b/app/models/user.rb index ef5c4d4..b24614f 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -7,4 +7,6 @@ class User < ApplicationRecord include DeviseTokenAuth::Concerns::User validates :username, presence: true, uniqueness: true + + has_many :tournaments, dependent: :destroy end diff --git a/db/migrate/0000_create_schema.rb b/db/migrate/0000_create_schema.rb index 0b6323c..8860203 100644 --- a/db/migrate/0000_create_schema.rb +++ b/db/migrate/0000_create_schema.rb @@ -2,62 +2,16 @@ class CreateSchema < ActiveRecord::Migration[5.2] def change - create_table :groups do |t| - t.string :name - - t.timestamps - end - - create_table :group_stages do |t| - t.integer :playoff_size - - t.timestamps - end - - create_table :matches do |t| - t.integer :score_team_1 - t.integer :score_team_2 - t.integer :state - t.integer :position - t.boolean :is_group_match - - t.timestamps - end - - create_table :playoff_stages do |t| - t.integer :level - - t.timestamps - end - - create_table :teams do |t| - t.string :name - t.integer :group_score - t.integer :group_points_scored - t.integer :group_points_recieved - - t.timestamps - end - - create_table :tournaments do |t| - t.string :name - t.string :code - t.string :description - t.boolean :public, default: true - - t.timestamps - end - create_table :users do |t| ## Required - t.string :provider, null: false, default: 'email' - t.string :uid, null: false, default: '' + t.string :provider, null: false, default: 'email', index: true + t.string :uid, null: false, default: '', index: { unique: true } ## Database authenticatable t.string :encrypted_password, null: false, default: '' ## Recoverable - t.string :reset_password_token + t.string :reset_password_token, index: { unique: true } t.datetime :reset_password_sent_at t.boolean :allow_password_change, default: false @@ -72,7 +26,7 @@ class CreateSchema < ActiveRecord::Migration[5.2] t.string :last_sign_in_ip ## Confirmable - t.string :confirmation_token + t.string :confirmation_token, index: { unique: true } t.datetime :confirmed_at t.datetime :confirmation_sent_at t.string :unconfirmed_email # Only if using reconfirmable @@ -83,8 +37,8 @@ class CreateSchema < ActiveRecord::Migration[5.2] # t.datetime :locked_at ## User Info - t.string :username - t.string :email + t.string :username, index: { unique: true } + t.string :email, index: { unique: true } ## Tokens t.text :tokens @@ -92,11 +46,69 @@ class CreateSchema < ActiveRecord::Migration[5.2] t.timestamps end - add_index :users, :username, unique: true - add_index :users, :email, unique: true - add_index :users, %i[uid provider], unique: true - add_index :users, :reset_password_token, unique: true - add_index :users, :confirmation_token, unique: true - # add_index :users, :unlock_token, unique: true + create_table :tournaments do |t| + t.string :name, null: false + t.string :code, null: false, index: { unique: true } + t.string :description + t.boolean :public, default: true + + # relation to owner + t.belongs_to :user, index: true, null: false, foreign_key: { on_delete: :cascade } + + t.timestamps + end + + create_table :stages do |t| + t.integer :level + + t.belongs_to :tournament, index: true, foreign_key: { on_delete: :cascade }, null: false + + t.timestamps + end + + create_table :groups do |t| + t.integer :number + + t.belongs_to :stage, index: true, foreign_key: { on_delete: :cascade }, null: false + + t.timestamps + end + + create_table :matches do |t| + t.integer :state, default: 0 + + t.belongs_to :stage, index: true, foreign_key: { on_delete: :cascade } + t.belongs_to :group, index: true, foreign_key: { on_delete: :cascade } + + t.timestamps + end + + create_table :teams do |t| + t.string :name + + t.belongs_to :tournament, index: true, foreign_key: { on_delete: :cascade } + + t.timestamps + end + + create_table :scores do |t| + t.integer :score, default: 0 + + t.belongs_to :match, index: true, null: false, foreign_key: { on_delete: :cascade } + t.belongs_to :team, index: true, null: false, foreign_key: { on_delete: :cascade } + + t.timestamps + end + + create_table :group_scores do |t| + t.integer :score, default: 0 + t.integer :points_scored, default: 0 + t.integer :points_received, default: 0 + + t.belongs_to :team, index: true, foreign_key: { on_delete: :cascade }, null: false + t.belongs_to :group, index: true, foreign_key: { on_delete: :cascade }, null: false + + t.timestamps + end end end diff --git a/spec/factories/group_scores.rb b/spec/factories/group_scores.rb new file mode 100644 index 0000000..558484a --- /dev/null +++ b/spec/factories/group_scores.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :group_score do + team + group + end +end diff --git a/spec/factories/groups.rb b/spec/factories/groups.rb new file mode 100644 index 0000000..841f741 --- /dev/null +++ b/spec/factories/groups.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :group do + number { 0 } + stage + end +end diff --git a/spec/factories/matches.rb b/spec/factories/matches.rb new file mode 100644 index 0000000..55e26c3 --- /dev/null +++ b/spec/factories/matches.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :match do + stage + group + end +end diff --git a/spec/factories/scores.rb b/spec/factories/scores.rb new file mode 100644 index 0000000..dd1977a --- /dev/null +++ b/spec/factories/scores.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :score do + score { 0 } + match + team + end +end diff --git a/spec/factories/stages.rb b/spec/factories/stages.rb new file mode 100644 index 0000000..4353af1 --- /dev/null +++ b/spec/factories/stages.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :stage do + tournament + end +end diff --git a/spec/factories/teams.rb b/spec/factories/teams.rb new file mode 100644 index 0000000..9836e0f --- /dev/null +++ b/spec/factories/teams.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :team do + name { Faker::Dog.name } + tournament + end +end diff --git a/spec/factories/tournaments.rb b/spec/factories/tournaments.rb new file mode 100644 index 0000000..36ae8bc --- /dev/null +++ b/spec/factories/tournaments.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :tournament do + name { Faker::Dog.name } + description { Faker::Lorem.sentence } + user + end +end diff --git a/spec/factories/users.rb b/spec/factories/users.rb new file mode 100644 index 0000000..cb6d278 --- /dev/null +++ b/spec/factories/users.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +FactoryBot.define do + factory :user do + username { Faker::Internet.username } + email { Faker::Internet.email } + end +end diff --git a/spec/models/group_score_spec.rb b/spec/models/group_score_spec.rb new file mode 100644 index 0000000..8056e32 --- /dev/null +++ b/spec/models/group_score_spec.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe GroupScore, type: :model do + describe 'association' do + it { should belong_to :team } + it { should belong_to :group } + end + + it 'has a valid factory' do + expect(build(:group_score)).to be_valid + end +end diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb new file mode 100644 index 0000000..0f5784a --- /dev/null +++ b/spec/models/group_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Group, type: :model do + describe 'association' do + it { should belong_to :stage } + it { should have_many :matches } + it { should have_many :group_scores } + end + + it 'has a valid factory' do + expect(build(:group)).to be_valid + end +end diff --git a/spec/models/match_spec.rb b/spec/models/match_spec.rb new file mode 100644 index 0000000..1162808 --- /dev/null +++ b/spec/models/match_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Match, type: :model do + context 'association' do + it { should have_many :scores } + it { should belong_to :stage } + it { should belong_to :group } + end + + context 'scores' do + before do + @match = create(:match) + @match.scores << build_pair(:score) + end + + it 'can only have two scores' do + @match.scores << build(:score) + expect(@match).to be_invalid + end + + it 'can access its scores' do + @match.scores[0].score = 0 + @match.scores[1].score = 0 + end + end + + it 'has a valid factory' do + expect(build(:match)).to be_valid + end +end diff --git a/spec/models/score_spec.rb b/spec/models/score_spec.rb new file mode 100644 index 0000000..4d68364 --- /dev/null +++ b/spec/models/score_spec.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Score, type: :model do + describe 'association' do + it { should belong_to :match } + it { should belong_to :team } + end + + it 'has a valid factory' do + expect(build(:score)).to be_valid + end +end diff --git a/spec/models/stage_spec.rb b/spec/models/stage_spec.rb new file mode 100644 index 0000000..4af3c30 --- /dev/null +++ b/spec/models/stage_spec.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Stage, type: :model do + describe 'association' do + it { should belong_to :tournament } + it { should have_many :matches } + it { should have_many :groups } + end + + it 'has a valid factory' do + expect(build(:stage)).to be_valid + end +end diff --git a/spec/models/team_spec.rb b/spec/models/team_spec.rb new file mode 100644 index 0000000..23b38bf --- /dev/null +++ b/spec/models/team_spec.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Team, type: :model do + describe 'validation' do + it { should validate_presence_of :name } + end + + describe 'association' do + it { should belong_to :tournament } + it { should have_many :group_scores } + it { should have_many :scores } + end + + it 'has a valid factory' do + expect(build(:team)).to be_valid + end +end diff --git a/spec/models/tournament_spec.rb b/spec/models/tournament_spec.rb new file mode 100644 index 0000000..5182e11 --- /dev/null +++ b/spec/models/tournament_spec.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Tournament, type: :model do + before do + @tournament = create(:tournament) + end + + describe 'validation' do + it { should validate_presence_of :name } + it { should validate_presence_of :code } + it { should validate_uniqueness_of :code } + it { should_not validate_presence_of :description } + it { should_not validate_presence_of :public } + end + + describe 'initialization' do + it 'should have a code' do + expect(@tournament.code.length).to be(6) + end + it 'should be public' do + expect(@tournament.public).to be(true) + end + end + + describe 'association' do + it { should belong_to :user } + it { should have_many :teams } + it { should have_many :stages } + end + + it 'has valid factory' do + expect(build(:tournament)).to be_valid + end +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb new file mode 100644 index 0000000..c4ab25f --- /dev/null +++ b/spec/models/user_spec.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe User, type: :model do + describe 'association' do + it { should have_many :tournaments } + end + + it 'has a valid factory' do + expect(build(:user)).to be_valid + end +end