Class: Seams::Generators::AccountsGenerator
- Inherits:
-
Rails::Generators::Base
- Object
- Rails::Generators::Base
- Seams::Generators::AccountsGenerator
show all
- Includes:
- EjectAware, HostInjector
- Defined in:
- lib/generators/seams/accounts/accounts_generator.rb
Overview
Generates a canonical Accounts engine on top of the generic engine scaffold. Adds:
- Accounts::Account model. Tenant boundary. UUID PK.
- Accounts::Membership model. Joins Auth::Identity to Account
with a role enum (owner/admin/member/system); identity_id is
nullable for system actors used by audit-log writes.
- Accounts::Current per-request namespace.
- Accounts::AccountScoped model concern (default_scope to
Current.account, opt-out via .unscoped).
- Accounts::Authorization controller concern (default-on
ensure_account_access; opt out via disallow_account_scope or
require_access_without_membership; helpers ensure_admin /
ensure_staff).
- Migrations for accounts + accounts_memberships (pgcrypto).
- lib/accounts/engine.rb registers the canonical events:
account.created.accounts, account.cancelled.accounts,
membership.created.accounts, membership.role_changed.accounts,
membership.removed.accounts.
The engine ships NO controllers in Wave 9 — hosts drive their own account-creation flows; this engine is the model + concern layer.
Run with: bin/rails generate seams:accounts
Constant Summary
collapse
- ENGINE_NAME =
"accounts"
Constants included
from EjectAware
EjectAware::EJECT_HEADER_PREFIX
Instance Method Summary
collapse
Methods included from EjectAware
#ejected?, #template_unless_ejected
#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
#create_concerns ⇒ Object
73
74
75
76
77
78
|
# File 'lib/generators/seams/accounts/accounts_generator.rb', line 73
def create_concerns
template_unless_ejected "lib/concerns/account_scoped.rb.tt",
engine_path("lib/accounts/concerns/account_scoped.rb")
template_unless_ejected "lib/concerns/authorization.rb.tt",
engine_path("lib/accounts/concerns/authorization.rb")
end
|
#create_dummy_app ⇒ Object
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
|
# File 'lib/generators/seams/accounts/accounts_generator.rb', line 113
def create_dummy_app
Seams::Generators::DummyAppWriter.write!(
engine_path: File.join(destination_root, "engines", ENGINE_NAME),
engine_module: "Accounts",
mount_at: "/accounts",
schema: dummy_schema,
host_user: dummy_host_identity,
host_user_path: "app/models/auth/identity.rb"
)
write_auth_current_stub
template "spec/runtime/accounts_boot_spec.rb.tt",
engine_path("spec/runtime/accounts_boot_spec.rb")
end
|
#create_engine_skeleton ⇒ Object
46
47
48
|
# File 'lib/generators/seams/accounts/accounts_generator.rb', line 46
def create_engine_skeleton
EngineGenerator.start([ENGINE_NAME], destination_root: destination_root)
end
|
#create_factories ⇒ Object
87
88
89
90
|
# File 'lib/generators/seams/accounts/accounts_generator.rb', line 87
def create_factories
template_unless_ejected "spec/factories/accounts.rb.tt",
engine_path("spec/factories/accounts.rb")
end
|
#create_migrations ⇒ Object
80
81
82
83
84
85
|
# File 'lib/generators/seams/accounts/accounts_generator.rb', line 80
def create_migrations
template "db/migrate/create_accounts.rb.tt",
engine_path("db/migrate/#{timestamp(0)}_create_accounts.rb")
template "db/migrate/create_accounts_memberships.rb.tt",
engine_path("db/migrate/#{timestamp(1)}_create_accounts_memberships.rb")
end
|
#create_models ⇒ Object
62
63
64
65
66
67
68
69
70
71
|
# File 'lib/generators/seams/accounts/accounts_generator.rb', line 62
def create_models
template_unless_ejected "app/models/application_record.rb.tt",
engine_path("app/models/accounts/application_record.rb")
template_unless_ejected "app/models/account.rb.tt",
engine_path("app/models/accounts/account.rb")
template_unless_ejected "app/models/membership.rb.tt",
engine_path("app/models/accounts/membership.rb")
template_unless_ejected "app/models/current.rb.tt",
engine_path("app/models/accounts/current.rb")
end
|
#create_runtime_specs ⇒ Object
156
157
158
159
160
|
# File 'lib/generators/seams/accounts/accounts_generator.rb', line 156
def create_runtime_specs
end
|
#create_unit_specs ⇒ Object
92
93
94
95
96
97
|
# File 'lib/generators/seams/accounts/accounts_generator.rb', line 92
def create_unit_specs
template_unless_ejected "spec/models/accounts/account_spec.rb.tt",
engine_path("spec/models/accounts/account_spec.rb")
template_unless_ejected "spec/models/accounts/membership_spec.rb.tt",
engine_path("spec/models/accounts/membership_spec.rb")
end
|
#overwrite_engine_entry_point ⇒ Object
50
51
52
53
54
55
56
|
# File 'lib/generators/seams/accounts/accounts_generator.rb', line 50
def overwrite_engine_entry_point
template "lib/engine.rb.tt", engine_path("lib/accounts/engine.rb"), force: true
template "lib/accounts.rb.tt", engine_path("lib/accounts.rb"), force: true
template_unless_ejected "lib/configuration.rb.tt",
engine_path("lib/accounts/configuration.rb")
end
|
#overwrite_readme ⇒ Object
99
100
101
|
# File 'lib/generators/seams/accounts/accounts_generator.rb', line 99
def overwrite_readme
template "README.md.tt", engine_path("README.md"), force: true
end
|
#overwrite_routes ⇒ Object
58
59
60
|
# File 'lib/generators/seams/accounts/accounts_generator.rb', line 58
def overwrite_routes
template_unless_ejected "config/routes.rb.tt", engine_path("config/routes.rb"), force: true
end
|
#report_summary ⇒ Object
172
173
174
175
176
177
178
179
180
181
182
|
# File 'lib/generators/seams/accounts/accounts_generator.rb', line 172
def report_summary
say ""
say " Accounts engine generated at engines/accounts/", :green
say ""
say " Next steps:", :yellow
say " 1. bin/rails db:migrate"
say " 2. Include Accounts::Authorization in your ApplicationController"
say " 3. Wire Accounts::Current.account in a before_action"
say " 4. Run the engine specs: bin/rails seams:test[accounts]"
say ""
end
|
#update_exposed_concerns ⇒ Object
103
104
105
106
107
108
109
110
111
|
# File 'lib/generators/seams/accounts/accounts_generator.rb', line 103
def update_exposed_concerns
rubocop_path = engine_path(".rubocop.yml")
return unless File.exist?(rubocop_path)
contents = File.read(rubocop_path)
replacement = " ExposedConcerns:\n - Accounts::AccountScoped\n - Accounts::Authorization"
contents.sub!(/^ ExposedConcerns: \[\]$/, replacement)
File.write(rubocop_path, contents)
end
|
#wire_into_host ⇒ Object
162
163
164
165
166
167
168
169
170
|
# File 'lib/generators/seams/accounts/accounts_generator.rb', line 162
def wire_into_host
host_inject_gem("factory_bot_rails", "~> 6.4", group: :test)
host_inject_mount(engine_class: "Accounts::Engine", at: "/accounts")
end
|
#write_auth_current_stub ⇒ Object
Write a tiny ‘Auth::Current` stub so the accounts engine specs (which read Current.identity) can run without pulling in the full auth engine.
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
# File 'lib/generators/seams/accounts/accounts_generator.rb', line 137
def write_auth_current_stub
path = File.join(destination_root, "engines", ENGINE_NAME,
"spec/dummy/app/models/auth/current.rb")
FileUtils.mkdir_p(File.dirname(path))
File.write(path, <<~RB)
# frozen_string_literal: true
# Slim Auth::Current stub for the accounts dummy app. Stands in
# for the real Auth::Current (which lives in the auth engine,
# not loaded by the dummy) so accounts specs can wire
# `Current.identity = identity` against the same surface area
# the canonical seams host uses.
module Auth
class Current < ActiveSupport::CurrentAttributes
attribute :identity
end
end
RB
end
|