rage_arch 0.1.3 → 0.1.4
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.
- checksums.yaml +4 -4
- data/README.md +157 -4
- data/lib/generators/rage_arch/templates/scaffold/post_repo.rb.tt +1 -1
- data/lib/rage_arch/dep_scanner.rb +1 -1
- data/lib/rage_arch/railtie.rb +4 -4
- data/lib/rage_arch/use_case.rb +3 -3
- data/lib/rage_arch/version.rb +1 -1
- data/lib/rage_arch.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 152d8ad4517ebc11b70103f2dadbc68d14363d66e93c83ed8513755b7f4d3faa
|
|
4
|
+
data.tar.gz: 5c15a711f1676d67644a75a66f7a7731504f56bc76ef75d1a2f420f4d900dae6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bb854fce9230a8e3dc7efbec5257ca1ea9f98d91bb417262d4fe3d3a014d1e47df337acffb93eea453f438d2c525f92c06d003b1e2eabc63f99d95af3a10e3bf
|
|
7
|
+
data.tar.gz: b350e0aa5995ac0b63a30a8ade4ab78314b1755f56884998622434b585fa1f5b7bf16ade394666706b6569561ea256c1be74386f8b711232ab73c16dd8982947
|
data/README.md
CHANGED
|
@@ -58,18 +58,120 @@ use_case = RageArch::UseCase::Base.build(:create_order)
|
|
|
58
58
|
result = use_case.call(reference: "REF-1", total_cents: 1000)
|
|
59
59
|
```
|
|
60
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
|
+
|
|
61
80
|
---
|
|
62
81
|
|
|
63
82
|
### `RageArch::Container` — dependency registration
|
|
64
83
|
|
|
65
84
|
```ruby
|
|
85
|
+
# Register by instance
|
|
66
86
|
RageArch.register(:order_store, MyApp::Deps::OrderStore.new)
|
|
67
|
-
|
|
68
|
-
|
|
87
|
+
|
|
88
|
+
# Register with a block (lazy evaluation)
|
|
89
|
+
RageArch.register(:mailer) { Mailer.new }
|
|
90
|
+
|
|
91
|
+
# Register an ActiveRecord model as dep (wraps it automatically)
|
|
92
|
+
RageArch.register_ar(:user_store, User)
|
|
93
|
+
|
|
94
|
+
# Resolve
|
|
95
|
+
RageArch.resolve(:order_store) # => the registered implementation
|
|
96
|
+
|
|
97
|
+
# Check if registered
|
|
98
|
+
RageArch.registered?(:order_store) # => true
|
|
69
99
|
```
|
|
70
100
|
|
|
71
101
|
---
|
|
72
102
|
|
|
103
|
+
### Dependencies (Deps)
|
|
104
|
+
|
|
105
|
+
A dep is any object that a use case needs from the outside world: persistence, mailers, external APIs, caches, etc. No base class required — any Ruby object can be a dep.
|
|
106
|
+
|
|
107
|
+
#### Writing a dep manually
|
|
108
|
+
|
|
109
|
+
```ruby
|
|
110
|
+
# app/deps/posts/post_store.rb
|
|
111
|
+
module Posts
|
|
112
|
+
class PostStore
|
|
113
|
+
def build(attrs = {})
|
|
114
|
+
Post.new(attrs)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def save(record)
|
|
118
|
+
record.save
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def find(id)
|
|
122
|
+
Post.find_by(id: id)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def list(filters: {})
|
|
126
|
+
Post.where(filters).to_a
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
```
|
|
131
|
+
|
|
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)`.
|
|
147
|
+
|
|
148
|
+
#### Generating a dep from use case analysis
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
rails g rage_arch:dep post_store
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Scans your use cases for method calls on `:post_store` and generates a class with stub methods for each one. If the file already exists, only missing methods are added.
|
|
155
|
+
|
|
156
|
+
#### Switching dep implementations
|
|
157
|
+
|
|
158
|
+
Use `dep_switch` to swap between multiple implementations of the same dep:
|
|
159
|
+
|
|
160
|
+
```bash
|
|
161
|
+
# Interactive — lists all available implementations and prompts you to choose
|
|
162
|
+
rails g rage_arch:dep_switch post_store
|
|
163
|
+
|
|
164
|
+
# Direct — activate a specific implementation
|
|
165
|
+
rails g rage_arch:dep_switch post_store PostgresPostStore
|
|
166
|
+
|
|
167
|
+
# Switch to ActiveRecord adapter
|
|
168
|
+
rails g rage_arch:dep_switch post_store ar
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
The generator scans `app/deps/` for files matching the symbol, updates `config/initializers/rage_arch.rb` by commenting out the old registration and adding the new one.
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
73
175
|
### `RageArch::Controller` — thin controller mixin
|
|
74
176
|
|
|
75
177
|
```ruby
|
|
@@ -84,6 +186,18 @@ end
|
|
|
84
186
|
- `run_result(symbol, params)` — runs and returns the `Result` directly
|
|
85
187
|
- `flash_errors(result)` — sets `flash.now[:alert]` from `result.errors`
|
|
86
188
|
|
|
189
|
+
**API controller example (JSON):**
|
|
190
|
+
|
|
191
|
+
```ruby
|
|
192
|
+
class Api::PostsController < ApplicationController
|
|
193
|
+
def create
|
|
194
|
+
run :posts_create, post_params,
|
|
195
|
+
success: ->(r) { render json: r.value[:post], status: :created },
|
|
196
|
+
failure: ->(r) { render json: { errors: r.errors }, status: :unprocessable_entity }
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
```
|
|
200
|
+
|
|
87
201
|
---
|
|
88
202
|
|
|
89
203
|
### `RageArch::EventPublisher` — domain events
|
|
@@ -147,8 +261,11 @@ end
|
|
|
147
261
|
| `rails g rage_arch:scaffold Post title:string --api` | Same but API-only (JSON responses) |
|
|
148
262
|
| `rails g rage_arch:scaffold Post title:string --skip-model` | Skip model/migration if it already exists |
|
|
149
263
|
| `rails g rage_arch:use_case CreateOrder` | Generates a base use case file |
|
|
264
|
+
| `rails g rage_arch:use_case orders/create` | Generates a namespaced use case (`Orders::Create`) |
|
|
150
265
|
| `rails g rage_arch:dep post_store` | Generates a dep class by scanning method calls in use cases |
|
|
151
266
|
| `rails g rage_arch:ar_dep post_store Post` | Generates a dep that wraps an ActiveRecord model |
|
|
267
|
+
| `rails g rage_arch:dep_switch post_store` | Lists implementations and switches which one is registered |
|
|
268
|
+
| `rails g rage_arch:dep_switch post_store PostgresPostStore` | Directly activates a specific implementation |
|
|
152
269
|
|
|
153
270
|
---
|
|
154
271
|
|
|
@@ -179,15 +296,51 @@ publisher.clear
|
|
|
179
296
|
|
|
180
297
|
---
|
|
181
298
|
|
|
299
|
+
## Configuration
|
|
300
|
+
|
|
301
|
+
```ruby
|
|
302
|
+
# config/application.rb or config/initializers/rage_arch.rb
|
|
303
|
+
|
|
304
|
+
# Disable automatic event publishing when use cases finish (default: true)
|
|
305
|
+
config.rage_arch.auto_publish_events = false
|
|
306
|
+
|
|
307
|
+
# Disable boot verification (default: true)
|
|
308
|
+
config.rage_arch.verify_deps = false
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
182
313
|
## Boot verification
|
|
183
314
|
|
|
184
|
-
At boot, `RageArch.verify_deps!` runs automatically and raises if
|
|
315
|
+
At boot, `RageArch.verify_deps!` runs automatically and raises if it finds wiring problems. It checks:
|
|
316
|
+
|
|
317
|
+
- Every dep declared with `deps :symbol` is registered in the container
|
|
318
|
+
- Every method called on a dep is implemented by the registered object (via static analysis)
|
|
319
|
+
- Every use case declared with `use_cases :symbol` exists in the registry
|
|
320
|
+
|
|
321
|
+
Example error output:
|
|
322
|
+
|
|
323
|
+
```
|
|
324
|
+
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
|
|
327
|
+
UseCase :posts_notify (Posts::Notify) declares use_cases :email_send — not registered in use case registry
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
Disable with `config.rage_arch.verify_deps = false`.
|
|
185
331
|
|
|
186
332
|
---
|
|
187
333
|
|
|
188
334
|
## Instrumentation
|
|
189
335
|
|
|
190
|
-
Every use case emits `"
|
|
336
|
+
Every use case emits `"rage_arch.use_case.run"` via `ActiveSupport::Notifications` with payload `symbol`, `params`, `success`, `errors`, `result`.
|
|
337
|
+
|
|
338
|
+
```ruby
|
|
339
|
+
ActiveSupport::Notifications.subscribe("rage_arch.use_case.run") do |*args|
|
|
340
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
341
|
+
Rails.logger.info "[UseCase] #{event.payload[:symbol]} (#{event.duration.round}ms) success=#{event.payload[:success]}"
|
|
342
|
+
end
|
|
343
|
+
```
|
|
191
344
|
|
|
192
345
|
---
|
|
193
346
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
# Dep for :<%= repo_symbol %>. Wraps the <%= model_class_name %> Active Record model.
|
|
4
|
-
# Registered by
|
|
4
|
+
# Registered by rage_arch:scaffold in config/initializers/rage_arch.rb
|
|
5
5
|
module <%= module_name %>
|
|
6
6
|
class <%= singular_name.camelize %>Repo
|
|
7
7
|
def initialize
|
|
@@ -4,7 +4,7 @@ require "set"
|
|
|
4
4
|
|
|
5
5
|
module RageArch
|
|
6
6
|
# Scans use case files to find dep symbols and the methods called on each dep.
|
|
7
|
-
# Used by the
|
|
7
|
+
# Used by the rage_arch:dep generator to create stub classes with the right methods.
|
|
8
8
|
# Also tracks which use case path each symbol appears in (for folder inference).
|
|
9
9
|
class DepScanner
|
|
10
10
|
def initialize(use_cases_root = nil)
|
data/lib/rage_arch/railtie.rb
CHANGED
|
@@ -5,9 +5,9 @@ require_relative "controller"
|
|
|
5
5
|
|
|
6
6
|
module RageArch
|
|
7
7
|
class Railtie < ::Rails::Railtie
|
|
8
|
-
config.
|
|
9
|
-
config.
|
|
10
|
-
config.
|
|
8
|
+
config.rage_arch = ActiveSupport::OrderedOptions.new
|
|
9
|
+
config.rage_arch.auto_publish_events = true
|
|
10
|
+
config.rage_arch.verify_deps = true
|
|
11
11
|
|
|
12
12
|
# Load use case files so they register their symbols in the registry.
|
|
13
13
|
# Without this, build(:symbol) would fail until the use case constant was referenced.
|
|
@@ -25,7 +25,7 @@ module RageArch
|
|
|
25
25
|
# registered there would not be visible yet. Apps should call
|
|
26
26
|
# RageArch.verify_deps! manually at the end of their own after_initialize
|
|
27
27
|
# (config/initializers/rage_arch.rb), after all deps are registered.
|
|
28
|
-
# Set config.
|
|
28
|
+
# Set config.rage_arch.verify_deps = false to opt out.
|
|
29
29
|
end
|
|
30
30
|
end
|
|
31
31
|
end
|
data/lib/rage_arch/use_case.rb
CHANGED
|
@@ -33,7 +33,7 @@ module RageArch
|
|
|
33
33
|
def call(params = {})
|
|
34
34
|
sym = self.class.use_case_symbol
|
|
35
35
|
if defined?(ActiveSupport::Notifications)
|
|
36
|
-
ActiveSupport::Notifications.instrument("
|
|
36
|
+
ActiveSupport::Notifications.instrument("rage_arch.use_case.run", symbol: sym, params: params) do |payload|
|
|
37
37
|
result = super(params)
|
|
38
38
|
payload[:success] = result.success?
|
|
39
39
|
payload[:errors] = result.errors unless result.success?
|
|
@@ -66,8 +66,8 @@ module RageArch
|
|
|
66
66
|
|
|
67
67
|
def auto_publish_enabled?
|
|
68
68
|
return false if self.class.skip_auto_publish?
|
|
69
|
-
return true unless defined?(Rails) && Rails.application.config.respond_to?(:
|
|
70
|
-
Rails.application.config.
|
|
69
|
+
return true unless defined?(Rails) && Rails.application.config.respond_to?(:rage_arch) && Rails.application.config.rage_arch
|
|
70
|
+
Rails.application.config.rage_arch.auto_publish_events != false
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
73
|
|
data/lib/rage_arch/version.rb
CHANGED
data/lib/rage_arch.rb
CHANGED
|
@@ -31,7 +31,7 @@ module RageArch
|
|
|
31
31
|
|
|
32
32
|
# Verifies that all deps and use_cases declared by registered use cases are
|
|
33
33
|
# available before the app handles any request. Call after all initializers run
|
|
34
|
-
# (done automatically by the Railtie unless config.
|
|
34
|
+
# (done automatically by the Railtie unless config.rage_arch.verify_deps = false).
|
|
35
35
|
#
|
|
36
36
|
# Raises RuntimeError listing every missing dep/use_case if any are absent.
|
|
37
37
|
# Returns true when everything is wired correctly.
|