Skip to content
Open
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
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -129,3 +129,5 @@ gem 'scout_apm'

gem 'carrierwave-aws', '~> 1.6'
gem 'sitemap_generator', '~> 7.0'

gem "solid_cache", "~> 1.0"
6 changes: 6 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,10 @@ GEM
snaky_hash (2.0.6)
hashie (>= 0.1.0, < 6)
version_gem (>= 1.1.8, < 3)
solid_cache (1.0.10)
activejob (>= 7.2)
activerecord (>= 7.2)
railties (>= 7.2)
sprockets (4.2.2)
concurrent-ruby (~> 1.0)
logger
Expand Down Expand Up @@ -697,6 +701,7 @@ DEPENDENCIES
simplecov
simplecov-lcov
sitemap_generator (~> 7.0)
solid_cache (~> 1.0)
sprockets-rails
stimulus-rails
stripe
Expand Down Expand Up @@ -916,6 +921,7 @@ CHECKSUMS
sitemap_generator (7.0.1) sha256=cb436da1c6cb073261a63c27e714e27b8292fca1e17ad503dac1db6518b3d2a8
slop (3.6.0) sha256=76ccab03be66bfcab4838cdc07cab019cd3e192a3538266246749e79e4788803
snaky_hash (2.0.6) sha256=3663cae48cdef582b517025cf8a39d8789996eaf0b4ed89e2f0624836505654a
solid_cache (1.0.10) sha256=bc05a2fb3ac78a6f43cbb5946679cf9db67dd30d22939ededc385cb93e120d41
sprockets (4.2.2) sha256=761e5a49f1c288704763f73139763564c845a8f856d52fba013458f8af1b59b1
sprockets-rails (3.5.2) sha256=a9e88e6ce9f8c912d349aa5401509165ec42326baf9e942a85de4b76dbc4119e
ssrf_filter (1.5.0) sha256=e03dcdb9d1730d7f6710532a606b3543df2a448a0293ce04a2d995523c5a97f6
Expand Down
75 changes: 75 additions & 0 deletions app/components/event_card_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
<% cache @event.cache_key_with_version do %>
<div class="card mb-4" data-test="event">
<div class="card-body">
<div class="d-md-flex justify-content-md-between">
<div class="order-md-2">
<% if @event.chapter %>
<span class="badge bg-primary mb-3 mb-md-0">
<%= link_to @event.chapter.name, @event.chapter.slug, class: "text-light text-decoration-none" %>
</span>
<% end %>
<% if user_presenter %>
<% if user_presenter.attending?(@event) %>
<span class="badge bg-success mb-3 mb-md-0">
<%= link_to "Attending", @event.path, class: "text-light text-decoration-none" %>
</span>
<% end %>
<% if user_presenter.organiser? && user_presenter.event_organiser?(@event) %>
<span class="badge bg-secondary mb-3 mb-md-0">
<%= link_to "Manage", @event.admin_path, class: "text-light text-decoration-none" %>
</span>
<% end %>
<% end %>
</div>
<div class="order-md-1">
<h3 class="h5">
<%= link_to @event.to_s, @event.path %>
<% if @event.venue.present? %>
at
<%= link_to @event.venue.name, @event.venue.website %>
<% end %>
</h3>
</div>
</div>

<div class="mt-3">
<p class="mb-0">
<i class="far fa-calendar"></i>
<%= @event.date %>
</p>
<p>
<i class="far fa-clock"></i>
<%= @event.time %>
</p>
</div>

<div class="d-md-flex justify-content-md-between align-items-md-center">
<% if @event.organisers.any? %>
<div class="mb-3 mb-md-0">
<% @event.organisers.each do |organiser| %>
<%= image_tag(organiser.avatar(26), class: "rounded-circle", title: organiser.full_name, alt: organiser.full_name) %>
<% end %>
</div>
<% end %>

<% if @event.sponsors.any? %>
<div class="ms-auto">
<% @event.sponsors.each do |sponsor| %>
<%= link_to sponsor.website, class: "d-inline-block" do %>
<%= image_tag(sponsor.avatar.thumb.url, class: "sponsor-sm mx-1", alt: sponsor.name) %>
<% end %>
<% end %>
</div>
<% end %>

<% if @event.is_a?(MeetingPresenter) && @event.venue.present? %>
<div class="ms-auto">
<%= link_to @event.venue.website, class: "d-inline-block" do %>
<%= image_tag(@event.venue.avatar.url, class: "sponsor-sm", alt: @event.venue.name) %>
<% end %>
</div>
<% end %>
</div>
</div>
</div>
<% end %>
11 changes: 11 additions & 0 deletions app/components/event_card_component.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
class EventCardComponent < ViewComponent::Base
def initialize(event_card:, user: nil)
@event = event_card
@user = user
end

# Wraps raw Member in MemberPresenter; double-wrapping a presenter is a no-op.
def user_presenter
@user && MemberPresenter.new(@user)
end
end
4 changes: 2 additions & 2 deletions app/views/chapter/show.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,13 @@
%h2.mb-4= t('homepage.events.upcoming')
- @upcoming_workshops.each do |date, workshops|
%h3.h5= date
= render workshops
= render EventCardComponent.with_collection(workshops)

- if @latest_workshops.any?
.pt-4
%h2.mb-4 Past Events
- @latest_workshops.each do |date, workshops|
= render workshops
= render EventCardComponent.with_collection(workshops)

- if @recent_sponsors.any?
.py-4.py-lg-5.bg-light
Expand Down
2 changes: 1 addition & 1 deletion app/views/dashboard/dashboard.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,4 @@
There are no upcoming events announced for the chapters you are subscribed to.
- @ordered_events.each do |date, workshops|
%h4= date
= render workshops
= render EventCardComponent.with_collection(workshops, user: current_user)
2 changes: 1 addition & 1 deletion app/views/dashboard/show.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
%h2.h3.mb-4= t('homepage.events.upcoming')
- @upcoming_workshops.each do |date, workshops|
%h3.h5= date
= render workshops
= render EventCardComponent.with_collection(workshops, user: current_user)

- if @has_more_events
= link_to 'Explore all events →', upcoming_events_path, class: 'btn btn-outline-primary mt-3'
Expand Down
40 changes: 0 additions & 40 deletions app/views/events/_event.html.haml

This file was deleted.

4 changes: 2 additions & 2 deletions app/views/events/_events.html.haml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
- grouped_events.each do |date, workshops|
- grouped_events.each do |date, events|
.row
.col-12
%h3.h5= date
.row
.col-md-8
= render workshops
= render EventCardComponent.with_collection(events)
39 changes: 0 additions & 39 deletions app/views/meetings/_meeting.html.haml

This file was deleted.

1 change: 0 additions & 1 deletion app/views/workshops/_workshop.html.haml

This file was deleted.

17 changes: 17 additions & 0 deletions config/cache.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
development:
store_options:
max_age: <%= 60.days.to_i %>
max_size: <%= 256.megabytes %>
namespace: <%= Rails.env %>

test:
store_options:
max_age: <%= 60.days.to_i %>
max_size: <%= 256.megabytes %>
namespace: <%= Rails.env %>

production:
store_options:
max_age: <%= 60.days.to_i %>
max_size: <%= 500.megabytes %>
namespace: <%= Rails.env %>
2 changes: 1 addition & 1 deletion config/environments/production.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
config.active_support.report_deprecations = false

# Replace the default in-process memory cache store with a durable alternative.
# config.cache_store = :mem_cache_store
config.cache_store = :solid_cache_store

# Replace the default in-process and non-durable queuing backend for Active Job.
# config.active_job.queue_adapter = :resque
Expand Down
12 changes: 12 additions & 0 deletions db/cache_schema.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
ActiveRecord::Schema[7.2].define(version: 1) do
create_table "solid_cache_entries", force: :cascade do |t|
t.binary "key", limit: 1024, null: false
t.binary "value", limit: 536870912, null: false
t.datetime "created_at", null: false
t.integer "key_hash", limit: 8, null: false
t.integer "byte_size", limit: 4, null: false
t.index ["byte_size"], name: "index_solid_cache_entries_on_byte_size"
t.index ["key_hash", "byte_size"], name: "index_solid_cache_entries_on_key_hash_and_byte_size"
t.index ["key_hash"], name: "index_solid_cache_entries_on_key_hash", unique: true
end
end
14 changes: 14 additions & 0 deletions db/migrate/20260702102700_create_solid_cache_entries.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
class CreateSolidCacheEntries < ActiveRecord::Migration[8.1]
def change
create_table "solid_cache_entries", force: :cascade do |t|
t.binary "key", limit: 1024, null: false
t.binary "value", limit: 536870912, null: false
t.datetime "created_at", null: false
t.integer "key_hash", limit: 8, null: false
t.integer "byte_size", limit: 4, null: false
t.index ["byte_size"], name: "index_solid_cache_entries_on_byte_size"
t.index ["key_hash", "byte_size"], name: "index_solid_cache_entries_on_key_hash_and_byte_size"
t.index ["key_hash"], name: "index_solid_cache_entries_on_key_hash", unique: true
end
end
end
13 changes: 12 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[8.1].define(version: 2026_06_21_050948) do
ActiveRecord::Schema[8.1].define(version: 2026_07_02_102700) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql"

Expand Down Expand Up @@ -483,6 +483,17 @@
t.datetime "updated_at", precision: nil
end

create_table "solid_cache_entries", force: :cascade do |t|
t.integer "byte_size", null: false
t.datetime "created_at", null: false
t.binary "key", null: false
t.bigint "key_hash", null: false
t.binary "value", null: false
t.index ["byte_size"], name: "index_solid_cache_entries_on_byte_size"
t.index ["key_hash", "byte_size"], name: "index_solid_cache_entries_on_key_hash_and_byte_size"
t.index ["key_hash"], name: "index_solid_cache_entries_on_key_hash", unique: true
end

create_table "sponsors", id: :serial, force: :cascade do |t|
t.text "accessibility_info"
t.string "avatar"
Expand Down
33 changes: 33 additions & 0 deletions script/benchmark_events.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/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"

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

def measure(session, path)
qc = 0
cb = ->(*, **) { qc += 1 }
time = nil
ActiveSupport::Notifications.subscribed(cb, "sql.active_record") do
time = Benchmark.measure { session.get(path) }
end
[time.real, qc]
end

puts "=== Database: #{ActiveRecord::Base.connection_db_config.database} ===\n\n"

%w[/events/upcoming /events/past /events/past?page=2].each do |path|
ActiveRecord::Base.connection.query_cache.clear
Rails.cache.clear if Rails.cache

cold_t, cold_q = measure(session, path)
warm = 3.times.map { measure(session, path).first }

puts "#{path}"
puts " cold: #{cold_t.round(3)}s | #{cold_q} queries"
puts " warm: #{warm.sort[1].round(3)}s (min #{warm.min.round(3)}, max #{warm.max.round(3)})"
puts
end
Loading