mediate 0.1.2 → 0.1.3
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/Gemfile.lock +1 -1
- data/README.md +51 -3
- data/lib/mediate/mediator.rb +19 -18
- data/lib/mediate/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6bf88906a3d562b832c1581d5775d2f6ae2b5a7d93b59166882edec77217dde5
|
|
4
|
+
data.tar.gz: 0c49d3981d01e37c29e3276672e2527a2fb61a67d9f17d6d51a48a0247ebffcb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: efe642b92ac4763623c79af4bdc79b330544eb320e07075061ba5cec489b98cc5af62f07de4baaf34435c13ebbeedddd91611b6b137e7811bfe79d091b84d7c4
|
|
7
|
+
data.tar.gz: 34e033244ffcd8438060c5d6c7e05046a572b5583047b75f3438323f10e9c774d5a140066a6d7911e8da43fc01ebb52354fee7d60a9f5fbf951517aa58237d79
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Mediate
|
|
2
2
|
|
|
3
|
+
[](https://github.com/rferg/mediate/actions/workflows/ci.yml) [](https://badge.fury.io/rb/mediate)
|
|
4
|
+
|
|
3
5
|
A simple mediator implementation for Ruby inspired by [Mediatr](https://github.com/jbogard/MediatR).
|
|
4
6
|
|
|
5
7
|
Decouple application components by sending a request through the mediator and receiving a response from a handler, instead of directly calling methods on imported classes.
|
|
@@ -17,6 +19,8 @@ Supports request/response, notifications (i.e., events), pre- and post-request h
|
|
|
17
19
|
- [Testing](#testing)
|
|
18
20
|
- [Testing implicit request handlers](#testing-implicit-request-handlers)
|
|
19
21
|
- [Using with Rails](#using-with-rails)
|
|
22
|
+
- [Nest request handler definitions within request classes](#nest-request-handler-definitions-within-request-classes)
|
|
23
|
+
- [Configure Rails to eager load other handlers in non-production environments](#configure-rails-to-eager-load-other-handlers-in-non-production-environments)
|
|
20
24
|
- [Development](#development)
|
|
21
25
|
- [Contributing](#contributing)
|
|
22
26
|
- [License](#license)
|
|
@@ -256,9 +260,9 @@ How can you test a request handler defined using `handle_with` and a lambda like
|
|
|
256
260
|
|
|
257
261
|
```ruby
|
|
258
262
|
class ExampleRequest < Mediate::Request
|
|
259
|
-
handle_with lambda
|
|
263
|
+
handle_with lambda { |request|
|
|
260
264
|
# ....
|
|
261
|
-
|
|
265
|
+
}
|
|
262
266
|
end
|
|
263
267
|
```
|
|
264
268
|
|
|
@@ -276,7 +280,51 @@ end
|
|
|
276
280
|
|
|
277
281
|
### Using with Rails
|
|
278
282
|
|
|
279
|
-
|
|
283
|
+
Handler registrations occur within methods called from class definitions. In non-production environments, by default, Rails [lazy loads](https://guides.rubyonrails.org/autoloading_and_reloading_constants.html) constants. Therefore, if handlers are not explicitly referenced, which is typical, their class definitions will not be loaded and they will not be registered as handlers on the mediator.
|
|
284
|
+
|
|
285
|
+
There are two things that need to be done to work around this behavior:
|
|
286
|
+
|
|
287
|
+
#### Nest request handler definitions within request classes
|
|
288
|
+
|
|
289
|
+
Since requests will be explicitly referenced in, say, controllers, we can force their handler constants to load with them by nesting those definitions within the request definitions. For example:
|
|
290
|
+
|
|
291
|
+
```ruby
|
|
292
|
+
class MyRequest < Mediate::Request
|
|
293
|
+
# ...
|
|
294
|
+
class MyRequestHandler < Mediate::RequestHandler
|
|
295
|
+
handles MyRequest
|
|
296
|
+
|
|
297
|
+
def handle(request)
|
|
298
|
+
# ...
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
This has the added benefit of colocating a handler with its request, making it easy to find. This is therefore the recommended way to declare requests and their handlers. [Implicit handler declarations](#implicit-handler-declaration) do this automatically.
|
|
305
|
+
|
|
306
|
+
#### Configure Rails to eager load other handlers in non-production environments
|
|
307
|
+
|
|
308
|
+
For other handlers (pre- and post-request behaviors, error handlers, notification handlers), it is typically either not possible or not practical to nest their declarations within the class definitions they handle. You will have to add configuration to eager load these in environments where global eager loading is turned off. This can be accomplished by adding their file paths to `config.eager_load_paths` in the relevant environment files, like so:
|
|
309
|
+
|
|
310
|
+
```ruby
|
|
311
|
+
# config/environments/development.rb
|
|
312
|
+
|
|
313
|
+
Rails.application.configure do
|
|
314
|
+
# ...
|
|
315
|
+
[
|
|
316
|
+
'app/use_cases/common/**/*.rb',
|
|
317
|
+
'app/use_cases/**/event_handlers/**/*.rb'
|
|
318
|
+
].each do |path|
|
|
319
|
+
config.eager_load_paths += Dir[path]
|
|
320
|
+
ActiveSupport::Reloader.to_prepare do
|
|
321
|
+
Dir[path].each { |f| require_dependency("#{Dir.pwd}/#{f}") }
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
end
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
(In the above, we're assuming, for example, that we have pre- and post-request behaviors and error handlers in `app/use_cases/common/` and notification handlers in `app/use_cases/**/event_handlers/**/`.)
|
|
280
328
|
|
|
281
329
|
## Development
|
|
282
330
|
|
data/lib/mediate/mediator.rb
CHANGED
|
@@ -42,12 +42,12 @@ module Mediate
|
|
|
42
42
|
def dispatch(request)
|
|
43
43
|
raise ArgumentError, "request cannot be nil" if request.nil?
|
|
44
44
|
|
|
45
|
-
request_handler = resolve_handler(
|
|
45
|
+
request_handler = resolve_handler(request_handlers, request.class, REQUEST_BASE)
|
|
46
46
|
raise Errors::NoHandlerError, request.class if request_handler == NullHandler
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
run_request_pipeline(request,
|
|
48
|
+
prerequest = collect_by_inheritance(prerequest_behaviors, request.class, REQUEST_BASE)
|
|
49
|
+
postrequest = collect_by_inheritance(postrequest_behaviors, request.class, REQUEST_BASE)
|
|
50
|
+
run_request_pipeline(request, prerequest, request_handler, postrequest)
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
#
|
|
@@ -60,7 +60,7 @@ module Mediate
|
|
|
60
60
|
def publish(notification)
|
|
61
61
|
raise ArgumentError, "notification cannot be nil" if notification.nil?
|
|
62
62
|
|
|
63
|
-
handler_classes = collect_by_inheritance(
|
|
63
|
+
handler_classes = collect_by_inheritance(notification_handlers, notification.class, NOTIF_BASE)
|
|
64
64
|
handler_classes.each do |handler_class|
|
|
65
65
|
handler_class.new.handle(notification)
|
|
66
66
|
rescue StandardError => e
|
|
@@ -73,26 +73,31 @@ module Mediate
|
|
|
73
73
|
def register_request_handler(handler_class, request_class)
|
|
74
74
|
validate_base_class(handler_class, Mediate::RequestHandler)
|
|
75
75
|
validate_base_class(request_class, REQUEST_BASE)
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
request_handlers.compute(request_class) do |old_value|
|
|
77
|
+
unless old_value.nil? || old_value == handler_class
|
|
78
|
+
raise Errors::RequestHandlerAlreadyExistsError.new(request_class, old_value, handler_class)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
handler_class
|
|
82
|
+
end
|
|
78
83
|
end
|
|
79
84
|
|
|
80
85
|
def register_notification_handler(handler_class, notif_class)
|
|
81
86
|
validate_base_class(handler_class, Mediate::NotificationHandler)
|
|
82
87
|
validate_base_class(notif_class, NOTIF_BASE, allow_base: true)
|
|
83
|
-
append_to_hash_value(
|
|
88
|
+
append_to_hash_value(notification_handlers, notif_class, handler_class)
|
|
84
89
|
end
|
|
85
90
|
|
|
86
91
|
def register_prerequest_behavior(behavior_class, request_class)
|
|
87
92
|
validate_base_class(behavior_class, Mediate::PrerequestBehavior)
|
|
88
93
|
validate_base_class(request_class, REQUEST_BASE, allow_base: true)
|
|
89
|
-
append_to_hash_value(
|
|
94
|
+
append_to_hash_value(prerequest_behaviors, request_class, behavior_class)
|
|
90
95
|
end
|
|
91
96
|
|
|
92
97
|
def register_postrequest_behavior(behavior_class, request_class)
|
|
93
98
|
validate_base_class(behavior_class, Mediate::PostrequestBehavior)
|
|
94
99
|
validate_base_class(request_class, REQUEST_BASE, allow_base: true)
|
|
95
|
-
append_to_hash_value(
|
|
100
|
+
append_to_hash_value(postrequest_behaviors, request_class, behavior_class)
|
|
96
101
|
end
|
|
97
102
|
|
|
98
103
|
def register_error_handler(handler_class, exception_class, dispatch_class)
|
|
@@ -119,18 +124,14 @@ module Mediate
|
|
|
119
124
|
|
|
120
125
|
private
|
|
121
126
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
return if registered.nil? || registered == new_handler
|
|
125
|
-
|
|
126
|
-
raise Errors::RequestHandlerAlreadyExistsError.new(request_class, registered, new_handler)
|
|
127
|
-
end
|
|
127
|
+
attr_reader :exception_handlers, :postrequest_behaviors, :prerequest_behaviors, :notification_handlers,
|
|
128
|
+
:request_handlers
|
|
128
129
|
|
|
129
130
|
def register_error_handler_for_dispatch(handler_class, exception_class, dispatch_class, dispatch_base_class)
|
|
130
131
|
validate_base_class(handler_class, Mediate::ErrorHandler)
|
|
131
132
|
validate_base_class(exception_class, StandardError, allow_base: true)
|
|
132
133
|
validate_base_class(dispatch_class, dispatch_base_class, allow_base: true)
|
|
133
|
-
map =
|
|
134
|
+
map = exception_handlers.fetch_or_store(exception_class, Concurrent::Map.new)
|
|
134
135
|
append_to_hash_value(map, dispatch_class, handler_class)
|
|
135
136
|
end
|
|
136
137
|
|
|
@@ -157,7 +158,7 @@ module Mediate
|
|
|
157
158
|
end
|
|
158
159
|
|
|
159
160
|
def handle_exception(dispatched, exception, dispatch_base_class)
|
|
160
|
-
exception_to_dispatched_maps = collect_by_inheritance(
|
|
161
|
+
exception_to_dispatched_maps = collect_by_inheritance(exception_handlers, exception.class, StandardError)
|
|
161
162
|
handler_classes = exception_to_dispatched_maps.reduce(Concurrent::Set.new) do |memo, curr|
|
|
162
163
|
collect_by_inheritance(curr, dispatched.class, dispatch_base_class, memo)
|
|
163
164
|
end
|
data/lib/mediate/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mediate
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ryan Ferguson
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2023-06-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: concurrent-ruby
|