Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 1 addition & 41 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# This configuration was generated by
# `rubocop --auto-gen-config --auto-gen-only-exclude --no-exclude-limit`
# on 2026-07-28 07:06:57 UTC using RuboCop version 1.88.2.
# on 2026-07-28 07:23:34 UTC using RuboCop version 1.88.2.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
Expand Down Expand Up @@ -1059,46 +1059,6 @@ Style/StringConcatenation:
- 'spec/features/admin/manage_sponsor_spec.rb'
- 'spec/presenters/workshop_presenter_spec.rb'

# Offense count: 173
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline.
# SupportedStyles: single_quotes, double_quotes
Style/StringLiterals:
Exclude:
- 'Gemfile'
- 'app/controllers/admin/chapters_controller.rb'
- 'app/controllers/events_controller.rb'
- 'app/mailers/member_mailer.rb'
- 'app/models/workshop.rb'
- 'lib/omniauth/strategies/codebar.rb'
- 'script/benchmark_events.rb'
- 'spec/components/event_card_component_spec.rb'
- 'spec/controllers/admin/invitations_controller_spec.rb'
- 'spec/controllers/admin/member_search_controller_spec.rb'
- 'spec/controllers/auth_services_controller_spec.rb'
- 'spec/controllers/members_controller_spec.rb'
- 'spec/fabricators/member_email_delivery_fabricator.rb'
- 'spec/features/accepting_invitation_spec.rb'
- 'spec/features/admin/manage_sponsor_spec.rb'
- 'spec/features/member_joining_spec.rb'
- 'spec/features/member_updating_details_spec.rb'
- 'spec/features/viewing_a_workshop_invitation_spec.rb'
- 'spec/features/viewing_a_workshop_spec.rb'
- 'spec/lib/services/flodesk_spec.rb'
- 'spec/lib/services/mailing_list_spec.rb'
- 'spec/lib/tasks/delete_member_rake_spec.rb'
- 'spec/lib/tasks/mailing_list_rake_spec.rb'
- 'spec/mailers/member_mailer_spec.rb'
- 'spec/mailers/previews/meeting_invitation_mailer_preview.rb'
- 'spec/models/event_spec.rb'
- 'spec/models/meeting_spec.rb'
- 'spec/models/workshop_spec.rb'
- 'spec/presenters/workshop_presenter_capacity_spec.rb'
- 'spec/serializers/workshop_calendar_spec.rb'
- 'spec/services/three_month_email_service_spec.rb'
- 'spec/support/select_from_tom_select.rb'
- 'spec/support/shared_examples/behaves_link_member_viewing_workshop.rb'

# Offense count: 10
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle, MinSize.
Expand Down
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,4 @@ gem 'scout_apm'
gem 'carrierwave-aws', '~> 1.6'
gem 'sitemap_generator', '~> 7.1'

gem "solid_cache", "~> 1.0"
gem 'solid_cache', '~> 1.0'
4 changes: 2 additions & 2 deletions app/controllers/admin/chapters_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -114,9 +114,9 @@ def set_chapter
def member_emails(chapter, type)
members =
case type
when "students"
when 'students'
chapter.students
when "coaches"
when 'coaches'
chapter.coaches
else
chapter.members
Expand Down
22 changes: 11 additions & 11 deletions app/controllers/events_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def fetch_past_events
def paginated_events(upcoming:)
now = Time.zone.now
page = (params[:page] || 1).to_i
direction = upcoming ? "ASC" : "DESC"
direction = upcoming ? 'ASC' : 'DESC'
comparator = upcoming ? :gteq : :lt

# Pure Arel subqueries — no ActiveRecord relation bind params to leak
Expand All @@ -128,7 +128,7 @@ def paginated_events(upcoming:)

# COUNT at DB level — single query
count_query = Arel::SelectManager.new
count_query.from(union.as("events"))
count_query.from(union.as('events'))
count_query.project(Arel.star.count)
total = ActiveRecord::Base.connection.select_value(count_query.to_sql).to_i
return nil if total.zero?
Expand All @@ -139,7 +139,7 @@ def paginated_events(upcoming:)

# Only 20 rows leave the database
pagination_query = Arel::SelectManager.new
pagination_query.from(union.as("events"))
pagination_query.from(union.as('events'))
pagination_query.project(:id, :event_type)
pagination_query.order(Arel.sql("date_and_time #{direction}"))
pagination_query.skip(pagy.offset).take(pagy.limit)
Expand All @@ -152,23 +152,23 @@ def paginated_events(upcoming:)
# with eager loading, preserving the UNION order.
def load_events(rows)
grouped = rows.each_with_object({}) do |row, hash|
(hash[row["event_type"]] ||= []) << row["id"].to_i
(hash[row['event_type']] ||= []) << row['id'].to_i
end

workshops = Workshop.includes(:chapter, :sponsors, :host, :permissions)
.where(id: grouped["Workshop"])
.where(id: grouped['Workshop'])
.to_a.index_by(&:id)
meetings = Meeting.includes(:venue).where(id: grouped["Meeting"])
meetings = Meeting.includes(:venue).where(id: grouped['Meeting'])
.to_a.index_by(&:id)
events = Event.includes(:venue, :sponsors, :sponsorships, :permissions)
.where(id: grouped["Event"])
.where(id: grouped['Event'])
.to_a.index_by(&:id)

rows.filter_map do |row|
case row["event_type"]
when "Workshop" then workshops[row["id"].to_i]
when "Meeting" then meetings[row["id"].to_i]
when "Event" then events[row["id"].to_i]
case row['event_type']
when 'Workshop' then workshops[row['id'].to_i]
when 'Meeting' then meetings[row['id'].to_i]
when 'Event' then events[row['id'].to_i]
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion app/mailers/member_mailer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class MemberMailer < ApplicationMailer

def chaser
@member = params[:member]
subject = "It’s been a while, how are you doing? ♥️"
subject = 'It’s been a while, how are you doing? ♥️'
mail_to_member(@member, subject, 'hello@codebar.io', 'hello@codebar.io') do |format|
format.html { render 'three_month_chaser' }
end
Expand Down
2 changes: 1 addition & 1 deletion app/models/workshop.rb
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,6 @@ def rsvp_close_before_workshop_start
return unless rsvp_closes_at && date_and_time
return if rsvp_closes_at < date_and_time

errors.add(:rsvp_close_local_date, "must be before the workshop start time")
errors.add(:rsvp_close_local_date, 'must be before the workshop start time')
end
end
2 changes: 1 addition & 1 deletion lib/omniauth/strategies/codebar.rb
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ def verify_jwt(token)
nil
end
rescue JWT::ExpiredSignature
Rails.logger.warn "Codebar auth: JWT expired"
Rails.logger.warn 'Codebar auth: JWT expired'
nil
end

Expand Down
10 changes: 5 additions & 5 deletions script/benchmark_events.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
#!/usr/bin/env ruby
# Run: DB_NAME=codebar_dump bundle exec ruby script/benchmark_events.rb
ENV["RAILS_ENV"] ||= "development"
require_relative "../config/environment"
require "benchmark"
ENV['RAILS_ENV'] ||= 'development'
require_relative '../config/environment'
require 'benchmark'

session = ActionDispatch::Integration::Session.new(Rails.application)
session.host = "localhost"
session.host = 'localhost'

def measure(session, path)
qc = 0
cb = ->(*, **) { qc += 1 }
time = nil
ActiveSupport::Notifications.subscribed(cb, "sql.active_record") do
ActiveSupport::Notifications.subscribed(cb, 'sql.active_record') do
time = Benchmark.measure { session.get(path) }
end
[time.real, qc]
Expand Down
42 changes: 21 additions & 21 deletions spec/components/event_card_component_spec.rb
Original file line number Diff line number Diff line change
@@ -1,89 +1,89 @@
require "rails_helper"
require "view_component/test_helpers"
require 'rails_helper'
require 'view_component/test_helpers'

RSpec.describe EventCardComponent, type: :component do
include ViewComponent::TestHelpers
let(:chapter) { Fabricate(:chapter, active: true) }

context "with a workshop" do
context 'with a workshop' do
let(:workshop) { Fabricate(:workshop, chapter: chapter) }
let(:presenter) { WorkshopPresenter.new(workshop) }

it "renders the workshop card" do
it 'renders the workshop card' do
render_inline(described_class.new(event_card: presenter))
expect(page).to have_css("[data-test='event']")
expect(page).to have_link(presenter.to_s)
expect(page).to have_text(presenter.date)
end

it "does not render user-specific badges without a user" do
it 'does not render user-specific badges without a user' do
render_inline(described_class.new(event_card: presenter))
expect(page).to have_no_text("Attending")
expect(page).to have_no_text("Manage")
expect(page).to have_no_text('Attending')
expect(page).to have_no_text('Manage')
end

it "renders chapter badge" do
it 'renders chapter badge' do
render_inline(described_class.new(event_card: presenter))
expect(page).to have_link(chapter.name)
end
end

context "with a meeting" do
context 'with a meeting' do
let(:meeting) { Fabricate(:meeting) }
let(:presenter) { MeetingPresenter.new(meeting) }

it "renders the meeting card" do
it 'renders the meeting card' do
render_inline(described_class.new(event_card: presenter))
expect(page).to have_css("[data-test='event']")
expect(page).to have_link(presenter.name)
end

it "renders venue image for meetings" do
it 'renders venue image for meetings' do
render_inline(described_class.new(event_card: presenter))
expect(page).to have_css(%(img[alt="#{meeting.venue.name}"]))
end

it "does not render sponsor logos for meetings" do
it 'does not render sponsor logos for meetings' do
render_inline(described_class.new(event_card: presenter))
# Venue image has sponsor-sm class, so check for mx-1 spacing (used only by sponsors)
expect(page).to have_no_css(".mx-1")
expect(page).to have_no_css('.mx-1')
end
end

context "with an event" do
context 'with an event' do
let(:event) { Fabricate(:event) }
let(:presenter) { EventPresenter.new(event) }

it "renders the event card" do
it 'renders the event card' do
render_inline(described_class.new(event_card: presenter))
expect(page).to have_css("[data-test='event']")
expect(page).to have_link(event.name)
end

it "renders sponsor logos for events" do
it 'renders sponsor logos for events' do
sponsor = Fabricate(:sponsor)
event.sponsors << sponsor
render_inline(described_class.new(event_card: presenter))
expect(page).to have_css(%(img[alt="#{sponsor.name}"]))
end
end

context "with a user" do
context 'with a user' do
let(:workshop) { Fabricate(:workshop, chapter: chapter) }
let(:presenter) { WorkshopPresenter.new(workshop) }
let(:member) { Fabricate(:member) }

it "renders attending badge when user is attending (as presenter)" do
it 'renders attending badge when user is attending (as presenter)' do
Fabricate(:workshop_invitation, workshop: workshop, member: member, attending: true)
user_presenter = MemberPresenter.new(member)
render_inline(described_class.new(event_card: presenter, user: user_presenter))
expect(page).to have_text("Attending")
expect(page).to have_text('Attending')
end

it "renders attending badge when raw Member is passed" do
it 'renders attending badge when raw Member is passed' do
Fabricate(:workshop_invitation, workshop: workshop, member: member, attending: true)
render_inline(described_class.new(event_card: presenter, user: member))
expect(page).to have_text("Attending")
expect(page).to have_text('Attending')
end
end

Expand Down
28 changes: 14 additions & 14 deletions spec/controllers/admin/invitations_controller_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,67 +3,67 @@
let(:workshop) { invitation.workshop }
let(:admin) { Fabricate(:chapter_organiser) }

describe "PUT #update" do
describe 'PUT #update' do
before do
admin.add_role(:organiser, workshop.chapter)

login admin
request.env["HTTP_REFERER"] = "/admin/member/3"
request.env['HTTP_REFERER'] = '/admin/member/3'
end

it "Successfuly updates an invitation" do
it 'Successfuly updates an invitation' do
expect(invitation.attending).to be_nil

put :update, params: { id: invitation.token, workshop_id: workshop.id, attending: "true" }
put :update, params: { id: invitation.token, workshop_id: workshop.id, attending: 'true' }

expect(invitation.reload.attending).to be true
expect(flash[:notice]).to match("You have added")
expect(flash[:notice]).to match('You have added')
end

# While similar to the previous test, this specifically tests that organisers
# have the ability to manually add a student to the workshop that has not
# selected a tutorial. This is helpful for when a student shows up for a
# workshop they have not have a spot — this happens from time to time.
it "Successfuly adds a user as attenting, even without a tutorial" do
it 'Successfuly adds a user as attenting, even without a tutorial' do
invitation.update_attribute(:tutorial, nil)
expect(invitation.automated_rsvp).to be_nil

put :update, params: { id: invitation.token, workshop_id: workshop.id, attending: "true" }
put :update, params: { id: invitation.token, workshop_id: workshop.id, attending: 'true' }
invitation.reload

expect(invitation.attending).to be true
expect(invitation.automated_rsvp).to be true
expect(flash[:notice]).to match("You have added")
expect(flash[:notice]).to match('You have added')
end

it "Records the organiser ID that overrides an invitations" do
put :update, params: { id: invitation.token, workshop_id: workshop.id, attending: "true" }
it 'Records the organiser ID that overrides an invitations' do
put :update, params: { id: invitation.token, workshop_id: workshop.id, attending: 'true' }
invitation.reload

expect(invitation.last_overridden_by_id).to be admin.id
end
end

describe "PUT #update with attended param" do
describe 'PUT #update with attended param' do
let(:workshop) { Fabricate(:workshop, date_and_time: Time.zone.now - 1.day) }
let(:invitation) { Fabricate(:workshop_invitation, workshop: workshop, attending: true) }
let(:admin) { Fabricate(:chapter_organiser) }

before do
admin.add_role(:organiser, workshop.chapter)
login admin
request.env["HTTP_REFERER"] = "/admin/workshop/#{workshop.id}"
request.env['HTTP_REFERER'] = "/admin/workshop/#{workshop.id}"
end

it "renders the attendance row partial via XHR when verifying" do
it 'renders the attendance row partial via XHR when verifying' do
put :update, params: { id: invitation.token, workshop_id: workshop.id, attended: true }, xhr: true

expect(response).to have_http_status(:success)
expect(response.media_type).to eq('text/html')
expect(invitation.reload.attended).to be true
end

it "renders the attendance row partial via XHR when unverifying" do
it 'renders the attendance row partial via XHR when unverifying' do
invitation.update!(attended: true)

put :update, params: { id: invitation.token, workshop_id: workshop.id, attended: false }, xhr: true
Expand Down
Loading