Skip to content
Draft
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
3 changes: 3 additions & 0 deletions .prosopite_ignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# This file contains paths to ignore for Prosopite N+1 query detection
# Add spec file paths (one per line) that should be excluded from N+1 detection
# Example: spec/system/some_feature
5 changes: 2 additions & 3 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ gem "rswag-api"
gem "rswag-ui"
gem "sablon" # Word document templating tool for Case Court Reports
gem "scout_apm"
gem "scout_apm_logging" # production metrics around speed
gem "sprockets-rails" # The original asset pipeline for Rails [https://github.com/rails/sprockets-rails]
gem "stimulus-rails"
gem "strong_migrations"
Expand All @@ -63,13 +64,13 @@ gem "flipper-ui"
gem "pghero"
gem "pg_query"
group :development, :test do
gem "bullet" # Detect and fix N+1 queries
gem "byebug", platforms: %i[mri mingw x64_mingw] # Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem "dotenv-rails"
gem "factory_bot_rails"
gem "parallel_tests"
gem "pry"
gem "pry-byebug"
gem "prosopite" # check for performance issues like N+1 queries
gem "rspec_junit_formatter"
gem "rspec-rails"
gem "rswag-specs"
Expand Down Expand Up @@ -110,5 +111,3 @@ group :test do
gem "simplecov", require: false
gem "webmock" # HTTP request stubber
end

gem "scout_apm_logging", "~> 2.1"
9 changes: 3 additions & 6 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,6 @@ GEM
bugsnag (6.28.0)
concurrent-ruby (~> 1.0)
builder (3.3.0)
bullet (8.1.0)
activesupport (>= 3.0.0)
uniform_notifier (~> 1.11)
bundler-audit (0.9.2)
bundler (>= 1.2.0, < 3)
thor (~> 1.0)
Expand Down Expand Up @@ -421,6 +418,7 @@ GEM
actionpack (>= 7.1)
prettyprint (0.2.0)
prism (1.5.1)
prosopite (2.1.2)
pry (0.15.2)
coderay (~> 1.1)
method_source (~> 1.0)
Expand Down Expand Up @@ -662,7 +660,6 @@ GEM
unicode-display_width (3.2.0)
unicode-emoji (~> 4.1)
unicode-emoji (4.1.0)
uniform_notifier (1.18.0)
useragent (0.16.11)
view_component (3.22.0)
activesupport (>= 5.2.0, < 8.1)
Expand Down Expand Up @@ -707,7 +704,6 @@ DEPENDENCIES
blueprinter
brakeman
bugsnag
bullet
bundler-audit
byebug
capybara
Expand Down Expand Up @@ -751,6 +747,7 @@ DEPENDENCIES
pg_query
pghero
pretender
prosopite
pry
pry-byebug
puma (= 7.0.4)
Expand All @@ -775,7 +772,7 @@ DEPENDENCIES
rubocop-rspec_rails
sablon
scout_apm
scout_apm_logging (~> 2.1)
scout_apm_logging
selenium-webdriver
shoulda-matchers
simplecov
Expand Down
11 changes: 11 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,17 @@ class ApplicationController < ActionController::Base
include Organizational
include Users::TimeZone

unless Rails.env.production?
around_action :n_plus_one_detection

def n_plus_one_detection
Prosopite.scan
yield
ensure
Prosopite.finish
end
end

protect_from_forgery
before_action :store_user_location!, if: :storable_location?
before_action :authenticate_user!
Expand Down
9 changes: 3 additions & 6 deletions config/environments/development.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,9 @@
# Raises error for missing translations.
# config.i18n.raise_on_missing_translations = true

config.after_initialize do
Bullet.enable = true
Bullet.console = true
Bullet.rails_logger = true
Bullet.bullet_logger = true
end
# Prosopite configuration for N+1 query detection
config.x.prosopite_enabled = true
config.x.prosopite_min_n_queries = 2

# Annotate rendered view with file names.
config.action_view.annotate_rendered_view_with_filenames = true
Expand Down
11 changes: 4 additions & 7 deletions config/environments/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,10 @@
# Raises error for missing translations.
config.i18n.raise_on_missing_translations = true

config.after_initialize do
Bullet.enable = true
Bullet.console = true
Bullet.bullet_logger = true
Bullet.rails_logger = true
# Bullet.raise = true # TODO https://github.com/rubyforgood/casa/issues/2441
end
# Prosopite configuration for N+1 query detection
# Detailed configuration is in spec/support/prosopite.rb
config.x.prosopite_enabled = false # Managed by spec/support/prosopite.rb
config.x.prosopite_min_n_queries = 2

# Annotate rendered view with file names.
# config.action_view.annotate_rendered_view_with_filenames = true
Expand Down
12 changes: 12 additions & 0 deletions config/initializers/prosopite.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# frozen_string_literal: true

if Rails.configuration.x.prosopite_enabled
require "prosopite/middleware/rack"
Rails.configuration.middleware.use(Prosopite::Middleware::Rack)
end

Rails.application.config.after_initialize do
Prosopite.enabled = Rails.configuration.x.prosopite_enabled
Prosopite.min_n_queries = Rails.configuration.x.prosopite_min_n_queries
Prosopite.rails_logger = true
end
2 changes: 1 addition & 1 deletion spec/models/case_court_report_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@
let(:casa_case_with_contacts) { volunteer.casa_cases.first }
let(:nonexistent_path) { "app/documents/templates/nonexisitent_report_template.docx" }

it "raises Zip::Error when generating report" do
it "raises Zip::Error when generating report", :disable_prosopite do
args = {
case_id: casa_case_with_contacts.id,
volunteer_id: volunteer.id,
Expand Down
9 changes: 3 additions & 6 deletions spec/rails_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -121,12 +121,9 @@
raise StandardError.new "\"#{example.full_description}\" in #{example.location} timed out."
end

# NOTE: not applicable currently, left to show how to skip bullet errrors
# config.around :each, :disable_bullet do |example|
# Bullet.raise = false
# example.run
# Bullet.raise = true
# end
# NOTE: not applicable currently, left to show how to skip prosopite errors
# You can use the :disable_prosopite metadata tag on specific examples
# See spec/support/prosopite.rb for configuration

config.around do |example|
Capybara.server_port = 7654 + ENV["TEST_ENV_NUMBER"].to_i
Expand Down
52 changes: 52 additions & 0 deletions spec/support/prosopite.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true

Prosopite.enabled = true
Prosopite.raise = true # Fail specs on N+1 detection
Prosopite.rails_logger = true
Prosopite.prosopite_logger = true
Prosopite.allow_stack_paths = [
"shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb",
"shoulda/matchers/active_model/validate_presence_of_matcher.rb",
"shoulda/matchers/active_model/validate_inclusion_of_matcher.rb",
]

PROSOPITE_IGNORE = File.read(".prosopite_ignore").lines.map(&:chomp)

# Monkey-patch FactoryBot to pause Prosopite during factory creation
# This prevents N+1 detection in factory callbacks, focusing on actual test code
module FactoryBot
module Strategy
class Create
alias_method :original_result, :result

def result(evaluation)
if defined?(Prosopite) && Prosopite.enabled?
Prosopite.pause do
original_result(evaluation)
end
else
original_result(evaluation)
end
end
end
end
end

RSpec.configure do |config|
config.around do |example|
should_ignore = PROSOPITE_IGNORE.any? { |path|
File.fnmatch?("./#{path}/*", example.metadata[:rerun_file_path])
}
if should_ignore || example.metadata[:disable_prosopite]
# Disable prosopite globally for this test (works across threads)
original_enabled = Prosopite.enabled?
Prosopite.enabled = false
example.run
Prosopite.enabled = original_enabled
else
Prosopite.scan do
example.run
end
end
end
end
Loading