Skip to content

Commit f9f111d

Browse files
committed
feat: add accept! and decline! to WorkshopInvitation
Replaces update_attribute usage with semantic methods that run validations. - accept!(rsvp_time:, automated_rsvp:) - confirms attendance with timestamp - decline! - cancels attendance Both methods use update! and raise RecordInvalid on validation failures. Includes unit tests.
1 parent 545bb52 commit f9f111d

File tree

2 files changed

+57
-3
lines changed

2 files changed

+57
-3
lines changed

app/models/workshop_invitation.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,12 @@ def student_attending?
4747
def not_attending?
4848
attending == false
4949
end
50+
51+
def accept!(rsvp_time: Time.zone.now, automated_rsvp: false)
52+
update!(attending: true, rsvp_time: rsvp_time, automated_rsvp: automated_rsvp)
53+
end
54+
55+
def decline!
56+
update!(attending: false)
57+
end
5058
end

spec/models/workshop_invitation_spec.rb

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
RSpec.describe WorkshopInvitation do
22
subject(:workshop_invitation) { Fabricate(:workshop_invitation) }
3+
34
it_behaves_like InvitationConcerns, :workshop_invitation, :workshop
45

56
context 'defaults' do
@@ -14,13 +15,14 @@
1415

1516
context 'if Student invitation' do
1617
before { allow(subject).to receive(:student_attending?).and_return(true) }
18+
1719
it { is_expected.to validate_presence_of(:tutorial) }
1820
it { is_expected.to validate_presence_of(:tutorial).on(:waitinglist) }
1921
end
2022
end
2123

2224
context 'scopes' do
23-
context '#attended' do
25+
describe '#attended' do
2426
it 'ignores when attended nil' do
2527
Fabricate(:workshop_invitation, attended: nil)
2628

@@ -40,7 +42,7 @@
4042
end
4143
end
4244

43-
context '#accepted_or_attended' do
45+
describe '#accepted_or_attended' do
4446
it 'ignores when attending nil and attended nil' do
4547
Fabricate(:workshop_invitation, attending: nil, attended: nil)
4648

@@ -83,7 +85,7 @@
8385
expect(WorkshopInvitation.year((Time.zone.now - 2.years).year).count).to eq(1)
8486
end
8587

86-
context '#not_reminded' do
88+
describe '#not_reminded' do
8789
it 'includes invitations without reminders' do
8890
not_reminded = Fabricate(:student_workshop_invitation, reminded_at: nil)
8991

@@ -104,4 +106,48 @@
104106
expect(WorkshopInvitation.on_waiting_list).to eq(waiting_list)
105107
end
106108
end
109+
110+
describe '#accept!' do
111+
let(:invitation) { Fabricate(:workshop_invitation, attending: false) }
112+
113+
it 'sets attending to true' do
114+
invitation.accept!
115+
expect(invitation.reload.attending).to be true
116+
end
117+
118+
it 'sets rsvp_time to current time by default' do
119+
invitation.accept!
120+
expect(invitation.reload.rsvp_time).to be_within(1.second).of(Time.zone.now)
121+
end
122+
123+
it 'allows custom rsvp_time' do
124+
custom_time = 1.day.ago
125+
invitation.accept!(rsvp_time: custom_time)
126+
expect(invitation.reload.rsvp_time).to eq(custom_time)
127+
end
128+
129+
it 'allows automated_rsvp flag' do
130+
invitation.accept!(automated_rsvp: true)
131+
expect(invitation.reload.automated_rsvp).to be true
132+
end
133+
134+
it 'raises RecordInvalid on validation failure' do
135+
allow(invitation).to receive(:valid?).and_return(false)
136+
expect { invitation.accept! }.to raise_error(ActiveRecord::RecordInvalid)
137+
end
138+
end
139+
140+
describe '#decline!' do
141+
let(:invitation) { Fabricate(:workshop_invitation, attending: true) }
142+
143+
it 'sets attending to false' do
144+
invitation.decline!
145+
expect(invitation.reload.attending).to be false
146+
end
147+
148+
it 'raises RecordInvalid on validation failure' do
149+
allow(invitation).to receive(:valid?).and_return(false)
150+
expect { invitation.decline! }.to raise_error(ActiveRecord::RecordInvalid)
151+
end
152+
end
107153
end

0 commit comments

Comments
 (0)