diff --git a/app-rails/.rubocop.yml b/app-rails/.rubocop.yml index 64099d058..c4be56320 100644 --- a/app-rails/.rubocop.yml +++ b/app-rails/.rubocop.yml @@ -1,5 +1,10 @@ -require: +plugins: - rubocop-rspec inherit_gem: pundit: config/rubocop-rspec.yml rubocop-rails-omakase: rubocop.yml + +RSpec/ExampleLength: + Enabled: false +RSpec/MultipleExpectations: + Enabled: false diff --git a/app-rails/Gemfile b/app-rails/Gemfile index 0e73e43c9..4935f1baf 100644 --- a/app-rails/Gemfile +++ b/app-rails/Gemfile @@ -3,7 +3,7 @@ source "https://rubygems.org" ruby "3.4.2" # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main" -gem "rails", "~> 7.1.3", ">= 7.1.3.2" +gem "rails", "~> 7.2.0", ">= 7.2.2.1" # The original asset pipeline for Rails [https://github.com/rails/sprockets-rails] gem "sprockets-rails" @@ -116,5 +116,5 @@ end group :production do # Add plugin for pg gem to support AWS RDS IAM - gem "pg-aws_rds_iam", "~> 0.5.0" + gem "pg-aws_rds_iam", "~> 0.7.0" end diff --git a/app-rails/Gemfile.lock b/app-rails/Gemfile.lock index df9d35b0f..fa5ee245d 100644 --- a/app-rails/Gemfile.lock +++ b/app-rails/Gemfile.lock @@ -1,51 +1,46 @@ GEM remote: https://rubygems.org/ specs: - actioncable (7.1.5.1) - actionpack (= 7.1.5.1) - activesupport (= 7.1.5.1) + actioncable (7.2.2.1) + actionpack (= 7.2.2.1) + activesupport (= 7.2.2.1) nio4r (~> 2.0) websocket-driver (>= 0.6.1) zeitwerk (~> 2.6) - actionmailbox (7.1.5.1) - actionpack (= 7.1.5.1) - activejob (= 7.1.5.1) - activerecord (= 7.1.5.1) - activestorage (= 7.1.5.1) - activesupport (= 7.1.5.1) - mail (>= 2.7.1) - net-imap - net-pop - net-smtp - actionmailer (7.1.5.1) - actionpack (= 7.1.5.1) - actionview (= 7.1.5.1) - activejob (= 7.1.5.1) - activesupport (= 7.1.5.1) - mail (~> 2.5, >= 2.5.4) - net-imap - net-pop - net-smtp + actionmailbox (7.2.2.1) + actionpack (= 7.2.2.1) + activejob (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) + mail (>= 2.8.0) + actionmailer (7.2.2.1) + actionpack (= 7.2.2.1) + actionview (= 7.2.2.1) + activejob (= 7.2.2.1) + activesupport (= 7.2.2.1) + mail (>= 2.8.0) rails-dom-testing (~> 2.2) - actionpack (7.1.5.1) - actionview (= 7.1.5.1) - activesupport (= 7.1.5.1) + actionpack (7.2.2.1) + actionview (= 7.2.2.1) + activesupport (= 7.2.2.1) nokogiri (>= 1.8.5) racc - rack (>= 2.2.4) + rack (>= 2.2.4, < 3.2) rack-session (>= 1.0.1) rack-test (>= 0.6.3) rails-dom-testing (~> 2.2) rails-html-sanitizer (~> 1.6) - actiontext (7.1.5.1) - actionpack (= 7.1.5.1) - activerecord (= 7.1.5.1) - activestorage (= 7.1.5.1) - activesupport (= 7.1.5.1) + useragent (~> 0.16) + actiontext (7.2.2.1) + actionpack (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) globalid (>= 0.6.0) nokogiri (>= 1.8.5) - actionview (7.1.5.1) - activesupport (= 7.1.5.1) + actionview (7.2.2.1) + activesupport (= 7.2.2.1) builder (~> 3.1) erubi (~> 1.11) rails-dom-testing (~> 2.2) @@ -56,57 +51,57 @@ GEM activestorage (>= 6.1.4) activesupport (>= 6.1.4) marcel (>= 1.0.3) - activejob (7.1.5.1) - activesupport (= 7.1.5.1) + activejob (7.2.2.1) + activesupport (= 7.2.2.1) globalid (>= 0.3.6) - activemodel (7.1.5.1) - activesupport (= 7.1.5.1) - activerecord (7.1.5.1) - activemodel (= 7.1.5.1) - activesupport (= 7.1.5.1) + activemodel (7.2.2.1) + activesupport (= 7.2.2.1) + activerecord (7.2.2.1) + activemodel (= 7.2.2.1) + activesupport (= 7.2.2.1) timeout (>= 0.4.0) - activestorage (7.1.5.1) - actionpack (= 7.1.5.1) - activejob (= 7.1.5.1) - activerecord (= 7.1.5.1) - activesupport (= 7.1.5.1) + activestorage (7.2.2.1) + actionpack (= 7.2.2.1) + activejob (= 7.2.2.1) + activerecord (= 7.2.2.1) + activesupport (= 7.2.2.1) marcel (~> 1.0) - activesupport (7.1.5.1) + activesupport (7.2.2.1) base64 benchmark (>= 0.3) bigdecimal - concurrent-ruby (~> 1.0, >= 1.0.2) + concurrent-ruby (~> 1.0, >= 1.3.1) connection_pool (>= 2.2.5) drb i18n (>= 1.6, < 2) logger (>= 1.4.2) minitest (>= 5.1) - mutex_m securerandom (>= 0.3) - tzinfo (~> 2.0) + tzinfo (~> 2.0, >= 2.0.5) addressable (2.8.7) public_suffix (>= 2.0.2, < 7.0) ast (2.4.2) - aws-eventstream (1.3.0) - aws-partitions (1.1044.0) - aws-sdk-cognitoidentityprovider (1.114.0) + aws-eventstream (1.3.1) + aws-partitions (1.1059.0) + aws-sdk-cognitoidentityprovider (1.117.0) aws-sdk-core (~> 3, >= 3.216.0) aws-sigv4 (~> 1.5) - aws-sdk-core (3.217.1) + aws-sdk-core (3.219.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) aws-sigv4 (~> 1.9) + base64 jmespath (~> 1, >= 1.6.1) - aws-sdk-kms (1.97.0) + aws-sdk-kms (1.99.0) aws-sdk-core (~> 3, >= 3.216.0) aws-sigv4 (~> 1.5) aws-sdk-rails (5.1.0) aws-sdk-core (~> 3) railties (>= 7.1.0) - aws-sdk-rds (1.267.0) + aws-sdk-rds (1.271.0) aws-sdk-core (~> 3, >= 3.216.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.179.0) + aws-sdk-s3 (1.182.0) aws-sdk-core (~> 3, >= 3.216.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) @@ -193,7 +188,7 @@ GEM actionview (>= 5.0.0) activesupport (>= 5.0.0) jmespath (1.6.2) - json (2.9.1) + json (2.10.1) jwt (2.10.1) base64 language_server-protocol (3.17.0.4) @@ -203,7 +198,8 @@ GEM logger (~> 1.6) letter_opener (1.10.0) launchy (>= 2.2, < 4) - logger (1.6.5) + lint_roller (1.1.0) + logger (1.6.6) loofah (2.24.0) crass (~> 1.0.2) nokogiri (>= 1.12.0) @@ -218,7 +214,6 @@ GEM mini_portile2 (2.8.8) minitest (5.25.4) msgpack (1.7.5) - mutex_m (0.3.0) net-http (0.6.0) uri net-imap (0.5.6) @@ -228,7 +223,7 @@ GEM net-protocol net-protocol (0.2.2) timeout - net-smtp (0.5.0) + net-smtp (0.5.1) net-protocol nio4r (2.7.4) nokogiri (1.18.3) @@ -252,11 +247,11 @@ GEM racc (~> 1.4) orm_adapter (0.5.0) parallel (1.26.3) - parser (3.3.7.0) + parser (3.3.7.1) ast (~> 2.4.1) racc pg (1.5.9) - pg-aws_rds_iam (0.5.0) + pg-aws_rds_iam (0.7.0) aws-sdk-rds (~> 1.0) pg (~> 1.1) pp (0.6.2) @@ -268,7 +263,7 @@ GEM public_suffix (6.0.1) puma (6.6.0) nio4r (~> 2.0) - pundit (2.4.0) + pundit (2.5.0) activesupport (>= 3.0.0) pundit-matchers (4.0.0) rspec-core (~> 3.12) @@ -284,20 +279,20 @@ GEM rack (>= 1.3) rackup (2.2.1) rack (>= 3) - rails (7.1.5.1) - actioncable (= 7.1.5.1) - actionmailbox (= 7.1.5.1) - actionmailer (= 7.1.5.1) - actionpack (= 7.1.5.1) - actiontext (= 7.1.5.1) - actionview (= 7.1.5.1) - activejob (= 7.1.5.1) - activemodel (= 7.1.5.1) - activerecord (= 7.1.5.1) - activestorage (= 7.1.5.1) - activesupport (= 7.1.5.1) + rails (7.2.2.1) + actioncable (= 7.2.2.1) + actionmailbox (= 7.2.2.1) + actionmailer (= 7.2.2.1) + actionpack (= 7.2.2.1) + actiontext (= 7.2.2.1) + actionview (= 7.2.2.1) + activejob (= 7.2.2.1) + activemodel (= 7.2.2.1) + activerecord (= 7.2.2.1) + activestorage (= 7.2.2.1) + activesupport (= 7.2.2.1) bundler (>= 1.15.0) - railties (= 7.1.5.1) + railties (= 7.2.2.1) rails-dom-testing (2.2.0) activesupport (>= 5.0.0) minitest @@ -310,17 +305,17 @@ GEM rails-html-sanitizer (1.6.2) loofah (~> 2.21) nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) - railties (7.1.5.1) - actionpack (= 7.1.5.1) - activesupport (= 7.1.5.1) - irb + railties (7.2.2.1) + actionpack (= 7.2.2.1) + activesupport (= 7.2.2.1) + irb (~> 1.13) rackup (>= 1.0.0) rake (>= 12.2) thor (~> 1.0, >= 1.2.2) zeitwerk (~> 2.6) rainbow (3.1.1) rake (13.2.1) - rdoc (6.11.0) + rdoc (6.12.0) psych (>= 4.0.0) regexp_parser (2.10.0) reline (0.6.0) @@ -328,7 +323,7 @@ GEM responders (3.1.1) actionpack (>= 5.2) railties (>= 5.2) - rexml (3.4.0) + rexml (3.4.1) route_translator (14.2.0) actionpack (>= 6.1) activesupport (>= 6.1) @@ -353,9 +348,10 @@ GEM rspec-mocks (~> 3.13) rspec-support (~> 3.13) rspec-support (3.13.2) - rubocop (1.71.1) + rubocop (1.73.2) json (~> 2.3) - language_server-protocol (>= 3.17.0) + language_server-protocol (~> 3.17.0.2) + lint_roller (~> 1.1.0) parallel (~> 1.10) parser (>= 3.3.0.2) rainbow (>= 2.2.2, < 4.0) @@ -363,26 +359,25 @@ GEM rubocop-ast (>= 1.38.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 4.0) - rubocop-ast (1.38.0) + rubocop-ast (1.38.1) parser (>= 3.3.1.0) - rubocop-minitest (0.36.0) - rubocop (>= 1.61, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) - rubocop-performance (1.23.1) - rubocop (>= 1.48.1, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) - rubocop-rails (2.29.1) + rubocop-performance (1.24.0) + lint_roller (~> 1.1) + rubocop (>= 1.72.1, < 2.0) + rubocop-ast (>= 1.38.0, < 2.0) + rubocop-rails (2.30.3) activesupport (>= 4.2.0) + lint_roller (~> 1.1) rack (>= 1.1) - rubocop (>= 1.52.0, < 2.0) - rubocop-ast (>= 1.31.1, < 2.0) - rubocop-rails-omakase (1.0.0) - rubocop - rubocop-minitest - rubocop-performance - rubocop-rails - rubocop-rspec (3.4.0) - rubocop (~> 1.61) + rubocop (>= 1.72.1, < 2.0) + rubocop-ast (>= 1.38.0, < 2.0) + rubocop-rails-omakase (1.1.0) + rubocop (>= 1.72) + rubocop-performance (>= 1.24) + rubocop-rails (>= 2.30) + rubocop-rspec (3.5.0) + lint_roller (~> 1.1) + rubocop (~> 1.72, >= 1.72.1) ruby-graphviz (1.2.5) rexml ruby-progressbar (1.13.0) @@ -390,7 +385,7 @@ GEM sassc (2.4.0) ffi (~> 1.9) securerandom (0.4.1) - selenium-webdriver (4.28.0) + selenium-webdriver (4.29.1) base64 (~> 0.2) logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) @@ -411,7 +406,7 @@ GEM sprockets (>= 3.0.0) stimulus-rails (1.3.4) railties (>= 6.0.0) - stringio (3.1.2) + stringio (3.1.5) thor (1.3.2) timeout (0.4.3) turbo-rails (2.0.11) @@ -423,6 +418,7 @@ GEM unicode-emoji (~> 4.0, >= 4.0.4) unicode-emoji (4.0.4) uri (1.0.3) + useragent (0.16.11) warden (1.2.9) rack (>= 2.0.9) web-console (4.2.1) @@ -437,7 +433,7 @@ GEM websocket-extensions (0.1.5) xpath (3.2.0) nokogiri (~> 1.8) - zeitwerk (2.7.1) + zeitwerk (2.7.2) PLATFORMS aarch64-linux @@ -474,11 +470,11 @@ DEPENDENCIES jwt letter_opener pg (~> 1.1) - pg-aws_rds_iam (~> 0.5.0) + pg-aws_rds_iam (~> 0.7.0) puma (>= 5.0) pundit pundit-matchers - rails (~> 7.1.3, >= 7.1.3.2) + rails (~> 7.2.0, >= 7.2.2.1) rails-erd route_translator rqrcode diff --git a/app-rails/bin/rubocop b/app-rails/bin/rubocop index 369a05bed..b3801537d 100755 --- a/app-rails/bin/rubocop +++ b/app-rails/bin/rubocop @@ -24,4 +24,7 @@ end require "rubygems" require "bundler/setup" +# explicit rubocop config increases performance slightly while avoiding config confusion. +ARGV.unshift("--config", File.expand_path("../.rubocop.yml", __dir__)) + load Gem.bin_path("rubocop", "rubocop") diff --git a/app-rails/bin/setup b/app-rails/bin/setup index 3cd5a9d78..92bd38ead 100755 --- a/app-rails/bin/setup +++ b/app-rails/bin/setup @@ -1,7 +1,6 @@ #!/usr/bin/env ruby require "fileutils" -# path to your application root. APP_ROOT = File.expand_path("..", __dir__) def system!(*args) diff --git a/app-rails/config/application.rb b/app-rails/config/application.rb index 833da5507..0c9d277d3 100644 --- a/app-rails/config/application.rb +++ b/app-rails/config/application.rb @@ -17,7 +17,7 @@ class Application < Rails::Application config.i18n.load_path += Dir[Rails.root.join("config", "locales", "**", "*.{rb,yml}")] # Initialize configuration defaults for originally generated Rails version. - config.load_defaults 7.1 + config.load_defaults 7.2 # Please, add to the `ignore` list any other `lib` subdirectories that do # not contain `.rb` files, or that should not be reloaded or eager loaded. diff --git a/app-rails/config/environments/development.rb b/app-rails/config/environments/development.rb index 2368da82b..2bc81a919 100644 --- a/app-rails/config/environments/development.rb +++ b/app-rails/config/environments/development.rb @@ -17,7 +17,7 @@ # Show full error reports. config.consider_all_requests_local = true - # Enable server timing + # Enable server timing. config.server_timing = true # Enable/disable caching. By default caching is disabled. @@ -43,8 +43,12 @@ # Don't care if the mailer can't send. config.action_mailer.raise_delivery_errors = false + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. config.action_mailer.perform_caching = false + config.action_mailer.default_url_options = { host: "localhost", port: 3100 } + # Print deprecation notices to the Rails logger. config.active_support.deprecation = :log @@ -73,11 +77,14 @@ # config.i18n.raise_on_missing_translations = true # Annotate rendered view with file names. - # config.action_view.annotate_rendered_view_with_filenames = true + config.action_view.annotate_rendered_view_with_filenames = true # Uncomment if you wish to allow Action Cable access from any origin. # config.action_cable.disable_request_forgery_protection = true - # Raise error when a before_action's only/except options reference missing actions + # Raise error when a before_action's only/except options reference missing actions. # config.action_controller.raise_on_missing_callback_actions = true + + # Apply autocorrection by RuboCop to files generated by `bin/rails generate`. + # config.generators.apply_rubocop_autocorrect_after_generate! end diff --git a/app-rails/config/environments/production.rb b/app-rails/config/environments/production.rb index 53c574af5..7fedaf77d 100644 --- a/app-rails/config/environments/production.rb +++ b/app-rails/config/environments/production.rb @@ -81,9 +81,12 @@ # config.active_job.queue_adapter = :resque # config.active_job.queue_name_prefix = "app-rails:production" - config.action_mailer.delivery_method = :sesv2 + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. config.action_mailer.perform_caching = false + config.action_mailer.delivery_method = :sesv2 + # Ignore bad email addresses and do not raise email delivery errors. # Set this to true and configure the email server for immediate delivery to raise delivery errors. # config.action_mailer.raise_delivery_errors = false @@ -98,6 +101,9 @@ # Do not dump schema after migrations. config.active_record.dump_schema_after_migration = false + # Only use :id for inspections in production. + config.active_record.attributes_for_inspect = [ :id ] + # Enable DNS rebinding protection and other `Host` header attacks. # config.hosts = [ # "example.com", # Allow requests from example.com diff --git a/app-rails/config/environments/test.rb b/app-rails/config/environments/test.rb index 785ed8710..7aa2887de 100644 --- a/app-rails/config/environments/test.rb +++ b/app-rails/config/environments/test.rb @@ -40,6 +40,8 @@ # Store uploaded files on the local file system in a temporary directory. config.active_storage.service = :test + # Disable caching for Action Mailer templates even if Action Controller + # caching is enabled. config.action_mailer.perform_caching = false # Tell Action Mailer not to deliver emails to the real world. @@ -47,6 +49,10 @@ # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test + # Unlike controllers, the mailer instance doesn't have any context about the + # incoming request so you'll need to provide the :host parameter yourself. + config.action_mailer.default_url_options = { host: "localhost", port: 3100 } + # Print deprecation notices to the stderr. config.active_support.deprecation = :stderr @@ -62,6 +68,6 @@ # Annotate rendered view with file names. # config.action_view.annotate_rendered_view_with_filenames = true - # Raise error when a before_action's only/except options reference missing actions + # Raise error when a before_action's only/except options reference missing actions. # config.action_controller.raise_on_missing_callback_actions = true end diff --git a/app-rails/config/initializers/filter_parameter_logging.rb b/app-rails/config/initializers/filter_parameter_logging.rb index c2d89e28a..c010b83dd 100644 --- a/app-rails/config/initializers/filter_parameter_logging.rb +++ b/app-rails/config/initializers/filter_parameter_logging.rb @@ -4,5 +4,5 @@ # Use this to limit dissemination of sensitive information. # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. Rails.application.config.filter_parameters += [ - :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn + :passw, :email, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn ] diff --git a/app-rails/config/initializers/new_framework_defaults_7_2.rb b/app-rails/config/initializers/new_framework_defaults_7_2.rb new file mode 100644 index 000000000..b549c4a25 --- /dev/null +++ b/app-rails/config/initializers/new_framework_defaults_7_2.rb @@ -0,0 +1,70 @@ +# Be sure to restart your server when you modify this file. +# +# This file eases your Rails 7.2 framework defaults upgrade. +# +# Uncomment each configuration one by one to switch to the new default. +# Once your application is ready to run with all new defaults, you can remove +# this file and set the `config.load_defaults` to `7.2`. +# +# Read the Guide for Upgrading Ruby on Rails for more info on each option. +# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html + +### +# Controls whether Active Job's `#perform_later` and similar methods automatically defer +# the job queuing to after the current Active Record transaction is committed. +# +# Example: +# Topic.transaction do +# topic = Topic.create(...) +# NewTopicNotificationJob.perform_later(topic) +# end +# +# In this example, if the configuration is set to `:never`, the job will +# be enqueued immediately, even though the `Topic` hasn't been committed yet. +# Because of this, if the job is picked up almost immediately, or if the +# transaction doesn't succeed for some reason, the job will fail to find this +# topic in the database. +# +# If `enqueue_after_transaction_commit` is set to `:default`, the queue adapter +# will define the behaviour. +# +# Note: Active Job backends can disable this feature. This is generally done by +# backends that use the same database as Active Record as a queue, hence they +# don't need this feature. +#++ +# Rails.application.config.active_job.enqueue_after_transaction_commit = :default + +### +# Adds image/webp to the list of content types Active Storage considers as an image +# Prevents automatic conversion to a fallback PNG, and assumes clients support WebP, as they support gif, jpeg, and png. +# This is possible due to broad browser support for WebP, but older browsers and email clients may still not support +# WebP. Requires imagemagick/libvips built with WebP support. +#++ +# Rails.application.config.active_storage.web_image_content_types = %w[image/png image/jpeg image/gif image/webp] + +### +# Enable validation of migration timestamps. When set, an ActiveRecord::InvalidMigrationTimestampError +# will be raised if the timestamp prefix for a migration is more than a day ahead of the timestamp +# associated with the current time. This is done to prevent forward-dating of migration files, which can +# impact migration generation and other migration commands. +# +# Applications with existing timestamped migrations that do not adhere to the +# expected format can disable validation by setting this config to `false`. +#++ +# Rails.application.config.active_record.validate_migration_timestamps = true + +### +# Controls whether the PostgresqlAdapter should decode dates automatically with manual queries. +# +# Example: +# ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.select_value("select '2024-01-01'::date") #=> Date +# +# This query used to return a `String`. +#++ +# Rails.application.config.active_record.postgresql_adapter_decode_dates = true + +### +# Enables YJIT as of Ruby 3.3, to bring sizeable performance improvements. If you are +# deploying to a memory constrained environment you may want to set this to `false`. +#++ +# Rails.application.config.yjit = true diff --git a/app-rails/config/puma.rb b/app-rails/config/puma.rb index 100c8fe65..09e60370e 100644 --- a/app-rails/config/puma.rb +++ b/app-rails/config/puma.rb @@ -2,34 +2,33 @@ # are invoked here are part of Puma's configuration DSL. For more information # about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html. -# Puma can serve each request in a thread from an internal thread pool. -# The `threads` method setting takes two numbers: a minimum and maximum. -# Any libraries that use thread pools should be configured to match -# the maximum value specified for Puma. Default is set to 5 threads for minimum -# and maximum; this matches the default thread size of Active Record. -max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } -min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } -threads min_threads_count, max_threads_count +# Puma starts a configurable number of processes (workers) and each process +# serves each request in a thread from an internal thread pool. +# +# The ideal number of threads per worker depends both on how much time the +# application spends waiting for IO operations and on how much you wish to +# to prioritize throughput over latency. +# +# As a rule of thumb, increasing the number of threads will increase how much +# traffic a given process can handle (throughput), but due to CRuby's +# Global VM Lock (GVL) it has diminishing returns and will degrade the +# response time (latency) of the application. +# +# The default is set to 3 threads as it's deemed a decent compromise between +# throughput and latency for the average Rails application. +# +# Any libraries that use a connection pool or another resource pool should +# be configured to provide at least as many connections as the number of +# threads. This includes Active Record's `pool` parameter in `database.yml`. +threads_count = ENV.fetch("RAILS_MAX_THREADS", 3) +threads threads_count, threads_count -# Specifies that the worker count should equal the number of processors in production. -if ENV["RAILS_ENV"] == "production" - require "concurrent-ruby" - worker_count = Integer(ENV.fetch("WEB_CONCURRENCY") { Concurrent.physical_processor_count }) - workers worker_count if worker_count > 1 -end - -# Specifies the `worker_timeout` threshold that Puma will use to wait before -# terminating a worker in development environments. -worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development" - -# Specifies the `port` that Puma will listen on to receive requests; default is 3100. -port ENV.fetch("PORT") { 3100 } - -# Specifies the `environment` that Puma will run in. -environment ENV.fetch("RAILS_ENV") { "development" } - -# Specifies the `pidfile` that Puma will use. -pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } +# Specifies the `port` that Puma will listen on to receive requests; default is 3000. +port ENV.fetch("PORT", 3100) # Allow puma to be restarted by `bin/rails restart` command. plugin :tmp_restart + +# Specify the PID file. Defaults to tmp/pids/server.pid in development. +# In other environments, only set the PID file if requested. +pidfile ENV["PIDFILE"] if ENV["PIDFILE"] diff --git a/app-rails/db/migrate/20250310140241_add_service_name_to_active_storage_blobs.active_storage.rb b/app-rails/db/migrate/20250310140241_add_service_name_to_active_storage_blobs.active_storage.rb new file mode 100644 index 000000000..a15c6ce8e --- /dev/null +++ b/app-rails/db/migrate/20250310140241_add_service_name_to_active_storage_blobs.active_storage.rb @@ -0,0 +1,22 @@ +# This migration comes from active_storage (originally 20190112182829) +class AddServiceNameToActiveStorageBlobs < ActiveRecord::Migration[6.0] + def up + return unless table_exists?(:active_storage_blobs) + + unless column_exists?(:active_storage_blobs, :service_name) + add_column :active_storage_blobs, :service_name, :string + + if configured_service = ActiveStorage::Blob.service.name + ActiveStorage::Blob.unscoped.update_all(service_name: configured_service) + end + + change_column :active_storage_blobs, :service_name, :string, null: false + end + end + + def down + return unless table_exists?(:active_storage_blobs) + + remove_column :active_storage_blobs, :service_name + end +end diff --git a/app-rails/db/migrate/20250310140242_create_active_storage_variant_records.active_storage.rb b/app-rails/db/migrate/20250310140242_create_active_storage_variant_records.active_storage.rb new file mode 100644 index 000000000..94ac83af0 --- /dev/null +++ b/app-rails/db/migrate/20250310140242_create_active_storage_variant_records.active_storage.rb @@ -0,0 +1,27 @@ +# This migration comes from active_storage (originally 20191206030411) +class CreateActiveStorageVariantRecords < ActiveRecord::Migration[6.0] + def change + return unless table_exists?(:active_storage_blobs) + + # Use Active Record's configured type for primary key + create_table :active_storage_variant_records, id: primary_key_type, if_not_exists: true do |t| + t.belongs_to :blob, null: false, index: false, type: blobs_primary_key_type + t.string :variation_digest, null: false + + t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true + t.foreign_key :active_storage_blobs, column: :blob_id + end + end + + private + def primary_key_type + config = Rails.configuration.generators + config.options[config.orm][:primary_key_type] || :primary_key + end + + def blobs_primary_key_type + pkey_name = connection.primary_key(:active_storage_blobs) + pkey_column = connection.columns(:active_storage_blobs).find { |c| c.name == pkey_name } + pkey_column.bigint? ? :bigint : pkey_column.type + end +end diff --git a/app-rails/db/migrate/20250310140243_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb b/app-rails/db/migrate/20250310140243_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb new file mode 100644 index 000000000..93c8b85ad --- /dev/null +++ b/app-rails/db/migrate/20250310140243_remove_not_null_on_active_storage_blobs_checksum.active_storage.rb @@ -0,0 +1,8 @@ +# This migration comes from active_storage (originally 20211119233751) +class RemoveNotNullOnActiveStorageBlobsChecksum < ActiveRecord::Migration[6.0] + def change + return unless table_exists?(:active_storage_blobs) + + change_column_null(:active_storage_blobs, :checksum, true) + end +end diff --git a/app-rails/db/schema.rb b/app-rails/db/schema.rb index 709bdc076..181ba686e 100644 --- a/app-rails/db/schema.rb +++ b/app-rails/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2024_04_10_213056) do +ActiveRecord::Schema[7.2].define(version: 2025_03_10_140243) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" diff --git a/app-rails/public/406-unsupported-browser.html b/app-rails/public/406-unsupported-browser.html new file mode 100644 index 000000000..7cf1e168e --- /dev/null +++ b/app-rails/public/406-unsupported-browser.html @@ -0,0 +1,66 @@ + + + + Your browser is not supported (406) + + + + + + +
+
+

Your browser is not supported.

+

Please upgrade your browser to continue.

+
+
+ + diff --git a/app-rails/public/apple-touch-icon-precomposed.png b/app-rails/public/apple-touch-icon-precomposed.png deleted file mode 100644 index e69de29bb..000000000 diff --git a/app-rails/public/apple-touch-icon.png b/app-rails/public/apple-touch-icon.png deleted file mode 100644 index e69de29bb..000000000 diff --git a/app-rails/public/favicon.ico b/app-rails/public/favicon.ico deleted file mode 100644 index e69de29bb..000000000 diff --git a/app-rails/spec/adapters/cognito_adapter_spec.rb b/app-rails/spec/adapters/auth/cognito_adapter_spec.rb similarity index 91% rename from app-rails/spec/adapters/cognito_adapter_spec.rb rename to app-rails/spec/adapters/auth/cognito_adapter_spec.rb index cf1ebdf96..94cc80c99 100644 --- a/app-rails/spec/adapters/cognito_adapter_spec.rb +++ b/app-rails/spec/adapters/auth/cognito_adapter_spec.rb @@ -1,8 +1,8 @@ require "rails_helper" RSpec.describe Auth::CognitoAdapter do - let(:mock_client) { instance_double("Aws::CognitoIdentityProvider::Client") } - let(:adapter) { Auth::CognitoAdapter.new(client: mock_client) } + let(:mock_client) { instance_double(Aws::CognitoIdentityProvider::Client) } + let(:adapter) { described_class.new(client: mock_client) } let(:email) { "test@example.com" } describe "#associate_software_token" do @@ -97,14 +97,9 @@ describe "#verify_software_token" do it "sets the MFA preference when the token is verified" do - allow(mock_client).to receive(:verify_software_token).and_return( - Aws::CognitoIdentityProvider::Types::VerifySoftwareTokenResponse.new( + allow(mock_client).to receive_messages(verify_software_token: Aws::CognitoIdentityProvider::Types::VerifySoftwareTokenResponse.new( status: "SUCCESS" - ) - ) - allow(mock_client).to receive(:set_user_mfa_preference).and_return( - Aws::CognitoIdentityProvider::Types::SetUserMFAPreferenceResponse.new - ) + ), set_user_mfa_preference: Aws::CognitoIdentityProvider::Types::SetUserMFAPreferenceResponse.new) adapter.verify_software_token("123456", "mock_token") diff --git a/app-rails/spec/controllers/users/sessions_controller_spec.rb b/app-rails/spec/controllers/users/sessions_controller_spec.rb index 69293bb0b..4cd99cebb 100644 --- a/app-rails/spec/controllers/users/sessions_controller_spec.rb +++ b/app-rails/spec/controllers/users/sessions_controller_spec.rb @@ -8,7 +8,9 @@ let (:uid_generator) { -> { uid } } before do + # rubocop:disable RSpec/InstanceVariable @request.env["devise.mapping"] = Devise.mappings[:user] + # rubocop:enable RSpec/InstanceVariable allow(controller).to receive(:auth_service).and_return( AuthService.new(Auth::MockAdapter.new(uid_generator: uid_generator)) diff --git a/app-rails/spec/forms/users/new_session_form_spec.rb b/app-rails/spec/forms/users/new_session_form_spec.rb index 80c6f55e8..59d529e30 100644 --- a/app-rails/spec/forms/users/new_session_form_spec.rb +++ b/app-rails/spec/forms/users/new_session_form_spec.rb @@ -2,7 +2,7 @@ RSpec.describe Users::NewSessionForm do it "passes validation with valid email and password" do - form = Users::NewSessionForm.new( + form = described_class.new( email: "test@example.com", password: "password" ) @@ -11,33 +11,33 @@ end it "requires email and password" do - form = Users::NewSessionForm.new({ + form = described_class.new({ email: "", password: "" }) expect(form).not_to be_valid - expect(form.errors.of_kind?(:email, :blank)).to be_truthy - expect(form.errors.of_kind?(:password, :blank)).to be_truthy + expect(form.errors).to be_of_kind(:email, :blank) + expect(form.errors).to be_of_kind(:password, :blank) end it "requires a valid email" do - form = Users::NewSessionForm.new( + form = described_class.new( email: "not_an_email" ) expect(form).not_to be_valid - expect(form.errors.of_kind?(:email, :invalid)).to be_truthy + expect(form.errors).to be_of_kind(:email, :invalid) end it "requires the honeypot field to be empty" do - form = Users::NewSessionForm.new( + form = described_class.new( email: "test@example.com", password: "password", spam_trap: "I am a bot" ) expect(form).not_to be_valid - expect(form.errors.of_kind?(:spam_trap, :present)).to be_truthy + expect(form.errors).to be_of_kind(:spam_trap, :present) end end diff --git a/app-rails/spec/forms/users/registration_form_spec.rb b/app-rails/spec/forms/users/registration_form_spec.rb index 49dbb6a4a..8ee1d99dd 100644 --- a/app-rails/spec/forms/users/registration_form_spec.rb +++ b/app-rails/spec/forms/users/registration_form_spec.rb @@ -3,7 +3,7 @@ valid_password = "password1234" RSpec.describe Users::RegistrationForm do - let (:form) { Users::RegistrationForm.new() } + let (:form) { described_class.new() } it "passes validation with valid email and password" do form.email = "test@example.com" @@ -18,8 +18,8 @@ form.password = "" expect(form).not_to be_valid - expect(form.errors.of_kind?(:email, :blank)).to be_truthy - expect(form.errors.of_kind?(:password, :blank)).to be_truthy + expect(form.errors).to be_of_kind(:email, :blank) + expect(form.errors).to be_of_kind(:password, :blank) end it "confirms the password matches" do @@ -27,14 +27,14 @@ form.password_confirmation = "not_the_same" expect(form).not_to be_valid - expect(form.errors.of_kind?(:password_confirmation, :confirmation)).to be_truthy + expect(form.errors).to be_of_kind(:password_confirmation, :confirmation) end it "requires a valid email" do form.email = "not_an_email" expect(form).not_to be_valid - expect(form.errors.of_kind?(:email, :invalid)).to be_truthy + expect(form.errors).to be_of_kind(:email, :invalid) end it "requires the honeypot field is empty" do @@ -43,6 +43,6 @@ form.spam_trap = "I am a bot" expect(form).not_to be_valid - expect(form.errors.of_kind?(:spam_trap, :present)).to be_truthy + expect(form.errors).to be_of_kind(:spam_trap, :present) end end diff --git a/app-rails/spec/forms/users/verify_account_form_spec.rb b/app-rails/spec/forms/users/verify_account_form_spec.rb index 83ed152da..a6b7cc6bb 100644 --- a/app-rails/spec/forms/users/verify_account_form_spec.rb +++ b/app-rails/spec/forms/users/verify_account_form_spec.rb @@ -2,30 +2,30 @@ RSpec.describe Users::VerifyAccountForm do it "passes validation with valid email and code" do - form = Users::VerifyAccountForm.new(email: "test@example.com", code: "123456") + form = described_class.new(email: "test@example.com", code: "123456") expect(form).to be_valid end it "requires email and code" do - form = Users::VerifyAccountForm.new(email: nil, code: nil) + form = described_class.new(email: nil, code: nil) expect(form).to be_invalid - expect(form.errors.of_kind?(:email, :blank)).to be_truthy - expect(form.errors.of_kind?(:code, :blank)).to be_truthy + expect(form.errors).to be_of_kind(:email, :blank) + expect(form.errors).to be_of_kind(:code, :blank) end it "requires email to be a valid email" do - form = Users::VerifyAccountForm.new(email: "invalid-email", code: "123456") + form = described_class.new(email: "invalid-email", code: "123456") expect(form).to be_invalid - expect(form.errors.of_kind?(:email, :invalid)).to be_truthy + expect(form.errors).to be_of_kind(:email, :invalid) end it "requires code to be 6 characters" do - form = Users::VerifyAccountForm.new(email: "test@example.com", code: "12345") + form = described_class.new(email: "test@example.com", code: "12345") expect(form).to be_invalid - expect(form.errors.of_kind?(:code, :wrong_length)).to be_truthy + expect(form.errors).to be_of_kind(:code, :wrong_length) end end diff --git a/app-rails/spec/models/user_spec.rb b/app-rails/spec/models/user_spec.rb index 6500b8015..ad9edf33a 100644 --- a/app-rails/spec/models/user_spec.rb +++ b/app-rails/spec/models/user_spec.rb @@ -17,11 +17,11 @@ } it "returns true if the access token expires within the designated minutes" do - expect(user.access_token_expires_within_minutes?(access_token, 5)).to eq(true) + expect(user.access_token_expires_within_minutes?(access_token, 5)).to be(true) end it "returns false if the access token is not expiring within the designated minutes" do - expect(user.access_token_expires_within_minutes?(access_token, 1)).to eq(false) + expect(user.access_token_expires_within_minutes?(access_token, 1)).to be(false) end end end diff --git a/app-rails/spec/services/auth_service_spec.rb b/app-rails/spec/services/auth_service_spec.rb index c88faca94..ec357f582 100644 --- a/app-rails/spec/services/auth_service_spec.rb +++ b/app-rails/spec/services/auth_service_spec.rb @@ -6,7 +6,7 @@ describe "#register" do it "creates a new user" do - auth_service = AuthService.new(mock_auth_adapter) + auth_service = described_class.new(mock_auth_adapter) auth_service.register("test@example.com", "password") @@ -19,7 +19,7 @@ describe "#change_email" do it "updates the user's email" do - auth_service = AuthService.new(mock_auth_adapter) + auth_service = described_class.new(mock_auth_adapter) User.create!(uid: mock_uid, email: "test@example.com", provider: "mock") auth_service.change_email(mock_uid, "new@example.com") @@ -31,7 +31,7 @@ describe "#initiate_auth" do it "creates a new user if one does not exist" do - auth_service = AuthService.new(mock_auth_adapter) + auth_service = described_class.new(mock_auth_adapter) response = auth_service.initiate_auth("test@example.com", "password") @@ -40,7 +40,7 @@ end it "updates the user's email if it has changed" do - auth_service = AuthService.new(mock_auth_adapter) + auth_service = described_class.new(mock_auth_adapter) User.create!(uid: mock_uid, email: "oldie@example.com", provider: "mock") response = auth_service.initiate_auth("new@example.com", "password") @@ -52,7 +52,7 @@ describe "#verify_account" do it "returns empty struct on success" do - auth_service = AuthService.new(mock_auth_adapter) + auth_service = described_class.new(mock_auth_adapter) expect(auth_service.verify_account("test@example.com", "123456")).to eq({}) end