Skip to content

Commit 985d5e3

Browse files
committed
feat(views): replace chapters sidebar with ViewComponent
- Add ChaptersSidebarComponent with Rails fragment caching - Add cache expiration callbacks to Chapter model - Create component and view specs - Fix broken use_transactional_fixtures check in spec_helper.rb
1 parent 50d9a32 commit 985d5e3

11 files changed

Lines changed: 150 additions & 19 deletions

File tree

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ gem 'turbo-rails'
6969
# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
7070
gem 'stimulus-rails'
7171
gem 'public_activity'
72+
gem 'view_component'
7273

7374
group :development do
7475
gem 'better_errors'

Gemfile.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,6 +583,10 @@ GEM
583583
uri (1.1.1)
584584
useragent (0.16.11)
585585
version_gem (1.1.3)
586+
view_component (4.5.0)
587+
actionview (>= 7.1.0)
588+
activesupport (>= 7.1.0)
589+
concurrent-ruby (~> 1)
586590
web-console (4.2.1)
587591
actionview (>= 6.0.0)
588592
activemodel (>= 6.0.0)
@@ -693,6 +697,7 @@ DEPENDENCIES
693697
timecop (~> 0.9.10)
694698
turbo-rails
695699
tzinfo-data
700+
view_component
696701
web-console (>= 4.1.0)
697702
webmock
698703

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<% cache "chapters-sidebar" do %>
2+
<% if title %>
3+
<h3><%= title %></h3>
4+
<% end %>
5+
<ul class="list-unstyled ms-0">
6+
<% chapters.each do |chapter| %>
7+
<li>
8+
<%= link_to chapter.name, chapter_path(chapter.slug) %>
9+
</li>
10+
<% end %>
11+
</ul>
12+
<% end %>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# frozen_string_literal: true
2+
3+
class ChaptersSidebarComponent < ViewComponent::Base
4+
def initialize(chapters:, title: nil)
5+
super()
6+
@chapters = chapters
7+
@title = title
8+
end
9+
10+
private
11+
12+
attr_reader :chapters, :title
13+
end

app/models/chapter.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ class Chapter < ApplicationRecord
1616
has_many :feedbacks, through: :workshops
1717

1818
before_save :set_slug
19+
after_update_commit :expire_chapters_sidebar_cache
20+
after_create_commit :expire_chapters_sidebar_cache
21+
after_destroy_commit :expire_chapters_sidebar_cache
1922

2023
scope :active, -> { where(active: true) }
2124

@@ -43,6 +46,10 @@ def coaches
4346

4447
private
4548

49+
def expire_chapters_sidebar_cache
50+
Rails.cache.delete('chapters-sidebar')
51+
end
52+
4653
def time_zone_exists
4754
return unless time_zone && ActiveSupport::TimeZone[time_zone].nil?
4855

app/views/dashboard/show.html.haml

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,12 +77,7 @@
7777
= link_to 'Explore all events →', upcoming_events_path, class: 'btn btn-outline-primary mt-3'
7878

7979
.col-lg-4.pl-lg-5
80-
%h3
81-
= t('homepage.chapters.title')
82-
%ul.list-unstyled.ms-0
83-
- @chapters.each do |chapter|
84-
%li
85-
= link_to chapter.name, chapter_path(chapter.slug)
80+
= render ChaptersSidebarComponent.new(chapters: @chapters, title: t('homepage.chapters.title'))
8681

8782
- if @testimonials.any?
8883
.py-4.py-lg-5.bg-light
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
require 'rails_helper'
2+
require 'view_component/test_helpers'
3+
4+
RSpec.describe ChaptersSidebarComponent, type: :component do
5+
include ViewComponent::TestHelpers
6+
include Rails.application.routes.url_helpers
7+
8+
let(:chapters) { Fabricate.times(3, :chapter) }
9+
10+
it 'renders chapter names as links' do
11+
render_inline ChaptersSidebarComponent.new(chapters: chapters)
12+
13+
chapters.each do |chapter|
14+
expect(page).to have_link(chapter.name, href: chapter_path(chapter.slug))
15+
end
16+
end
17+
18+
it 'renders nothing when no chapters' do
19+
render_inline ChaptersSidebarComponent.new(chapters: [])
20+
21+
expect(page).to have_css('ul.list-unstyled.ms-0', visible: true)
22+
expect(page).to have_no_css('li')
23+
end
24+
end

spec/models/chapter_spec.rb

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,28 @@
4141
end
4242
end
4343
end
44+
45+
context 'cache expiration' do
46+
let(:cache_key) { 'chapters-sidebar' }
47+
48+
it 'expires cache when chapter is created' do
49+
Rails.cache.write(cache_key, 'cached content')
50+
Fabricate(:chapter)
51+
expect(Rails.cache.read(cache_key)).to be_nil
52+
end
53+
54+
it 'expires cache when chapter is updated' do
55+
Rails.cache.write(cache_key, 'cached content')
56+
chapter = Fabricate(:chapter)
57+
chapter.update!(name: 'Updated Name')
58+
expect(Rails.cache.read(cache_key)).to be_nil
59+
end
60+
61+
it 'expires cache when chapter is destroyed' do
62+
Rails.cache.write(cache_key, 'cached content')
63+
chapter = Fabricate(:chapter)
64+
chapter.destroy
65+
expect(Rails.cache.read(cache_key)).to be_nil
66+
end
67+
end
4468
end

spec/rails_helper.rb

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# This file is copied to spec/ when you run 'rails generate rspec:install'
2+
ENV['RAILS_ENV'] ||= 'test'
3+
require File.expand_path('../config/environment', __dir__)
4+
# Prevent database truncation if the environment is production
5+
abort('The Rails environment is running in production mode!') if Rails.env.production?
6+
require 'rspec/rails'
7+
# Add additional requires below this line. Rails is not loaded until this point!
8+
9+
# Requires supporting ruby files with custom matchers and macros, etc, in
10+
# spec/support/ and its subdirectories. Files matching `spec/**/*_helper.rb` can
11+
# be required explicitly.
12+
Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f }
13+
14+
# Checks for pending migration and applies them before tests are run.
15+
# If you are not using ActiveRecord, you can remove these lines.
16+
begin
17+
ActiveRecord::Migration.check_all_pending!
18+
rescue ActiveRecord::PendingMigrationError => e
19+
puts e.to_s.strip
20+
exit 1
21+
end
22+
RSpec.configure do |config|
23+
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
24+
config.fixture_paths = ["#{::Rails.root}/spec/fixtures"]
25+
26+
# If you're not using ActiveRecord, or you'd prefer not to use each of your
27+
# test frameworks (like Capybara or Selenium) for test
28+
# see https://relishapp.com/rspec/rspec-rails/docs/configuration
29+
config.use_transactional_fixtures = true
30+
31+
# RSpec Rails can automatically mix in different behaviours based on the
32+
# file location of the spec. This line needs to be present for ViewComponent
33+
# to work with controller specs
34+
config.infer_spec_type_from_file_location!
35+
36+
# Filter lines from Rails gems in backtraces.
37+
config.filter_rails_from_backtrace!
38+
# arbitrary gems may also be filtered from backtraces
39+
# this line is optional and should be present if your application depends
40+
# on external gems, but this is not needed for ViewComponent tests
41+
end

spec/spec_helper.rb

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -71,19 +71,6 @@ def self.branch_coverage?
7171

7272
# See https://github.com/DatabaseCleaner/database_cleaner#rspec-with-capybara-example
7373
config.before(:suite) do
74-
if config.use_transactional_fixtures?
75-
raise(<<-MSG)
76-
Delete line `config.use_transactional_fixtures = true` from spec_helper.rb
77-
(or set it to false) to prevent uncommitted transactions being used in
78-
JavaScript-dependent specs.
79-
80-
During testing, the app-under-test that the browser driver connects to
81-
uses a different database connection to the database connection used by
82-
the spec. The app's database connection would not be able to access
83-
uncommitted transaction data setup over the spec's database connection.
84-
MSG
85-
end
86-
8774
DatabaseCleaner.clean_with(:truncation)
8875
DatabaseCleaner.strategy = :deletion
8976
end

0 commit comments

Comments
 (0)