Class: Seams::Generators::InstallGenerator
- Inherits:
-
Rails::Generators::Base
- Object
- Rails::Generators::Base
- Seams::Generators::InstallGenerator
- Includes:
- HostInjector
- Defined in:
- lib/generators/seams/install/install_generator.rb
Overview
Adds the Seams framework to a host Rails application:
- config/initializers/seams.rb (configure adapters)
- config/initializers/seams_engines.rb (load engines/* into autoload)
- engines/.keep (where future engines live)
- lib/tasks/seams.rake (rake namespace)
Run with: bin/rails generate seams:install
Instance Method Summary collapse
- #append_engines_to_eager_load ⇒ Object
- #create_architecture_doc ⇒ Object
- #create_bin_seams ⇒ Object
- #create_ci_workflow ⇒ Object
- #create_deployment_templates ⇒ Object
- #create_engines_directory ⇒ Object
-
#create_helper_scripts ⇒ Object
Phase 1.5 — per-host helper scripts and architecture doc.
- #create_host_rubocop ⇒ Object
- #create_initializer ⇒ Object
- #create_rake_tasks ⇒ Object
- #create_ruby_version ⇒ Object
-
#post_install_message ⇒ Object
rubocop:disable Metrics/AbcSize, Metrics/MethodLength.
- #wire_engines_into_application_rb ⇒ Object
- #wire_into_host ⇒ Object
Methods included from HostInjector
#host_inject_gem, #host_inject_include_in_application_controller, #host_inject_include_in_user, #host_inject_mount, #host_uninject_gem, #host_uninject_include, #host_uninject_mount, #routes_draw_anchor
Instance Method Details
#append_engines_to_eager_load ⇒ Object
34 35 36 |
# File 'lib/generators/seams/install/install_generator.rb', line 34 def append_engines_to_eager_load template "seams_engines.rb.tt", "config/seams_engines.rb" end |
#create_architecture_doc ⇒ Object
141 142 143 |
# File 'lib/generators/seams/install/install_generator.rb', line 141 def create_architecture_doc template_if_missing "doc/ARCHITECTURE.md.tt", "doc/ARCHITECTURE.md" end |
#create_bin_seams ⇒ Object
126 127 128 129 130 |
# File 'lib/generators/seams/install/install_generator.rb', line 126 def create_bin_seams template "bin_seams.tt", "bin/seams" full_path = File.join(destination_root, "bin/seams") File.chmod(0o755, full_path) if File.exist?(full_path) end |
#create_ci_workflow ⇒ Object
110 111 112 |
# File 'lib/generators/seams/install/install_generator.rb', line 110 def create_ci_workflow template "ci.yml.tt", ".github/workflows/ci.yml" end |
#create_deployment_templates ⇒ Object
114 115 116 117 118 119 120 121 122 123 124 |
# File 'lib/generators/seams/install/install_generator.rb', line 114 def create_deployment_templates # Skip any file the host already has — Rails 8 ships its own # Dockerfile and bin/docker-entrypoint. template_if_missing "Dockerfile.tt", "Dockerfile" template_if_missing "docker-entrypoint.tt", "bin/docker-entrypoint" template_if_missing "Procfile.tt", "Procfile" template_if_missing "deploy.yml.tt", "config/deploy.yml" full = File.join(destination_root, "bin/docker-entrypoint") File.chmod(0o755, full) if File.exist?(full) end |
#create_engines_directory ⇒ Object
25 26 27 28 |
# File 'lib/generators/seams/install/install_generator.rb', line 25 def create_engines_directory empty_directory "engines" create_file "engines/.keep" end |
#create_helper_scripts ⇒ Object
Phase 1.5 — per-host helper scripts and architecture doc.
133 134 135 136 137 138 139 |
# File 'lib/generators/seams/install/install_generator.rb', line 133 def create_helper_scripts template_if_missing "script/collate_coverage.rb.tt", "script/collate_coverage.rb" template_if_missing "script/run_affected_tests.sh.tt", "script/run_affected_tests.sh" runner = File.join(destination_root, "script/run_affected_tests.sh") File.chmod(0o755, runner) if File.exist?(runner) end |
#create_host_rubocop ⇒ Object
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/generators/seams/install/install_generator.rb', line 70 def create_host_rubocop # Three cases: # 1. Host has no .rubocop.yml → write the seams baseline. # 2. Host already has one → don't overwrite, but inject an # `engines/**/*` Exclude so host RuboCop (which may use # rubocop-rails-omakase or another flavor) doesn't lint # engine code under rules written for application code. # Engines have their own self-contained .rubocop.yml. host_path = File.join(destination_root, ".rubocop.yml") unless File.exist?(host_path) template "rubocop.yml.tt", ".rubocop.yml" return end return if File.read(host_path).include?("engines/**/*") say " inject .rubocop.yml (Exclude engines + seams.rake)", :green append_to_file(host_path, <<~YML) # Engines have their own self-contained .rubocop.yml. Linting them # from the host runs gem-style code under whatever flavor of rules # the host uses (omakase / etc.) and produces noisy false positives. # The gem-generated lib/tasks/seams.rake is excluded for the same # reason. AllCops: Exclude: - "engines/**/*" - "lib/tasks/seams.rake" YML end |
#create_initializer ⇒ Object
21 22 23 |
# File 'lib/generators/seams/install/install_generator.rb', line 21 def create_initializer template "seams.rb.tt", "config/initializers/seams.rb" end |
#create_rake_tasks ⇒ Object
30 31 32 |
# File 'lib/generators/seams/install/install_generator.rb', line 30 def create_rake_tasks template "seams.rake.tt", "lib/tasks/seams.rake" end |
#create_ruby_version ⇒ Object
101 102 103 104 105 106 107 108 |
# File 'lib/generators/seams/install/install_generator.rb', line 101 def create_ruby_version # The host CI workflow does `ruby-version: ".ruby-version"`, so the # host needs a `.ruby-version` file. Rails 8's `rails new` doesn't # ship one. Don't overwrite if the host has pinned their own. return if File.exist?(File.join(destination_root, ".ruby-version")) template "ruby-version.tt", ".ruby-version" end |
#post_install_message ⇒ Object
rubocop:disable Metrics/AbcSize, Metrics/MethodLength
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 |
# File 'lib/generators/seams/install/install_generator.rb', line 170 def say "" say " Seams is installed. Generate your first engine with:", :green say " bin/seams core (or bin/rails generate seams:core)" say "" say " Canonical generators (run in this order):", :yellow say " bin/seams core - Core engine (concerns, audit log)" say " bin/seams auth - Auth engine (Identity, sessions, OAuth)" say " bin/seams accounts - Accounts engine (tenant + Membership + system actor)" say " bin/seams notifications - Notifications engine" say " bin/seams billing - Billing engine" say " bin/seams teams - Teams engine" say "" say " Optional engines (generate after the canonical six are in place):", :yellow say " bin/seams admin - Admin engine (Administrate-backed dashboards" say " for the canonical models, Pundit-gated," say " audit-log auto-write). Requires auth + accounts." say "" say " Follow-up generators (extend an already-installed engine):", :yellow say " bin/rails generate seams:auth:add_oauth_provider <name>" say " - add a new OAuth provider adapter" say " (e.g. linkedin, apple, microsoft)" say "" say " Other useful commands:", :yellow say " bin/seams list - list engines + their events" say " bin/seams resolve --eject <engine>/<file>" say " - mark a host file as host-owned" say " (skipped on regenerate)" say " bin/seams resolve --list-markers <engine>" say " - list insertion-point markers" say " bin/seams resolve --list-ejected - list every ejected file under engines/" say "" say " Recommended order: core -> auth -> accounts -> notifications -> billing -> teams.", :yellow say " Optional: append `admin` last for an Administrate-backed admin surface.", :yellow say " See doc/CURRENT_ATTRIBUTES.md (after install) for the per-request namespace cascade.", :yellow say " See doc/WRITING_FOLLOW_UP_GENERATORS.md to write your own follow-up generator.", :yellow say "" end |
#wire_engines_into_application_rb ⇒ Object
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/generators/seams/install/install_generator.rb', line 38 def wire_engines_into_application_rb # Each engine under engines/ must be required BEFORE # Rails.application.initialize! so its Railtie registers paths # (db/migrate, app/*) and initializers with the host. The # `require_relative` is injected directly after Bundler.require. application_rb = File.join(destination_root, "config/application.rb") return unless File.exist?(application_rb) snippet = %(require_relative "seams_engines") contents = File.read(application_rb) return if contents.include?(snippet) # The default Rails 8 application.rb contains # `Bundler.require(*Rails.groups)` verbatim. If a host has # customised it (Rails 4-style asset groups, multi-arg # Bundler.require, brace-form do-block, trailing comment, ...), # the regex misses and Thor silently warns "File unchanged!" # — leaving the host bootable but with engines never required. # That is the worst kind of failure: silent + production-bug. # Print a loud red warning so the user knows to wire it by hand. anchor = /Bundler\.require\(\*Rails\.groups\)\n/ unless contents.match?(anchor) say " WARNING config/application.rb has no `Bundler.require(*Rails.groups)` line — " \ "add `#{snippet}` manually after Bundler.require so engines load before initialize!", :red return end say " inject config/application.rb (require_relative \"seams_engines\")", :green inject_into_file(application_rb, "\n#{snippet}\n", after: anchor) end |
#wire_into_host ⇒ Object
145 146 147 148 149 150 151 152 153 154 |
# File 'lib/generators/seams/install/install_generator.rb', line 145 def wire_into_host # Auto-add seams to the host Gemfile if not already present — # covers the `gem install seams` global-install path. Pinned to # a pessimistic 0.x to keep major-version bumps explicit. host_inject_gem("seams", "~> #{Seams::VERSION}") # Every Seams host needs rspec-rails so the per-engine # spec/dummy specs can actually run. Idempotent — skipped if # the host already has these gems. host_inject_gem("rspec-rails", "~> 7.1", group: :test) end |