From ad04158b9072d6f3cf1d7119bac6790c865f28b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nenad=20Panteli=C4=87?= Date: Mon, 30 Nov 2020 17:12:52 +0100 Subject: [PATCH] Add podcast_appearances joined table (forem#82) (#11354) * Add podcast_appearances joined table (forem#82) * Add podcast appearances keys unique constraint and creator property (forem#82) * Add podcast_appearance role validation forem(#82) * Add spec test for podcast_appearances model (forem#82) * Small refactoring - place podcast appearance association by alphabetical order * Adapt m2m association to pass rspec test (forem#82) * Rename podcast appearance model to podcast episode appearance and remove fk indexing * Rename podcast appearance model to podcast episode appearance and remove fk indexing * Rename podcast episode appearance models and spec models. Update podcast episode and user spec tests (forem#82) * Rename podcast episode appearances composite index to follow Rails naming pattern (forem#82) * Remove ddl_transaction disabling and conurrent index adding from appearance migration * Add role validation in model spec (forem#82) --- app/models/podcast_episode.rb | 2 ++ app/models/podcast_episode_appearance.rb | 7 ++++++ app/models/user.rb | 2 ++ ...1600_create_podcast_episode_appearances.rb | 16 +++++++++++++ db/schema.rb | 15 +++++++++++- spec/factories/podcast_episode_appearances.rb | 7 ++++++ .../models/podcast_episode_appearance_spec.rb | 23 +++++++++++++++++++ spec/models/podcast_episode_spec.rb | 2 ++ spec/models/user_spec.rb | 2 ++ 9 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 app/models/podcast_episode_appearance.rb create mode 100644 db/migrate/20201107111600_create_podcast_episode_appearances.rb create mode 100644 spec/factories/podcast_episode_appearances.rb create mode 100644 spec/models/podcast_episode_appearance_spec.rb diff --git a/app/models/podcast_episode.rb b/app/models/podcast_episode.rb index 75a761082..788064a7b 100644 --- a/app/models/podcast_episode.rb +++ b/app/models/podcast_episode.rb @@ -16,6 +16,8 @@ class PodcastEpisode < ApplicationRecord belongs_to :podcast has_many :comments, as: :commentable, inverse_of: :commentable, dependent: :nullify + has_many :podcast_episode_appearances, dependent: :destroy + has_many :users, through: :podcast_episode_appearances mount_uploader :image, ProfileImageUploader mount_uploader :social_image, ProfileImageUploader diff --git a/app/models/podcast_episode_appearance.rb b/app/models/podcast_episode_appearance.rb new file mode 100644 index 000000000..92a711e2a --- /dev/null +++ b/app/models/podcast_episode_appearance.rb @@ -0,0 +1,7 @@ +class PodcastEpisodeAppearance < ApplicationRecord + belongs_to :user, class_name: "User", inverse_of: :podcast_episode_appearances + belongs_to :podcast_episode + validates :podcast_episode_id, uniqueness: { scope: :user_id } + validates :podcast_episode_id, :user_id, :role, presence: true + validates :role, inclusion: { in: %w[host guest], message: "provided role is not valid" } +end diff --git a/app/models/user.rb b/app/models/user.rb index b35cf09f7..8ee4622d9 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -118,6 +118,8 @@ class User < ApplicationRecord has_many :organizations, through: :organization_memberships # we keep page views as they belong to the article, not to the user who viewed it has_many :page_views, dependent: :nullify + has_many :podcast_episode_appearances, dependent: :destroy, inverse_of: :user + has_many :podcast_episodes, through: :podcast_episode_appearances, source: :podcast_episode has_many :podcast_ownerships, dependent: :destroy, inverse_of: :owner has_many :podcasts_owned, through: :podcast_ownerships, source: :podcast has_many :poll_skips, dependent: :destroy diff --git a/db/migrate/20201107111600_create_podcast_episode_appearances.rb b/db/migrate/20201107111600_create_podcast_episode_appearances.rb new file mode 100644 index 000000000..288501a7b --- /dev/null +++ b/db/migrate/20201107111600_create_podcast_episode_appearances.rb @@ -0,0 +1,16 @@ +class CreatePodcastEpisodeAppearances < ActiveRecord::Migration[6.0] + def change + create_table :podcast_episode_appearances do |t| + t.references :user, null: false, foreign_key: true, index: false + t.references :podcast_episode, null: false, foreign_key: true, index: false + t.string :role, null: false, default: "guest" + t.boolean :approved, null: false, default: false + t.boolean :featured_on_user_profile, null: false, default: false + t.timestamps + end + add_index :podcast_episode_appearances, + %i[podcast_episode_id user_id], + unique: true, + name: "index_pod_episode_appearances_on_podcast_episode_id_and_user_id" + end +end diff --git a/db/schema.rb b/db/schema.rb index 152c8ed9b..d2aa37bf5 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,8 +10,8 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2020_11_19_153512) do +ActiveRecord::Schema.define(version: 2020_11_19_153512) do # These are extensions that must be enabled in order to support this database enable_extension "citext" enable_extension "pgcrypto" @@ -848,6 +848,17 @@ ActiveRecord::Schema.define(version: 2020_11_19_153512) do t.index ["slug"], name: "index_pages_on_slug", unique: true end + create_table "podcast_episode_appearances", force: :cascade do |t| + t.boolean "approved", default: false, null: false + t.datetime "created_at", precision: 6, null: false + t.boolean "featured_on_user_profile", default: false, null: false + t.bigint "podcast_episode_id", null: false + t.string "role", default: "guest", null: false + t.datetime "updated_at", precision: 6, null: false + t.bigint "user_id", null: false + t.index ["podcast_episode_id", "user_id"], name: "index_pod_episode_appearances_on_podcast_episode_id_and_user_id", unique: true + end + create_table "podcast_episodes", force: :cascade do |t| t.boolean "any_comments_hidden", default: false t.text "body" @@ -1436,6 +1447,8 @@ ActiveRecord::Schema.define(version: 2020_11_19_153512) do add_foreign_key "organization_memberships", "users", on_delete: :cascade add_foreign_key "page_views", "articles", on_delete: :cascade add_foreign_key "page_views", "users", on_delete: :nullify + add_foreign_key "podcast_episode_appearances", "podcast_episodes" + add_foreign_key "podcast_episode_appearances", "users" add_foreign_key "podcast_episodes", "podcasts", on_delete: :cascade add_foreign_key "podcast_ownerships", "podcasts" add_foreign_key "podcast_ownerships", "users" diff --git a/spec/factories/podcast_episode_appearances.rb b/spec/factories/podcast_episode_appearances.rb new file mode 100644 index 000000000..3b1ec6eb2 --- /dev/null +++ b/spec/factories/podcast_episode_appearances.rb @@ -0,0 +1,7 @@ +FactoryBot.define do + factory :podcast_episode_appearance do + user + podcast_episode + role { "guest" } + end +end diff --git a/spec/models/podcast_episode_appearance_spec.rb b/spec/models/podcast_episode_appearance_spec.rb new file mode 100644 index 000000000..d69d70072 --- /dev/null +++ b/spec/models/podcast_episode_appearance_spec.rb @@ -0,0 +1,23 @@ +require "rails_helper" + +RSpec.describe PodcastEpisodeAppearance, type: :model do + let(:podcast_episode_appearance) { create(:podcast_episode_appearance) } + + describe "validations" do + subject { podcast_episode_appearance } + + it { is_expected.to belong_to(:user).inverse_of(:podcast_episode_appearances) } + it { is_expected.to belong_to(:podcast_episode) } + + it { is_expected.to validate_presence_of(:podcast_episode_id) } + it { is_expected.to validate_presence_of(:user_id) } + it { is_expected.to validate_presence_of(:role) } + + it do + expect(podcast_episode_appearance).to validate_inclusion_of(:role).in_array(%w[host guest]) + .with_message("provided role is not valid") + end + + it { is_expected.to validate_uniqueness_of(:podcast_episode_id).scoped_to(:user_id) } + end +end diff --git a/spec/models/podcast_episode_spec.rb b/spec/models/podcast_episode_spec.rb index f15392813..1431a52f4 100644 --- a/spec/models/podcast_episode_spec.rb +++ b/spec/models/podcast_episode_spec.rb @@ -9,6 +9,8 @@ RSpec.describe PodcastEpisode, type: :model do it { is_expected.to belong_to(:podcast) } it { is_expected.to have_many(:comments).inverse_of(:commentable).dependent(:nullify) } + it { is_expected.to have_many(:podcast_episode_appearances).dependent(:destroy) } + it { is_expected.to have_many(:users).through(:podcast_episode_appearances) } it { is_expected.to validate_presence_of(:comments_count) } it { is_expected.to validate_presence_of(:guid) } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 1a799abc8..6d8ddb4e3 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -68,6 +68,8 @@ RSpec.describe User, type: :model do it { is_expected.to have_many(:organization_memberships).dependent(:destroy) } it { is_expected.to have_many(:organizations).through(:organization_memberships) } it { is_expected.to have_many(:page_views).dependent(:nullify) } + it { is_expected.to have_many(:podcast_episode_appearances).dependent(:destroy) } + it { is_expected.to have_many(:podcast_episodes).through(:podcast_episode_appearances).source(:podcast_episode) } it { is_expected.to have_many(:podcast_ownerships).dependent(:destroy) } it { is_expected.to have_many(:podcasts_owned).through(:podcast_ownerships).source(:podcast) } it { is_expected.to have_many(:poll_skips).dependent(:destroy) }