senro_usecaser 0.1.0
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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +72 -0
- data/LICENSE +21 -0
- data/README.md +1069 -0
- data/Rakefile +12 -0
- data/Steepfile +24 -0
- data/examples/RBS_GENERATION.md +16 -0
- data/examples/namespace_demo.rb +751 -0
- data/examples/order_system.rb +1279 -0
- data/examples/sig/namespace_demo.rbs +279 -0
- data/examples/sig/order_system.rbs +685 -0
- data/lefthook.yml +31 -0
- data/lib/senro_usecaser/base.rb +660 -0
- data/lib/senro_usecaser/configuration.rb +149 -0
- data/lib/senro_usecaser/container.rb +315 -0
- data/lib/senro_usecaser/error.rb +88 -0
- data/lib/senro_usecaser/provider.rb +212 -0
- data/lib/senro_usecaser/result.rb +182 -0
- data/lib/senro_usecaser/version.rb +8 -0
- data/lib/senro_usecaser.rb +155 -0
- data/sig/generated/senro_usecaser/base.rbs +365 -0
- data/sig/generated/senro_usecaser/configuration.rbs +80 -0
- data/sig/generated/senro_usecaser/container.rbs +190 -0
- data/sig/generated/senro_usecaser/error.rbs +58 -0
- data/sig/generated/senro_usecaser/provider.rbs +153 -0
- data/sig/generated/senro_usecaser/result.rbs +109 -0
- data/sig/generated/senro_usecaser/version.rbs +6 -0
- data/sig/generated/senro_usecaser.rbs +113 -0
- data/sig/overrides.rbs +16 -0
- metadata +77 -0
|
@@ -0,0 +1,751 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
# rubocop:disable all
|
|
5
|
+
|
|
6
|
+
# rbs_inline: enabled
|
|
7
|
+
|
|
8
|
+
require_relative "../lib/senro_usecaser"
|
|
9
|
+
|
|
10
|
+
# =============================================================================
|
|
11
|
+
# サンプル: Namespace を活用したマルチドメインシステム
|
|
12
|
+
# =============================================================================
|
|
13
|
+
#
|
|
14
|
+
# このサンプルでは以下の Namespace 機能を検証します:
|
|
15
|
+
# 1. 基本的な namespace でのサービス登録
|
|
16
|
+
# 2. ネストした namespace (admin::reports)
|
|
17
|
+
# 3. namespace のフォールバック(子から親へ)
|
|
18
|
+
# 4. UseCase での namespace 指定(自動解決)
|
|
19
|
+
# 5. resolve_in による明示的な namespace 解決
|
|
20
|
+
# 6. Scoped Container と namespace の組み合わせ
|
|
21
|
+
# 7. infer_namespace_from_module によるモジュール名からの namespace 自動推論
|
|
22
|
+
#
|
|
23
|
+
# =============================================================================
|
|
24
|
+
|
|
25
|
+
# サンプル全体を NamespaceDemo モジュールで囲み、
|
|
26
|
+
# 他のサンプル(order_system.rb)とのクラス名衝突を回避
|
|
27
|
+
module NamespaceDemo
|
|
28
|
+
# ===========================================================================
|
|
29
|
+
# モデル
|
|
30
|
+
# ===========================================================================
|
|
31
|
+
|
|
32
|
+
class User
|
|
33
|
+
#: Integer
|
|
34
|
+
attr_reader :id
|
|
35
|
+
|
|
36
|
+
#: String
|
|
37
|
+
attr_reader :name
|
|
38
|
+
|
|
39
|
+
#: String
|
|
40
|
+
attr_reader :role
|
|
41
|
+
|
|
42
|
+
#: (id: Integer, name: String, role: String) -> void
|
|
43
|
+
def initialize(id:, name:, role:)
|
|
44
|
+
@id = id
|
|
45
|
+
@name = name
|
|
46
|
+
@role = role
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
class AuditLog
|
|
51
|
+
#: String
|
|
52
|
+
attr_reader :action
|
|
53
|
+
|
|
54
|
+
#: User
|
|
55
|
+
attr_reader :performed_by
|
|
56
|
+
|
|
57
|
+
#: Time
|
|
58
|
+
attr_reader :timestamp
|
|
59
|
+
|
|
60
|
+
#: (action: String, performed_by: User, timestamp: Time) -> void
|
|
61
|
+
def initialize(action:, performed_by:, timestamp:)
|
|
62
|
+
@action = action
|
|
63
|
+
@performed_by = performed_by
|
|
64
|
+
@timestamp = timestamp
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# ===========================================================================
|
|
69
|
+
# 共有インターフェース
|
|
70
|
+
# ===========================================================================
|
|
71
|
+
|
|
72
|
+
# 全 namespace で共有されるロガー
|
|
73
|
+
class Logger
|
|
74
|
+
#: String
|
|
75
|
+
attr_reader :prefix
|
|
76
|
+
|
|
77
|
+
#: (?String) -> void
|
|
78
|
+
def initialize(prefix = "[LOG]")
|
|
79
|
+
@prefix = prefix
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
#: (String) -> void
|
|
83
|
+
def info(message)
|
|
84
|
+
puts " #{@prefix} #{message}"
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# ===========================================================================
|
|
89
|
+
# Public 用のサービス実装
|
|
90
|
+
# ===========================================================================
|
|
91
|
+
|
|
92
|
+
module Public
|
|
93
|
+
class UserRepository
|
|
94
|
+
#: () -> void
|
|
95
|
+
def initialize
|
|
96
|
+
@users = {
|
|
97
|
+
1 => User.new(id: 1, name: "一般ユーザーA", role: "user"),
|
|
98
|
+
2 => User.new(id: 2, name: "一般ユーザーB", role: "user")
|
|
99
|
+
} #: Hash[Integer, User]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
#: (Integer) -> User?
|
|
103
|
+
def find(id)
|
|
104
|
+
@users[id]
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
#: () -> Array[User]
|
|
108
|
+
def all
|
|
109
|
+
@users.values
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
class NotificationService
|
|
114
|
+
#: (User, String) -> bool
|
|
115
|
+
def notify(user, message)
|
|
116
|
+
puts " [Public Notification] #{user.name}: #{message}"
|
|
117
|
+
true
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# ===========================================================================
|
|
123
|
+
# Admin 用のサービス実装
|
|
124
|
+
# ===========================================================================
|
|
125
|
+
|
|
126
|
+
module Admin
|
|
127
|
+
class UserRepository
|
|
128
|
+
#: () -> void
|
|
129
|
+
def initialize
|
|
130
|
+
@users = {
|
|
131
|
+
100 => User.new(id: 100, name: "管理者A", role: "admin"),
|
|
132
|
+
101 => User.new(id: 101, name: "管理者B", role: "super_admin")
|
|
133
|
+
} #: Hash[Integer, User]
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
#: (Integer) -> User?
|
|
137
|
+
def find(id)
|
|
138
|
+
@users[id]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
#: () -> Array[User]
|
|
142
|
+
def all
|
|
143
|
+
@users.values
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Admin 専用: ユーザーの全情報を取得
|
|
147
|
+
#: (Integer) -> Hash[Symbol, untyped]?
|
|
148
|
+
def find_with_permissions(id)
|
|
149
|
+
user = @users[id]
|
|
150
|
+
return nil unless user
|
|
151
|
+
|
|
152
|
+
{
|
|
153
|
+
user: user,
|
|
154
|
+
permissions: user.role == "super_admin" ? [:all] : %i[read write],
|
|
155
|
+
last_login: Time.now - 3600
|
|
156
|
+
}
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
class NotificationService
|
|
161
|
+
#: (User, String) -> bool
|
|
162
|
+
def notify(user, message)
|
|
163
|
+
puts " [Admin Notification] [PRIORITY] #{user.name}: #{message}"
|
|
164
|
+
true
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
class AuditLogger
|
|
169
|
+
#: () -> void
|
|
170
|
+
def initialize
|
|
171
|
+
@logs = [] #: Array[AuditLog]
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
#: (action: String, performed_by: User) -> AuditLog
|
|
175
|
+
def log(action:, performed_by:)
|
|
176
|
+
entry = AuditLog.new(action: action, performed_by: performed_by, timestamp: Time.now)
|
|
177
|
+
@logs << entry
|
|
178
|
+
puts " [Audit] #{performed_by.name}: #{action}"
|
|
179
|
+
entry
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
#: () -> Array[AuditLog]
|
|
183
|
+
def recent_logs
|
|
184
|
+
@logs.last(10)
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# ===========================================================================
|
|
190
|
+
# Admin::Reports 用のサービス実装(ネストした namespace)
|
|
191
|
+
# ===========================================================================
|
|
192
|
+
|
|
193
|
+
module Admin
|
|
194
|
+
module Reports
|
|
195
|
+
class ReportGenerator
|
|
196
|
+
#: (String, Hash[Symbol, untyped]) -> String
|
|
197
|
+
def generate(type, data)
|
|
198
|
+
puts " [ReportGenerator] レポート生成: #{type}"
|
|
199
|
+
"Report: #{type} - #{data.keys.join(", ")}"
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
class ReportExporter
|
|
204
|
+
#: (String, Symbol) -> String
|
|
205
|
+
def export(report, format)
|
|
206
|
+
puts " [ReportExporter] エクスポート: #{format}"
|
|
207
|
+
"#{report} (#{format}形式)"
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# ===========================================================================
|
|
214
|
+
# Provider 定義
|
|
215
|
+
# ===========================================================================
|
|
216
|
+
|
|
217
|
+
# 共有サービス(ルート namespace)
|
|
218
|
+
class CoreProvider < SenroUsecaser::Provider
|
|
219
|
+
#: (SenroUsecaser::Container) -> void
|
|
220
|
+
def register(container)
|
|
221
|
+
container.register(:logger, Logger.new)
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# ===========================================================================
|
|
226
|
+
# パターン1: Provider の namespace DSL を使用
|
|
227
|
+
# ===========================================================================
|
|
228
|
+
# メリット: シンプル、Provider レベルで namespace を宣言
|
|
229
|
+
# デメリット: Steep の型チェックでエラーが出る場合がある
|
|
230
|
+
|
|
231
|
+
# Public namespace のサービス(namespace DSL 使用)
|
|
232
|
+
class PublicProvider < SenroUsecaser::Provider
|
|
233
|
+
namespace :public
|
|
234
|
+
depends_on CoreProvider
|
|
235
|
+
|
|
236
|
+
#: (SenroUsecaser::Container) -> void
|
|
237
|
+
def register(container)
|
|
238
|
+
container.register_singleton(:user_repository) { |_c| Public::UserRepository.new }
|
|
239
|
+
container.register_singleton(:notification_service) { |_c| Public::NotificationService.new }
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# ===========================================================================
|
|
244
|
+
# パターン2: Container の namespace メソッドを直接使用
|
|
245
|
+
# ===========================================================================
|
|
246
|
+
# メリット: 型チェックが通りやすい、柔軟なネスト構造が可能
|
|
247
|
+
# デメリット: やや冗長
|
|
248
|
+
|
|
249
|
+
# Admin namespace のサービス(Container.namespace 直接使用)
|
|
250
|
+
class AdminProvider < SenroUsecaser::Provider
|
|
251
|
+
depends_on CoreProvider
|
|
252
|
+
|
|
253
|
+
#: (SenroUsecaser::Container) -> void
|
|
254
|
+
def register(container)
|
|
255
|
+
container.namespace(:admin) do
|
|
256
|
+
# @type self: SenroUsecaser::Container
|
|
257
|
+
register_singleton(:user_repository) { |_c| Admin::UserRepository.new }
|
|
258
|
+
register_singleton(:notification_service) { |_c| Admin::NotificationService.new }
|
|
259
|
+
register_singleton(:audit_logger) { |_c| Admin::AuditLogger.new }
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
#: (SenroUsecaser::Container) -> void
|
|
264
|
+
def after_boot(container)
|
|
265
|
+
logger = container.resolve(:logger) #: Logger
|
|
266
|
+
logger.info("AdminProvider: 管理者サービス初期化完了")
|
|
267
|
+
end
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
# Admin::Reports namespace のサービス(Container.namespace ネスト使用)
|
|
271
|
+
class AdminReportsProvider < SenroUsecaser::Provider
|
|
272
|
+
depends_on AdminProvider
|
|
273
|
+
|
|
274
|
+
#: (SenroUsecaser::Container) -> void
|
|
275
|
+
def register(container)
|
|
276
|
+
container.namespace(:admin) do
|
|
277
|
+
# @type self: SenroUsecaser::Container
|
|
278
|
+
namespace(:reports) do
|
|
279
|
+
# @type self: SenroUsecaser::Container
|
|
280
|
+
register_singleton(:report_generator) { |_c| Admin::Reports::ReportGenerator.new }
|
|
281
|
+
register_singleton(:report_exporter) { |_c| Admin::Reports::ReportExporter.new }
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
end
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
# ===========================================================================
|
|
288
|
+
# UseCase 定義
|
|
289
|
+
# ===========================================================================
|
|
290
|
+
|
|
291
|
+
# Public namespace の UseCase
|
|
292
|
+
class ListPublicUsersUseCase < SenroUsecaser::Base
|
|
293
|
+
namespace :public
|
|
294
|
+
|
|
295
|
+
class Output
|
|
296
|
+
#: (users: Array[User]) -> void
|
|
297
|
+
def initialize(users:)
|
|
298
|
+
@users = users #: Array[User]
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
#: () -> Array[User]
|
|
302
|
+
attr_reader :users
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
depends_on :user_repository, Public::UserRepository
|
|
306
|
+
depends_on :logger, Logger # ルートからフォールバック解決される
|
|
307
|
+
|
|
308
|
+
# @rbs!
|
|
309
|
+
# def user_repository: () -> Public::UserRepository
|
|
310
|
+
# def logger: () -> Logger
|
|
311
|
+
|
|
312
|
+
output Output
|
|
313
|
+
|
|
314
|
+
#: (?untyped, **untyped) -> SenroUsecaser::Result[Output]
|
|
315
|
+
def call(_input = nil, **_args)
|
|
316
|
+
logger.info("Public ユーザー一覧を取得")
|
|
317
|
+
users = user_repository.all
|
|
318
|
+
success(Output.new(users: users))
|
|
319
|
+
end
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# Admin namespace の UseCase
|
|
323
|
+
class ListAdminUsersUseCase < SenroUsecaser::Base
|
|
324
|
+
namespace :admin
|
|
325
|
+
|
|
326
|
+
class Output
|
|
327
|
+
#: (users: Array[User]) -> void
|
|
328
|
+
def initialize(users:)
|
|
329
|
+
@users = users #: Array[User]
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
#: () -> Array[User]
|
|
333
|
+
attr_reader :users
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
depends_on :user_repository, Admin::UserRepository
|
|
337
|
+
depends_on :audit_logger, Admin::AuditLogger
|
|
338
|
+
depends_on :logger, Logger # ルートからフォールバック解決される
|
|
339
|
+
|
|
340
|
+
# @rbs!
|
|
341
|
+
# def user_repository: () -> Admin::UserRepository
|
|
342
|
+
# def audit_logger: () -> Admin::AuditLogger
|
|
343
|
+
# def logger: () -> Logger
|
|
344
|
+
|
|
345
|
+
output Output
|
|
346
|
+
|
|
347
|
+
#: (?untyped, **untyped) -> SenroUsecaser::Result[Output]
|
|
348
|
+
def call(_input = nil, **_args)
|
|
349
|
+
logger.info("Admin ユーザー一覧を取得")
|
|
350
|
+
|
|
351
|
+
# 管理者操作なので監査ログを記録
|
|
352
|
+
admin = user_repository.find(100)
|
|
353
|
+
audit_logger.log(action: "list_admin_users", performed_by: admin) if admin
|
|
354
|
+
|
|
355
|
+
users = user_repository.all
|
|
356
|
+
success(Output.new(users: users))
|
|
357
|
+
end
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
# Admin::Reports namespace の UseCase(ネストした namespace)
|
|
361
|
+
class GenerateUserReportUseCase < SenroUsecaser::Base
|
|
362
|
+
namespace "admin::reports"
|
|
363
|
+
|
|
364
|
+
class Input
|
|
365
|
+
#: (report_type: String) -> void
|
|
366
|
+
def initialize(report_type:)
|
|
367
|
+
@report_type = report_type #: String
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
#: () -> String
|
|
371
|
+
attr_reader :report_type
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
class Output
|
|
375
|
+
#: (report: String) -> void
|
|
376
|
+
def initialize(report:)
|
|
377
|
+
@report = report #: String
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
#: () -> String
|
|
381
|
+
attr_reader :report
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
depends_on :report_generator, Admin::Reports::ReportGenerator
|
|
385
|
+
depends_on :report_exporter, Admin::Reports::ReportExporter
|
|
386
|
+
depends_on :user_repository, Admin::UserRepository # admin namespace からフォールバック
|
|
387
|
+
depends_on :audit_logger, Admin::AuditLogger # admin namespace からフォールバック
|
|
388
|
+
depends_on :logger, Logger # ルートからフォールバック
|
|
389
|
+
|
|
390
|
+
# @rbs!
|
|
391
|
+
# def report_generator: () -> Admin::Reports::ReportGenerator
|
|
392
|
+
# def report_exporter: () -> Admin::Reports::ReportExporter
|
|
393
|
+
# def user_repository: () -> Admin::UserRepository
|
|
394
|
+
# def audit_logger: () -> Admin::AuditLogger
|
|
395
|
+
# def logger: () -> Logger
|
|
396
|
+
|
|
397
|
+
input Input
|
|
398
|
+
output Output
|
|
399
|
+
|
|
400
|
+
#: (Input) -> SenroUsecaser::Result[Output]
|
|
401
|
+
def call(input)
|
|
402
|
+
logger.info("レポート生成開始: #{input.report_type}")
|
|
403
|
+
|
|
404
|
+
users = user_repository.all
|
|
405
|
+
report = report_generator.generate(input.report_type, { users: users, count: users.length })
|
|
406
|
+
exported = report_exporter.export(report, :pdf)
|
|
407
|
+
|
|
408
|
+
admin = user_repository.find(100)
|
|
409
|
+
audit_logger.log(action: "generate_report:#{input.report_type}", performed_by: admin) if admin
|
|
410
|
+
|
|
411
|
+
success(Output.new(report: exported))
|
|
412
|
+
end
|
|
413
|
+
end
|
|
414
|
+
|
|
415
|
+
# リクエストスコープで current_user を注入しつつ、namespace を活用
|
|
416
|
+
class AdminActionUseCase < SenroUsecaser::Base
|
|
417
|
+
namespace :admin
|
|
418
|
+
|
|
419
|
+
class Output
|
|
420
|
+
#: (message: String) -> void
|
|
421
|
+
def initialize(message:)
|
|
422
|
+
@message = message #: String
|
|
423
|
+
end
|
|
424
|
+
|
|
425
|
+
#: () -> String
|
|
426
|
+
attr_reader :message
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
depends_on :current_user, User
|
|
430
|
+
depends_on :audit_logger, Admin::AuditLogger
|
|
431
|
+
depends_on :notification_service, Admin::NotificationService
|
|
432
|
+
|
|
433
|
+
# @rbs!
|
|
434
|
+
# def current_user: () -> User
|
|
435
|
+
# def audit_logger: () -> Admin::AuditLogger
|
|
436
|
+
# def notification_service: () -> Admin::NotificationService
|
|
437
|
+
|
|
438
|
+
output Output
|
|
439
|
+
|
|
440
|
+
#: (?untyped, **untyped) -> SenroUsecaser::Result[Output]
|
|
441
|
+
def call(_input = nil, **_args)
|
|
442
|
+
audit_logger.log(action: "admin_action", performed_by: current_user)
|
|
443
|
+
notification_service.notify(current_user, "アクションを実行しました")
|
|
444
|
+
success(Output.new(message: "#{current_user.name} がアクションを実行"))
|
|
445
|
+
end
|
|
446
|
+
end
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
# =============================================================================
|
|
450
|
+
# infer_namespace_from_module のデモ用 UseCase
|
|
451
|
+
# =============================================================================
|
|
452
|
+
# NamespaceDemo モジュールの外に定義することで、
|
|
453
|
+
# モジュール名がそのまま namespace として推論される
|
|
454
|
+
|
|
455
|
+
# Public::InferredUseCase → namespace "public" として推論
|
|
456
|
+
module Public
|
|
457
|
+
class InferredUseCase < SenroUsecaser::Base
|
|
458
|
+
# namespace を明示的に設定しない!
|
|
459
|
+
# infer_namespace_from_module = true の場合、モジュール名 "Public" から
|
|
460
|
+
# namespace "public" が自動的に推論される
|
|
461
|
+
|
|
462
|
+
class Output
|
|
463
|
+
#: (message: String) -> void
|
|
464
|
+
def initialize(message:)
|
|
465
|
+
@message = message #: String
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
#: () -> String
|
|
469
|
+
attr_reader :message
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
depends_on :user_repository, NamespaceDemo::Public::UserRepository
|
|
473
|
+
depends_on :logger, NamespaceDemo::Logger
|
|
474
|
+
|
|
475
|
+
# @rbs!
|
|
476
|
+
# def user_repository: () -> NamespaceDemo::Public::UserRepository
|
|
477
|
+
# def logger: () -> NamespaceDemo::Logger
|
|
478
|
+
|
|
479
|
+
output Output
|
|
480
|
+
|
|
481
|
+
#: (?untyped, **untyped) -> SenroUsecaser::Result[Output]
|
|
482
|
+
def call(_input = nil, **_args)
|
|
483
|
+
logger.info("InferredUseCase: namespace を自動推論して実行")
|
|
484
|
+
users = user_repository.all
|
|
485
|
+
success(Output.new(message: "#{users.length} 人のユーザーを取得(namespace 自動推論)"))
|
|
486
|
+
end
|
|
487
|
+
end
|
|
488
|
+
end
|
|
489
|
+
|
|
490
|
+
# Admin::Reports::InferredReportUseCase → namespace "admin::reports" として推論
|
|
491
|
+
module Admin
|
|
492
|
+
module Reports
|
|
493
|
+
class InferredReportUseCase < SenroUsecaser::Base
|
|
494
|
+
# namespace を明示的に設定しない!
|
|
495
|
+
# infer_namespace_from_module = true の場合、モジュール名から
|
|
496
|
+
# namespace "admin::reports" が自動的に推論される
|
|
497
|
+
|
|
498
|
+
class Output
|
|
499
|
+
#: (message: String) -> void
|
|
500
|
+
def initialize(message:)
|
|
501
|
+
@message = message #: String
|
|
502
|
+
end
|
|
503
|
+
|
|
504
|
+
#: () -> String
|
|
505
|
+
attr_reader :message
|
|
506
|
+
end
|
|
507
|
+
|
|
508
|
+
depends_on :report_generator, NamespaceDemo::Admin::Reports::ReportGenerator
|
|
509
|
+
depends_on :user_repository, NamespaceDemo::Admin::UserRepository
|
|
510
|
+
depends_on :logger, NamespaceDemo::Logger
|
|
511
|
+
|
|
512
|
+
# @rbs!
|
|
513
|
+
# def report_generator: () -> NamespaceDemo::Admin::Reports::ReportGenerator
|
|
514
|
+
# def user_repository: () -> NamespaceDemo::Admin::UserRepository
|
|
515
|
+
# def logger: () -> NamespaceDemo::Logger
|
|
516
|
+
|
|
517
|
+
output Output
|
|
518
|
+
|
|
519
|
+
#: (?untyped, **untyped) -> SenroUsecaser::Result[Output]
|
|
520
|
+
def call(_input = nil, **_args)
|
|
521
|
+
logger.info("InferredReportUseCase: ネストした namespace を自動推論して実行")
|
|
522
|
+
users = user_repository.all
|
|
523
|
+
report = report_generator.generate("inferred_report", { count: users.length })
|
|
524
|
+
success(Output.new(message: "レポート生成完了: #{report}(namespace 自動推論)"))
|
|
525
|
+
end
|
|
526
|
+
end
|
|
527
|
+
end
|
|
528
|
+
end
|
|
529
|
+
|
|
530
|
+
# =============================================================================
|
|
531
|
+
# infer_namespace_from_module を使った Provider のデモ
|
|
532
|
+
# =============================================================================
|
|
533
|
+
# Provider もモジュール名から namespace を自動推論できる
|
|
534
|
+
|
|
535
|
+
# Inferred::ServiceProvider → namespace "inferred" として推論
|
|
536
|
+
module Inferred
|
|
537
|
+
class ServiceProvider < SenroUsecaser::Provider
|
|
538
|
+
# namespace を明示的に設定しない!
|
|
539
|
+
# infer_namespace_from_module = true の場合、モジュール名 "Inferred" から
|
|
540
|
+
# namespace "inferred" が自動的に推論される
|
|
541
|
+
|
|
542
|
+
#: (SenroUsecaser::Container) -> void
|
|
543
|
+
def register(container)
|
|
544
|
+
container.register(:inferred_service, "This service was registered in inferred namespace")
|
|
545
|
+
end
|
|
546
|
+
end
|
|
547
|
+
end
|
|
548
|
+
|
|
549
|
+
# =============================================================================
|
|
550
|
+
# テスト実行
|
|
551
|
+
# =============================================================================
|
|
552
|
+
|
|
553
|
+
puts "=" * 70
|
|
554
|
+
puts "SenroUsecaser Namespace デモ"
|
|
555
|
+
puts "=" * 70
|
|
556
|
+
puts
|
|
557
|
+
|
|
558
|
+
# 状態をリセット
|
|
559
|
+
SenroUsecaser.reset!
|
|
560
|
+
|
|
561
|
+
# Provider を設定
|
|
562
|
+
SenroUsecaser.configure do |config|
|
|
563
|
+
config.providers = [
|
|
564
|
+
NamespaceDemo::CoreProvider,
|
|
565
|
+
NamespaceDemo::PublicProvider,
|
|
566
|
+
NamespaceDemo::AdminProvider,
|
|
567
|
+
NamespaceDemo::AdminReportsProvider
|
|
568
|
+
]
|
|
569
|
+
end
|
|
570
|
+
|
|
571
|
+
puts "1. Provider の起動とnamespace登録"
|
|
572
|
+
puts "-" * 70
|
|
573
|
+
SenroUsecaser.boot!
|
|
574
|
+
puts
|
|
575
|
+
|
|
576
|
+
# -----------------------------------------------------------------------------
|
|
577
|
+
puts "2. Container の namespace 構造確認"
|
|
578
|
+
puts "-" * 70
|
|
579
|
+
|
|
580
|
+
container = SenroUsecaser.container
|
|
581
|
+
puts " 登録済みキー:"
|
|
582
|
+
container.keys.sort.each do |key|
|
|
583
|
+
puts " - #{key}"
|
|
584
|
+
end
|
|
585
|
+
puts
|
|
586
|
+
|
|
587
|
+
# -----------------------------------------------------------------------------
|
|
588
|
+
puts "3. namespace フォールバック確認"
|
|
589
|
+
puts "-" * 70
|
|
590
|
+
|
|
591
|
+
# admin namespace から logger(ルートに登録)を解決
|
|
592
|
+
puts " admin namespace から logger を解決:"
|
|
593
|
+
admin_logger = container.resolve_in(:admin, :logger) #: NamespaceDemo::Logger
|
|
594
|
+
admin_logger.info("admin namespace から解決された logger です")
|
|
595
|
+
|
|
596
|
+
# admin::reports namespace から audit_logger(admin に登録)を解決
|
|
597
|
+
puts " admin::reports namespace から audit_logger を解決:"
|
|
598
|
+
reports_audit_logger = container.resolve_in("admin::reports", :audit_logger) #: NamespaceDemo::Admin::AuditLogger
|
|
599
|
+
admin = container.resolve_in(:admin, :user_repository).find(100) #: NamespaceDemo::User?
|
|
600
|
+
reports_audit_logger.log(action: "test_fallback", performed_by: admin) if admin
|
|
601
|
+
puts
|
|
602
|
+
|
|
603
|
+
# -----------------------------------------------------------------------------
|
|
604
|
+
puts "4. Public namespace UseCase 実行"
|
|
605
|
+
puts "-" * 70
|
|
606
|
+
|
|
607
|
+
result = NamespaceDemo::ListPublicUsersUseCase.call
|
|
608
|
+
if result.success?
|
|
609
|
+
output = result.value!
|
|
610
|
+
puts " 取得したユーザー数: #{output.users.length}"
|
|
611
|
+
output.users.each do |user|
|
|
612
|
+
puts " - #{user.name} (#{user.role})"
|
|
613
|
+
end
|
|
614
|
+
end
|
|
615
|
+
puts
|
|
616
|
+
|
|
617
|
+
# -----------------------------------------------------------------------------
|
|
618
|
+
puts "5. Admin namespace UseCase 実行"
|
|
619
|
+
puts "-" * 70
|
|
620
|
+
|
|
621
|
+
result = NamespaceDemo::ListAdminUsersUseCase.call
|
|
622
|
+
if result.success?
|
|
623
|
+
output = result.value!
|
|
624
|
+
puts " 取得した管理者数: #{output.users.length}"
|
|
625
|
+
output.users.each do |user|
|
|
626
|
+
puts " - #{user.name} (#{user.role})"
|
|
627
|
+
end
|
|
628
|
+
end
|
|
629
|
+
puts
|
|
630
|
+
|
|
631
|
+
# -----------------------------------------------------------------------------
|
|
632
|
+
puts "6. Admin::Reports namespace UseCase 実行(ネストした namespace)"
|
|
633
|
+
puts "-" * 70
|
|
634
|
+
|
|
635
|
+
input = NamespaceDemo::GenerateUserReportUseCase::Input.new(report_type: "monthly_summary")
|
|
636
|
+
result = NamespaceDemo::GenerateUserReportUseCase.call(input)
|
|
637
|
+
if result.success?
|
|
638
|
+
output = result.value!
|
|
639
|
+
puts " 生成されたレポート: #{output.report}"
|
|
640
|
+
end
|
|
641
|
+
puts
|
|
642
|
+
|
|
643
|
+
# -----------------------------------------------------------------------------
|
|
644
|
+
puts "7. 同じキーの異なる namespace 解決確認"
|
|
645
|
+
puts "-" * 70
|
|
646
|
+
|
|
647
|
+
public_repo = container.resolve_in(:public, :user_repository) #: NamespaceDemo::Public::UserRepository
|
|
648
|
+
admin_repo = container.resolve_in(:admin, :user_repository) #: NamespaceDemo::Admin::UserRepository
|
|
649
|
+
|
|
650
|
+
puts " Public UserRepository:"
|
|
651
|
+
public_repo.all.each do |user|
|
|
652
|
+
puts " - #{user.name}"
|
|
653
|
+
end
|
|
654
|
+
|
|
655
|
+
puts " Admin UserRepository:"
|
|
656
|
+
admin_repo.all.each do |user|
|
|
657
|
+
puts " - #{user.name}"
|
|
658
|
+
end
|
|
659
|
+
puts
|
|
660
|
+
|
|
661
|
+
# -----------------------------------------------------------------------------
|
|
662
|
+
puts "8. Scoped Container と namespace の組み合わせ"
|
|
663
|
+
puts "-" * 70
|
|
664
|
+
|
|
665
|
+
# リクエストスコープのコンテナを作成(admin namespace に current_user を追加)
|
|
666
|
+
current_admin = NamespaceDemo::User.new(id: 100, name: "現在の管理者", role: "admin")
|
|
667
|
+
|
|
668
|
+
scoped_container = container.scope do
|
|
669
|
+
# @type self: SenroUsecaser::Container
|
|
670
|
+
namespace(:admin) do
|
|
671
|
+
# @type self: SenroUsecaser::Container
|
|
672
|
+
register(:current_user, current_admin)
|
|
673
|
+
end
|
|
674
|
+
end
|
|
675
|
+
|
|
676
|
+
result = NamespaceDemo::AdminActionUseCase.call(container: scoped_container)
|
|
677
|
+
if result.success?
|
|
678
|
+
output = result.value!
|
|
679
|
+
puts " 結果: #{output.message}"
|
|
680
|
+
end
|
|
681
|
+
puts
|
|
682
|
+
|
|
683
|
+
# -----------------------------------------------------------------------------
|
|
684
|
+
puts "9. registered? と registered_in? の確認"
|
|
685
|
+
puts "-" * 70
|
|
686
|
+
|
|
687
|
+
puts " container.registered_in?(:admin, :user_repository) = #{container.registered_in?(:admin, :user_repository)}"
|
|
688
|
+
puts " container.registered_in?(:public, :user_repository) = #{container.registered_in?(:public, :user_repository)}"
|
|
689
|
+
puts " container.registered_in?(:admin, :logger) = #{container.registered_in?(:admin, :logger)} (ルートからフォールバック)"
|
|
690
|
+
puts " container.registered_in?(\"admin::reports\", :report_generator) = #{container.registered_in?("admin::reports",
|
|
691
|
+
:report_generator)}"
|
|
692
|
+
puts " container.registered_in?(\"admin::reports\", :audit_logger) = #{container.registered_in?("admin::reports",
|
|
693
|
+
:audit_logger)} (adminからフォールバック)"
|
|
694
|
+
puts " container.registered_in?(:public, :audit_logger) = #{container.registered_in?(:public, :audit_logger)} (存在しない)"
|
|
695
|
+
puts
|
|
696
|
+
|
|
697
|
+
# -----------------------------------------------------------------------------
|
|
698
|
+
puts "10. infer_namespace_from_module の確認"
|
|
699
|
+
puts "-" * 70
|
|
700
|
+
|
|
701
|
+
# 設定を有効化
|
|
702
|
+
SenroUsecaser.configuration.infer_namespace_from_module = true
|
|
703
|
+
puts " infer_namespace_from_module = true に設定"
|
|
704
|
+
|
|
705
|
+
# Public::InferredUseCase は namespace を明示していないが、
|
|
706
|
+
# モジュール名から "public" namespace が推論される
|
|
707
|
+
puts " Public::InferredUseCase を実行(namespace 自動推論):"
|
|
708
|
+
result = Public::InferredUseCase.call
|
|
709
|
+
if result.success?
|
|
710
|
+
output = result.value!
|
|
711
|
+
puts " 結果: #{output.message}"
|
|
712
|
+
end
|
|
713
|
+
|
|
714
|
+
# Admin::Reports::InferredReportUseCase はネストしたモジュールから
|
|
715
|
+
# "admin::reports" namespace が推論される
|
|
716
|
+
puts " Admin::Reports::InferredReportUseCase を実行(ネストした namespace 自動推論):"
|
|
717
|
+
result = Admin::Reports::InferredReportUseCase.call
|
|
718
|
+
if result.success?
|
|
719
|
+
output = result.value!
|
|
720
|
+
puts " 結果: #{output.message}"
|
|
721
|
+
end
|
|
722
|
+
puts
|
|
723
|
+
|
|
724
|
+
# Provider も同様にモジュール名から namespace を推論できる
|
|
725
|
+
puts " Provider の namespace 自動推論:"
|
|
726
|
+
# Inferred::ServiceProvider を登録(namespace "inferred" が推論される)
|
|
727
|
+
Inferred::ServiceProvider.call(SenroUsecaser.container)
|
|
728
|
+
puts " Inferred::ServiceProvider を登録"
|
|
729
|
+
|
|
730
|
+
# 登録されたキーを確認
|
|
731
|
+
inferred_keys = SenroUsecaser.container.keys.select { |k| k.start_with?("inferred") }
|
|
732
|
+
puts " 登録されたキー: #{inferred_keys.join(", ")}"
|
|
733
|
+
|
|
734
|
+
# 値を取得
|
|
735
|
+
value = SenroUsecaser.container.resolve_in(:inferred, :inferred_service)
|
|
736
|
+
puts " 取得した値: #{value}"
|
|
737
|
+
|
|
738
|
+
# 設定を元に戻す
|
|
739
|
+
SenroUsecaser.configuration.infer_namespace_from_module = false
|
|
740
|
+
puts
|
|
741
|
+
|
|
742
|
+
# -----------------------------------------------------------------------------
|
|
743
|
+
puts "11. シャットダウン"
|
|
744
|
+
puts "-" * 70
|
|
745
|
+
SenroUsecaser.shutdown!
|
|
746
|
+
puts " 完了"
|
|
747
|
+
puts
|
|
748
|
+
|
|
749
|
+
puts "=" * 70
|
|
750
|
+
puts "Namespace デモ完了"
|
|
751
|
+
puts "=" * 70
|