rage_arch 0.1.4 → 0.2.1

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 (34) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +125 -69
  3. data/lib/generators/rage_arch/controller_generator.rb +53 -0
  4. data/lib/generators/rage_arch/dep_generator.rb +2 -0
  5. data/lib/generators/rage_arch/job_generator.rb +28 -0
  6. data/lib/generators/rage_arch/mailer_generator.rb +42 -0
  7. data/lib/generators/rage_arch/resource_generator.rb +110 -0
  8. data/lib/generators/rage_arch/scaffold_generator.rb +0 -12
  9. data/lib/generators/rage_arch/templates/controller/action_use_case.rb.tt +12 -0
  10. data/lib/generators/rage_arch/templates/controller/controller.rb.tt +12 -0
  11. data/lib/generators/rage_arch/templates/dep.rb.tt +1 -1
  12. data/lib/generators/rage_arch/templates/job/job.rb.tt +15 -0
  13. data/lib/generators/rage_arch/templates/mailer/mailer_dep.rb.tt +17 -0
  14. data/lib/generators/rage_arch/templates/rage_arch.rb.tt +5 -8
  15. data/lib/generators/rage_arch/templates/scaffold/controller.rb.tt +8 -3
  16. data/lib/generators/rage_arch/templates/scaffold/create.rb.tt +0 -1
  17. data/lib/generators/rage_arch/templates/scaffold/destroy.rb.tt +0 -1
  18. data/lib/generators/rage_arch/templates/scaffold/list.rb.tt +0 -1
  19. data/lib/generators/rage_arch/templates/scaffold/new.rb.tt +0 -1
  20. data/lib/generators/rage_arch/templates/scaffold/post_repo.rb.tt +1 -1
  21. data/lib/generators/rage_arch/templates/scaffold/show.rb.tt +0 -1
  22. data/lib/generators/rage_arch/templates/scaffold/update.rb.tt +0 -1
  23. data/lib/generators/rage_arch/templates/use_case.rb.tt +2 -4
  24. data/lib/rage_arch/auto_registry.rb +95 -0
  25. data/lib/rage_arch/container.rb +36 -3
  26. data/lib/rage_arch/railtie.rb +6 -10
  27. data/lib/rage_arch/rspec_helpers.rb +19 -0
  28. data/lib/rage_arch/subscriber_job.rb +13 -0
  29. data/lib/rage_arch/use_case.rb +102 -35
  30. data/lib/rage_arch/version.rb +1 -1
  31. data/lib/rage_arch.rb +37 -3
  32. metadata +18 -8
  33. data/lib/generators/rage_arch/ar_dep_generator.rb +0 -74
  34. data/lib/generators/rage_arch/templates/ar_dep.rb.tt +0 -46
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 152d8ad4517ebc11b70103f2dadbc68d14363d66e93c83ed8513755b7f4d3faa
4
- data.tar.gz: 5c15a711f1676d67644a75a66f7a7731504f56bc76ef75d1a2f420f4d900dae6
3
+ metadata.gz: 67361dabc2170be9ff033ec5cacbc0c271d01351bccde53e04804417043fc1ac
4
+ data.tar.gz: fede1669810366cc822f31ce1e14152c564365ed5934cab2b34eb0707e5245c6
5
5
  SHA512:
6
- metadata.gz: bb854fce9230a8e3dc7efbec5257ca1ea9f98d91bb417262d4fe3d3a014d1e47df337acffb93eea453f438d2c525f92c06d003b1e2eabc63f99d95af3a10e3bf
7
- data.tar.gz: b350e0aa5995ac0b63a30a8ade4ab78314b1755f56884998622434b585fa1f5b7bf16ade394666706b6569561ea256c1be74386f8b711232ab73c16dd8982947
6
+ metadata.gz: 725210b1791995e3dd9f7964676b2ca7a87016113eb05454dcb7e327efdde2f2715e3691b8689c47ac56292cc800244a32faa9f8d1374a1d0d3e434b58c69664
7
+ data.tar.gz: d846584108e497e4b2ab036a3965f7ef6d6466a6ad57c1fa33e63aab005223b618583153f3710380663ad0e3ae3b3837a2b069b7d1d90fc462b2500d7997a0ef
data/README.md CHANGED
@@ -35,50 +35,6 @@ result.errors # => ["Validation error"]
35
35
 
36
36
  ---
37
37
 
38
- ### `RageArch::UseCase::Base` — use cases
39
-
40
- ```ruby
41
- class CreateOrder < RageArch::UseCase::Base
42
- use_case_symbol :create_order
43
- deps :order_store, :notifications # injected by symbol
44
-
45
- def call(params = {})
46
- order = order_store.build(params)
47
- return failure(order.errors) unless order_store.save(order)
48
- notifications.notify(:order_created, order)
49
- success(order)
50
- end
51
- end
52
- ```
53
-
54
- Build and run manually:
55
-
56
- ```ruby
57
- use_case = RageArch::UseCase::Base.build(:create_order)
58
- result = use_case.call(reference: "REF-1", total_cents: 1000)
59
- ```
60
-
61
- #### `ar_dep` — inline ActiveRecord dep
62
-
63
- When a dep is a simple wrapper over an ActiveRecord model, declare it directly in the use case instead of creating a separate class:
64
-
65
- ```ruby
66
- class Posts::Create < RageArch::UseCase::Base
67
- use_case_symbol :posts_create
68
- ar_dep :post_store, Post # auto-creates an AR adapter if :post_store is not registered
69
-
70
- def call(params = {})
71
- post = post_store.build(params)
72
- return failure(post.errors.full_messages) unless post_store.save(post)
73
- success(post: post)
74
- end
75
- end
76
- ```
77
-
78
- If `:post_store` is registered in the container, that implementation is used. Otherwise, `RageArch::Deps::ActiveRecord.for(Post)` is used as fallback.
79
-
80
- ---
81
-
82
38
  ### `RageArch::Container` — dependency registration
83
39
 
84
40
  ```ruby
@@ -98,6 +54,15 @@ RageArch.resolve(:order_store) # => the registered implementation
98
54
  RageArch.registered?(:order_store) # => true
99
55
  ```
100
56
 
57
+ **Convention-based auto-registration:** Use cases from `app/use_cases/` and deps from `app/deps/` are auto-registered at boot. The initializer is only needed to override conventions or register external adapters.
58
+
59
+ **AR model auto-resolution:** If a dep symbol ends in `_store` or `_repo` and no file exists in `app/deps/`, rage_arch looks for an ActiveRecord model automatically:
60
+
61
+ - `:post_store` or `:post_repo` resolves to `Post`
62
+ - `:appointment_store` or `:appointment_repo` resolves to `Appointment`
63
+
64
+ Explicit `RageArch.register(...)` always takes priority.
65
+
101
66
  ---
102
67
 
103
68
  ### Dependencies (Deps)
@@ -129,21 +94,7 @@ module Posts
129
94
  end
130
95
  ```
131
96
 
132
- Register it in `config/initializers/rage_arch.rb`:
133
-
134
- ```ruby
135
- RageArch.register(:post_store, Posts::PostStore.new)
136
- ```
137
-
138
- #### ActiveRecord dep (generated)
139
-
140
- For deps that simply wrap an AR model with standard CRUD, use the generator:
141
-
142
- ```bash
143
- rails g rage_arch:ar_dep post_store Post
144
- ```
145
-
146
- This creates `app/deps/posts/post_store.rb` with `build`, `find`, `save`, `update`, `destroy`, and `list` methods backed by `RageArch::Deps::ActiveRecord.for(Post)`.
97
+ Auto-registered by convention from `app/deps/` — no manual registration needed.
147
98
 
148
99
  #### Generating a dep from use case analysis
149
100
 
@@ -172,6 +123,79 @@ The generator scans `app/deps/` for files matching the symbol, updates `config/i
172
123
 
173
124
  ---
174
125
 
126
+ ### `RageArch::UseCase::Base` — use cases
127
+
128
+ Use cases declare their dependencies by symbol, receive them via injection, and return a `Result`. The symbol is inferred from the class name by convention:
129
+
130
+ ```ruby
131
+ class Orders::Create < RageArch::UseCase::Base
132
+ # symbol :orders_create is inferred automatically
133
+ deps :order_store, :notifications
134
+
135
+ def call(params = {})
136
+ order = order_store.build(params)
137
+ return failure(order.errors) unless order_store.save(order)
138
+ notifications.notify(:order_created, order)
139
+ success(order: order)
140
+ end
141
+ end
142
+ ```
143
+
144
+ Symbol inference: `Orders::Create` becomes `:orders_create`. Explicit `use_case_symbol :my_symbol` still works as override.
145
+
146
+ Build and run manually:
147
+
148
+ ```ruby
149
+ use_case = RageArch::UseCase::Base.build(:orders_create)
150
+ result = use_case.call(reference: "REF-1", total_cents: 1000)
151
+ ```
152
+
153
+ ---
154
+
155
+ ### undo — automatic rollback on failure
156
+
157
+ Define `def undo(value)` on any use case. If `call` returns `failure(...)`, `undo` is called automatically:
158
+
159
+ ```ruby
160
+ class Payments::Charge < RageArch::UseCase::Base
161
+ deps :payment_gateway
162
+
163
+ def call(params = {})
164
+ charge = payment_gateway.charge(params[:amount])
165
+ return failure(["Payment failed"]) unless charge.success?
166
+ success(charge_id: charge.id)
167
+ end
168
+
169
+ def undo(value)
170
+ payment_gateway.refund(value[:charge_id]) if value
171
+ end
172
+ end
173
+ ```
174
+
175
+ **Cascade undo with `use_cases`:** When a parent use case orchestrates children via `use_cases` and returns failure, each successfully-completed child has its `undo` called in reverse order:
176
+
177
+ ```ruby
178
+ class Bookings::Create < RageArch::UseCase::Base
179
+ use_cases :payments_charge, :slots_reserve, :notifications_send
180
+
181
+ def call(params = {})
182
+ charge = payments_charge.call(amount: params[:amount])
183
+ return charge unless charge.success?
184
+
185
+ reserve = slots_reserve.call(slot_id: params[:slot_id])
186
+ return failure(reserve.errors) unless reserve.success?
187
+ # If this fails, slots_reserve.undo and payments_charge.undo run automatically
188
+
189
+ notifications_send.call(booking: params)
190
+ success(booking: params)
191
+ end
192
+ end
193
+ ```
194
+
195
+ No DSL, no configuration. Just define `undo` where you need rollback.
196
+
197
+ ---
198
+
175
199
  ### `RageArch::Controller` — thin controller mixin
176
200
 
177
201
  ```ruby
@@ -202,13 +226,12 @@ end
202
226
 
203
227
  ### `RageArch::EventPublisher` — domain events
204
228
 
205
- Every use case automatically publishes an event when it finishes. Other use cases subscribe to react:
229
+ Every use case automatically publishes an event when it finishes. Other use cases subscribe to react. **Subscribers run asynchronously via ActiveJob by default:**
206
230
 
207
231
  ```ruby
208
232
  class Notifications::SendPostCreatedEmail < RageArch::UseCase::Base
209
- use_case_symbol :send_post_created_email
210
233
  deps :mailer
211
- subscribe :posts_create # runs when :posts_create event is published
234
+ subscribe :posts_create # async by default
212
235
 
213
236
  def call(payload = {})
214
237
  return success unless payload[:success]
@@ -218,6 +241,12 @@ class Notifications::SendPostCreatedEmail < RageArch::UseCase::Base
218
241
  end
219
242
  ```
220
243
 
244
+ **Synchronous subscribers** (opt-in):
245
+
246
+ ```ruby
247
+ subscribe :posts_create, async: false
248
+ ```
249
+
221
250
  Subscribe to multiple events or everything:
222
251
 
223
252
  ```ruby
@@ -237,7 +266,6 @@ skip_auto_publish
237
266
 
238
267
  ```ruby
239
268
  class CreateOrderWithNotification < RageArch::UseCase::Base
240
- use_case_symbol :create_order_with_notification
241
269
  deps :order_store
242
270
  use_cases :orders_create, :notifications_send
243
271
 
@@ -260,12 +288,15 @@ end
260
288
  | `rails g rage_arch:scaffold Post title:string` | Full CRUD: model, use cases, dep, controller, views, routes |
261
289
  | `rails g rage_arch:scaffold Post title:string --api` | Same but API-only (JSON responses) |
262
290
  | `rails g rage_arch:scaffold Post title:string --skip-model` | Skip model/migration if it already exists |
291
+ | `rails g rage_arch:resource Post title:string` | Like scaffold but without views (API-style controller) |
292
+ | `rails g rage_arch:controller Pages home about` | Thin controller + use case per action + routes |
263
293
  | `rails g rage_arch:use_case CreateOrder` | Generates a base use case file |
264
294
  | `rails g rage_arch:use_case orders/create` | Generates a namespaced use case (`Orders::Create`) |
265
295
  | `rails g rage_arch:dep post_store` | Generates a dep class by scanning method calls in use cases |
266
- | `rails g rage_arch:ar_dep post_store Post` | Generates a dep that wraps an ActiveRecord model |
267
296
  | `rails g rage_arch:dep_switch post_store` | Lists implementations and switches which one is registered |
268
297
  | `rails g rage_arch:dep_switch post_store PostgresPostStore` | Directly activates a specific implementation |
298
+ | `rails g rage_arch:mailer PostMailer post_created` | Rails mailer + dep wrapper (auto-registered) |
299
+ | `rails g rage_arch:job ProcessOrder orders_create` | ActiveJob that runs a use case by symbol |
269
300
 
270
301
  ---
271
302
 
@@ -274,7 +305,23 @@ end
274
305
  ```ruby
275
306
  # spec/rails_helper.rb
276
307
  require "rage_arch/rspec_matchers"
308
+ require "rage_arch/rspec_helpers"
277
309
  require "rage_arch/fake_event_publisher"
310
+
311
+ RSpec.configure do |config|
312
+ config.include RageArch::RSpecHelpers # auto-isolates each test
313
+ end
314
+ ```
315
+
316
+ **Test isolation:** `RageArch::RSpecHelpers` wraps each example in `RageArch.isolate`, so dep registrations never bleed between tests.
317
+
318
+ **Manual isolation** (without the helper):
319
+
320
+ ```ruby
321
+ RageArch.isolate do
322
+ RageArch.register(:payment_gateway, FakeGateway.new)
323
+ # registrations inside are scoped; originals restored on exit
324
+ end
278
325
  ```
279
326
 
280
327
  **Result matchers:**
@@ -306,6 +353,9 @@ config.rage_arch.auto_publish_events = false
306
353
 
307
354
  # Disable boot verification (default: true)
308
355
  config.rage_arch.verify_deps = false
356
+
357
+ # Run all subscribers synchronously — useful for test environments (default: true)
358
+ config.rage_arch.async_subscribers = false
309
359
  ```
310
360
 
311
361
  ---
@@ -314,16 +364,17 @@ config.rage_arch.verify_deps = false
314
364
 
315
365
  At boot, `RageArch.verify_deps!` runs automatically and raises if it finds wiring problems. It checks:
316
366
 
317
- - Every dep declared with `deps :symbol` is registered in the container
367
+ - Every dep declared with `deps :symbol` is registered in the container (or auto-resolved for `_store`/`_repo` deps)
318
368
  - Every method called on a dep is implemented by the registered object (via static analysis)
319
369
  - Every use case declared with `use_cases :symbol` exists in the registry
370
+ - Warns if `use_case_symbol` doesn't match the convention-inferred symbol
320
371
 
321
372
  Example error output:
322
373
 
323
374
  ```
324
375
  RageArch boot verification failed:
325
- UseCase :posts_create (Posts::Create) declares dep :post_store — not registered in container
326
- UseCase :posts_create (Posts::Create) calls :post_store#save — Posts::PostStore does not implement #save
376
+ UseCase :posts_create (Posts::Create) declares dep :post_store — not registered in container and no AR model found for _store/_repo
377
+ UseCase :posts_create (Posts::Create) calls dep :post_store#save — Posts::PostStore does not implement #save
327
378
  UseCase :posts_notify (Posts::Notify) declares use_cases :email_send — not registered in use case registry
328
379
  ```
329
380
 
@@ -346,9 +397,14 @@ end
346
397
 
347
398
  ## Documentation
348
399
 
349
- - [`doc/REFERENCE.md`](doc/REFERENCE.md) — Full API reference with all options and examples
350
- - [`doc/DOCUMENTATION.md`](doc/DOCUMENTATION.md) — Detailed behaviour (use cases, deps, events, config)
351
400
  - [`doc/GETTING_STARTED.md`](doc/GETTING_STARTED.md) — Getting started guide with common tasks
401
+ - [`doc/TUTORIAL.md`](doc/TUTORIAL.md) — Side-by-side Rails vs Rails+RageArch comparison
402
+ - [`doc/DOCUMENTATION.md`](doc/DOCUMENTATION.md) — Detailed behaviour (use cases, deps, events, config)
403
+ - [`doc/REFERENCE.md`](doc/REFERENCE.md) — Quick-lookup API reference (classes, methods, options)
404
+
405
+ ## AI context
406
+
407
+ If you use an AI coding agent, point it to [`IA.md`](IA.md) for a compact reference of the full gem API, conventions, and architecture.
352
408
 
353
409
  ## License
354
410
 
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+
5
+ module RageArch
6
+ module Generators
7
+ class ControllerGenerator < ::Rails::Generators::NamedBase
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ argument :actions, type: :array, default: [], banner: "action action"
11
+
12
+ desc "Generate a thin RageArch controller with use cases for each action."
13
+ def create_controller
14
+ template "controller/controller.rb.tt", File.join("app/controllers", "#{plural_file_name}_controller.rb")
15
+ end
16
+
17
+ def create_use_cases
18
+ actions.each do |action|
19
+ @current_action = action
20
+ template "controller/action_use_case.rb.tt", File.join("app/use_cases", plural_file_name, "#{action}.rb")
21
+ end
22
+ end
23
+
24
+ def add_routes
25
+ return if actions.empty?
26
+ lines = actions.map { |a| " get \"#{plural_file_name}/#{a}\", to: \"#{plural_file_name}##{a}\"" }
27
+ route lines.join("\n")
28
+ end
29
+
30
+ private
31
+
32
+ def plural_file_name
33
+ file_name.pluralize
34
+ end
35
+
36
+ def controller_class_name
37
+ plural_file_name.camelize
38
+ end
39
+
40
+ def module_name
41
+ plural_file_name.camelize
42
+ end
43
+
44
+ def current_action
45
+ @current_action
46
+ end
47
+
48
+ def use_case_symbol(action)
49
+ "#{plural_file_name}_#{action}"
50
+ end
51
+ end
52
+ end
53
+ end
@@ -27,6 +27,8 @@ module RageArch
27
27
  end
28
28
  end
29
29
 
30
+ private
31
+
30
32
  # When the dep file already exists, parse it for existing method names and insert only stubs for missing ones.
31
33
  def add_missing_methods_only(relative_path)
32
34
  full_path = File.join(destination_root, relative_path)
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+
5
+ module RageArch
6
+ module Generators
7
+ class JobGenerator < ::Rails::Generators::NamedBase
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ argument :use_case_symbol, type: :string, required: false, default: nil, banner: "use_case_symbol"
11
+
12
+ desc "Generate an ActiveJob that runs a RageArch use case by symbol."
13
+ def create_job
14
+ template "job/job.rb.tt", File.join("app/jobs", "#{file_name}_job.rb")
15
+ end
16
+
17
+ private
18
+
19
+ def job_class_name
20
+ file_name.camelize
21
+ end
22
+
23
+ def inferred_symbol
24
+ use_case_symbol || file_name
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+
5
+ module RageArch
6
+ module Generators
7
+ class MailerGenerator < ::Rails::Generators::NamedBase
8
+ source_root File.expand_path("templates", __dir__)
9
+
10
+ argument :actions, type: :array, default: [], banner: "action action"
11
+
12
+ desc "Generate a Rails mailer and a RageArch dep wrapper (auto-registered from app/deps/)."
13
+ def create_rails_mailer
14
+ args = [name] + actions
15
+ invoke "mailer", args
16
+ end
17
+
18
+ def create_dep
19
+ dep_dir = File.join("app/deps")
20
+ template "mailer/mailer_dep.rb.tt", File.join(dep_dir, "#{dep_file_name}.rb")
21
+ end
22
+
23
+ private
24
+
25
+ def mailer_class_name
26
+ name.camelize
27
+ end
28
+
29
+ def dep_class_name
30
+ "#{mailer_class_name}Dep"
31
+ end
32
+
33
+ def dep_file_name
34
+ "#{file_name}_dep"
35
+ end
36
+
37
+ def dep_symbol
38
+ file_name.underscore
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,110 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators/base"
4
+ require "rails/generators/active_record"
5
+
6
+ module RageArch
7
+ module Generators
8
+ class ResourceGenerator < ::Rails::Generators::NamedBase
9
+ source_root File.expand_path("templates", __dir__)
10
+
11
+ argument :attributes, type: :array, default: [], banner: "field:type field:type"
12
+ class_option :skip_model, type: :boolean, default: false, desc: "Skip model and migration (use when model already exists)"
13
+
14
+ desc "Generate a RageArch resource: model, migration, CRUD use cases, dep, API-style controller, and routes (no views)."
15
+ def create_all
16
+ create_model_and_migration
17
+ create_use_cases
18
+ create_dep
19
+ create_controller
20
+ add_route
21
+ end
22
+
23
+ private
24
+
25
+ def create_model_and_migration
26
+ return if options[:skip_model]
27
+ args = [name] + attributes.map(&:to_s)
28
+ invoke "active_record:model", args
29
+ end
30
+
31
+ def create_use_cases
32
+ dir = File.join("app/use_cases", plural_name)
33
+ empty_directory dir
34
+ template "scaffold/list.rb.tt", File.join(dir, "list.rb")
35
+ template "scaffold/show.rb.tt", File.join(dir, "show.rb")
36
+ template "scaffold/new.rb.tt", File.join(dir, "new.rb")
37
+ template "scaffold/create.rb.tt", File.join(dir, "create.rb")
38
+ template "scaffold/update.rb.tt", File.join(dir, "update.rb")
39
+ template "scaffold/destroy.rb.tt", File.join(dir, "destroy.rb")
40
+ end
41
+
42
+ def create_dep
43
+ dep_dir = File.join("app/deps", plural_name)
44
+ empty_directory dep_dir
45
+ template "scaffold/post_repo.rb.tt", File.join(dep_dir, "#{singular_name}_repo.rb")
46
+ end
47
+
48
+ def create_controller
49
+ template "scaffold/api_controller.rb.tt", File.join("app/controllers", "#{plural_name}_controller.rb")
50
+ end
51
+
52
+ def add_route
53
+ route "resources :#{plural_name}"
54
+ end
55
+
56
+ def plural_name
57
+ name.underscore.pluralize
58
+ end
59
+
60
+ def singular_name
61
+ name.underscore
62
+ end
63
+
64
+ def model_class_name
65
+ name.camelize
66
+ end
67
+
68
+ def module_name
69
+ plural_name.camelize
70
+ end
71
+
72
+ def repo_symbol
73
+ "#{singular_name}_repo"
74
+ end
75
+
76
+ def repo_class_name
77
+ "#{module_name}::#{singular_name.camelize}Repo"
78
+ end
79
+
80
+ def list_symbol
81
+ "#{plural_name}_list"
82
+ end
83
+
84
+ def show_symbol
85
+ "#{plural_name}_show"
86
+ end
87
+
88
+ def create_symbol
89
+ "#{plural_name}_create"
90
+ end
91
+
92
+ def update_symbol
93
+ "#{plural_name}_update"
94
+ end
95
+
96
+ def destroy_symbol
97
+ "#{plural_name}_destroy"
98
+ end
99
+
100
+ def new_symbol
101
+ "#{plural_name}_new"
102
+ end
103
+
104
+ def attribute_names_for_permit
105
+ return [] if attributes.blank?
106
+ attributes.map { |a| a.to_s.split(":").first.to_sym }
107
+ end
108
+ end
109
+ end
110
+ end
@@ -20,7 +20,6 @@ module RageArch
20
20
  invoke_rails_scaffold_views
21
21
  create_controller
22
22
  add_route
23
- inject_register_ar
24
23
  end
25
24
 
26
25
  private
@@ -65,17 +64,6 @@ module RageArch
65
64
  route "resources :#{plural_name}"
66
65
  end
67
66
 
68
- def inject_register_ar
69
- initializer_path = File.join(destination_root, "config/initializers/rage_arch.rb")
70
- return unless File.exist?(initializer_path)
71
- content = File.read(initializer_path)
72
- return if content.include?("register_ar(:#{repo_symbol})")
73
- inject_line = " RageArch.register_ar(:#{repo_symbol}, #{model_class_name})\n"
74
- content.sub!(/(Rails\.application\.config\.after_initialize do\s*\n)/m, "\\1#{inject_line}")
75
- File.write(initializer_path, content)
76
- say_status :inject, "config/initializers/rage_arch.rb (register_ar :#{repo_symbol})", :green
77
- end
78
-
79
67
  def plural_name
80
68
  name.underscore.pluralize
81
69
  end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= module_name %>
4
+ class <%= current_action.camelize %> < RageArch::UseCase::Base
5
+ # deps :example_repo
6
+
7
+ def call(params = {})
8
+ # TODO: implement use case logic
9
+ success(params)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= controller_class_name %>Controller < ApplicationController
4
+ <%- actions.each do |action| -%>
5
+ def <%= action %>
6
+ run :<%= use_case_symbol(action) %>,
7
+ success: ->(r) { render :<%= action %> },
8
+ failure: ->(r) { flash_errors(r); redirect_to root_path }
9
+ end
10
+
11
+ <%- end -%>
12
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  # Dep for :<%= symbol_name %>.
4
4
  # Methods detected from use cases: <%= @methods.join(', ') %>.
5
- # Register in config/initializers/rage_arch.rb: RageArch.register(:<%= symbol_name %>, <%= full_class_name %>.new)
5
+ # Auto-registered by convention from app/deps/
6
6
  module <%= module_name %>
7
7
  class <%= class_name %>
8
8
  <% @methods.each do |method_name| -%>
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class <%= job_class_name %>Job < ApplicationJob
4
+ queue_as :default
5
+
6
+ def perform(**params)
7
+ result = RageArch::UseCase::Base.build(:<%= inferred_symbol %>).call(params)
8
+
9
+ unless result.success?
10
+ Rails.logger.error "[<%= job_class_name %>Job] Use case :<%= inferred_symbol %> failed: #{result.errors.inspect}"
11
+ end
12
+
13
+ result
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Dep for :<%= dep_symbol %>. Wraps <%= mailer_class_name %>.
4
+ # Auto-registered by convention from app/deps/
5
+ class <%= dep_class_name %>
6
+ <%- actions.each do |action| -%>
7
+ def <%= action %>(*args)
8
+ <%= mailer_class_name %>.<%=action %>(*args).deliver_later
9
+ end
10
+
11
+ <%- end -%>
12
+ <%- if actions.empty? -%>
13
+ # def send_email(*args)
14
+ # <%= mailer_class_name %>.send_email(*args).deliver_later
15
+ # end
16
+ <%- end -%>
17
+ end
@@ -1,19 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # Register your app's deps here. Deps are grouped by module (e.g. app/deps/posts/post_repo.rb → Posts::PostRepo).
4
- # Use RageArch.register(:symbol, ClassName.new) or RageArch.register_ar(:symbol, Model) for AR-backed deps.
3
+ # rage_arch auto-registers use cases from app/use_cases/ and deps from app/deps/
4
+ # Add registrations here only to override conventions or register external adapters.
5
+ #
6
+ # Example:
7
+ # RageArch.register(:payment_gateway, Payments::StripeGateway.new)
5
8
 
6
9
  Rails.application.config.after_initialize do
7
- # Deps
8
- # RageArch.register(:post_repo, Posts::PostRepo.new)
9
- # RageArch.register_ar(:user_repo, User)
10
-
11
10
  # Event publisher: use cases that declare subscribe :event_name are wired here.
12
11
  publisher = RageArch::EventPublisher.new
13
12
  RageArch::UseCase::Base.wire_subscriptions_to(publisher)
14
13
  RageArch.register(:event_publisher, publisher)
15
14
 
16
- # Called here (after all deps are registered) instead of relying on the Railtie,
17
- # which runs before this block. Skipped during asset precompilation.
18
15
  RageArch.verify_deps! unless ENV["SECRET_KEY_BASE_DUMMY"].present?
19
16
  end