From b015690f4f01c2fd3797afbd3529ee41ecc8ee04 Mon Sep 17 00:00:00 2001 From: Manasa <43354059+Manasa2850@users.noreply.github.com> Date: Thu, 19 Nov 2020 17:11:17 +0530 Subject: [PATCH] Podcast ownership join table (MLH Fellowship #81) (#11410) * added podcast_ownership route * added podcast_ownership table * added podcast_ownership controller and pundit policy * fixed codeclimate * removed controller and pundit policy * codeclimate * codeclimate * schema.rb * schema.rb * schema.rb * user.rb * made the suggested changes * added validations * fixed build error * changed name to owned_podcasts * added validation and tests * added tests * added tests * changed spec * added factory * added factory * merge conflict * factory * factory * factory * modified spec file * modified spec file * fix build error * added class_name * adjusted m2m relations * made suggested changes * fixed build error * added subject * schema * ran migration * removed index:true from singular fields * changed schema file * set index: false --- app/models/podcast.rb | 5 +++++ app/models/podcast_ownership.rb | 7 +++++++ app/models/user.rb | 4 ++-- .../20201114130315_create_podcast_ownerships.rb | 10 ++++++++++ db/schema.rb | 10 ++++++++++ spec/factories/podcast_ownership.rb | 6 ++++++ spec/models/podcast_ownership_spec.rb | 16 ++++++++++++++++ spec/models/podcast_spec.rb | 3 +++ spec/models/user_spec.rb | 2 ++ 9 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 app/models/podcast_ownership.rb create mode 100644 db/migrate/20201114130315_create_podcast_ownerships.rb create mode 100644 spec/factories/podcast_ownership.rb create mode 100644 spec/models/podcast_ownership_spec.rb diff --git a/app/models/podcast.rb b/app/models/podcast.rb index 4796e310c..9e4363ed0 100644 --- a/app/models/podcast.rb +++ b/app/models/podcast.rb @@ -2,8 +2,13 @@ class Podcast < ApplicationRecord resourcify belongs_to :creator, class_name: "User", inverse_of: :created_podcasts, optional: true + has_many :podcast_episodes, dependent: :destroy + # order here is important, the :through association has to be defined after the m2m + has_many :podcast_ownerships, dependent: :destroy + has_many :owners, through: :podcast_ownerships + mount_uploader :image, ProfileImageUploader mount_uploader :pattern_image, ProfileImageUploader diff --git a/app/models/podcast_ownership.rb b/app/models/podcast_ownership.rb new file mode 100644 index 000000000..c0fd9f450 --- /dev/null +++ b/app/models/podcast_ownership.rb @@ -0,0 +1,7 @@ +class PodcastOwnership < ApplicationRecord + belongs_to :owner, class_name: "User", foreign_key: :user_id, inverse_of: :podcasts_owned + belongs_to :podcast + + validates :podcast_id, uniqueness: { scope: :user_id } + validates :podcast_id, :user_id, presence: true +end diff --git a/app/models/user.rb b/app/models/user.rb index 0d9e6d8dd..aa615eddd 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -116,10 +116,10 @@ class User < ApplicationRecord inverse_of: :offender, foreign_key: :offender_id, dependent: :nullify has_many :organization_memberships, dependent: :destroy 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_ownerships, dependent: :destroy, inverse_of: :owner + has_many :podcasts_owned, through: :podcast_ownerships, source: :podcast has_many :poll_skips, dependent: :destroy has_many :poll_votes, dependent: :destroy has_many :profile_pins, as: :profile, inverse_of: :profile, dependent: :delete_all diff --git a/db/migrate/20201114130315_create_podcast_ownerships.rb b/db/migrate/20201114130315_create_podcast_ownerships.rb new file mode 100644 index 000000000..9439d5c9d --- /dev/null +++ b/db/migrate/20201114130315_create_podcast_ownerships.rb @@ -0,0 +1,10 @@ +class CreatePodcastOwnerships < ActiveRecord::Migration[6.0] + def change + create_table :podcast_ownerships do |t| + t.references :podcast, foreign_key: true, index: false, null: false + t.references :user, foreign_key: true, index: false, null: false + t.timestamps + end + add_index :podcast_ownerships, %i[podcast_id user_id], unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 8685b2bf4..9a56943df 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -878,6 +878,14 @@ ActiveRecord::Schema.define(version: 2020_11_14_151157) do t.index ["website_url"], name: "index_podcast_episodes_on_website_url" end + create_table "podcast_ownerships", force: :cascade do |t| + t.datetime "created_at", precision: 6, null: false + t.bigint "podcast_id", null: false + t.datetime "updated_at", precision: 6, null: false + t.bigint "user_id", null: false + t.index ["podcast_id", "user_id"], name: "index_podcast_ownerships_on_podcast_id_and_user_id", unique: true + end + create_table "podcasts", force: :cascade do |t| t.string "android_url" t.datetime "created_at", null: false @@ -1427,6 +1435,8 @@ ActiveRecord::Schema.define(version: 2020_11_14_151157) do add_foreign_key "page_views", "articles", on_delete: :cascade add_foreign_key "page_views", "users", on_delete: :nullify add_foreign_key "podcast_episodes", "podcasts", on_delete: :cascade + add_foreign_key "podcast_ownerships", "podcasts" + add_foreign_key "podcast_ownerships", "users" add_foreign_key "podcasts", "users", column: "creator_id" add_foreign_key "poll_options", "polls", on_delete: :cascade add_foreign_key "poll_skips", "polls", on_delete: :cascade diff --git a/spec/factories/podcast_ownership.rb b/spec/factories/podcast_ownership.rb new file mode 100644 index 000000000..adeccd7d7 --- /dev/null +++ b/spec/factories/podcast_ownership.rb @@ -0,0 +1,6 @@ +FactoryBot.define do + factory :podcast_ownership do + owner factory: :user + podcast + end +end diff --git a/spec/models/podcast_ownership_spec.rb b/spec/models/podcast_ownership_spec.rb new file mode 100644 index 000000000..acd268a72 --- /dev/null +++ b/spec/models/podcast_ownership_spec.rb @@ -0,0 +1,16 @@ +require "rails_helper" + +RSpec.describe PodcastOwnership, type: :model do + let(:podcast_ownership) { create(:podcast_ownership) } + + describe "validations" do + subject { podcast_ownership } + + it { is_expected.to belong_to(:owner).class_name("User").with_foreign_key(:user_id).inverse_of(:podcasts_owned) } + it { is_expected.to belong_to(:podcast) } + + it { is_expected.to validate_presence_of(:podcast_id) } + it { is_expected.to validate_presence_of(:user_id) } + it { is_expected.to validate_uniqueness_of(:podcast_id).scoped_to(:user_id) } + end +end diff --git a/spec/models/podcast_spec.rb b/spec/models/podcast_spec.rb index 8e68795a9..1e71b8724 100644 --- a/spec/models/podcast_spec.rb +++ b/spec/models/podcast_spec.rb @@ -22,7 +22,10 @@ RSpec.describe Podcast, type: :model do subject { podcast } it { is_expected.to belong_to(:creator).class_name("User").inverse_of(:created_podcasts).optional } + + it { is_expected.to have_many(:owners).through(:podcast_ownerships) } it { is_expected.to have_many(:podcast_episodes).dependent(:destroy) } + it { is_expected.to have_many(:podcast_ownerships).dependent(:destroy) } it { is_expected.to validate_presence_of(:feed_url) } it { is_expected.to validate_presence_of(:image) } diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 04fe534d2..dbe83ce67 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_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) } it { is_expected.to have_many(:poll_votes).dependent(:destroy) } it { is_expected.to have_many(:profile_pins).dependent(:delete_all) }