From c3eb178546fe341b1fa1ad62b9362d79296cb06b Mon Sep 17 00:00:00 2001 From: AEtherC0r3 Date: Tue, 30 May 2017 20:54:48 +0300 Subject: [PATCH] Modify CFP to accept proposals for other things Add field cfp_type and relevant validations Add Program#cfp to preserve backwards compatibility Add 'for_events' scope to the cfp, in order for it to be used like program.cfps.events Add useful methods Add rspec test for the new code The supported cfp types can be viewed via Cfp::TYPES --- .rubocop_todo.yml | 1 + app/controllers/admin/cfps_controller.rb | 3 +- app/models/cfp.rb | 23 ++++++++++ app/models/program.rb | 21 +++++++++- db/migrate/20170530072155_add_type_to_cfps.rb | 15 +++++++ db/schema.rb | 1 + spec/models/cfp_spec.rb | 42 ++++++++++++++++++- spec/models/program_spec.rb | 19 +++++++++ 8 files changed, 120 insertions(+), 5 deletions(-) create mode 100644 db/migrate/20170530072155_add_type_to_cfps.rb diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 56ff7885..68360907 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -645,6 +645,7 @@ Style/PercentLiteralDelimiters: - 'app/models/contact.rb' - 'app/models/registration.rb' - 'app/models/subscription.rb' + - 'app/models/cfp.rb' - 'app/uploaders/picture_uploader.rb' - 'spec/models/ability_spec.rb' - 'spec/models/program_spec.rb' diff --git a/app/controllers/admin/cfps_controller.rb b/app/controllers/admin/cfps_controller.rb index 42d10218..81fd78a9 100644 --- a/app/controllers/admin/cfps_controller.rb +++ b/app/controllers/admin/cfps_controller.rb @@ -2,7 +2,7 @@ module Admin class CfpsController < Admin::BaseController load_and_authorize_resource :conference, find_by: :short_title load_and_authorize_resource :program, through: :conference, singleton: true - load_and_authorize_resource through: :program, singleton: true + load_and_authorize_resource through: :program def show; end @@ -27,7 +27,6 @@ module Admin end def update - @cfp = @program.cfp @cfp.assign_attributes(cfp_params) send_mail_on_cfp_dates_updates = @cfp.notify_on_cfp_date_update? diff --git a/app/models/cfp.rb b/app/models/cfp.rb index affcc408..92058e87 100644 --- a/app/models/cfp.rb +++ b/app/models/cfp.rb @@ -1,6 +1,10 @@ # cannot delete program if there are events submitted class Cfp < ActiveRecord::Base + TYPES = %w(events).freeze + + scope :for_events, (-> { find_by(cfp_type: 'events') }) + has_paper_trail ignore: [:updated_at], meta: { conference_id: :conference_id } belongs_to :program @@ -8,6 +12,15 @@ class Cfp < ActiveRecord::Base validates :start_date, :end_date, presence: true validate :before_end_of_conference validate :start_after_end_date + validates :cfp_type, + presence: true, + inclusion: { + in: TYPES + }, + uniqueness: { + scope: :program, + case_sensitive: false + } ## # Checks whether cfp date is updated @@ -55,6 +68,16 @@ class Cfp < ActiveRecord::Base result > 0 ? result : 0 end + ## + # Checks if the call for papers is currently open + # + # ====Returns + # * +false+ -> If the CFP is not set or today isn't in the CFP period. + # * +true+ -> If today is in the CFP period. + def open? + (start_date..end_date).cover?(Date.current) + end + private def before_end_of_conference diff --git a/app/models/program.rb b/app/models/program.rb index e36d65bf..09bb3627 100644 --- a/app/models/program.rb +++ b/app/models/program.rb @@ -5,7 +5,7 @@ class Program < ActiveRecord::Base belongs_to :conference - has_one :cfp, dependent: :destroy + has_many :cfps, dependent: :destroy has_many :event_types, dependent: :destroy has_many :tracks, dependent: :destroy has_many :difficulty_levels, dependent: :destroy @@ -135,7 +135,7 @@ class Program < ActiveRecord::Base # * +false+ -> If the CFP is not set or today isn't in the CFP period. # * +true+ -> If today is in the CFP period. def cfp_open? - cfp = self.cfp + cfp = cfps.events cfp.present? && (cfp.start_date..cfp.end_date).cover?(Date.current) end @@ -163,6 +163,23 @@ class Program < ActiveRecord::Base EventSchedule.where(schedule: selected_schedule).where(start_time: parsed_date..(parsed_date + 1)).any? end + ## + # Provides backwards compatibility for when the program had one cfp + # + # ====Returns + # * +ActiveRecord+ -> The program's cfp with cfp_type == 'events' + def cfp + return nil if cfps.for_events.blank? + cfps.for_events + end + + ## + # ====Returns + # * +Array+ -> The types of cfps for which a cfp doesn't exist yet + def remaining_cfp_types + Cfp::TYPES - cfps.pluck(:cfp_type) + end + private ## diff --git a/db/migrate/20170530072155_add_type_to_cfps.rb b/db/migrate/20170530072155_add_type_to_cfps.rb new file mode 100644 index 00000000..50423281 --- /dev/null +++ b/db/migrate/20170530072155_add_type_to_cfps.rb @@ -0,0 +1,15 @@ +class AddTypeToCfps < ActiveRecord::Migration + class TmpCfp < ActiveRecord::Base + self.table_name = 'cfps' + end + + def change + add_column :cfps, :cfp_type, :string + + TmpCfp.reset_column_information + TmpCfp.find_each do |cfp| + cfp.cfp_type = 'events' + cfp.save! + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 19a3df19..348f6170 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -52,6 +52,7 @@ ActiveRecord::Schema.define(version: 20170603095900) do t.datetime "created_at" t.datetime "updated_at" t.integer "program_id" + t.string "cfp_type" end create_table "comments", force: :cascade do |t| diff --git a/spec/models/cfp_spec.rb b/spec/models/cfp_spec.rb index 3f8e7a0f..606f5f9f 100644 --- a/spec/models/cfp_spec.rb +++ b/spec/models/cfp_spec.rb @@ -1,8 +1,24 @@ require 'spec_helper' describe Cfp do + subject { create(:cfp) } let!(:conference) { create(:conference, end_date: Date.today) } - let!(:cfp) { build(:cfp, start_date: Date.today - 2, end_date: Date.today - 1, program_id: conference.program.id) } + let!(:cfp) { create(:cfp, start_date: Date.today - 2, end_date: Date.today - 1, program_id: conference.program.id) } + + describe 'scope' do + describe '#for_events' do + it 'returns the cfp for events' do + expect(conference.program.cfps.for_events).to be_a Cfp + expect(conference.program.cfps.for_events.cfp_type).to eq('events') + end + end + end + + describe 'validations' do + it { is_expected.to validate_presence_of(:cfp_type) } + it { is_expected.to validate_inclusion_of(:cfp_type).in_array(Cfp::TYPES) } + it { is_expected.to validate_uniqueness_of(:cfp_type).scoped_to(:program_id).case_insensitive } + end describe '#before_end_of_conference' do describe 'fails to save cfp' do @@ -97,4 +113,28 @@ describe Cfp do end end end + + describe '#open?' do + context 'returns false' do + it 'when start and end dates are in the past' do + cfp.start_date = Date.current - 3 + cfp.end_date = Date.current - 1 + expect(cfp.open?).to eq(false) + end + + it 'when start and end dates are in the future' do + cfp.start_date = Date.current + 1 + cfp.end_date = Date.current + 3 + expect(cfp.open?).to eq(false) + end + end + + context 'returns true' do + it 'when start date is in the past and end date is in the future' do + cfp.start_date = Date.current - 1 + cfp.end_date = Date.current + 1 + expect(cfp.open?).to eq(true) + end + end + end end diff --git a/spec/models/program_spec.rb b/spec/models/program_spec.rb index a2106791..6ea62cf0 100644 --- a/spec/models/program_spec.rb +++ b/spec/models/program_spec.rb @@ -240,4 +240,23 @@ describe Program do end end + describe '#cfp' do + it 'returns the cfp for events' do + create(:cfp, cfp_type: 'events', program: program, end_date: Date.current + 1) + expect(program.cfp).to be_a Cfp + expect(program.cfp.cfp_type).to eq('events') + end + + it 'returns nil if the program doesn\'t have a cfp' do + expect(program.cfp).to eq(nil) + end + end + + describe '#remaining_cfp_types' do + it 'returns an array with the types for which a cfp doesn\'t exist' do + expect(program.remaining_cfp_types).to eq(Cfp::TYPES) + create(:cfp, cfp_type: 'events', program: program, end_date: Date.current + 1) + expect(program.remaining_cfp_types).to eq([]) + end + end end