suspenders 20250317.0 → 20251219.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -0
  3. data/CODEOWNERS +9 -0
  4. data/CONTRIBUTING.md +71 -0
  5. data/FEATURES.md +177 -0
  6. data/GOALS.md +65 -0
  7. data/LICENSE +21 -0
  8. data/NEWS.md +808 -0
  9. data/README.md +59 -67
  10. data/RELEASING.md +18 -0
  11. data/Rakefile +6 -13
  12. data/SECURITY.md +19 -0
  13. data/exe/suspenders +38 -0
  14. data/lib/suspenders/cli.rb +64 -0
  15. data/lib/suspenders/version.rb +4 -4
  16. data/lib/suspenders.rb +5 -6
  17. data/lib/templates/Procfile +3 -0
  18. data/lib/templates/Procfile.dev +2 -0
  19. data/lib/{generators/templates/views/flashes.html.erb → templates/app/views/application/_flashes.html.erb} +4 -2
  20. data/lib/templates/app/views/application/_form_errors.html.erb +11 -0
  21. data/lib/templates/config/initializers/sidekiq.rb +14 -0
  22. data/lib/templates/lib/development/seeder.rb +21 -0
  23. data/lib/templates/lib/tasks/development.rake +15 -0
  24. data/lib/templates/spec/support/action_mailer.rb +11 -0
  25. data/lib/{generators/templates/factories/factory_bot_rspec.rb → templates/spec/support/factory_bot.rb} +0 -2
  26. data/lib/templates/web.rb +544 -0
  27. data/sig/suspenders.rbs +4 -0
  28. metadata +34 -119
  29. data/lib/generators/suspenders/accessibility_generator.rb +0 -24
  30. data/lib/generators/suspenders/advisories_generator.rb +0 -32
  31. data/lib/generators/suspenders/ci_generator.rb +0 -54
  32. data/lib/generators/suspenders/email_generator.rb +0 -56
  33. data/lib/generators/suspenders/environments/development_generator.rb +0 -50
  34. data/lib/generators/suspenders/environments/production_generator.rb +0 -27
  35. data/lib/generators/suspenders/environments/test_generator.rb +0 -39
  36. data/lib/generators/suspenders/factories_generator.rb +0 -64
  37. data/lib/generators/suspenders/inline_svg_generator.rb +0 -24
  38. data/lib/generators/suspenders/install/web_generator.rb +0 -85
  39. data/lib/generators/suspenders/jobs_generator.rb +0 -34
  40. data/lib/generators/suspenders/lint_generator.rb +0 -94
  41. data/lib/generators/suspenders/prerequisites_generator.rb +0 -19
  42. data/lib/generators/suspenders/rake_generator.rb +0 -25
  43. data/lib/generators/suspenders/setup_generator.rb +0 -14
  44. data/lib/generators/suspenders/styles_generator.rb +0 -84
  45. data/lib/generators/suspenders/tasks_generator.rb +0 -20
  46. data/lib/generators/suspenders/testing_generator.rb +0 -113
  47. data/lib/generators/suspenders/views_generator.rb +0 -38
  48. data/lib/generators/templates/ci/ci.yml.tt +0 -148
  49. data/lib/generators/templates/email/email_interceptor.rb +0 -11
  50. data/lib/generators/templates/factories/factories.rb +0 -2
  51. data/lib/generators/templates/factories/factories_test.rb +0 -9
  52. data/lib/generators/templates/inline_svg/inline_svg.rb +0 -3
  53. data/lib/generators/templates/install/web/CONTRIBUTING.md +0 -94
  54. data/lib/generators/templates/lint/config_better_html.yml +0 -2
  55. data/lib/generators/templates/lint/config_initializers_better_html.rb +0 -9
  56. data/lib/generators/templates/lint/erb-lint.yml +0 -63
  57. data/lib/generators/templates/lint/erblint.rake +0 -47
  58. data/lib/generators/templates/lint/eslintrc.json +0 -7
  59. data/lib/generators/templates/lint/package.json +0 -4
  60. data/lib/generators/templates/lint/prettierignore +0 -1
  61. data/lib/generators/templates/lint/prettierrc +0 -11
  62. data/lib/generators/templates/lint/rubocop.yml.tt +0 -7
  63. data/lib/generators/templates/lint/stylelintrc.json +0 -3
  64. data/lib/generators/templates/prerequisites/node-version.tt +0 -1
  65. data/lib/generators/templates/setup/bin_setup.rb +0 -39
  66. data/lib/generators/templates/styles/postcss.config.js +0 -11
  67. data/lib/generators/templates/tasks/dev.rake +0 -12
  68. data/lib/generators/templates/testing/action_mailer.rb +0 -5
  69. data/lib/install/web.rb +0 -70
  70. data/lib/suspenders/cleanup/generate_readme.rb +0 -165
  71. data/lib/suspenders/cleanup/organize_gemfile.rb +0 -134
  72. data/lib/suspenders/engine.rb +0 -5
  73. data/lib/suspenders/generators.rb +0 -126
  74. data/lib/suspenders/railtie.rb +0 -4
  75. data/lib/tasks/suspenders.rake +0 -37
  76. /data/lib/{generators/templates/factories → templates/spec}/factories_spec.rb +0 -0
  77. /data/lib/{generators/templates/testing → templates/spec/support}/driver.rb +0 -0
  78. /data/lib/{generators/templates/testing → templates/spec/support}/i18n.rb +0 -0
  79. /data/lib/{generators/templates/testing → templates/spec/support}/shoulda_matchers.rb +0 -0
data/README.md CHANGED
@@ -2,122 +2,114 @@
2
2
 
3
3
  [![CI](https://github.com/thoughtbot/suspenders/actions/workflows/main.yml/badge.svg)](https://github.com/thoughtbot/suspenders/actions/workflows/main.yml)
4
4
 
5
- Suspenders is a [Rails Engine][] containing generators for configuring Rails
6
- applications with these [features][].
5
+ Suspenders is intended to create a new Rails applications with these
6
+ [features][], and is optimised for deployment on Heroku.
7
7
 
8
- It is used by thoughtbot to get a jump start on a new or existing app. Use
9
- Suspenders if you're in a rush to build something amazing; don't use it if you
10
- like missing deadlines.
8
+ It is used by thoughtbot to get a jump start on new apps. Use Suspenders if
9
+ you're in a rush to build something amazing; don't use it if you like missing
10
+ deadlines.
11
11
 
12
- [Rails Engine]: https://guides.rubyonrails.org/engines.html
13
12
  [features]: ./FEATURES.md
14
13
 
15
14
  ![Suspenders boy](https://media.tumblr.com/1TEAMALpseh5xzf0Jt6bcwSMo1_400.png)
16
15
 
17
- ## Requirements
16
+ ## Prerequisites
18
17
 
19
- - Rails `~> 8.0`
20
- - Ruby `>= 3.1`
21
- - Node `>= 20.0.0`
18
+ Suspenders requires the **latest** version of [Rails][] and its dependencies.
22
19
 
23
- ## Usage
20
+ Additionally, Suspenders requires [PostgreSQL][] and [Redis][].
24
21
 
25
- Suspenders can be used to create a new Rails application, or to enhance an
26
- existing Rails application.
22
+ [Rails]: https://guides.rubyonrails.org/install_ruby_on_rails.html
23
+ [PostgreSQL]: https://formulae.brew.sh/formula/postgresql@17
24
+ [Redis]: https://formulae.brew.sh/formula/redis
27
25
 
28
- ### With New Rails Applications
26
+ ## Installation
29
27
 
30
- This approach uses an [application template][] to generate a new Rails
31
- application with Suspenders.
28
+ ```
29
+ gem install suspenders
30
+ ```
32
31
 
33
- We skip the [default test framework][] in favor of [RSpec][], and [prefer
34
- PostgreSQL][] as our database.
32
+ ## Usage
35
33
 
36
- We skip [RuboCop rules by default][] in favor of our [holistic linting rules][].
34
+ First, make sure you're on the latest version of Rails.
37
35
 
38
- #### Use the latest suspenders release:
36
+ ```
37
+ gem update rails
38
+ ```
39
+
40
+ Then, create a new application with Suspenders.
39
41
 
40
42
  ```
41
- rails new app_name \
42
- --skip-rubocop \
43
- --skip-test \
44
- -d=postgresql \
45
- -m=https://raw.githubusercontent.com/thoughtbot/suspenders/main/lib/install/web.rb
43
+ suspenders new <app_name>
46
44
  ```
47
45
 
48
- #### OR use the current (possibly unreleased) `main` branch of suspenders:
46
+ Under the hood, Suspenders uses an [application template][] to generate a new Rails
47
+ application like so:
49
48
 
50
49
  ```
51
- rails new app_name \
52
- --suspenders-main \
53
- --skip-rubocop \
54
- --skip-test \
50
+ rails new <app_name> \
55
51
  -d=postgresql \
56
- -m=https://raw.githubusercontent.com/thoughtbot/suspenders/main/lib/install/web.rb
52
+ --skip-test \
53
+ --skip-solid \
54
+ --m=~/path/to/template.rb
57
55
  ```
58
56
 
59
- Then run `bin/setup` within the newly generated application.
57
+ We skip the [default test framework][] in favor of [RSpec][], and [prefer
58
+ PostgreSQL][] as our database. We skip the Solid ecosystem since we prefer
59
+ [Sidekiq][], and because Solid Queue has [performance issues][] on Heroku.
60
60
 
61
- Alternatively, if you're using our [dotfiles][], then you can just run `rails new
62
- app_name`, or create your own [railsrc][] file with the following configuration:
61
+ > [!IMPORTANT]
62
+ > Since Suspenders generates an application that enables `require_master_key`,
63
+ > you'll need to add it to GitHub as a [secret][] in order for GitHub Actions to
64
+ > work.
63
65
 
64
66
  ```
65
- --skip-rubocop
66
- --skip-test
67
- --database=postgresql
68
- -m=https://raw.githubusercontent.com/thoughtbot/suspenders/main/lib/install/web.rb
67
+ cd <app_name>
68
+
69
+ gh secret set RAILS_MASTER_KEY value-from-config-master.key
69
70
  ```
70
71
 
71
72
  [application template]: https://guides.rubyonrails.org/rails_application_templates.html
72
73
  [default test framework]: https://guides.rubyonrails.org/testing.html
73
74
  [RSpec]: http://rspec.info
74
75
  [prefer PostgreSQL]: https://github.com/thoughtbot/dotfiles/pull/728
75
- [dotfiles]: https://github.com/thoughtbot/dotfiles
76
- [railsrc]: https://github.com/rails/rails/blob/7f7f9df8641e35a076fe26bd097f6a1b22cb4e2d/railties/lib/rails/generators/rails/app/USAGE#L5C1-L7
77
- [RuboCop rules by default]: https://guides.rubyonrails.org/v7.2/7_2_release_notes.html#add-omakase-rubocop-rules-by-default
78
- [holistic linting rules]: https://github.com/thoughtbot/suspenders/blob/main/FEATURES.md#linting
76
+ [Sidekiq]: https://github.com/sidekiq/sidekiq/
77
+ [performance issues]: https://github.com/rails/solid_queue/issues/330
78
+ [secret]: https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets
79
79
 
80
- ### With Existing Rails Applications
80
+ ## Initial deployment to Heroku
81
81
 
82
- Suspenders can be used on an existing Rails application by adding it to the
83
- `:development` and `:test` group.
84
-
85
- ```ruby
86
- group :development, :test do
87
- gem "suspenders"
88
- end
89
- ```
82
+ Once your application is generated, you can deploy to Heroku with [Heroku CLI][cli].
90
83
 
91
- Once installed, you can invoke the web installation generator, which will
92
- invoke all generators.
93
84
 
94
85
  ```
95
- bin/rails g suspenders:install:web
96
- ```
86
+ cd <app_name>
97
87
 
98
- Or, you can invoke generators individually. To see a list of available
99
- generators run:
88
+ heroku apps:create
100
89
 
101
- ```
102
- bin/rails g | grep suspenders
90
+ heroku buildpacks:set heroku/ruby
91
+
92
+ heroku addons:create heroku-postgresql:essential-0
93
+ heroku addons:create heroku-redis:mini
103
94
  ```
104
95
 
105
- To learn more about a generator, run:
96
+ Once the application is provisioned, you'll want to set the following required
97
+ environment variables.
106
98
 
107
99
  ```
108
- bin/rails g suspenders:[generator_name] --help
100
+ heroku config:set \
101
+ APPLICATION_HOST=value-from-heroku
102
+ RAILS_MASTER_KEY=value-from-config-master.key
109
103
  ```
110
104
 
111
- ### Available Tasks
112
-
113
- Suspenders ships with several custom Rake tasks.
105
+ Finally, don't forget to enable the `worker`.
114
106
 
115
107
  ```
116
- bin/rails suspenders:rake
117
- bin/rails suspenders:db:migrate
118
- bin/rails suspenders:cleanup:organize_gemfile
108
+ heroku ps:scale worker=1
119
109
  ```
120
110
 
111
+ [cli]: https://devcenter.heroku.com/articles/heroku-cli
112
+
121
113
  ## Contributing
122
114
 
123
115
  See the [CONTRIBUTING] document.
data/RELEASING.md ADDED
@@ -0,0 +1,18 @@
1
+ # Releasing
2
+
3
+ 1. Update `NEWS.md` to reflect the changes since last release.
4
+ 2. Update `lib/suspenders/version.rb` file accordingly.
5
+ 3. Commit changes. There shouldn't be code changes, and thus CI doesn't need to
6
+ run; you can add `[ci skip]` to the commit message.
7
+ 4. Tag the release: `git tag vVERSION -a -s`. The tag message should contain the
8
+ appropriate `NEWS.md` subsection.
9
+ 5. Push changes: `git push --tags`
10
+ 6. Build and publish to rubygems:
11
+ ```sh
12
+ gem build suspenders.gemspec
13
+ gem push suspenders-*.gem
14
+ ```
15
+ 7. Add a new GitHub release:
16
+ https://github.com/thoughtbot/suspenders/releases/new?tag=vVERSION
17
+ 8. Announce the new release, making sure to thank the contributors who helped
18
+ shape this version!
data/Rakefile CHANGED
@@ -1,17 +1,10 @@
1
- require "bundler/setup"
2
- require "bundler/gem_tasks"
3
- require "minitest/test_task"
4
- require "standard/rake"
1
+ # frozen_string_literal: true
5
2
 
6
- require File.expand_path("test/dummy/config/application", __dir__)
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
7
5
 
8
- Rails.application.load_tasks
6
+ RSpec::Core::RakeTask.new(:spec)
9
7
 
10
- Minitest::TestTask.create(:test) do |t|
11
- t.libs << "test"
12
- t.libs << "lib"
13
- t.warning = false
14
- t.test_globs = ["test/**/*_test.rb"]
15
- end
8
+ require "standard/rake"
16
9
 
17
- task default: %i[test standard]
10
+ task default: %i[spec standard]
data/SECURITY.md ADDED
@@ -0,0 +1,19 @@
1
+ <!-- START /templates/security.md -->
2
+ # Security Policy
3
+
4
+ ## Supported Versions
5
+
6
+ Only the the latest version of this project is supported at a given time. If
7
+ you find a security issue with an older version, please try updating to the
8
+ latest version first.
9
+
10
+ If for some reason you can't update to the latest version, please let us know
11
+ your reasons so that we can have a better understanding of your situation.
12
+
13
+ ## Reporting a Vulnerability
14
+
15
+ For security inquiries or vulnerability reports, visit
16
+ <https://thoughtbot.com/security>.
17
+
18
+ If you have any suggestions to improve this policy, visit <https://thoughtbot.com/security>.
19
+ <!-- END /templates/security.md -->
data/exe/suspenders ADDED
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative "../lib/suspenders"
4
+
5
+ if ARGV.length > 2 || ARGV.length == 0
6
+ puts "Error: Invalid command"
7
+ puts "Available commands are"
8
+ puts "suspenders new APP_NAME"
9
+ puts "suspenders -v"
10
+ exit 1
11
+ end
12
+
13
+ command = ARGV[0]
14
+ app_name = ARGV[1]
15
+
16
+ if ["-v", "--version"].include?(command)
17
+ puts Suspenders::VERSION
18
+ exit 0
19
+ elsif ["new"].include?(command)
20
+ if app_name.nil? || app_name.empty?
21
+ puts "Error: Application name required"
22
+ puts "Usage: suspenders new APP_NAME"
23
+ exit 1
24
+ end
25
+ else
26
+ puts "Error: Invalid command"
27
+ puts "Available commands are"
28
+ puts "suspenders new APP_NAME"
29
+ puts "suspenders -v"
30
+ exit 1
31
+ end
32
+
33
+ begin
34
+ Suspenders::CLI.run(app_name)
35
+ exit 0
36
+ rescue Suspenders::Error => e
37
+ abort "Error: #{e.message}"
38
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Suspenders
4
+ # Command-line interface for generating Rails applications with Suspenders.
5
+ # This class handles the creation of new Rails apps using a custom template
6
+ # and predefined configuration options.
7
+ class CLI
8
+ # Base options passed to the Rails generator for all new applications.
9
+ # These options configure PostgreSQL as the database, skip test setup,
10
+ # and skip Solid-related features.
11
+ BASE_OPTIONS = [
12
+ "-d=postgresql",
13
+ "--skip-test",
14
+ "--skip-solid"
15
+ ]
16
+
17
+ # Initializes a new CLI instance.
18
+ #
19
+ # @param app_name [String] the name of the Rails application to create
20
+ def initialize(app_name)
21
+ @app_name = app_name
22
+ end
23
+
24
+ # Creates and runs a new CLI instance for the given application name.
25
+ #
26
+ # @param app_name [String] the name of the Rails application to create
27
+ # @return [Boolean] true if the Rails app was created successfully
28
+ # @raise [Error] if Rails is not installed or app creation fails
29
+ def self.run(app_name)
30
+ new(app_name).run
31
+ end
32
+
33
+ # Executes the CLI workflow to generate a new Rails application.
34
+ # Verifies Rails installation and generates the app with Suspenders template.
35
+ #
36
+ # @return [Boolean] true if the Rails app was created successfully
37
+ # @raise [Error] if Rails is not installed or app creation fails
38
+ def run
39
+ verify_rails_exists!
40
+ generate_new_rails_app
41
+ end
42
+
43
+ private
44
+
45
+ attr_reader :app_name
46
+
47
+ def verify_rails_exists!
48
+ unless system("which", "rails", out: File::NULL, err: File::NULL)
49
+ raise Error, "Rails not found. Install with: gem install rails"
50
+ end
51
+ end
52
+
53
+ def generate_new_rails_app
54
+ template_path = File.expand_path("../templates/web.rb", __dir__)
55
+ options = BASE_OPTIONS + ["-m=#{template_path}"]
56
+
57
+ if system("rails", "new", app_name, *options)
58
+ true
59
+ else
60
+ raise Error, "Failed to create Rails app"
61
+ end
62
+ end
63
+ end
64
+ end
@@ -1,6 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Suspenders
2
- VERSION = "20250317.0".freeze
3
- RAILS_VERSION = "~> 8.0".freeze
4
- MINIMUM_RUBY_VERSION = ">= 3.1".freeze
5
- MINIMUM_NODE_VERSION = "20.0.0".freeze
4
+ VERSION = "20251219.0"
5
+ MINIMUM_RUBY_VERSION = ">= 3.2.0"
6
6
  end
data/lib/suspenders.rb CHANGED
@@ -1,10 +1,9 @@
1
- require "suspenders/version"
2
- require "suspenders/engine"
3
- require "suspenders/railtie"
4
- require "suspenders/generators"
5
- require "suspenders/cleanup/organize_gemfile"
6
- require "suspenders/cleanup/generate_readme"
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "suspenders/version"
4
+ require_relative "suspenders/cli"
7
5
 
8
6
  module Suspenders
7
+ class Error < StandardError; end
9
8
  # Your code goes here...
10
9
  end
@@ -0,0 +1,3 @@
1
+ release: bundle exec rails db:migrate; bundle exec rails db:seed
2
+ web: bundle exec puma -C config/puma.rb
3
+ worker: bundle exec sidekiq -c 10
@@ -0,0 +1,2 @@
1
+ web: env RUBY_DEBUG_OPEN=true bin/rails server
2
+ worker: bundle exec sidekiq
@@ -1,7 +1,9 @@
1
1
  <% if flash.any? %>
2
- <div class="flashes">
2
+ <div>
3
3
  <% flash.each do |type, message| -%>
4
- <div class="flash-<%= type %>"><%= message %></div>
4
+ <div role="alert">
5
+ <%= message %>
6
+ </div>
5
7
  <% end -%>
6
8
  </div>
7
9
  <% end %>
@@ -0,0 +1,11 @@
1
+ <%# locals: (resource: ) %>
2
+
3
+ <% if resource.errors.any? %>
4
+ <div id="error_explanation" role="alert" data-turbo-cache="false">
5
+ <ul>
6
+ <% resource.errors.full_messages.each do |message| %>
7
+ <li><%= message %></li>
8
+ <% end %>
9
+ </ul>
10
+ </div>
11
+ <% end %>
@@ -0,0 +1,14 @@
1
+ # https://github.com/sidekiq/sidekiq/wiki/Heroku
2
+
3
+ SIDEKIQ_REDIS_CONFIGURATION = {
4
+ url: ENV.fetch(ENV.fetch("REDIS_PROVIDER", "REDIS_URL"), nil), # use REDIS_PROVIDER for Redis environment variable name, defaulting to REDIS_URL
5
+ ssl_params: {verify_mode: OpenSSL::SSL::VERIFY_NONE} # we must trust Heroku and AWS here
6
+ }
7
+
8
+ Sidekiq.configure_server do |config|
9
+ config.redis = SIDEKIQ_REDIS_CONFIGURATION
10
+ end
11
+
12
+ Sidekiq.configure_client do |config|
13
+ config.redis = SIDEKIQ_REDIS_CONFIGURATION
14
+ end
@@ -0,0 +1,21 @@
1
+ # This file should ensure the existence of records required to run the application in development.
2
+ # The code here should be idempotent so that it can be executed at any point in development.
3
+ # The data can then be loaded with the bin/rails development:db:seed command.
4
+
5
+ module Development
6
+ class Seeder
7
+ def self.load_seeds
8
+ if Rails.env.development?
9
+ new.load_seeds
10
+ else
11
+ raise "Development::Seeder can only be run in a development environment."
12
+ end
13
+ end
14
+
15
+ def load_seeds
16
+ # ["Ruby", "Ralph"].each do |name|
17
+ # User.find_or_create_by!(name:)
18
+ # end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ if Rails.env.development?
2
+ namespace :development do
3
+ namespace :db do
4
+ desc "Loads seed data into development."
5
+ task seed: ["environment", "db:seed"] do
6
+ Development::Seeder.load_seeds
7
+ end
8
+
9
+ namespace :seed do
10
+ desc "Truncate tables of each database for development and loads seed data."
11
+ task replant: ["environment", "db:truncate_all", "development:db:seed"]
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ RSpec.configure do |config|
2
+ # https://guides.rubyonrails.org/testing.html#the-basic-test-case
3
+ #
4
+ # The ActionMailer::Base.deliveries array is only reset automatically in
5
+ # ActionMailer::TestCase and ActionDispatch::IntegrationTest tests. If
6
+ # you want to have a clean slate outside these test cases, you can reset
7
+ # it manually with: ActionMailer::Base.deliveries.clear
8
+ config.before(:each) do
9
+ ActionMailer::Base.deliveries.clear
10
+ end
11
+ end
@@ -1,5 +1,3 @@
1
- FactoryBot.use_parent_strategy = true
2
-
3
1
  RSpec.configure do |config|
4
2
  config.include FactoryBot::Syntax::Methods
5
3
  end