diff --git a/app/controllers/proposals_controller.rb b/app/controllers/proposals_controller.rb index 99c318a0..3a468c03 100644 --- a/app/controllers/proposals_controller.rb +++ b/app/controllers/proposals_controller.rb @@ -50,10 +50,10 @@ def update private def proposal_params - params.require(:proposal).permit(:title, :body, :tag_list, :speaker_id).to_h + params.require(:proposal).permit(:title, :body, :tag_list, speaker_ids: []).to_h end def speakers - Speaker.all.order('name ASC') + Speaker.all.order(name: :asc) end end diff --git a/app/models/proposal.rb b/app/models/proposal.rb index a3052f30..8315b10f 100644 --- a/app/models/proposal.rb +++ b/app/models/proposal.rb @@ -1,5 +1,6 @@ class Proposal < ApplicationRecord - belongs_to :speaker + has_many :proposal_speakers + has_many :speakers, through: :proposal_speakers has_many :submissions validates_presence_of :title diff --git a/app/models/proposal_speaker.rb b/app/models/proposal_speaker.rb new file mode 100644 index 00000000..7ff9def3 --- /dev/null +++ b/app/models/proposal_speaker.rb @@ -0,0 +1,4 @@ +class ProposalSpeaker < ApplicationRecord + belongs_to :proposal + belongs_to :speaker +end diff --git a/app/models/speaker.rb b/app/models/speaker.rb index 692c8dd2..12a77d5f 100644 --- a/app/models/speaker.rb +++ b/app/models/speaker.rb @@ -1,5 +1,6 @@ class Speaker < ApplicationRecord - has_many :proposals + has_many :proposal_speakers + has_many :proposals, through: :proposal_speakers validates_presence_of :name validates_uniqueness_of :name diff --git a/app/views/proposals/_form.html.erb b/app/views/proposals/_form.html.erb index 9819f1b0..8a2b6e69 100644 --- a/app/views/proposals/_form.html.erb +++ b/app/views/proposals/_form.html.erb @@ -4,7 +4,7 @@ <% end %> <%= form_for @proposal do |f| %> - <%= f.collection_select(:speaker_id, @speakers, :id, :name) %> + <%= f.collection_select(:speaker_ids, @speakers, :id, :name, {}, { multiple: true }) %> <%= f.label(:title) %> <%= f.text_field(:title, class: "u-full-width medium-height") %> <%= f.label(:tags, "Tags (comma-separated, max 3)", for: :proposal_tag_list) %> diff --git a/app/views/proposals/show.html.erb b/app/views/proposals/show.html.erb index 62147662..36c1a967 100644 --- a/app/views/proposals/show.html.erb +++ b/app/views/proposals/show.html.erb @@ -1,7 +1,12 @@

<%= @proposal.title %>, - by <%= link_to @proposal.speaker.name, speaker_path(@proposal.speaker) %> + by + <% last_speaker_id = @proposal.speakers.last.id %> + <% @proposal.speakers.each do |speaker| %> + <%= link_to speaker.name, speaker_path(speaker) %><%= ", " unless speaker.id == last_speaker_id %> + <% end %> +

<% @proposal.tag_list.each do |tag| %> #<%= tag.gsub(" ", "") %> diff --git a/app/views/shared/_instance_submissions.html.erb b/app/views/shared/_instance_submissions.html.erb index f3bf2e92..d61be05c 100644 --- a/app/views/shared/_instance_submissions.html.erb +++ b/app/views/shared/_instance_submissions.html.erb @@ -1,8 +1,12 @@ <%-# Sort submissions by title before sorting by result -%> -<% instance.sorted_submissions.includes(proposal: :speaker).each do |submission| %> +<% instance.sorted_submissions.includes(proposal: :speakers).each do |submission| %>

<%= link_to submission.proposal.title, submission.proposal %> - - <%= link_to submission.proposal.speaker.name, submission.proposal.speaker %> - + <% last_speaker_id = submission.proposal.speakers.last.id %> + <% submission.proposal.speakers.each do |speaker| %> + <%= link_to speaker.name, speaker %><%= ", " unless last_speaker_id == speaker.id %> + <% end %> + - <%= submission.result.capitalize %>

<% end %> diff --git a/db/migrate/20250511200328_create_proposal_speakers.rb b/db/migrate/20250511200328_create_proposal_speakers.rb new file mode 100644 index 00000000..1e82bdd4 --- /dev/null +++ b/db/migrate/20250511200328_create_proposal_speakers.rb @@ -0,0 +1,9 @@ +class CreateProposalSpeakers < ActiveRecord::Migration[7.2] + def change + create_table :proposal_speakers do |t| + t.references :proposal, foreign_key: true + t.references :speaker, foreign_key: true + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 7553a904..ab3e59af 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_05_08_172840) do +ActiveRecord::Schema[7.2].define(version: 2025_05_11_200328) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -29,6 +29,15 @@ t.index ["name"], name: "index_events_on_name", unique: true end + create_table "proposal_speakers", force: :cascade do |t| + t.bigint "proposal_id" + t.bigint "speaker_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["proposal_id"], name: "index_proposal_speakers_on_proposal_id" + t.index ["speaker_id"], name: "index_proposal_speakers_on_speaker_id" + end + create_table "proposals", id: :serial, force: :cascade do |t| t.string "title" t.text "body" @@ -80,6 +89,8 @@ end add_foreign_key "event_instances", "events" + add_foreign_key "proposal_speakers", "proposals" + add_foreign_key "proposal_speakers", "speakers" add_foreign_key "proposals", "speakers" add_foreign_key "submissions", "event_instances" add_foreign_key "submissions", "proposals" diff --git a/db/seeds.rb b/db/seeds.rb index b6566ed5..11d03b8a 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -24,8 +24,15 @@ end puts "5 events with 3 instances each added (15 total instances)..." + speaker_counts = [1,2,3,4] + speaker_ids = Speaker.ids [' the ', ' and the '].each do | mid_text | - 10.times { Proposal.create!(title: Faker::Hacker.ingverb.titleize + mid_text + Faker::Hacker.adjective.titleize + " " + Faker::Hacker.noun.titleize, body: Faker::Movies::VForVendetta, speaker_id: Speaker.ids.sample) } + 10.times do + speakers = Speaker.where(id: speaker_ids.sample(speaker_counts.sample)) + title = Faker::Hacker.ingverb.titleize + mid_text + Faker::Hacker.adjective.titleize + " " + Faker::Hacker.noun.titleize + body = Faker::Movies::VForVendetta + Proposal.create!(title:, body:, speakers:) + end end puts "20 proposals added..." diff --git a/lib/tasks/migrate.rake b/lib/tasks/migrate.rake new file mode 100644 index 00000000..cd471abb --- /dev/null +++ b/lib/tasks/migrate.rake @@ -0,0 +1,10 @@ +namespace :migrate do + desc "Migrate proposals from belongs_to :speaker to has_many :speakers" + task speakers: :environment do + Proposal.all.each do |proposal| + speaker = Speaker.find_by(id: proposal.speaker_id) + next if speaker.blank? + ProposalSpeaker.create(proposal:, speaker:) + end + end +end \ No newline at end of file diff --git a/spec/controllers/proposals_controller_spec.rb b/spec/controllers/proposals_controller_spec.rb index 5e496e50..1eba2f50 100644 --- a/spec/controllers/proposals_controller_spec.rb +++ b/spec/controllers/proposals_controller_spec.rb @@ -3,17 +3,17 @@ RSpec.describe ProposalsController do describe 'POST #create' do context 'with valid attributes' do - - let(:expected_proposal) { Proposal.new(id: 7, title: 'A great talk', body: 'Come listen to a great talk', tag_list: 'talks, proposals', speaker_id: 5 ) } + let(:speaker) { create(:speaker) } + let(:expected_proposal) { Proposal.new(id: 7, title: 'A great talk', body: 'Come listen to a great talk', tag_list: 'talks, proposals', speaker_ids: [speaker.id] ) } before do allow(Proposal).to receive(:new).and_return(expected_proposal) allow(expected_proposal).to receive(:save).and_return(true) - post :create, params: { proposal: { title: 'A great talk', body: 'Come listen to a great talk', tag_list: 'talks, proposals',speaker_id: 5 } } + post :create, params: { proposal: { title: 'A great talk', body: 'Come listen to a great talk', tag_list: 'talks, proposals',speaker_ids: [speaker.id] } } end it 'creates a new proposal' do - expect(Proposal).to have_received(:new).with(title: 'A great talk', body: 'Come listen to a great talk', tag_list: 'talks, proposals', speaker_id: '5') + expect(Proposal).to have_received(:new).with(title: 'A great talk', body: 'Come listen to a great talk', tag_list: 'talks, proposals', speaker_ids: [speaker.id.to_s]) end it { should redirect_to(proposal_path(expected_proposal)) } diff --git a/spec/controllers/speakers_controller_spec.rb b/spec/controllers/speakers_controller_spec.rb index 85f53d8d..83be3788 100644 --- a/spec/controllers/speakers_controller_spec.rb +++ b/spec/controllers/speakers_controller_spec.rb @@ -8,8 +8,8 @@ create(:speaker, name: "LazyTed") create(:speaker, name: "Angel") - create(:proposal, speaker: Speaker.find_by(name: "Angel")) - create(:proposal, speaker: Speaker.find_by(name: "Zebra")) + create(:proposal, speakers: [Speaker.find_by(name: "Angel")]) + create(:proposal, speakers: [Speaker.find_by(name: "Zebra")]) get :index end diff --git a/spec/factories/proposal.rb b/spec/factories/proposal.rb index a1dcee47..58eee743 100644 --- a/spec/factories/proposal.rb +++ b/spec/factories/proposal.rb @@ -2,6 +2,6 @@ factory :proposal do title { "All The Little Things" } body { "A talk about all the little things in code we cannot see" } - speaker + speakers { [create(:speaker)] } end end diff --git a/spec/factories/proposal_speakers.rb b/spec/factories/proposal_speakers.rb new file mode 100644 index 00000000..d1ce6f09 --- /dev/null +++ b/spec/factories/proposal_speakers.rb @@ -0,0 +1,6 @@ +FactoryBot.define do + factory :proposal_speaker do + proposal_id { 1 } + speaker_id { 1 } + end +end diff --git a/spec/models/proposal_speaker_spec.rb b/spec/models/proposal_speaker_spec.rb new file mode 100644 index 00000000..25d6b77d --- /dev/null +++ b/spec/models/proposal_speaker_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe ProposalSpeaker, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end