Top Level Namespace
Defined Under Namespace
Modules: Development, Suspenders
Constant Summary collapse
- SIDEKIQ_REDIS_CONFIGURATION =
{ url: ENV.fetch(ENV.fetch("REDIS_PROVIDER", "REDIS_URL"), nil), # use REDIS_PROVIDER for Redis environment variable name, defaulting to REDIS_URL ssl_params: {verify_mode: OpenSSL::SSL::VERIFY_NONE} # we must trust Heroku and AWS here }
Instance Method Summary collapse
- #add_procfiles ⇒ Object
- #commit_final_application_state ⇒ Object
- #commit_initial_application_state ⇒ Object
- #configure_action_cable ⇒ Object
- #configure_ci ⇒ Object
- #configure_database ⇒ Object
- #configure_development_seeder ⇒ Object
- #configure_inline_svg ⇒ Object
- #configure_mailer_interceptor ⇒ Object
- #configure_sidekiq ⇒ Object
- #configure_strong_migrations ⇒ Object
- #configure_test_suite ⇒ Object
- #install_gems ⇒ Object
- #lint_codebase ⇒ Object
- #print_message ⇒ Object
- #ralph ⇒ Object
- #run_migrations ⇒ Object
- #setup_application ⇒ Object
- #setup_development_environment ⇒ Object
- #setup_production_environment ⇒ Object
- #setup_test_environment ⇒ Object
-
#source_paths ⇒ Object
Methods like
copy_filewill accept relative paths to the template's location. - #update_bin_dev ⇒ Object
- #update_layout ⇒ Object
- #update_readme ⇒ Object
Instance Method Details
#add_procfiles ⇒ Object
292 293 294 295 |
# File 'lib/templates/web.rb', line 292 def add_procfiles copy_file "Procfile" copy_file "Procfile.dev" end |
#commit_final_application_state ⇒ Object
527 528 529 |
# File 'lib/templates/web.rb', line 527 def commit_final_application_state git add: ".", commit: %(-m 'Changes introduced by Suspenders version #{Suspenders::VERSION}') unless ENV["CI"] end |
#commit_initial_application_state ⇒ Object
78 79 80 |
# File 'lib/templates/web.rb', line 78 def commit_initial_application_state git add: ".", commit: %(-m 'Initial commit from rails new') unless ENV["CI"] end |
#configure_action_cable ⇒ Object
198 199 200 201 202 203 204 205 206 |
# File 'lib/templates/web.rb', line 198 def configure_action_cable gsub_file "config/cable.yml", /adapter: async/, "adapter: redis\n url: redis://localhost:6379/1" gsub_file "config/cable.yml", /channel_prefix: .*$/, '\0' + "\n ssl_params:\n verify_mode: <%= OpenSSL::SSL::VERIFY_NONE %> # https://devcenter.heroku.com/articles/connecting-heroku-redis#connecting-in-ruby" end |
#configure_ci ⇒ Object
128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 |
# File 'lib/templates/web.rb', line 128 def configure_ci # https://thoughtbot.com/blog/rspec-rails-github-actions-configuration append_to_file ".github/workflows/ci.yml", "\n" + <<~YAML.gsub(/^/, " ") test: runs-on: ubuntu-latest services: postgres: image: postgres env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres ports: - 5432:5432 options: --health-cmd="pg_isready" --health-interval=10s --health-timeout=5s --health-retries=3 # redis: # image: valkey/valkey:8 # ports: # - 6379:6379 # options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5 steps: - name: Install packages run: sudo apt-get update && sudo apt-get install --no-install-recommends -y libpq-dev libvips - name: Checkout code uses: actions/checkout@v5 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: bundler-cache: true - name: Run Tests env: RAILS_ENV: test DATABASE_URL: postgres://postgres:postgres@localhost:5432 RAILS_MASTER_KEY: ${{ secrets.RAILS_MASTER_KEY }} # REDIS_URL: redis://localhost:6379/0 run: bin/rails db:setup spec - name: Keep screenshots from failed system tests uses: actions/upload-artifact@v4 if: failure() with: name: screenshots path: ${{ github.workspace }}/tmp/capybara if-no-files-found: ignore YAML end |
#configure_database ⇒ Object
82 83 84 85 86 87 88 |
# File 'lib/templates/web.rb', line 82 def configure_database gsub_file "config/database.yml", /^production:.*?password:.*?\n/m, <<~YAML production: <<: *default url: <%= ENV["DATABASE_URL"] %> YAML end |
#configure_development_seeder ⇒ Object
238 239 240 241 |
# File 'lib/templates/web.rb', line 238 def configure_development_seeder copy_file "lib/development/seeder.rb" copy_file "lib/tasks/development.rake" end |
#configure_inline_svg ⇒ Object
230 231 232 233 234 235 236 |
# File 'lib/templates/web.rb', line 230 def configure_inline_svg initializer "inline_svg.rb", <<~RUBY InlineSvg.configure do |config| config.raise_on_file_not_found = true end RUBY end |
#configure_mailer_interceptor ⇒ Object
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 |
# File 'lib/templates/web.rb', line 212 def configure_mailer_interceptor lib "email_interceptor.rb", <<~RUBY class EmailInterceptor def self.delivering_email(message) message.to = ENV.fetch("INTERCEPTOR_ADDRESSES", "").split(",") end end RUBY initializer "email_interceptor.rb", <<~RUBY Rails.application.configure do if ENV.fetch("INTERCEPTOR_ADDRESSES", "").split(",").any? config.action_mailer.interceptors = %w[EmailInterceptor] end end RUBY end |
#configure_sidekiq ⇒ Object
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/templates/web.rb', line 180 def configure_sidekiq # TODO: Use #initializer instead copy_file "config/initializers/sidekiq.rb" prepend_to_file "config/routes.rb", "require \"sidekiq/web\"\n\n" sidekiq_route = <<-RUBY if Rails.env.local? mount Sidekiq::Web => "/sidekiq" end RUBY insert_into_file "config/routes.rb", sidekiq_route, after: "Rails.application.routes.draw do\n # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html\n" # https://github.com/sidekiq/sidekiq/wiki/Active+Job environment "config.active_job.queue_adapter = :sidekiq" environment "config.active_job.queue_adapter = :inline", env: "test" end |
#configure_strong_migrations ⇒ Object
208 209 210 |
# File 'lib/templates/web.rb', line 208 def configure_strong_migrations rails_command "generate strong_migrations:install" end |
#configure_test_suite ⇒ Object
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
# File 'lib/templates/web.rb', line 90 def configure_test_suite rails_command "generate rspec:install" # Update default configuration uncomment_lines "spec/rails_helper.rb", /config\.infer_spec_type_from_file_location!/ uncomment_lines "spec/rails_helper.rb", /Rails\.root\.glob/ gsub_file "spec/spec_helper.rb", /^=begin\n/, "" gsub_file "spec/spec_helper.rb", /^=end\n/, "" # Configure Webmock inject_into_file "spec/spec_helper.rb", "require \"webmock/rspec\"\n", before: /^RSpec\.configure/ append_to_file "spec/spec_helper.rb", <<~RUBY WebMock.disable_net_connect!( allow_localhost: true, allow: [ /(chromedriver|storage).googleapis.com/, "googlechromelabs.github.io" ] ) RUBY # Custom configuration copy_file "spec/support/action_mailer.rb" copy_file "spec/support/driver.rb" copy_file "spec/support/i18n.rb" copy_file "spec/support/factory_bot.rb" copy_file "spec/support/shoulda_matchers.rb" # Custom specs copy_file "spec/factories_spec.rb" empty_directory "spec/system" create_file "spec/system/.gitkeep" # Ignore spec/examples.txt append_to_file ".gitignore", "/spec/examples.txt" end |
#install_gems ⇒ Object
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/templates/web.rb', line 8 def install_gems uncomment_lines "Gemfile", /gem\s"redis"/ gem "inline_svg" gem "sidekiq" gem "strong_migrations" gem_group :test do # TODO: How can we ensure we're notified of new releases? gem "action_dispatch-testing-integration-capybara", github: "thoughtbot/action_dispatch-testing-integration-capybara", tag: "v0.2.0", require: "action_dispatch/testing/integration/capybara/rspec" gem "capybara" gem "capybara_accessibility_audit" # TODO: How can we ensure we're notified of new releases? gem "capybara_accessible_selectors", git: "https://github.com/citizensadvice/capybara_accessible_selectors", tag: "v0.16.0" gem "selenium-webdriver" gem "shoulda-matchers", "~> 7.0" gem "webmock" end gem_group :development do gem "hotwire-spark" end gem_group :development, :test do gem "factory_bot_rails" gem "rspec-rails", "~> 8.0.0" end end |
#lint_codebase ⇒ Object
523 524 525 |
# File 'lib/templates/web.rb', line 523 def lint_codebase run "bin/rubocop -a" end |
#print_message ⇒ Object
531 532 533 534 535 536 |
# File 'lib/templates/web.rb', line 531 def say "" say "Congratulations! You just pulled our suspenders." say "" say ralph end |
#ralph ⇒ Object
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 |
# File 'lib/templates/web.rb', line 538 def ralph <<~ASCII ################################################## ################+ ################ ############ -########### ######### =################*. :######## #######- =####= =#### ####### ########+ ###+ +##+ ######## ###########. .###############- ########## ###########= +####= :+####* ########### ###############= *############## ############## +###########- ############## ###############=*#################+############### ################################################## #########: ######### ######### ######## ######### ######## ######### ##. +#= ######## ######### #= # #* #. ######## ######### #. # #= #: ######## ######### :##+ ### ######## ######### ######## ######### ######## ######### ######## ######### ######## #########+ ######### ################################################## ################################################## ASCII end |
#run_migrations ⇒ Object
314 315 316 317 |
# File 'lib/templates/web.rb', line 314 def run_migrations rails_command "db:create" rails_command "db:migrate" end |
#setup_application ⇒ Object
261 262 263 264 265 |
# File 'lib/templates/web.rb', line 261 def setup_application environment "config.active_record.strict_loading_by_default = true" environment "config.active_record.strict_loading_mode = :n_plus_one_only" environment "config.require_master_key = true" end |
#setup_development_environment ⇒ Object
248 249 250 251 252 |
# File 'lib/templates/web.rb', line 248 def setup_development_environment environment "config.active_model.i18n_customize_full_message = true", env: "development" uncomment_lines "config/environments/development.rb", /config\.i18n\.raise_on_missing_translations/ uncomment_lines "config/environments/development.rb", /config\.generators\.apply_rubocop_autocorrect_after_generate!/ end |
#setup_production_environment ⇒ Object
254 255 256 257 258 259 |
# File 'lib/templates/web.rb', line 254 def setup_production_environment environment "config.sandbox_by_default = true", env: "production" environment "config.active_record.action_on_strict_loading_violation = :log", env: "production" gsub_file "config/environments/production.rb", /# config\.asset_host =.*$/, 'config.asset_host = ENV["ASSET_HOST"]' gsub_file "config/environments/production.rb", /config\.action_mailer\.default_url_options = \{ host: .*? \}/, 'config.action_mailer.default_url_options = { host: ENV.fetch("APPLICATION_HOST") }' end |
#setup_test_environment ⇒ Object
243 244 245 246 |
# File 'lib/templates/web.rb', line 243 def setup_test_environment gsub_file "config/environments/test.rb", /config\.action_dispatch\.show_exceptions = :rescuable/, "config.action_dispatch.show_exceptions = :none" uncomment_lines "config/environments/test.rb", /config\.i18n\.raise_on_missing_translations/ end |
#source_paths ⇒ Object
Methods like copy_file will accept relative paths to the template's location.
4 5 6 |
# File 'lib/templates/web.rb', line 4 def source_paths Array(super) + [__dir__] end |
#update_bin_dev ⇒ Object
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 |
# File 'lib/templates/web.rb', line 267 def update_bin_dev # https://github.com/rails/jsbundling-rails/blob/main/lib/install/dev # rubocop:disable Style/RedundantStringEscape create_file "bin/dev", force: true do <<~BASH #!/usr/bin/env sh if gem list --no-installed --exact --silent foreman; then echo "Installing foreman..." gem install foreman fi # Default to port 3000 if not specified export PORT="\${PORT:-3000}" exec foreman start -f Procfile.dev --env /dev/null "$@" BASH end # rubocop:enable Style/RedundantStringEscape # rubocop:disable Style/NumericLiteralPrefix chmod "bin/dev", 0755 # rubocop:enable Style/NumericLiteralPrefix end |
#update_layout ⇒ Object
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/templates/web.rb', line 297 def update_layout # General partials copy_file "app/views/application/_form_errors.html.erb" copy_file "app/views/application/_flashes.html.erb" # Application Layout gsub_file "app/views/layouts/application.html.erb", /<html>/, "<html lang=\"<%= I18n.locale %>\">" application_html_erb = <<-ERB <main> <%= render "flashes" %> <%= yield %> </main> ERB gsub_file "app/views/layouts/application.html.erb", /^ <%= yield %>\n/, application_html_erb insert_into_file "app/views/layouts/application.html.erb", " <meta name=\"turbo-prefetch\" content=\"false\">\n", after: "</title>\n" end |
#update_readme ⇒ Object
319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 |
# File 'lib/templates/web.rb', line 319 def update_readme create_file "README.md", force: true do <<~MARKDOWN # README This application was initially generated with [Suspenders][]. [Suspenders]: https://github.com/thoughtbot/suspenders ## Local Development Run `bin/dev` to start the web server and Sidekiq worker. Then, navigate to [http://localhost:3000][local] [local]: http://localhost:3000 ### Strong Migrations Uses [Strong Migrations][] to catch unsafe migrations in development. [Strong Migrations]: https://github.com/ankane/strong_migrations ### Seed Data Follows [our guidance][seed-data-guide] for managing seed data. Use `db/seeds.rb` for data required in **all** environments, and `development:db:seed` for data specific to development environments. Place idempotent seed data in `Development::Seeder`. To load development seed data: ```bash bin/rails development:db:seed ``` To reset your database and reload seed data: ```bash bin/rails development:db:seed:replant ``` The `replant` command truncates all tables and reloads the seed data, providing a clean slate for development. [seed-data-guide]: https://github.com/thoughtbot/guides/blob/main/rails/how-to/seed-data.md ## Environment Variables The following environment variables are available in `production`: - `APPLICATION_HOST` - The domain where your application is hosted (required) - `ASSET_HOST` - CDN or asset host URL (optional) - `RAILS_MASTER_KEY` - Used for decrypting credentials (required) ## Rails Console In deployed environments, the Rails console starts in sandbox mode by default. This means any changes made in the console will be rolled back when you exit. To modify data in deployed environments, you must explicitly disable sandbox mode: ``` bin/rails console --no-sandbox ``` This configuration helps prevent accidental data modifications in production. ## Configuration ### All Environments - Enables [strict_loading_by_default][]. - Sets [strict_loading_mode][] to `:n_plus_one`. - Enables [require_master_key][]. [strict_loading_by_default]: https://guides.rubyonrails.org/configuring.html#config-active-record-strict-loading-by-default [strict_loading_mode]: https://guides.rubyonrails.org/configuring.html#config-active-record-strict-loading-mode [require_master_key]: https://guides.rubyonrails.org/configuring.html#config-require-master-key ### Test - Enables [raise_on_missing_translations][]. - Sets [action_dispatch.show_exceptions][] to `:none`. [raise_on_missing_translations]: https://guides.rubyonrails.org/configuring.html#config-i18n-raise-on-missing-translations [action_dispatch.show_exceptions]: https://edgeguides.rubyonrails.org/configuring.html#config-action-dispatch-show-exceptions ### Development - Enables [raise_on_missing_translations][]. - Enables [i18n_customize_full_message][]. - Enables [apply_rubocop_autocorrect_after_generate!][]. [raise_on_missing_translations]: https://guides.rubyonrails.org/configuring.html#config-i18n-raise-on-missing-translations [i18n_customize_full_message]: https://guides.rubyonrails.org/configuring.html#config-active-model-i18n-customize-full-message [apply_rubocop_autocorrect_after_generate!]: https://guides.rubyonrails.org/configuring.html#configuring-generators ### Production - Enables [sandbox_by_default][]. - Sets [action_on_strict_loading_violation][] to `:log`. [sandbox_by_default]: https://guides.rubyonrails.org/configuring.html#config-sandbox-by-default [action_on_strict_loading_violation]: https://guides.rubyonrails.org/configuring.html#config-active-record-action-on-strict-loading-violation ## Testing Uses [RSpec][] and [RSpec Rails][] in favor of the [default test suite][]. The test suite can be run with `bin/rails spec`. Configuration can be found in the following files: ``` spec/rails_helper.rb spec/spec_helper.rb spec/support/action_mailer.rb spec/support/driver.rb spec/support/i18n.rb spec/support/shoulda_matchers.rb ``` - Uses [action_dispatch-testing-integration-capybara][] to introduce Capybara assertions into Request specs. - Uses [shoulda-matchers][] for simple one-liner tests for common Rails functionality. - Uses [webmock][] for stubbing and setting expectations on HTTP requests in Ruby. [RSpec]: http://rspec.info [RSpec Rails]: https://github.com/rspec/rspec-rails [default test suite]: https://guides.rubyonrails.org/testing.html [action_dispatch-testing-integration-capybara]: https://github.com/thoughtbot/action_dispatch-testing-integration-capybara [shoulda-matchers]: https://github.com/thoughtbot/shoulda-matchers [webmock]: https://github.com/bblimke/webmock ### Factories Uses [FactoryBot][] as an alternative to [Fixtures][] to help you define dummy and test data for your test suite. The `create`, `build`, and `build_stubbed` class methods are directly available to all tests. Place FactoryBot definitions in `spec/factories.rb`, at least until it grows unwieldy. This helps reduce confusion around circular dependencies and makes it easy to jump between definitions. [FactoryBot]: https://github.com/thoughtbot/factory_bot [Fixtures]: https://guides.rubyonrails.org/testing.html#the-low-down-on-fixtures ## Accessibility Uses [capybara_accessibility_audit][] and [capybara_accessible_selectors][] to encourage and enforce accessibility best practices. [capybara_accessibility_audit]: https://github.com/thoughtbot/capybara_accessibility_audit [capybara_accessible_selectors]: https://github.com/citizensadvice/capybara_accessible_selectors ## Mailers [Intercept][] emails in non-production environments by setting `INTERCEPTOR_ADDRESSES`. ```sh INTERCEPTOR_ADDRESSES="[email protected],[email protected]" bin/rails s ``` Configuration can be found at `config/initializers/email_interceptor.rb`. Interceptor can be found at `lib/email_interceptor.rb`. [Intercept]: https://guides.rubyonrails.org/action_mailer_basics.html#intercepting-emails ## Jobs Uses [Sidekiq][] for [background job][] processing. Configures the `test` environment to use the [inline][] adapter. [Sidekiq]: https://github.com/sidekiq/sidekiq [background job]: https://guides.rubyonrails.org/active_job_basics.html [inline]: https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/InlineAdapter.html ## Layout and Assets ### Inline SVG Uses [inline_svg][] for embedding SVG documents into views. Configuration can be found at `config/initializers/inline_svg.rb` [inline_svg]: https://github.com/jamesmartin/inline_svg ### Layout - A [partial][] for [flash messages][] is located in `app/views/application/_flashes.html.erb`. - A [partial][] for form errors is located in `app/views/application/_form_errors.html.erb`. - Sets [lang][] attribute on `<html>` element to `en` via `I18n.local`. - Disables Turbo's [Prefetch][] in an effort to reduce unnecessary network requests. [partial]: https://guides.rubyonrails.org/layouts_and_rendering.html#using-partials [flash messages]: https://guides.rubyonrails.org/action_controller_overview.html#the-flash [lang]: https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/lang [title]: https://github.com/calebhearth/title [Prefetch]: https://turbo.hotwired.dev/handbook/drive#prefetching-links-on-hover MARKDOWN end end |