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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e4be8aa2c59b8c0e1f4d15a2af2c9075a21a674cf80401ddd7e8ad655ce3a6f8
4
- data.tar.gz: e2cccfe9fec1c8e630b2b3eff45eab25ec208075dcce4d54422f913693b1f14e
3
+ metadata.gz: 152d8ad4517ebc11b70103f2dadbc68d14363d66e93c83ed8513755b7f4d3faa
4
+ data.tar.gz: 5c15a711f1676d67644a75a66f7a7731504f56bc76ef75d1a2f420f4d900dae6
5
5
  SHA512:
6
- metadata.gz: c03dd919d2b32226ec4ea0975c2d748d2e4f6049b44a5e635e192b9d5cecac120b68ec600151e2c08ca94e7b5f4a28a456ffb4cf92365c516f13de1ba250efa6
7
- data.tar.gz: 2087a0af99636d741197191157c7f2c395ba81183363c656ead9e437fba232360f6d2072a428f2fb0a363ee4743c2646182e8c46dfa38075804680df22b11bf6
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
- RageArch.register_ar(:user_store, User) # automatic ActiveRecord wrapper
68
- RageArch.resolve(:order_store)
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 any dep, method, or use case reference is unregistered. Disable with `config.rage.verify_deps = false`.
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 `"rage.use_case.run"` via `ActiveSupport::Notifications` with payload `symbol`, `params`, `success`, `errors`, `result`.
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 rage:scaffold in config/initializers/rage_arch.rb
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 rage:dep generator to create stub classes with the right methods.
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)
@@ -5,9 +5,9 @@ require_relative "controller"
5
5
 
6
6
  module RageArch
7
7
  class Railtie < ::Rails::Railtie
8
- config.rage = ActiveSupport::OrderedOptions.new
9
- config.rage.auto_publish_events = true
10
- config.rage.verify_deps = true
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.rage.verify_deps = false to opt out.
28
+ # Set config.rage_arch.verify_deps = false to opt out.
29
29
  end
30
30
  end
31
31
  end
@@ -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("rage.use_case.run", symbol: sym, params: params) do |payload|
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?(:rage) && Rails.application.config.rage
70
- Rails.application.config.rage.auto_publish_events != false
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
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RageArch
4
- VERSION = "0.1.3"
4
+ VERSION = "0.1.4"
5
5
  end
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.rage.verify_deps = false).
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.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rage_arch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rage Corp