Skip to content

Rework to be a proper Rails plugin#181

Open
OutlawAndy wants to merge 9 commits intomasterfrom
proper-rails-plugin
Open

Rework to be a proper Rails plugin#181
OutlawAndy wants to merge 9 commits intomasterfrom
proper-rails-plugin

Conversation

@OutlawAndy
Copy link
Member

@OutlawAndy OutlawAndy commented Mar 1, 2026

Why?

  1. This gem eager loads all generator classes during host application boot. This is unnecessary and actually goes against Rails best practices. I've recently learned that the proper way to supply generators to a Rails app requires a Railtie / Engine to notify the host application of their location on the file system and allow the host app to lazy load them if/when needed.
  2. This gem introduces 2 top-level namespaces (Rolemodel & RolemodelRails) which is a general anti-pattern for RubyGems.
  3. Naming convention for plugins like this one are to use a - rather than a _. e.g. rspec-rails, slim-rails, rubocop-rails, etc. This ties back into #2 because rolemodel-rails translates to Rolemodel::Rails, while rolemodel_rails translates to RolemodelRails.

What Changed

What changed in this PR?

  • rename to rolemodel-rails
  • add an Engine file to properly notify the host app about the generators
  • don't eager load generators
  • fix test setup which does need all constants that are tested to be eager loaded.
  • Update the recreate_current_example script to use the AppGenerator with dummy_app set to true. (this is how the Rails PluginGenerator does it)
    • I left the legacy app generation script alone, because I couldn't get the AppGenerator to actually build a legacy Rails app. It will only build apps consistent with the current version, even if the gemfile points to an old version.
  • use a modified boot.rb file in the example app which points to the plugin, rather then adding the plugin to it's gemfile (also how the Rails PluginGenerator does it)

Future Considerations

As a "Rails Plugin" with a Railtie/Engine file, there may be specific functionality which we decide to fold into RolemodelRails. Instead of everything being a generator and all code living in each project code base, we might opt to treat some features as provided by the engine and therefor upgradable via bundle update.

Pre-merge checklist

  • Update relevant READMEs
  • Run bin/bump_version or bin/bump_version --patch

@OutlawAndy OutlawAndy self-assigned this Mar 1, 2026
@OutlawAndy OutlawAndy force-pushed the proper-rails-plugin branch 2 times, most recently from d486dd2 to 14beaf4 Compare March 1, 2026 23:36
@OutlawAndy OutlawAndy requested a review from Copilot March 2, 2026 14:17
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Reworks the gem into a proper Rails plugin by renaming it to rolemodel-rails, introducing a Rails Engine for generator registration, and removing generator eager-loading during host app boot.

Changes:

  • Renames the gem from rolemodel_rails to rolemodel-rails and collapses the public namespace to Rolemodel.
  • Adds a Rolemodel::Engine to register generator support without eager-loading all generators at boot.
  • Updates specs, example apps, and tooling scripts to use the new gem name and loading behavior.

Reviewed changes

Copilot reviewed 16 out of 24 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
spec/support/helpers/example_app.rb Updates Gemfile cleanup to remove rolemodel-rails entries in the test app.
spec/spec_helper.rb Updates spec bootstrapping to load Rails + the gem and eagerly require generator classes for tests.
spec/rolemodel_rails_spec.rb Updates version constant expectations to the new Rolemodel namespace.
spec/generators/rolemodel/github_generator_spec.rb Updates expected template text to reference the new version file path.
rolemodel-rails.gemspec Renames the gem, updates metadata, and points version loading at Rolemodel::VERSION.
lib/rolemodel_rails.rb Removes the old entrypoint that eagerly loaded generators.
lib/rolemodel/version.rb Moves version constant under Rolemodel module.
lib/rolemodel/engine.rb Adds Rails Engine for generator registration.
lib/rolemodel-rails.rb Adds new gem entrypoint that defines version constants and requires version/engine.
lib/generators/rolemodel/base_generator.rb Introduces a standalone BaseGenerator (previously defined in lib/generators/rolemodel.rb).
lib/generators/rolemodel.rb Removes the old generator aggregator that loaded all generators eagerly.
example_rails8/Gemfile / Gemfile.lock Updates example app to depend on rolemodel-rails.
example_rails7/Gemfile / Gemfile.lock Updates example app to depend on rolemodel-rails.
bin/recreate_legacy_example Updates bundle add invocation to rolemodel-rails.
bin/recreate_current_example Updates bundle add invocation to rolemodel-rails.
bin/console Updates require to rolemodel-rails.
bin/bump_version Updates paths and gemspec name used by the version bumper.
README.md Updates documentation to reference rolemodel-rails.
Gemfile / Gemfile.lock Updates development dependency name to rolemodel-rails.
.gitignore Adds .DS_Store ignore.
.github/workflows/ci.yml Updates runner and GitHub Action versions used in CI.
Comments suppressed due to low confidence (1)

rolemodel-rails.gemspec:5

  • rolemodel-rails.gemspec no longer has # frozen_string_literal: true at the top. This repo consistently uses frozen string literals in Ruby files, and removing it may introduce RuboCop/style inconsistencies.

Suggestion: add the frozen-string-literal magic comment back at the top of the gemspec (before any require).

@OutlawAndy
Copy link
Member Author

@copilot open a new pull request to apply changes based on the comments in this thread

Copy link

Copilot AI commented Mar 11, 2026

@OutlawAndy I've opened a new pull request, #182, to work on those changes. Once the pull request is ready, I'll request review from you.

@OutlawAndy OutlawAndy force-pushed the proper-rails-plugin branch 6 times, most recently from cce8455 to 73df8e0 Compare March 12, 2026 22:35
OutlawAndy and others added 5 commits March 18, 2026 11:14
# Conflicts:
#	Gemfile.lock
#	example_rails7/Gemfile.lock
#	example_rails8/Gemfile.lock
#	lib/rolemodel/version.rb
…182)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: OutlawAndy <1504753+OutlawAndy@users.noreply.github.com>
# Conflicts:
#	Gemfile.lock
#	example_rails7/Gemfile.lock
#	example_rails8/Gemfile.lock
#	lib/rolemodel/version.rb
@OutlawAndy OutlawAndy force-pushed the proper-rails-plugin branch from ffa615c to f1751c8 Compare March 18, 2026 16:16
@OutlawAndy OutlawAndy marked this pull request as ready for review March 18, 2026 16:29
@RoleModel RoleModel deleted a comment from blacksmith-sh bot Mar 18, 2026
@OutlawAndy OutlawAndy force-pushed the proper-rails-plugin branch from 3be3980 to 26004ee Compare March 19, 2026 15:23
Copy link
Member

@timirwin timirwin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks wonderful. Just a few questions to ponder. You may not need any changes if they all check out.

run_specs:
name: Rspec
runs-on: ubuntu-latest
runs-on: blacksmith-4vcpu-ubuntu-2404
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@OutlawAndy Since this is public repo and running on Github, do we need to (or want to) switch to Blacksmith?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good question. I don't know. blacksmith is significantly faster, but might be safest to switch back. 👍

Comment on lines +19 to +28
skip_brakeman: true,
skip_bundler_audit: true,
skip_ci: true,
skip_git: true,
skip_jbuilder: true,
skip_kamal: true,
skip_rubocop: true,
skip_solid: true,
skip_test: true,
skip_thruster: true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for alphabetizing this list. 👍

set -euo pipefail
IFS=$'\n\t'
set -vx
RAILS_VERSION = ARGV[0] || '8.1.2'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a usage comment at the top of the file?

end

gem "rolemodel_rails", "> 0.20", group: :development, path: ".."
gem "rolemodel-rails", "> 0.20", group: :development, path: ".."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would we want to update the version specifier too?

Suggested change
gem "rolemodel-rails", "> 0.20", group: :development, path: ".."
gem "rolemodel-rails", ">= 1.0", group: :development, path: ".."

# Replace the default in-process and non-durable queuing backend for Active Job.
config.active_job.queue_adapter = :solid_queue
config.solid_queue.connects_to = { database: { writing: :queue } }
# config.active_job.queue_adapter = :resque
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔

Comment on lines +8 to +10
adapter: redis
url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
channel_prefix: example_rails8_production
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the new default for Rails 8? I believe we were leaving most of the example_rails8 files as close to rails new baseline as we can so recreating would have the least amount of changes necessary. Then again, maybe this is the right call moving forward?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the default for apps generated with --no-solid. That flag is also responsible for the change you highlighted in config/environments/production.rb.

I think it might be worth while to discuss what our default should be for new apps regarding the solid components. We've discussed sticking with GoodJob over solid-queue, but not what to do about cache & cable. Using either SolidCache or SolidCable requires multiple databases on Heroku & a few minor changes to development rake tasks (namely db commands. see screenshot)

iTerm2 03 19 2026@14 50 21

CI.run do
step "Setup", "bin/setup --skip-server"

step "Style: Ruby", "bin/rubocop"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would we want to have rubocop verify the output of our generator's output?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think one of our generators sets up running the rubocop formatter against generated files.. @wesrich can you confirm?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love some of these changes. 👍

{
"name": "app",
"private": true,
"packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need this even though we're keeping yarn.lock? Presumably it must work or you wouldn't have committed it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this went away because we migrated to yarn4+ a while back and the example app just hadn't been regenerated until now, but I'll try and confirm that.

gem 'stimulus-rails'
gem 'turbo-rails'

# Start debugger with binding.b [https://github.com/ruby/debug]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use the alias or keep it long form (binding.break)? Fine either way if binding.b is common knowledge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants