event_engine-event_definition 0.2.0 → 0.3.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 +32 -0
- data/README.md +82 -7
- data/docs/examples.md +1 -1
- data/lib/event_engine/definition/event_schema.rb +82 -0
- data/lib/event_engine/definition/schema_registry.rb +73 -0
- data/lib/event_engine/definition/version.rb +1 -1
- data/lib/event_engine/definition.rb +19 -2
- data/lib/event_engine/domain_pack_build.rb +2 -0
- data/lib/event_engine/dsl_compiler.rb +1 -1
- data/lib/event_engine/event_engine_helpers_writer.rb +10 -2
- data/lib/tasks/event_definition.rake +20 -0
- metadata +5 -5
- data/lib/event_engine/event_schema.rb +0 -80
- data/lib/event_engine/schema_registry.rb +0 -71
- data/lib/tasks/event_engine_definition.rake +0 -19
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0a8a55e00106d9d21d704bc1efff1e19e08e7ffde3f294b54b373a92e6725db7
|
|
4
|
+
data.tar.gz: 66cdbbc7a48f66eba5be87ac226a6de151133e9b6efdfa0e147315845d5bba64
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 734d64cce2925dafa21b91d90998fd9b94113a620a526b89d682c90c617715d9bf1f6159cd7a525d10676333ff86570990837324d8a7af2b6a3d32ac5134b15a
|
|
7
|
+
data.tar.gz: 8e0a63cc27fd94b3bd6aeec2230e59b3d44d9e9c57b2e9e4880e9145817530332cd41293a3258c55eb8c9f2784e565074f15449e4467d1e8d599da267e93e440
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,38 @@ All notable changes to this gem are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [0.3.0] - 2026-07-29
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Pack self-registration. A generated pack calls
|
|
13
|
+
`EventEngine::Definition.register_pack(self)` when required, so consumers can
|
|
14
|
+
discover every pack's `schema.json` instead of being handed each path by
|
|
15
|
+
configuration. Exposed as `Definition.packs` and `Definition.pack_schema_paths`.
|
|
16
|
+
- The generate task reports the files it wrote.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **Breaking.** `event_engine:definition:dump` is now `event_definition:generate`,
|
|
21
|
+
and `tasks/event_engine_definition.rake` is now `tasks/event_definition.rake`.
|
|
22
|
+
Packs must update the `load` line in their Rakefile. "dump" did not say what the
|
|
23
|
+
task produces, and the `event_engine:` prefix implied a dependency on the runtime
|
|
24
|
+
that this gem does not have.
|
|
25
|
+
- **Breaking.** `EventEngine::SchemaRegistry` and `EventEngine::EventSchema` are now
|
|
26
|
+
`EventEngine::Definition::SchemaRegistry` and `EventEngine::Definition::EventSchema`.
|
|
27
|
+
Both previously sat at the top level and shadowed the `event_engine` runtime's own
|
|
28
|
+
classes of the same name on the same require path, so in any app installing both
|
|
29
|
+
gems the runtime silently operated on this gem's classes instead of its own.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- Generating a pack into a path whose directory did not exist raised
|
|
34
|
+
`Errno::ENOENT`. The directory is now created.
|
|
35
|
+
- The README recommended generating into `lib/generated/`, which Rails 7.1+
|
|
36
|
+
autoloads. Zeitwerk expects that file to define `Generated::MarketingEvents`, so an
|
|
37
|
+
app following the README booted in development and raised on eager load in
|
|
38
|
+
production. Documented how to keep the helper out of the autoload paths.
|
|
39
|
+
|
|
8
40
|
## [0.2.0] - 2026-07-20
|
|
9
41
|
|
|
10
42
|
First published release of `event_engine-event_definition`, the plain-Ruby
|
data/README.md
CHANGED
|
@@ -70,7 +70,7 @@ The helper doesn't exist until you generate it. `event_engine-event_definition`
|
|
|
70
70
|
```ruby
|
|
71
71
|
# Rakefile (in your domain pack)
|
|
72
72
|
require "event_engine/definition"
|
|
73
|
-
load "tasks/
|
|
73
|
+
load "tasks/event_definition.rake"
|
|
74
74
|
|
|
75
75
|
EventEngine::Definition.configure do |config|
|
|
76
76
|
config.definitions_path = "app/event_definitions" # where your EventDefinitions live
|
|
@@ -80,7 +80,7 @@ end
|
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
```sh
|
|
83
|
-
$ rake
|
|
83
|
+
$ rake event_definition:generate
|
|
84
84
|
```
|
|
85
85
|
|
|
86
86
|
The task loads every definition under `definitions_path` — **no Rails required** — and writes two files you commit:
|
|
@@ -90,6 +90,69 @@ The task loads every definition under `definitions_path` — **no Rails required
|
|
|
90
90
|
|
|
91
91
|
This is a build-time step; your app never runs it while serving requests. If your events declare a `subject`, register them too: `config.subject_registry = EventEngine::SubjectRegistry.define { subject :lead }`.
|
|
92
92
|
|
|
93
|
+
#### Generating inside a Rails app
|
|
94
|
+
|
|
95
|
+
The path above is right for a **pack gem**, whose `lib/` a host never autoloads. If
|
|
96
|
+
you generate into a **Rails app** instead, keep the helper out of the autoload paths.
|
|
97
|
+
Rails 7.1+ autoloads `lib` by default, so Zeitwerk would expect
|
|
98
|
+
`lib/generated/marketing_events.rb` to define `Generated::MarketingEvents` and raise
|
|
99
|
+
on eager load — which means the app boots in development and fails in production:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
Zeitwerk::NameError: expected file lib/generated/marketing_events.rb
|
|
103
|
+
to define constant Generated::MarketingEvents
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Add the directory to the ignore list:
|
|
107
|
+
|
|
108
|
+
```ruby
|
|
109
|
+
# config/application.rb
|
|
110
|
+
config.autoload_lib(ignore: %w[assets tasks generated])
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The helper is meant to be required explicitly, not autoloaded — it registers the pack
|
|
114
|
+
on load, so it needs to be required once at boot:
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
# config/initializers/event_engine.rb
|
|
118
|
+
require Rails.root.join("lib/generated/marketing_events")
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
The generated helper looks like this — note that it registers itself:
|
|
122
|
+
|
|
123
|
+
```ruby
|
|
124
|
+
require "event_engine/definition"
|
|
125
|
+
|
|
126
|
+
module MarketingEvents
|
|
127
|
+
def self.schema_path
|
|
128
|
+
File.expand_path("schema.json", __dir__)
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def self.lead_created(lead:, event_version: nil, occurred_at: nil, ...)
|
|
132
|
+
EventEngine::Definition.publisher.publish(
|
|
133
|
+
:lead_created, domain: :marketing, inputs: { lead: lead }, ...
|
|
134
|
+
)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
EventEngine::Definition.register_pack(self)
|
|
138
|
+
end
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Pack self-registration
|
|
142
|
+
|
|
143
|
+
Requiring a generated pack registers it, so consumers can discover every pack's
|
|
144
|
+
schema without being told about each one:
|
|
145
|
+
|
|
146
|
+
```ruby
|
|
147
|
+
EventEngine::Definition.packs # => [MarketingEvents, SalesEvents]
|
|
148
|
+
EventEngine::Definition.pack_schema_paths # => ["/…/marketing/schema.json", "/…/sales/schema.json"]
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
This is what lets `event_engine`'s catalog task find your events with no
|
|
152
|
+
per-pack configuration. `register_pack` is idempotent, so requiring a pack more
|
|
153
|
+
than once is safe. `EventEngine::Definition.reset_packs!` clears the registry
|
|
154
|
+
(useful in tests).
|
|
155
|
+
|
|
93
156
|
### Emit the event (at runtime)
|
|
94
157
|
|
|
95
158
|
With the helper generated, producing the event anywhere in your app is one call. You hand it the whole `lead`; the contract decides what is captured off it:
|
|
@@ -110,10 +173,14 @@ Every `lead_created` event, from anywhere in the app, carries exactly the shape
|
|
|
110
173
|
}
|
|
111
174
|
```
|
|
112
175
|
|
|
113
|
-
> **Where the work happens:** this gem *records* the `from:`/`attr:` mapping and forwards the raw `lead` under `inputs:`. Reading `lead.id`, `lead.email`, … to build that payload is done by the publisher the `event_engine` runtime supplies — see [How it fits](#how-it-fits-with-event_engine).
|
|
176
|
+
> **Where the work happens:** this gem *records* the `from:`/`attr:` mapping and forwards the raw `lead` under `inputs:`. Reading `lead.id`, `lead.email`, … to build that payload is done by the publisher the `event_engine` runtime supplies — see [How it fits](#how-it-fits-with-event_engine).
|
|
177
|
+
>
|
|
178
|
+
> **With `event_engine` installed you wire nothing** — it registers `EventEngine::DefinitionPublisher` at boot and the helper just works.
|
|
179
|
+
>
|
|
180
|
+
> Without it, the default publisher raises `PublisherNotConfigured`. Assign your own — any object with `#publish(event_name, domain:, inputs:, **envelope)`:
|
|
114
181
|
>
|
|
115
182
|
> ```ruby
|
|
116
|
-
> EventEngine::Definition.publisher =
|
|
183
|
+
> EventEngine::Definition.publisher = MyPublisher.new
|
|
117
184
|
> ```
|
|
118
185
|
|
|
119
186
|
### More examples
|
|
@@ -161,7 +228,7 @@ A payload field must also have a `from:` that references a declared input, and e
|
|
|
161
228
|
|
|
162
229
|
### Inspecting the compiled schema
|
|
163
230
|
|
|
164
|
-
Every definition compiles to
|
|
231
|
+
Every definition compiles to an `EventEngine::EventDefinition::Schemas::Schema` value object:
|
|
165
232
|
|
|
166
233
|
```ruby
|
|
167
234
|
schema = LeadCreated.schema
|
|
@@ -186,10 +253,18 @@ EventDefinition DSL registers as the publisher
|
|
|
186
253
|
│ compile ────────────────────►
|
|
187
254
|
▼ reads the raw inputs via the
|
|
188
255
|
generated helper ──publish(event)──► schema's from:/attr: mapping,
|
|
189
|
-
+ committed schema.json then
|
|
256
|
+
+ committed schema.json then routes it to a processor
|
|
190
257
|
```
|
|
191
258
|
|
|
192
|
-
A domain pack depends only on `event_engine-event_definition` to declare its events and build its helper file. In an app that also has `event_engine` installed, `event_engine`
|
|
259
|
+
A domain pack depends only on `event_engine-event_definition` to declare its events and build its helper file. In an app that also has `event_engine` installed, `event_engine` assigns `EventEngine::DefinitionPublisher` to `EventEngine::Definition.publisher` **at boot**, so calling a generated helper hands the event to the full runtime with no wiring in the host.
|
|
260
|
+
|
|
261
|
+
Nothing in this gem knows how events are processed — that decision lives entirely in `event_engine`, declared in its rules file. A definition describes *what an event carries*, never *what happens to it*.
|
|
262
|
+
|
|
263
|
+
### Namespacing
|
|
264
|
+
|
|
265
|
+
Public API sits at `EventEngine::EventDefinition` (the DSL you subclass) and `EventEngine::Definition` (configuration, the publisher port, the pack registry).
|
|
266
|
+
|
|
267
|
+
This gem's internals live under `EventEngine::Definition::*` — `Definition::SchemaRegistry`, `Definition::EventSchema` — deliberately, so they do not collide with `event_engine`'s own classes of the same name. Both gems are loaded into one `EventEngine` namespace in a host, and a shared require path means whichever gem wins `$LOAD_PATH` silently shadows the other.
|
|
193
268
|
|
|
194
269
|
## Development
|
|
195
270
|
|
data/docs/examples.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Each example follows the same three beats: the **data** you already have, the **event** you define, and the **call** you make. For the full DSL reference and how this plugs into `event_engine`, see the [README](../README.md).
|
|
4
4
|
|
|
5
|
-
The `SalesEvents` / `ProductEvents` modules below are the helpers produced by generating the pack — the `rake
|
|
5
|
+
The `SalesEvents` / `ProductEvents` modules below are the helpers produced by generating the pack — the `rake event_definition:generate` build step in the README. These examples assume that step has already run, so they focus on defining the event and calling it.
|
|
6
6
|
|
|
7
7
|
## An event built from multiple inputs
|
|
8
8
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
module EventEngine
|
|
2
|
+
module Definition
|
|
3
|
+
class EventSchema
|
|
4
|
+
class DuplicateEventNameError < StandardError; end
|
|
5
|
+
|
|
6
|
+
def self.define(&block)
|
|
7
|
+
schema = new
|
|
8
|
+
block.call(schema)
|
|
9
|
+
schema
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@schemas_by_event = {}
|
|
14
|
+
@finalized = false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def register(schema)
|
|
18
|
+
raise FrozenError, "EventSchema is finalized" if @finalized
|
|
19
|
+
key = key_for(schema.domain, schema.event_name)
|
|
20
|
+
version = schema.event_version
|
|
21
|
+
|
|
22
|
+
@schemas_by_event[key] ||= {}
|
|
23
|
+
guard_duplicate_event_name!(@schemas_by_event[key][version], schema)
|
|
24
|
+
@schemas_by_event[key][version] = schema
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def guard_duplicate_event_name!(existing, incoming)
|
|
28
|
+
return unless existing
|
|
29
|
+
|
|
30
|
+
raise DuplicateEventNameError,
|
|
31
|
+
"duplicate (domain, event_name) " \
|
|
32
|
+
"(#{incoming.domain.inspect}, #{incoming.event_name.inspect}): " \
|
|
33
|
+
"already registered at version #{existing.event_version.inspect}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def events(domain: nil)
|
|
37
|
+
@schemas_by_event.keys
|
|
38
|
+
.select { |(schema_domain, _name)| domain.nil? || schema_domain == domain }
|
|
39
|
+
.map { |(_domain, event_name)| event_name }
|
|
40
|
+
.uniq
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def versions_for(event_name, domain: nil)
|
|
44
|
+
version_sets_for(event_name, domain).flat_map(&:keys).uniq.sort
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def schema_for(event_name, version, domain: nil)
|
|
48
|
+
set = version_sets_for(event_name, domain).find { |versions| versions.key?(version) }
|
|
49
|
+
set && set[version]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def latest_for(event_name, domain: nil)
|
|
53
|
+
merged = version_sets_for(event_name, domain).reduce({}, :merge)
|
|
54
|
+
return nil if merged.empty?
|
|
55
|
+
merged[merged.keys.max]
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def finalize!
|
|
59
|
+
@finalized = true
|
|
60
|
+
@schemas_by_event.each_value(&:freeze)
|
|
61
|
+
@schemas_by_event.freeze
|
|
62
|
+
freeze
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def schemas_by_event
|
|
66
|
+
@schemas_by_event
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private
|
|
70
|
+
|
|
71
|
+
def key_for(domain, event_name)
|
|
72
|
+
[domain, event_name]
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def version_sets_for(event_name, domain = nil)
|
|
76
|
+
@schemas_by_event.select do |(schema_domain, name), _versions|
|
|
77
|
+
name == event_name && (domain.nil? || schema_domain == domain)
|
|
78
|
+
end.values
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module EventEngine
|
|
2
|
+
module Definition
|
|
3
|
+
class SchemaRegistry
|
|
4
|
+
class UnknownEventError < StandardError; end
|
|
5
|
+
|
|
6
|
+
class RegistryFrozenError < StandardError; end
|
|
7
|
+
|
|
8
|
+
def initialize(event_schema = EventSchema.new)
|
|
9
|
+
@event_schema = event_schema
|
|
10
|
+
@loaded = false
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def register(schema)
|
|
14
|
+
@event_schema.register(schema)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def events
|
|
18
|
+
@event_schema.events
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def versions_for(event_name, domain: nil)
|
|
22
|
+
@event_schema.versions_for(event_name, domain: domain)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def load_from_schema!(schema)
|
|
26
|
+
raise RegistryFrozenError, "EventRegistry already loaded" if loaded?
|
|
27
|
+
@event_schema = schema
|
|
28
|
+
|
|
29
|
+
@loaded = true
|
|
30
|
+
self
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def reset!
|
|
34
|
+
@event_schema = {}
|
|
35
|
+
@loaded = false
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def schema(event_name, version: nil, domain: nil)
|
|
39
|
+
raise RegistryFrozenError, "EventRegistry not loaded" unless loaded?
|
|
40
|
+
|
|
41
|
+
schema =
|
|
42
|
+
if version
|
|
43
|
+
@event_schema.schema_for(event_name, version, domain: domain)
|
|
44
|
+
else
|
|
45
|
+
@event_schema.latest_for(event_name, domain: domain)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
unless schema
|
|
49
|
+
raise UnknownEventError,
|
|
50
|
+
"Unknown #{version ? "version #{version} for " : ""}event: #{event_name}"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
schema
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def latest_for(event_name, domain: nil)
|
|
57
|
+
@event_schema.latest_for(event_name, domain: domain)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def event_schema
|
|
61
|
+
@event_schema
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def finalize!
|
|
65
|
+
@event_schema.finalize!
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def loaded?
|
|
69
|
+
@loaded == true
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -5,8 +5,8 @@ require_relative "definition/version"
|
|
|
5
5
|
require "event_engine/subject_registry"
|
|
6
6
|
require "event_engine/event_definition"
|
|
7
7
|
require "event_engine/definition_loader"
|
|
8
|
-
require "event_engine/event_schema"
|
|
9
|
-
require "event_engine/schema_registry"
|
|
8
|
+
require "event_engine/definition/event_schema"
|
|
9
|
+
require "event_engine/definition/schema_registry"
|
|
10
10
|
require "event_engine/lifecycle_definition"
|
|
11
11
|
require "event_engine/dsl_compiler"
|
|
12
12
|
require "event_engine/definition/configuration"
|
|
@@ -26,6 +26,23 @@ module EventEngine
|
|
|
26
26
|
@publisher = nil
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
def packs
|
|
30
|
+
@packs ||= []
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def register_pack(pack)
|
|
34
|
+
packs << pack unless packs.include?(pack)
|
|
35
|
+
pack
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def pack_schema_paths
|
|
39
|
+
packs.map(&:schema_path)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def reset_packs!
|
|
43
|
+
@packs = nil
|
|
44
|
+
end
|
|
45
|
+
|
|
29
46
|
def configuration
|
|
30
47
|
@configuration ||= Configuration.new
|
|
31
48
|
end
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "json"
|
|
4
|
+
require "fileutils"
|
|
4
5
|
require "event_engine/dsl_compiler"
|
|
5
6
|
require "event_engine/subject_registry"
|
|
6
7
|
require "event_engine/event_engine_helpers_writer"
|
|
@@ -39,6 +40,7 @@ module EventEngine
|
|
|
39
40
|
|
|
40
41
|
def run
|
|
41
42
|
event_schema = compile
|
|
43
|
+
FileUtils.mkdir_p(File.dirname(@helper_path))
|
|
42
44
|
write_helper(event_schema)
|
|
43
45
|
write_schema_json(event_schema)
|
|
44
46
|
self
|
|
@@ -16,7 +16,7 @@ module EventEngine
|
|
|
16
16
|
].freeze
|
|
17
17
|
|
|
18
18
|
def self.compile(definitions, subject_registry: SubjectRegistry.new)
|
|
19
|
-
registry = SchemaRegistry.new
|
|
19
|
+
registry = Definition::SchemaRegistry.new
|
|
20
20
|
subject_violations = []
|
|
21
21
|
name_violations = []
|
|
22
22
|
reserved_violations = []
|
|
@@ -24,9 +24,17 @@ module EventEngine
|
|
|
24
24
|
def self.generate(event_schema, root_module: "EventEngine", emit: "EventEngine.emit",
|
|
25
25
|
header: HEADER, group_by_domain: true, schema_filename: nil)
|
|
26
26
|
body = group_by_domain ? grouped_body(event_schema, emit) : flat_body(event_schema, emit)
|
|
27
|
-
body = "#{schema_path_accessor(schema_filename)}#{body}" if schema_filename
|
|
27
|
+
body = "#{schema_path_accessor(schema_filename)}#{body}#{pack_registration}" if schema_filename
|
|
28
28
|
|
|
29
|
-
"#{header}module #{root_module}\n#{body}end\n"
|
|
29
|
+
"#{header}#{pack_require if schema_filename}module #{root_module}\n#{body}end\n"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def self.pack_require
|
|
33
|
+
%(require "event_engine/definition"\n\n)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.pack_registration
|
|
37
|
+
"\n EventEngine::Definition.register_pack(self)\n"
|
|
30
38
|
end
|
|
31
39
|
|
|
32
40
|
def self.schema_path_accessor(filename)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require "event_engine/definition"
|
|
2
|
+
|
|
3
|
+
namespace :event_definition do
|
|
4
|
+
desc "Generate the pack's helper and schema.json from its EventDefinitions"
|
|
5
|
+
task :generate do
|
|
6
|
+
config = EventEngine::Definition.configuration
|
|
7
|
+
|
|
8
|
+
definitions = EventEngine::DefinitionLoader.load!(config.definitions_path)
|
|
9
|
+
|
|
10
|
+
build = EventEngine::DomainPackBuild.run(
|
|
11
|
+
definitions,
|
|
12
|
+
helper_path: config.helper_path,
|
|
13
|
+
root_module: config.root_module,
|
|
14
|
+
subject_registry: config.subject_registry
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
puts "Wrote #{config.root_module} helper to #{config.helper_path}"
|
|
18
|
+
puts "Wrote #{config.root_module} schema to #{build.schema_path}"
|
|
19
|
+
end
|
|
20
|
+
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: event_engine-event_definition
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- tylercschneider
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-07-
|
|
11
|
+
date: 2026-07-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: 'The plain-Ruby foundation of the EventEngine pipeline: the EventDefinition
|
|
14
14
|
DSL and the shared schema-contract value objects, with no Rails dependency. Lightweight
|
|
@@ -27,7 +27,9 @@ files:
|
|
|
27
27
|
- docs/examples.md
|
|
28
28
|
- lib/event_engine/definition.rb
|
|
29
29
|
- lib/event_engine/definition/configuration.rb
|
|
30
|
+
- lib/event_engine/definition/event_schema.rb
|
|
30
31
|
- lib/event_engine/definition/null_publisher.rb
|
|
32
|
+
- lib/event_engine/definition/schema_registry.rb
|
|
31
33
|
- lib/event_engine/definition/version.rb
|
|
32
34
|
- lib/event_engine/definition_loader.rb
|
|
33
35
|
- lib/event_engine/domain_pack_build.rb
|
|
@@ -38,11 +40,9 @@ files:
|
|
|
38
40
|
- lib/event_engine/event_definition/schemas.rb
|
|
39
41
|
- lib/event_engine/event_definition/validation.rb
|
|
40
42
|
- lib/event_engine/event_engine_helpers_writer.rb
|
|
41
|
-
- lib/event_engine/event_schema.rb
|
|
42
43
|
- lib/event_engine/lifecycle_definition.rb
|
|
43
|
-
- lib/event_engine/schema_registry.rb
|
|
44
44
|
- lib/event_engine/subject_registry.rb
|
|
45
|
-
- lib/tasks/
|
|
45
|
+
- lib/tasks/event_definition.rake
|
|
46
46
|
- sig/event_engine/definition.rbs
|
|
47
47
|
homepage: https://eventengine.co
|
|
48
48
|
licenses:
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
module EventEngine
|
|
2
|
-
class EventSchema
|
|
3
|
-
class DuplicateEventNameError < StandardError; end
|
|
4
|
-
|
|
5
|
-
def self.define(&block)
|
|
6
|
-
schema = new
|
|
7
|
-
block.call(schema)
|
|
8
|
-
schema
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def initialize
|
|
12
|
-
@schemas_by_event = {}
|
|
13
|
-
@finalized = false
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def register(schema)
|
|
17
|
-
raise FrozenError, "EventSchema is finalized" if @finalized
|
|
18
|
-
key = key_for(schema.domain, schema.event_name)
|
|
19
|
-
version = schema.event_version
|
|
20
|
-
|
|
21
|
-
@schemas_by_event[key] ||= {}
|
|
22
|
-
guard_duplicate_event_name!(@schemas_by_event[key][version], schema)
|
|
23
|
-
@schemas_by_event[key][version] = schema
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def guard_duplicate_event_name!(existing, incoming)
|
|
27
|
-
return unless existing
|
|
28
|
-
|
|
29
|
-
raise DuplicateEventNameError,
|
|
30
|
-
"duplicate (domain, event_name) " \
|
|
31
|
-
"(#{incoming.domain.inspect}, #{incoming.event_name.inspect}): " \
|
|
32
|
-
"already registered at version #{existing.event_version.inspect}"
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
def events(domain: nil)
|
|
36
|
-
@schemas_by_event.keys
|
|
37
|
-
.select { |(schema_domain, _name)| domain.nil? || schema_domain == domain }
|
|
38
|
-
.map { |(_domain, event_name)| event_name }
|
|
39
|
-
.uniq
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def versions_for(event_name, domain: nil)
|
|
43
|
-
version_sets_for(event_name, domain).flat_map(&:keys).uniq.sort
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def schema_for(event_name, version, domain: nil)
|
|
47
|
-
set = version_sets_for(event_name, domain).find { |versions| versions.key?(version) }
|
|
48
|
-
set && set[version]
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def latest_for(event_name, domain: nil)
|
|
52
|
-
merged = version_sets_for(event_name, domain).reduce({}, :merge)
|
|
53
|
-
return nil if merged.empty?
|
|
54
|
-
merged[merged.keys.max]
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
def finalize!
|
|
58
|
-
@finalized = true
|
|
59
|
-
@schemas_by_event.each_value(&:freeze)
|
|
60
|
-
@schemas_by_event.freeze
|
|
61
|
-
freeze
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def schemas_by_event
|
|
65
|
-
@schemas_by_event
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
private
|
|
69
|
-
|
|
70
|
-
def key_for(domain, event_name)
|
|
71
|
-
[domain, event_name]
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def version_sets_for(event_name, domain = nil)
|
|
75
|
-
@schemas_by_event.select do |(schema_domain, name), _versions|
|
|
76
|
-
name == event_name && (domain.nil? || schema_domain == domain)
|
|
77
|
-
end.values
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
end
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
module EventEngine
|
|
2
|
-
class SchemaRegistry
|
|
3
|
-
class UnknownEventError < StandardError; end
|
|
4
|
-
|
|
5
|
-
class RegistryFrozenError < StandardError; end
|
|
6
|
-
|
|
7
|
-
def initialize(event_schema = EventSchema.new)
|
|
8
|
-
@event_schema = event_schema
|
|
9
|
-
@loaded = false
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def register(schema)
|
|
13
|
-
@event_schema.register(schema)
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def events
|
|
17
|
-
@event_schema.events
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def versions_for(event_name, domain: nil)
|
|
21
|
-
@event_schema.versions_for(event_name, domain: domain)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def load_from_schema!(schema)
|
|
25
|
-
raise RegistryFrozenError, "EventRegistry already loaded" if loaded?
|
|
26
|
-
@event_schema = schema
|
|
27
|
-
|
|
28
|
-
@loaded = true
|
|
29
|
-
self
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def reset!
|
|
33
|
-
@event_schema = {}
|
|
34
|
-
@loaded = false
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def schema(event_name, version: nil, domain: nil)
|
|
38
|
-
raise RegistryFrozenError, "EventRegistry not loaded" unless loaded?
|
|
39
|
-
|
|
40
|
-
schema =
|
|
41
|
-
if version
|
|
42
|
-
@event_schema.schema_for(event_name, version, domain: domain)
|
|
43
|
-
else
|
|
44
|
-
@event_schema.latest_for(event_name, domain: domain)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
unless schema
|
|
48
|
-
raise UnknownEventError,
|
|
49
|
-
"Unknown #{version ? "version #{version} for " : ""}event: #{event_name}"
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
schema
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
def latest_for(event_name, domain: nil)
|
|
56
|
-
@event_schema.latest_for(event_name, domain: domain)
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def event_schema
|
|
60
|
-
@event_schema
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def finalize!
|
|
64
|
-
@event_schema.finalize!
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
def loaded?
|
|
68
|
-
@loaded == true
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
end
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
require "event_engine/definition"
|
|
2
|
-
|
|
3
|
-
namespace :event_engine do
|
|
4
|
-
namespace :definition do
|
|
5
|
-
desc "Generate the pack's helper and schema.json from its EventDefinitions"
|
|
6
|
-
task :dump do
|
|
7
|
-
config = EventEngine::Definition.configuration
|
|
8
|
-
|
|
9
|
-
definitions = EventEngine::DefinitionLoader.load!(config.definitions_path)
|
|
10
|
-
|
|
11
|
-
EventEngine::DomainPackBuild.run(
|
|
12
|
-
definitions,
|
|
13
|
-
helper_path: config.helper_path,
|
|
14
|
-
root_module: config.root_module,
|
|
15
|
-
subject_registry: config.subject_registry
|
|
16
|
-
)
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
end
|