Skip to content

Commit

Permalink
Prevent deletion of course which has ClassSchedules or ExaminationRes…
Browse files Browse the repository at this point in the history
…ults
  • Loading branch information
mpugach committed Mar 6, 2023
1 parent b0b6ac2 commit a54b353
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 21 deletions.
33 changes: 30 additions & 3 deletions app/controllers/courses_controller.rb
Expand Up @@ -49,9 +49,36 @@ def update
end

def destroy
@course.destroy.destroyed?

respond_with(@course)
errors = []

if @course.class_schedules_count.positive?
errors << t(
'courses.destroy.remove_class_schedules_first',
class_schedules_count: @course.class_schedules_count
)
end

if @course.examination_results_count.positive?
errors << t(
'courses.destroy.remove_examination_results_first',
examination_results_count: @course.examination_results_count
)
end

if errors.any?
joined_errors = "#{t('courses.destroy.unable_to_destroy')} #{errors.join(', ')}"

redirect_back(
fallback_location: courses_path,
flash: {
danger: joined_errors
}
)
else
@course.destroy

respond_with(@course)
end
end

private
Expand Down
10 changes: 10 additions & 0 deletions app/helpers/courses_helper.rb
@@ -0,0 +1,10 @@
module CoursesHelper
def link_to_destroy_course(course)
link_to_disabled_destroy(
policy(course).destroy?,
course.class_schedules_count.positive? || course.examination_results_count.positive?,
course_path(course),
t('courses.destroy.prerequisites_are_not_met')
)
end
end
2 changes: 1 addition & 1 deletion app/views/courses/index.html.haml
Expand Up @@ -35,4 +35,4 @@
- if policy(course).tap { |p| break p.edit? || p.destroy? }
%td.col-xs-2.col-sm-3.col-md-2.text-right
= link_to_edit(policy(course).edit?, edit_course_path(course))
= link_to_destroy(policy(course).destroy?, course_path(course))
= link_to_destroy_course(course)
2 changes: 1 addition & 1 deletion app/views/courses/show.html.haml
Expand Up @@ -10,7 +10,7 @@

.pull-right
= link_to_edit(policy(@course).edit?, edit_course_path(@course))
= link_to_destroy(policy(@course).destroy?, course_path(@course))
= link_to_destroy_course(@course)

.col-xs-12.col-sm-8.col-sm-offset-2.vert-offset-top-1
%h5
Expand Down
5 changes: 5 additions & 0 deletions config/locales/general.ru.yml
Expand Up @@ -137,6 +137,11 @@ ru:
results_per_examination: 'Количество сданных зачетных работ'
course_description_help: 'Может отображаться студентам'
course_variant_help: 'Отображается только администраторам, чтобы отличать очень похожие предметы'
destroy:
unable_to_destroy: 'Невозможно удалить предмет так как'
remove_class_schedules_first: "с ним связаны %{class_schedules_count} расписаний занятий"
remove_examination_results_first: 'он имеет %{examination_results_count} сданных зачетных работ'
prerequisites_are_not_met: "С этим предметом связаны расписания занятий и сданы зачетные работы. Сначала уберите эти связи"
crops:
crop_image:
title: 'Кадрирование изображения'
Expand Down
5 changes: 5 additions & 0 deletions config/locales/general.uk.yml
Expand Up @@ -137,6 +137,11 @@ uk:
results_per_examination: 'Кількість зданих залікових робіт'
course_description_help: 'Може відображатися студентам'
course_variant_help: 'Відображається тільки адміністраторам, щоб відрізняти дуже схожі предмети'
destroy:
unable_to_destroy: 'Неможливо видалити предмет бо'
remove_class_schedules_first: "з ним пов'язано %{class_schedules_count} розкладів занять"
remove_examination_results_first: 'він має %{examination_results_count} зданих залікових робіт'
prerequisites_are_not_met: "З цим предметом пов`язані розклади занять та здані залікові роботи. Спочатку приберіть ці зв'язки"
crops:
crop_image:
title: 'Кадрування зображення'
Expand Down
53 changes: 52 additions & 1 deletion spec/controllers/courses_controller_spec.rb
Expand Up @@ -113,16 +113,67 @@

describe '#destroy' do
Given(:actions) { ['course:destroy'] }
Given!(:course) { create :course }

Given { expect(ClassScheduleWithPeople).to receive(:refresh_later) }

describe 'with success' do
Given!(:course) { create :course }

Then { expect { delete :destroy, params: { id: course.id } }.to change(Course, :count).by(-1) }
And { expect(response).to redirect_to(courses_path) }
And { is_expected.to set_flash[:notice] }
And { expect(assigns(:course)).to eq(course) }
end

shared_examples :courses_destroy_prerequisites_are_not_met do
Then { expect { delete :destroy, params: { id: course.id } }.not_to change(Course, :count) }
And { expect(response).to redirect_to(courses_path) }
And { is_expected.to set_flash[:danger].to(expected_flash_message) }
And { expect(assigns(:course)).to eq(course) }
end

describe 'class_schedules_count is not zero' do
Given!(:course) { create :course, class_schedules_count: 1 }
Given(:expected_flash_message) do
I18n.t('courses.destroy.unable_to_destroy') + ' ' + I18n.t(
'courses.destroy.remove_class_schedules_first',
class_schedules_count: 1
)
end

it_behaves_like :courses_destroy_prerequisites_are_not_met
end

describe 'examination_results_count is not zero' do
Given!(:course) { create :course, examination_results_count: 2 }
Given(:expected_flash_message) do
I18n.t('courses.destroy.unable_to_destroy') + ' ' + I18n.t(
'courses.destroy.remove_examination_results_first',
examination_results_count: 2
)
end

it_behaves_like :courses_destroy_prerequisites_are_not_met
end

describe 'both class_schedules_count and examination_results_count are not zero' do
Given!(:course) { create :course, class_schedules_count: 1, examination_results_count: 2 }
Given(:expected_flash_message) do
I18n.t('courses.destroy.unable_to_destroy') +
' ' +
I18n.t(
'courses.destroy.remove_class_schedules_first',
class_schedules_count: 1
) +
', ' +
I18n.t(
'courses.destroy.remove_examination_results_first',
examination_results_count: 2
)
end

it_behaves_like :courses_destroy_prerequisites_are_not_met
end
end
end
end
Expand Down
39 changes: 32 additions & 7 deletions spec/views/courses/index.html.haml_spec.rb
Expand Up @@ -4,7 +4,7 @@
Given(:page) { Capybara::Node::Simple.new(response.body) }
Given(:user) { create :person, roles: [create(:role, activities: activities)] }
Given(:course) { create :course }
Given(:activities) { %w(course:index) }
Given(:activities) { %w[course:index] }

Given { allow(view).to receive(:policy).with(course).and_return(CoursePolicy.new(user, course)) }

Expand Down Expand Up @@ -36,7 +36,7 @@
end

context 'with :show rights' do
Given(:activities) { %w(course:index course:show) }
Given(:activities) { %w[course:index course:show] }

Then { expect(row).to have_link(course.title, href: course_path(course)) }
And { no_new_link }
Expand All @@ -45,7 +45,7 @@
end

context 'with :new rights' do
Given(:activities) { %w(course:index course:new) }
Given(:activities) { %w[course:index course:new] }

Then { expect(table_container).to have_link('', href: new_course_path) }
And { no_show_link }
Expand All @@ -54,7 +54,7 @@
end

context 'with :edit rights' do
Given(:activities) { %w(course:index course:edit) }
Given(:activities) { %w[course:index course:edit] }

Then { expect(table_container).to have_link(I18n.t('links.edit'), href: edit_course_path(course)) }
And { no_show_link }
Expand All @@ -63,9 +63,34 @@
end

context 'with :destroy rights' do
Given(:activities) { %w(course:index course:destroy) }

Then { expect(table_container).to have_link(I18n.t('links.delete'), href: "/courses/#{course.id}") }
Given(:course_with_schedule) { create :course, class_schedules_count: 1 }
Given(:course_with_examination_results) { create :course, examination_results_count: 2 }
Given(:course_with_both) { create :course, class_schedules_count: 1, examination_results_count: 2 }

Given(:activities) { %w[course:index course:destroy] }
Given(:have_disabled_destroy_link) { have_selector('.disabled-button-with-popover a.btn-danger.disabled') }

Given { allow(view).to receive(:policy).with(course_with_schedule).and_return(CoursePolicy.new(user, course_with_schedule)) }
Given { allow(view).to receive(:policy).with(course_with_examination_results).and_return(CoursePolicy.new(user, course_with_examination_results)) }
Given { allow(view).to receive(:policy).with(course_with_both).and_return(CoursePolicy.new(user, course_with_both)) }
Given { assign(:courses, [course, course_with_schedule, course_with_examination_results, course_with_both]) }

def course_row_by_title(course)
table_container.find('tbody tr', text: course.title)
end

def have_course_link(course)
have_link(I18n.t('links.delete'), href: "/courses/#{course.id}")
end

Then { expect(course_row_by_title(course)).to have_course_link(course) }
And { expect(course_row_by_title(course)).not_to have_disabled_destroy_link }
And { expect(course_row_by_title(course_with_schedule)).to have_disabled_destroy_link }
And { expect(course_row_by_title(course_with_examination_results)).to have_disabled_destroy_link }
And { expect(course_row_by_title(course_with_both)).to have_disabled_destroy_link }
And { expect(course_row_by_title(course_with_schedule)).not_to have_course_link(course_with_schedule) }
And { expect(course_row_by_title(course_with_examination_results)).not_to have_course_link(course_with_examination_results) }
And { expect(course_row_by_title(course_with_both)).not_to have_course_link(course_with_both) }
And { no_show_link }
And { no_edit_link }
And { no_new_link }
Expand Down
50 changes: 42 additions & 8 deletions spec/views/courses/show.html.haml_spec.rb
Expand Up @@ -5,13 +5,15 @@
Given(:user) { create :person, roles: [create(:role, activities: activities)] }
Given(:course) { create :course, title: course_title }
Given(:academic_group) { create :academic_group }
Given(:activities) { %w(course:show) }
Given(:activities) { %w[course:show] }
Given(:course_title) { 'Bhakta Program' }

Given { allow(view).to receive(:policy).with(course).and_return(CoursePolicy.new(user, course)) }
Given { allow(view).to receive(:policy).with(Course).and_return(CoursePolicy.new(user, Course)) }
Given { allow(view).to receive(:policy).with(Examination).and_return(CoursePolicy.new(user, Examination)) }
Given { allow(view).to receive(:policy).with(academic_group).and_return(AcademicGroupPolicy.new(user, academic_group)) }
Given do
allow(view).to receive(:policy).with(academic_group).and_return(AcademicGroupPolicy.new(user, academic_group))
end

Given { assign(:course, course) }
Given { assign(:academic_groups, [academic_group]) }
Expand All @@ -36,23 +38,55 @@
end

context 'with course:edit rights' do
Given(:activities) { %w(course:edit) }
Given(:activities) { %w[course:edit] }

Then { expect(container).to(have_edit_course_link) }
And { expect_no_destroy_link }
And { expect_no_academic_group_link }
end

context 'with course:destroy rights' do
Given(:activities) { %w(course:destroy) }
Given(:activities) { %w[course:destroy] }

Then { expect(container).to(have_destroy_course_link) }
And { expect_no_edit_link }
And { expect_no_academic_group_link }
describe 'with enabled "destroy" link' do
Then { expect(container).to(have_destroy_course_link) }
And { expect_no_edit_link }
And { expect_no_academic_group_link }
end

describe 'with disabled "destroy" link' do
shared_examples :courses_destroy_is_disabled do
Then { expect(container).not_to(have_destroy_course_link) }

And do
expect(container).to(
have_selector('.col-xs-12:nth-child(2) .disabled-button-with-popover a.btn-danger.disabled')
)
end
end

describe 'course has ClassSchedules' do
Given(:course) { create :course, title: course_title, class_schedules_count: 1 }

it_behaves_like :courses_destroy_is_disabled
end

describe 'course has ExaminationResults' do
Given(:course) { create :course, title: course_title, examination_results_count: 2 }

it_behaves_like :courses_destroy_is_disabled
end

describe 'course has both ClassSchedules and ExaminationResults' do
Given(:course) { create :course, title: course_title, class_schedules_count: 1, examination_results_count: 2 }

it_behaves_like :courses_destroy_is_disabled
end
end
end

context 'with academic_group:show rights' do
Given(:activities) { %w(academic_group:show) }
Given(:activities) { %w[academic_group:show] }

Then { expect(container).to(have_academic_group_link) }
And { expect_no_destroy_link }
Expand Down

0 comments on commit a54b353

Please sign in to comment.