senro_usecaser 0.1.0 → 0.2.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 +4 -4
- data/CHANGELOG.md +29 -0
- data/README.md +87 -6
- data/lib/senro_usecaser/base.rb +56 -9
- data/lib/senro_usecaser/hook.rb +180 -0
- data/lib/senro_usecaser/version.rb +1 -1
- data/lib/senro_usecaser.rb +1 -0
- data/sig/generated/senro_usecaser/base.rbs +24 -3
- data/sig/generated/senro_usecaser/hook.rbs +112 -0
- metadata +4 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 91d516c2b304a836d4b476c177083ddff38bf8344ddbba14293aa60bee3e07f5
|
|
4
|
+
data.tar.gz: 0167aac25683c97feeb378123294073025c86d405307facb5e0392f5bf8eec35
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9592f2b84a831c56b80010cd207588e171407f07a143f19bb25f6cb4bb103bfa23b449c98517c887b4a0d5ff571b5e666afa82877df887978881121c01943996
|
|
7
|
+
data.tar.gz: f4cdbec706095b338c537eec61179ec9a2d9e251bd570d5ddceef2d0b2503deede7be514501e395c47e191a6e1c5c97f49144c0eb0fc728b3741d98b47ecc0e3
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.2.0] - 2026-01-31
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Hook class with dependency injection** - New `SenroUsecaser::Hook` base class for creating hooks with `depends_on` support
|
|
13
|
+
- Supports `namespace` declaration for scoped dependency resolution
|
|
14
|
+
- Supports `infer_namespace_from_module` configuration
|
|
15
|
+
- Can inherit namespace from the UseCase when not explicitly declared
|
|
16
|
+
- **Block hooks can access dependencies**
|
|
17
|
+
- `before` and `after` blocks now run in instance context via `instance_exec`
|
|
18
|
+
- Dependencies declared with `depends_on` are directly accessible in block hooks
|
|
19
|
+
|
|
20
|
+
### Changed
|
|
21
|
+
|
|
22
|
+
- **Around block signature** - Changed from `|input, &block|` to `|input, use_case, &block|`
|
|
23
|
+
- The `use_case` argument provides access to dependencies
|
|
24
|
+
- **Input class is now mandatory** - All UseCases must define an `input` class
|
|
25
|
+
- **Renamed `context` to `input`** - Hook parameters renamed for clarity
|
|
26
|
+
|
|
27
|
+
## [0.1.0] - 2026-01-30
|
|
28
|
+
|
|
29
|
+
- Initial release
|
data/README.md
CHANGED
|
@@ -659,20 +659,30 @@ end
|
|
|
659
659
|
|
|
660
660
|
##### Block Syntax
|
|
661
661
|
|
|
662
|
+
Block hooks are executed in the UseCase instance context, allowing access to `depends_on` dependencies.
|
|
663
|
+
|
|
662
664
|
```ruby
|
|
663
665
|
class CreateUserUseCase < SenroUsecaser::Base
|
|
666
|
+
depends_on :logger
|
|
667
|
+
depends_on :metrics
|
|
668
|
+
input Input
|
|
669
|
+
|
|
670
|
+
# before/after blocks can access dependencies directly
|
|
664
671
|
before do |input|
|
|
665
|
-
|
|
672
|
+
logger.info("Starting with #{input.class.name}")
|
|
666
673
|
end
|
|
667
674
|
|
|
668
675
|
after do |input, result|
|
|
669
|
-
#
|
|
676
|
+
logger.info("Finished: #{result.success? ? 'success' : 'failure'}")
|
|
677
|
+
metrics.increment(:use_case_completed)
|
|
670
678
|
end
|
|
671
679
|
|
|
672
|
-
around
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
680
|
+
# around block receives use_case as second argument for dependency access
|
|
681
|
+
around do |input, use_case, &block|
|
|
682
|
+
use_case.logger.info("Transaction start")
|
|
683
|
+
result = ActiveRecord::Base.transaction { block.call }
|
|
684
|
+
use_case.logger.info("Transaction end")
|
|
685
|
+
result
|
|
676
686
|
end
|
|
677
687
|
|
|
678
688
|
def call(input)
|
|
@@ -681,6 +691,77 @@ class CreateUserUseCase < SenroUsecaser::Base
|
|
|
681
691
|
end
|
|
682
692
|
```
|
|
683
693
|
|
|
694
|
+
##### Hook Classes
|
|
695
|
+
|
|
696
|
+
For more complex hooks with their own dependencies, use `SenroUsecaser::Hook` class:
|
|
697
|
+
|
|
698
|
+
```ruby
|
|
699
|
+
class LoggingHook < SenroUsecaser::Hook
|
|
700
|
+
depends_on :logger
|
|
701
|
+
depends_on :metrics
|
|
702
|
+
|
|
703
|
+
def before(input)
|
|
704
|
+
logger.info("Starting with #{input.class.name}")
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
def after(input, result)
|
|
708
|
+
logger.info("Finished: #{result.success? ? 'success' : 'failure'}")
|
|
709
|
+
metrics.increment(:use_case_completed)
|
|
710
|
+
end
|
|
711
|
+
|
|
712
|
+
def around(input)
|
|
713
|
+
logger.info("Around start")
|
|
714
|
+
result = yield
|
|
715
|
+
logger.info("Around end")
|
|
716
|
+
result
|
|
717
|
+
end
|
|
718
|
+
end
|
|
719
|
+
|
|
720
|
+
class CreateUserUseCase < SenroUsecaser::Base
|
|
721
|
+
extend_with LoggingHook
|
|
722
|
+
|
|
723
|
+
def call(input)
|
|
724
|
+
# main logic
|
|
725
|
+
end
|
|
726
|
+
end
|
|
727
|
+
```
|
|
728
|
+
|
|
729
|
+
Hook classes support:
|
|
730
|
+
- `depends_on` for dependency injection
|
|
731
|
+
- `namespace` for scoped dependency resolution
|
|
732
|
+
- Automatic namespace inference from module structure (when `infer_namespace_from_module` is enabled)
|
|
733
|
+
- Inheriting namespace from the UseCase if not explicitly declared
|
|
734
|
+
|
|
735
|
+
```ruby
|
|
736
|
+
# Hook with explicit namespace
|
|
737
|
+
class Admin::AuditHook < SenroUsecaser::Hook
|
|
738
|
+
namespace :admin
|
|
739
|
+
depends_on :audit_logger
|
|
740
|
+
|
|
741
|
+
def after(input, result)
|
|
742
|
+
audit_logger.log(action: "create", success: result.success?)
|
|
743
|
+
end
|
|
744
|
+
end
|
|
745
|
+
|
|
746
|
+
# Hook inheriting namespace from UseCase
|
|
747
|
+
class MetricsHook < SenroUsecaser::Hook
|
|
748
|
+
depends_on :metrics # resolved from UseCase's namespace
|
|
749
|
+
|
|
750
|
+
def after(input, result)
|
|
751
|
+
metrics.increment(:completed)
|
|
752
|
+
end
|
|
753
|
+
end
|
|
754
|
+
|
|
755
|
+
class Admin::CreateUserUseCase < SenroUsecaser::Base
|
|
756
|
+
namespace :admin
|
|
757
|
+
extend_with MetricsHook # metrics resolved from :admin namespace
|
|
758
|
+
|
|
759
|
+
def call(input)
|
|
760
|
+
# ...
|
|
761
|
+
end
|
|
762
|
+
end
|
|
763
|
+
```
|
|
764
|
+
|
|
684
765
|
##### Input/Output Validation
|
|
685
766
|
|
|
686
767
|
Use `extend_with` to integrate validation libraries like ActiveModel::Validations:
|
data/lib/senro_usecaser/base.rb
CHANGED
|
@@ -262,8 +262,9 @@ module SenroUsecaser
|
|
|
262
262
|
end
|
|
263
263
|
|
|
264
264
|
# Adds an around hook
|
|
265
|
+
# Block receives (input, use_case, &block) where use_case allows access to dependencies
|
|
265
266
|
#
|
|
266
|
-
#: () { (untyped) { () -> Result[untyped] } -> Result[untyped] } -> void
|
|
267
|
+
#: () { (untyped, Base) { () -> Result[untyped] } -> Result[untyped] } -> void
|
|
267
268
|
def around(&block)
|
|
268
269
|
around_hooks << block if block
|
|
269
270
|
end
|
|
@@ -440,22 +441,39 @@ module SenroUsecaser
|
|
|
440
441
|
#: (untyped, Proc) -> Proc
|
|
441
442
|
def build_around_chain(input, core_block)
|
|
442
443
|
wrapped_core = -> { wrap_result(core_block.call) }
|
|
443
|
-
|
|
444
|
+
chain = wrap_extension_around_hooks(input, wrapped_core)
|
|
445
|
+
wrap_block_around_hooks(input, chain)
|
|
446
|
+
end
|
|
444
447
|
|
|
445
|
-
|
|
448
|
+
# Wraps extension/module around hooks
|
|
449
|
+
#
|
|
450
|
+
#: (untyped, Proc) -> Proc
|
|
451
|
+
def wrap_extension_around_hooks(input, chain)
|
|
452
|
+
collect_extension_around_hooks.reverse.reduce(chain) do |inner, hook|
|
|
446
453
|
-> { wrap_result(hook.call(input) { inner.call }) }
|
|
447
454
|
end
|
|
448
455
|
end
|
|
449
456
|
|
|
450
|
-
#
|
|
457
|
+
# Wraps block-based around hooks (pass self as second argument)
|
|
458
|
+
#
|
|
459
|
+
#: (untyped, Proc) -> Proc
|
|
460
|
+
def wrap_block_around_hooks(input, chain)
|
|
461
|
+
use_case_instance = self
|
|
462
|
+
self.class.around_hooks.reverse.reduce(chain) do |inner, hook|
|
|
463
|
+
-> { wrap_result(hook.call(input, use_case_instance) { inner.call }) }
|
|
464
|
+
end
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
# Collects around hooks from Hook classes and extension modules (not block-based)
|
|
451
468
|
#
|
|
452
469
|
#: () -> Array[Proc]
|
|
453
|
-
def
|
|
454
|
-
hooks =
|
|
470
|
+
def collect_extension_around_hooks
|
|
471
|
+
hooks = hook_instances.map { |hook_instance| hook_instance.method(:around).to_proc }
|
|
455
472
|
self.class.extensions.each do |ext|
|
|
473
|
+
next if hook_class?(ext)
|
|
474
|
+
|
|
456
475
|
hooks << ext.method(:around).to_proc if ext.respond_to?(:around)
|
|
457
476
|
end
|
|
458
|
-
hooks.concat(self.class.around_hooks)
|
|
459
477
|
hooks
|
|
460
478
|
end
|
|
461
479
|
|
|
@@ -463,20 +481,49 @@ module SenroUsecaser
|
|
|
463
481
|
#
|
|
464
482
|
#: (untyped) -> void
|
|
465
483
|
def run_before_hooks(input)
|
|
484
|
+
hook_instances.each do |hook_instance|
|
|
485
|
+
hook_instance.before(input)
|
|
486
|
+
end
|
|
466
487
|
self.class.extensions.each do |ext|
|
|
488
|
+
next if hook_class?(ext)
|
|
489
|
+
|
|
467
490
|
ext.send(:before, input) if ext.respond_to?(:before)
|
|
468
491
|
end
|
|
469
|
-
self.class.before_hooks.each { |hook|
|
|
492
|
+
self.class.before_hooks.each { |hook| instance_exec(input, &hook) } # steep:ignore BlockTypeMismatch
|
|
470
493
|
end
|
|
471
494
|
|
|
472
495
|
# Runs all after hooks
|
|
473
496
|
#
|
|
474
497
|
#: (untyped, Result[untyped]) -> void
|
|
475
498
|
def run_after_hooks(input, result)
|
|
499
|
+
hook_instances.each do |hook_instance|
|
|
500
|
+
hook_instance.after(input, result)
|
|
501
|
+
end
|
|
476
502
|
self.class.extensions.each do |ext|
|
|
503
|
+
next if hook_class?(ext)
|
|
504
|
+
|
|
477
505
|
ext.send(:after, input, result) if ext.respond_to?(:after)
|
|
478
506
|
end
|
|
479
|
-
self.class.after_hooks.each { |hook|
|
|
507
|
+
self.class.after_hooks.each { |hook| instance_exec(input, result, &hook) } # steep:ignore BlockTypeMismatch
|
|
508
|
+
end
|
|
509
|
+
|
|
510
|
+
# Returns instantiated hook objects
|
|
511
|
+
#
|
|
512
|
+
#: () -> Array[Hook]
|
|
513
|
+
def hook_instances
|
|
514
|
+
@hook_instances ||= self.class.extensions.filter_map do |ext|
|
|
515
|
+
next unless hook_class?(ext)
|
|
516
|
+
|
|
517
|
+
hook_class = ext #: singleton(Hook)
|
|
518
|
+
hook_class.new(container: @_container, use_case_namespace: effective_namespace)
|
|
519
|
+
end
|
|
520
|
+
end
|
|
521
|
+
|
|
522
|
+
# Checks if the extension is a Hook class
|
|
523
|
+
#
|
|
524
|
+
#: (untyped) -> bool
|
|
525
|
+
def hook_class?(ext)
|
|
526
|
+
ext.is_a?(Class) && ext < Hook
|
|
480
527
|
end
|
|
481
528
|
|
|
482
529
|
# Resolves dependencies from the container
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# rbs_inline: enabled
|
|
4
|
+
|
|
5
|
+
module SenroUsecaser
|
|
6
|
+
# Base class for hooks with dependency injection support
|
|
7
|
+
#
|
|
8
|
+
# Hook classes provide a way to define before/after/around hooks
|
|
9
|
+
# with access to the DI container and automatic dependency resolution.
|
|
10
|
+
#
|
|
11
|
+
# @example Basic hook
|
|
12
|
+
# class LoggingHook < SenroUsecaser::Hook
|
|
13
|
+
# depends_on :logger, Logger
|
|
14
|
+
#
|
|
15
|
+
# def before(input)
|
|
16
|
+
# logger.info("Starting with #{input.class.name}")
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# def after(input, result)
|
|
20
|
+
# logger.info("Finished: #{result.success? ? 'success' : 'failure'}")
|
|
21
|
+
# end
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# @example Hook with namespace
|
|
25
|
+
# class Admin::AuditHook < SenroUsecaser::Hook
|
|
26
|
+
# namespace :admin
|
|
27
|
+
# depends_on :audit_logger, AuditLogger
|
|
28
|
+
#
|
|
29
|
+
# def after(input, result)
|
|
30
|
+
# audit_logger.log(input: input, result: result)
|
|
31
|
+
# end
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
# @example Hook with around
|
|
35
|
+
# class TransactionHook < SenroUsecaser::Hook
|
|
36
|
+
# def around(input)
|
|
37
|
+
# ActiveRecord::Base.transaction { yield }
|
|
38
|
+
# end
|
|
39
|
+
# end
|
|
40
|
+
class Hook
|
|
41
|
+
class << self
|
|
42
|
+
# Declares a dependency to be injected from the container
|
|
43
|
+
#
|
|
44
|
+
#: (Symbol, ?Class) -> void
|
|
45
|
+
def depends_on(name, type = nil)
|
|
46
|
+
dependencies << name unless dependencies.include?(name)
|
|
47
|
+
dependency_types[name] = type if type
|
|
48
|
+
|
|
49
|
+
define_method(name) do
|
|
50
|
+
@_dependencies[name]
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Returns the list of declared dependencies
|
|
55
|
+
#
|
|
56
|
+
#: () -> Array[Symbol]
|
|
57
|
+
def dependencies
|
|
58
|
+
@dependencies ||= []
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Returns the dependency type mapping
|
|
62
|
+
#
|
|
63
|
+
#: () -> Hash[Symbol, Class]
|
|
64
|
+
def dependency_types
|
|
65
|
+
@dependency_types ||= {}
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Sets or returns the namespace for dependency resolution
|
|
69
|
+
#
|
|
70
|
+
#: (?(Symbol | String)) -> (Symbol | String)?
|
|
71
|
+
def namespace(name = nil)
|
|
72
|
+
if name
|
|
73
|
+
@hook_namespace = name
|
|
74
|
+
else
|
|
75
|
+
@hook_namespace
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Alias for namespace() without arguments
|
|
80
|
+
#
|
|
81
|
+
#: () -> (Symbol | String)?
|
|
82
|
+
def hook_namespace # rubocop:disable Style/TrivialAccessors
|
|
83
|
+
@hook_namespace
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @api private
|
|
87
|
+
def inherited(subclass)
|
|
88
|
+
super
|
|
89
|
+
subclass.instance_variable_set(:@dependencies, dependencies.dup)
|
|
90
|
+
subclass.instance_variable_set(:@dependency_types, dependency_types.dup)
|
|
91
|
+
subclass.instance_variable_set(:@hook_namespace, @hook_namespace)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Initializes the hook with dependencies resolved from the container
|
|
96
|
+
#
|
|
97
|
+
#: (container: Container, ?use_case_namespace: (Symbol | String)?) -> void
|
|
98
|
+
def initialize(container:, use_case_namespace: nil)
|
|
99
|
+
@_container = container
|
|
100
|
+
@_use_case_namespace = use_case_namespace
|
|
101
|
+
@_dependencies = {} #: Hash[Symbol, untyped]
|
|
102
|
+
|
|
103
|
+
resolve_dependencies
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Called before the UseCase executes
|
|
107
|
+
# Override in subclass to add before logic
|
|
108
|
+
#
|
|
109
|
+
#: (untyped) -> void
|
|
110
|
+
def before(input)
|
|
111
|
+
# Override in subclass
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Called after the UseCase executes
|
|
115
|
+
# Override in subclass to add after logic
|
|
116
|
+
#
|
|
117
|
+
#: (untyped, Result[untyped]) -> void
|
|
118
|
+
def after(input, result)
|
|
119
|
+
# Override in subclass
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
# Wraps the UseCase execution
|
|
123
|
+
# Override in subclass to add around logic
|
|
124
|
+
#
|
|
125
|
+
#: (untyped) { () -> Result[untyped] } -> Result[untyped]
|
|
126
|
+
def around(_input)
|
|
127
|
+
yield
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
private
|
|
131
|
+
|
|
132
|
+
# Returns the effective namespace for dependency resolution
|
|
133
|
+
#
|
|
134
|
+
#: () -> (Symbol | String)?
|
|
135
|
+
def effective_namespace
|
|
136
|
+
return self.class.hook_namespace if self.class.hook_namespace
|
|
137
|
+
return @_use_case_namespace if @_use_case_namespace
|
|
138
|
+
return nil unless SenroUsecaser.configuration.infer_namespace_from_module
|
|
139
|
+
|
|
140
|
+
infer_namespace_from_class
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Infers namespace from the class's module structure
|
|
144
|
+
#
|
|
145
|
+
#: () -> String?
|
|
146
|
+
def infer_namespace_from_class
|
|
147
|
+
class_name = self.class.name
|
|
148
|
+
return nil unless class_name
|
|
149
|
+
|
|
150
|
+
parts = class_name.split("::")
|
|
151
|
+
return nil if parts.length <= 1
|
|
152
|
+
|
|
153
|
+
module_parts = parts[0...-1] || [] #: Array[String]
|
|
154
|
+
return nil if module_parts.empty?
|
|
155
|
+
|
|
156
|
+
module_parts.map { |part| part.gsub(/([a-z])([A-Z])/, '\1_\2').downcase }.join("::")
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Resolves dependencies from the container
|
|
160
|
+
#
|
|
161
|
+
#: () -> void
|
|
162
|
+
def resolve_dependencies
|
|
163
|
+
self.class.dependencies.each do |name|
|
|
164
|
+
@_dependencies[name] = resolve_from_container(name)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Resolves a single dependency from the container
|
|
169
|
+
#
|
|
170
|
+
#: (Symbol) -> untyped
|
|
171
|
+
def resolve_from_container(name)
|
|
172
|
+
namespace = effective_namespace
|
|
173
|
+
if namespace
|
|
174
|
+
@_container.resolve_in(namespace, name)
|
|
175
|
+
else
|
|
176
|
+
@_container.resolve(name)
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
data/lib/senro_usecaser.rb
CHANGED
|
@@ -8,6 +8,7 @@ require_relative "senro_usecaser/result"
|
|
|
8
8
|
require_relative "senro_usecaser/container"
|
|
9
9
|
require_relative "senro_usecaser/configuration"
|
|
10
10
|
require_relative "senro_usecaser/provider"
|
|
11
|
+
require_relative "senro_usecaser/hook"
|
|
11
12
|
require_relative "senro_usecaser/base"
|
|
12
13
|
|
|
13
14
|
# SenroUsecaser is a type-safe UseCase pattern implementation library for Ruby.
|
|
@@ -178,8 +178,9 @@ module SenroUsecaser
|
|
|
178
178
|
def self.after_hooks: () -> Array[Proc]
|
|
179
179
|
|
|
180
180
|
# Adds an around hook
|
|
181
|
+
# Block receives (input, use_case, &block) where use_case allows access to dependencies
|
|
181
182
|
#
|
|
182
|
-
# : () { (untyped) { () -> Result[untyped] } -> Result[untyped] } -> void
|
|
183
|
+
# : () { (untyped, Base) { () -> Result[untyped] } -> Result[untyped] } -> void
|
|
183
184
|
def self.around: () ?{ (?) -> untyped } -> untyped
|
|
184
185
|
|
|
185
186
|
# Returns the list of around hooks
|
|
@@ -281,10 +282,20 @@ module SenroUsecaser
|
|
|
281
282
|
# : (untyped, Proc) -> Proc
|
|
282
283
|
def build_around_chain: (untyped, Proc) -> Proc
|
|
283
284
|
|
|
284
|
-
#
|
|
285
|
+
# Wraps extension/module around hooks
|
|
286
|
+
#
|
|
287
|
+
# : (untyped, Proc) -> Proc
|
|
288
|
+
def wrap_extension_around_hooks: (untyped, Proc) -> Proc
|
|
289
|
+
|
|
290
|
+
# Wraps block-based around hooks (pass self as second argument)
|
|
291
|
+
#
|
|
292
|
+
# : (untyped, Proc) -> Proc
|
|
293
|
+
def wrap_block_around_hooks: (untyped, Proc) -> Proc
|
|
294
|
+
|
|
295
|
+
# Collects around hooks from Hook classes and extension modules (not block-based)
|
|
285
296
|
#
|
|
286
297
|
# : () -> Array[Proc]
|
|
287
|
-
def
|
|
298
|
+
def collect_extension_around_hooks: () -> Array[Proc]
|
|
288
299
|
|
|
289
300
|
# Runs all before hooks
|
|
290
301
|
#
|
|
@@ -296,6 +307,16 @@ module SenroUsecaser
|
|
|
296
307
|
# : (untyped, Result[untyped]) -> void
|
|
297
308
|
def run_after_hooks: (untyped, Result[untyped]) -> void
|
|
298
309
|
|
|
310
|
+
# Returns instantiated hook objects
|
|
311
|
+
#
|
|
312
|
+
# : () -> Array[Hook]
|
|
313
|
+
def hook_instances: () -> Array[Hook]
|
|
314
|
+
|
|
315
|
+
# Checks if the extension is a Hook class
|
|
316
|
+
#
|
|
317
|
+
# : (untyped) -> bool
|
|
318
|
+
def hook_class?: (untyped) -> bool
|
|
319
|
+
|
|
299
320
|
# Resolves dependencies from the container
|
|
300
321
|
#
|
|
301
322
|
# : (Container, Hash[Symbol, untyped]) -> void
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Generated from lib/senro_usecaser/hook.rb with RBS::Inline
|
|
2
|
+
|
|
3
|
+
module SenroUsecaser
|
|
4
|
+
# Base class for hooks with dependency injection support
|
|
5
|
+
#
|
|
6
|
+
# Hook classes provide a way to define before/after/around hooks
|
|
7
|
+
# with access to the DI container and automatic dependency resolution.
|
|
8
|
+
#
|
|
9
|
+
# @example Basic hook
|
|
10
|
+
# class LoggingHook < SenroUsecaser::Hook
|
|
11
|
+
# depends_on :logger, Logger
|
|
12
|
+
#
|
|
13
|
+
# def before(input)
|
|
14
|
+
# logger.info("Starting with #{input.class.name}")
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# def after(input, result)
|
|
18
|
+
# logger.info("Finished: #{result.success? ? 'success' : 'failure'}")
|
|
19
|
+
# end
|
|
20
|
+
# end
|
|
21
|
+
#
|
|
22
|
+
# @example Hook with namespace
|
|
23
|
+
# class Admin::AuditHook < SenroUsecaser::Hook
|
|
24
|
+
# namespace :admin
|
|
25
|
+
# depends_on :audit_logger, AuditLogger
|
|
26
|
+
#
|
|
27
|
+
# def after(input, result)
|
|
28
|
+
# audit_logger.log(input: input, result: result)
|
|
29
|
+
# end
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
# @example Hook with around
|
|
33
|
+
# class TransactionHook < SenroUsecaser::Hook
|
|
34
|
+
# def around(input)
|
|
35
|
+
# ActiveRecord::Base.transaction { yield }
|
|
36
|
+
# end
|
|
37
|
+
# end
|
|
38
|
+
class Hook
|
|
39
|
+
# Declares a dependency to be injected from the container
|
|
40
|
+
#
|
|
41
|
+
# : (Symbol, ?Class) -> void
|
|
42
|
+
def self.depends_on: (Symbol, ?Class) -> void
|
|
43
|
+
|
|
44
|
+
# Returns the list of declared dependencies
|
|
45
|
+
#
|
|
46
|
+
# : () -> Array[Symbol]
|
|
47
|
+
def self.dependencies: () -> Array[Symbol]
|
|
48
|
+
|
|
49
|
+
# Returns the dependency type mapping
|
|
50
|
+
#
|
|
51
|
+
# : () -> Hash[Symbol, Class]
|
|
52
|
+
def self.dependency_types: () -> Hash[Symbol, Class]
|
|
53
|
+
|
|
54
|
+
# Sets or returns the namespace for dependency resolution
|
|
55
|
+
#
|
|
56
|
+
# : (?(Symbol | String)) -> (Symbol | String)?
|
|
57
|
+
def self.namespace: (?Symbol | String) -> (Symbol | String)?
|
|
58
|
+
|
|
59
|
+
# Alias for namespace() without arguments
|
|
60
|
+
#
|
|
61
|
+
# : () -> (Symbol | String)?
|
|
62
|
+
def self.hook_namespace: () -> (Symbol | String)?
|
|
63
|
+
|
|
64
|
+
# @api private
|
|
65
|
+
def self.inherited: (untyped subclass) -> untyped
|
|
66
|
+
|
|
67
|
+
# Initializes the hook with dependencies resolved from the container
|
|
68
|
+
#
|
|
69
|
+
# : (container: Container, ?use_case_namespace: (Symbol | String)?) -> void
|
|
70
|
+
def initialize: (container: Container, ?use_case_namespace: (Symbol | String)?) -> void
|
|
71
|
+
|
|
72
|
+
# Called before the UseCase executes
|
|
73
|
+
# Override in subclass to add before logic
|
|
74
|
+
#
|
|
75
|
+
# : (untyped) -> void
|
|
76
|
+
def before: (untyped) -> void
|
|
77
|
+
|
|
78
|
+
# Called after the UseCase executes
|
|
79
|
+
# Override in subclass to add after logic
|
|
80
|
+
#
|
|
81
|
+
# : (untyped, Result[untyped]) -> void
|
|
82
|
+
def after: (untyped, Result[untyped]) -> void
|
|
83
|
+
|
|
84
|
+
# Wraps the UseCase execution
|
|
85
|
+
# Override in subclass to add around logic
|
|
86
|
+
#
|
|
87
|
+
# : (untyped) { () -> Result[untyped] } -> Result[untyped]
|
|
88
|
+
def around: (untyped) { () -> Result[untyped] } -> Result[untyped]
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
# Returns the effective namespace for dependency resolution
|
|
93
|
+
#
|
|
94
|
+
# : () -> (Symbol | String)?
|
|
95
|
+
def effective_namespace: () -> (Symbol | String)?
|
|
96
|
+
|
|
97
|
+
# Infers namespace from the class's module structure
|
|
98
|
+
#
|
|
99
|
+
# : () -> String?
|
|
100
|
+
def infer_namespace_from_class: () -> String?
|
|
101
|
+
|
|
102
|
+
# Resolves dependencies from the container
|
|
103
|
+
#
|
|
104
|
+
# : () -> void
|
|
105
|
+
def resolve_dependencies: () -> void
|
|
106
|
+
|
|
107
|
+
# Resolves a single dependency from the container
|
|
108
|
+
#
|
|
109
|
+
# : (Symbol) -> untyped
|
|
110
|
+
def resolve_from_container: (Symbol) -> untyped
|
|
111
|
+
end
|
|
112
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: senro_usecaser
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Shogo Kawahara
|
|
@@ -22,6 +22,7 @@ extra_rdoc_files: []
|
|
|
22
22
|
files:
|
|
23
23
|
- ".rspec"
|
|
24
24
|
- ".rubocop.yml"
|
|
25
|
+
- CHANGELOG.md
|
|
25
26
|
- LICENSE
|
|
26
27
|
- README.md
|
|
27
28
|
- Rakefile
|
|
@@ -37,6 +38,7 @@ files:
|
|
|
37
38
|
- lib/senro_usecaser/configuration.rb
|
|
38
39
|
- lib/senro_usecaser/container.rb
|
|
39
40
|
- lib/senro_usecaser/error.rb
|
|
41
|
+
- lib/senro_usecaser/hook.rb
|
|
40
42
|
- lib/senro_usecaser/provider.rb
|
|
41
43
|
- lib/senro_usecaser/result.rb
|
|
42
44
|
- lib/senro_usecaser/version.rb
|
|
@@ -45,6 +47,7 @@ files:
|
|
|
45
47
|
- sig/generated/senro_usecaser/configuration.rbs
|
|
46
48
|
- sig/generated/senro_usecaser/container.rbs
|
|
47
49
|
- sig/generated/senro_usecaser/error.rbs
|
|
50
|
+
- sig/generated/senro_usecaser/hook.rbs
|
|
48
51
|
- sig/generated/senro_usecaser/provider.rbs
|
|
49
52
|
- sig/generated/senro_usecaser/result.rbs
|
|
50
53
|
- sig/generated/senro_usecaser/version.rbs
|