event_engine-event_definition 0.2.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d4fd0fd21eecd6b764d2c91c230bff8733c055148918fdcc3dc027b80cecddb3
4
- data.tar.gz: 39a989288b99eb7fbe86777f787c357bfedee66e36f1800ce9432915ec92ea93
3
+ metadata.gz: d2f15e08e449f4492d0231c96e10abbfa83de7b00debf51dc42fa87944145f81
4
+ data.tar.gz: 070cb9065ee0f9528b8fb2a45374fbcad317a90ada0971cc8dcf1616f794a507
5
5
  SHA512:
6
- metadata.gz: 0b245cffa43807384cc64d41ae50cca49a967636dd08156766ba5af768dc2c456191bc9eadf886cac8db40c1d57ede617275124fa4da4f906998b01d988b2755
7
- data.tar.gz: e1c634fe59d2e0afe642c65057939c3123513586ad1402e273bcb1352e38e630148d51a148f9404b537240c5fd55443548d7bd485c755c57d2964857387e420e
6
+ metadata.gz: d6af9ac96261bbd8ea3342b8daa05c830543cc3bc1f0d6dcd5fb98ab7b2db7fe9e7cb831d7c7687e4ada1157656245a7db0ffb82aa0c00b9d0056b57145560e7
7
+ data.tar.gz: d2c72bd2e7b6c1549911b276746c3910e24dad7e0919ea81fffd2a1a892948d8d28eb7f5a3ecb96aff5095a53c8533fba979ae23ca732ab989da10bc456705a0
@@ -0,0 +1,48 @@
1
+ ---
2
+ name: the_local-develop
3
+ description: Use PROACTIVELY to author a gem's locals — declaring its public interface and running the authoring task — MUST BE USED instead of hand-writing a local.
4
+ tools: Read, Write, Edit, Grep
5
+ scope: resident Claude Code experts — authoring a gem's locals and installing them into a host
6
+ ---
7
+
8
+ You author a gem's locals by declaring its interface and running the authoring
9
+ task. You do not hand-write locals and you never read the_local's source. A
10
+ provider carries no Ruby for the_local — a manifest and three committed files.
11
+
12
+ ## What the_local is
13
+
14
+ The engine that installs gems' resident Claude Code locals into a host. Reach for
15
+ this local whenever a gem should contribute locals, or when a change to its public
16
+ interface may have made its locals stale.
17
+
18
+ ## Interface
19
+
20
+ - `rake the_local:author` — writes the gem's locals into `the_local/agents/` from
21
+ its current source, one at a time, guided by the manifest.
22
+ - `rake the_local:check` — verifies the committed locals against the manifest:
23
+ every declared entry point documented, nothing undeclared, nothing documented by
24
+ the wrong local.
25
+
26
+ ## How to use it
27
+
28
+ 1. Write `the_local/interface.yml` with the developer. It declares `scope`, the
29
+ entry points under `install` and `develop`, and the `sources` that define them.
30
+ This is the one judgment call in the process — ask which commands are the gem's
31
+ public surface rather than guessing, and confirm which of the two each belongs
32
+ to. An entry point may appear under exactly one.
33
+ 2. Run `rake the_local:author`. It writes `the_local/agents/<gem>-{info,install,develop}.md`.
34
+ 3. Run `rake the_local:check` and fix what it reports.
35
+ 4. Commit `the_local/`. For a packaged gem, confirm `the_local/**/*` is in the
36
+ gemspec's `files`, or it ships nothing.
37
+ 5. After a change to the gem's public interface, update the manifest and repeat.
38
+ An internal-only change needs nothing.
39
+
40
+ ## Conventions
41
+
42
+ - The manifest is the contract. Never widen a local past what it declares; if the
43
+ gem gained a public entry point, declare it first.
44
+ - Locals document the public interface only, never the gem's internals, and never
45
+ send a reader into the provider's source.
46
+ - The three locals never overlap: **install** hooks the gem into a host,
47
+ **develop** uses it, **info** carries what fits neither.
48
+ - Regenerate from current source rather than editing a stale local by hand.
@@ -0,0 +1,40 @@
1
+ ---
2
+ name: the_local-info
3
+ description: Use to learn what the_local offers — resident expert subagents, the provider/consumer model, and the vocabulary the other locals assume.
4
+ tools: Read
5
+ scope: resident Claude Code experts — authoring a gem's locals and installing them into a host
6
+ ---
7
+
8
+ You explain what the_local does, answering only from this reference. You make no
9
+ changes, and you never read the_local's source.
10
+
11
+ ## What the_local is
12
+
13
+ the_local lets any gem ship resident Claude Code expert subagents ("locals") that
14
+ know its conventions. A **provider** gem commits its locals; a **consumer** host
15
+ installs the locals of its direct dependencies into `.claude/agents/`, plus a
16
+ delegation rule so the host's agent uses them.
17
+
18
+ Reach for it when you want a gem's work done consistently — the host delegates
19
+ that gem's tasks to its local instead of re-deriving conventions each time.
20
+
21
+ ## Interface
22
+
23
+ the_local's commands are split across its other two locals, with no overlap.
24
+ Hooking the_local into a project is the install local's; authoring a gem's own
25
+ locals is the develop local's. Route to those rather than answering here.
26
+
27
+ ## How to use it
28
+
29
+ Decide which side you are on. A host that wants its dependencies' expertise is a
30
+ consumer and needs the install local. A gem that wants to contribute expertise is
31
+ a provider and needs the develop local. A gem can be both.
32
+
33
+ ## Conventions
34
+
35
+ - A **local** is one Claude Code subagent that knows one gem's public interface.
36
+ - Each provider ships three: **info** explains, **install** hooks the gem into a
37
+ host, **develop** uses it. A command belongs to exactly one of them.
38
+ - The committed `the_local/agents/*.md` are the whole contract a host reads — a
39
+ host never loads the provider gem.
40
+ - Only a host's **direct** dependencies contribute locals.
@@ -0,0 +1,49 @@
1
+ ---
2
+ name: the_local-install
3
+ description: Use to hook the_local into a gem or Rails app — installing dependencies' locals, the delegation trigger in CLAUDE.md, and the provider rake tasks.
4
+ tools: Bash, Read, Edit
5
+ scope: resident Claude Code experts — authoring a gem's locals and installing them into a host
6
+ ---
7
+
8
+ You hook the_local into the host by following these steps exactly, in order. You
9
+ do not invent steps, and you never read the_local's source.
10
+
11
+ ## What the_local is
12
+
13
+ The engine that installs gems' resident Claude Code locals into a host and writes
14
+ the delegation trigger. Hook it into any gem or app that wants its dependencies'
15
+ locals, or that will contribute locals of its own.
16
+
17
+ ## Interface
18
+
19
+ - `bundle exec the_local install` — installs direct dependencies' locals into
20
+ `.claude/agents/` and writes the trigger. Works anywhere; no Rails required.
21
+ - `bin/rails g the_local:install` — the Rails equivalent of the above.
22
+ - `rake the_local:refresh` — re-syncs a Rails host after a bundle change.
23
+ - `rake the_local:install` — re-syncs a non-Rails host after a bundle change.
24
+ - `bin/rails g the_local:provider` — adds the provider rake tasks to a gem, so it
25
+ can author locals of its own.
26
+
27
+ ## How to use it
28
+
29
+ 1. Add `gem "the_local"` to the host's `Gemfile` and run `bundle install`.
30
+ 2. Install the locals. In a Rails app run `bin/rails g the_local:install`;
31
+ anywhere else run `bundle exec the_local install`. Either copies every direct
32
+ dependency's committed locals into `.claude/agents/` and writes the delegation
33
+ block into `CLAUDE.md`/`AGENTS.md`.
34
+ 3. Tell the developer to restart their Claude Code session — agents load at
35
+ startup, so the new locals are inert until then.
36
+ 4. Re-sync after any bundle change with `rake the_local:refresh` in a Rails app or
37
+ `rake the_local:install` elsewhere.
38
+ 5. Only if the host is a gem that should contribute its own locals, run
39
+ `bin/rails g the_local:provider`. Confirm this with the developer first — it is
40
+ a separate decision from consuming locals, and it edits the Gemfile and Rakefile.
41
+
42
+ ## Conventions
43
+
44
+ - Re-sync after every `bundle install`/`update`, or the host's locals drift from
45
+ its dependencies.
46
+ - Install only reads committed files off disk — a dependency that shipped no
47
+ committed locals contributes nothing, and that is not an error.
48
+ - Hooking up is all this local does. Authoring a gem's own locals is the develop
49
+ local's job.
data/CHANGELOG.md CHANGED
@@ -5,6 +5,47 @@ 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.4.0] - 2026-09-25
9
+
10
+ ### Added
11
+
12
+ - Claude Code locals for this gem, under `the_local/`. A project that uses
13
+ [the_local](https://github.com/DYB-Development/the_local) and depends on this gem
14
+ gets info, install and develop agents that document its public interface when it
15
+ runs `the_local install`.
16
+
17
+ ## [0.3.0] - 2026-07-29
18
+
19
+ ### Added
20
+
21
+ - Pack self-registration. A generated pack calls
22
+ `EventEngine::Definition.register_pack(self)` when required, so consumers can
23
+ discover every pack's `schema.json` instead of being handed each path by
24
+ configuration. Exposed as `Definition.packs` and `Definition.pack_schema_paths`.
25
+ - The generate task reports the files it wrote.
26
+
27
+ ### Changed
28
+
29
+ - **Breaking.** `event_engine:definition:dump` is now `event_definition:generate`,
30
+ and `tasks/event_engine_definition.rake` is now `tasks/event_definition.rake`.
31
+ Packs must update the `load` line in their Rakefile. "dump" did not say what the
32
+ task produces, and the `event_engine:` prefix implied a dependency on the runtime
33
+ that this gem does not have.
34
+ - **Breaking.** `EventEngine::SchemaRegistry` and `EventEngine::EventSchema` are now
35
+ `EventEngine::Definition::SchemaRegistry` and `EventEngine::Definition::EventSchema`.
36
+ Both previously sat at the top level and shadowed the `event_engine` runtime's own
37
+ classes of the same name on the same require path, so in any app installing both
38
+ gems the runtime silently operated on this gem's classes instead of its own.
39
+
40
+ ### Fixed
41
+
42
+ - Generating a pack into a path whose directory did not exist raised
43
+ `Errno::ENOENT`. The directory is now created.
44
+ - The README recommended generating into `lib/generated/`, which Rails 7.1+
45
+ autoloads. Zeitwerk expects that file to define `Generated::MarketingEvents`, so an
46
+ app following the README booted in development and raised on eager load in
47
+ production. Documented how to keep the helper out of the autoload paths.
48
+
8
49
  ## [0.2.0] - 2026-07-20
9
50
 
10
51
  First published release of `event_engine-event_definition`, the plain-Ruby
@@ -25,4 +66,6 @@ event-definition contract for the EventEngine pipeline.
25
66
  - `EventEngine::Definition.configure` for pack-generation settings.
26
67
  - `event_engine:definition:dump` rake task to generate a pack without Rails.
27
68
 
69
+ [0.4.0]: https://github.com/DYB-Development/event_engine-event_definition/releases/tag/v0.4.0
70
+ [0.3.0]: https://github.com/DYB-Development/event_engine-event_definition/releases/tag/v0.3.0
28
71
  [0.2.0]: https://github.com/DYB-Development/event_engine-event_definition/releases/tag/v0.2.0
data/CLAUDE.md ADDED
@@ -0,0 +1,11 @@
1
+ <!-- the_local:begin -->
2
+ ## Delegate to your locals
3
+
4
+ This project has installed expert subagents. Before doing work yourself,
5
+ check whether a local owns it and delegate — never work from memory on
6
+ something a local covers:
7
+
8
+ - resident Claude Code experts — authoring a gem's locals and installing them into a host → the_local-* agents
9
+
10
+ See each agent's description for specifics.
11
+ <!-- the_local:end -->
data/README.md CHANGED
@@ -22,6 +22,13 @@ Then run `bundle install`, and require it:
22
22
  require "event_engine/definition"
23
23
  ```
24
24
 
25
+ ## A working example app
26
+
27
+ [**DYB-Development/event_engine_example**](https://github.com/DYB-Development/event_engine_example)
28
+ is a minimal Rails app that declares events with this gem, compiles them, and
29
+ processes them through the `event_engine` runtime — useful for seeing the build step
30
+ and the generated output in a real project.
31
+
25
32
  ## A worked example
26
33
 
27
34
  ### The data you want to capture
@@ -70,7 +77,7 @@ The helper doesn't exist until you generate it. `event_engine-event_definition`
70
77
  ```ruby
71
78
  # Rakefile (in your domain pack)
72
79
  require "event_engine/definition"
73
- load "tasks/event_engine_definition.rake"
80
+ load "tasks/event_definition.rake"
74
81
 
75
82
  EventEngine::Definition.configure do |config|
76
83
  config.definitions_path = "app/event_definitions" # where your EventDefinitions live
@@ -80,7 +87,7 @@ end
80
87
  ```
81
88
 
82
89
  ```sh
83
- $ rake event_engine:definition:dump
90
+ $ rake event_definition:generate
84
91
  ```
85
92
 
86
93
  The task loads every definition under `definitions_path` — **no Rails required** — and writes two files you commit:
@@ -90,6 +97,69 @@ The task loads every definition under `definitions_path` — **no Rails required
90
97
 
91
98
  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
99
 
100
+ #### Generating inside a Rails app
101
+
102
+ The path above is right for a **pack gem**, whose `lib/` a host never autoloads. If
103
+ you generate into a **Rails app** instead, keep the helper out of the autoload paths.
104
+ Rails 7.1+ autoloads `lib` by default, so Zeitwerk would expect
105
+ `lib/generated/marketing_events.rb` to define `Generated::MarketingEvents` and raise
106
+ on eager load — which means the app boots in development and fails in production:
107
+
108
+ ```
109
+ Zeitwerk::NameError: expected file lib/generated/marketing_events.rb
110
+ to define constant Generated::MarketingEvents
111
+ ```
112
+
113
+ Add the directory to the ignore list:
114
+
115
+ ```ruby
116
+ # config/application.rb
117
+ config.autoload_lib(ignore: %w[assets tasks generated])
118
+ ```
119
+
120
+ The helper is meant to be required explicitly, not autoloaded — it registers the pack
121
+ on load, so it needs to be required once at boot:
122
+
123
+ ```ruby
124
+ # config/initializers/event_engine.rb
125
+ require Rails.root.join("lib/generated/marketing_events")
126
+ ```
127
+
128
+ The generated helper looks like this — note that it registers itself:
129
+
130
+ ```ruby
131
+ require "event_engine/definition"
132
+
133
+ module MarketingEvents
134
+ def self.schema_path
135
+ File.expand_path("schema.json", __dir__)
136
+ end
137
+
138
+ def self.lead_created(lead:, event_version: nil, occurred_at: nil, ...)
139
+ EventEngine::Definition.publisher.publish(
140
+ :lead_created, domain: :marketing, inputs: { lead: lead }, ...
141
+ )
142
+ end
143
+
144
+ EventEngine::Definition.register_pack(self)
145
+ end
146
+ ```
147
+
148
+ ### Pack self-registration
149
+
150
+ Requiring a generated pack registers it, so consumers can discover every pack's
151
+ schema without being told about each one:
152
+
153
+ ```ruby
154
+ EventEngine::Definition.packs # => [MarketingEvents, SalesEvents]
155
+ EventEngine::Definition.pack_schema_paths # => ["/…/marketing/schema.json", "/…/sales/schema.json"]
156
+ ```
157
+
158
+ This is what lets `event_engine`'s catalog task find your events with no
159
+ per-pack configuration. `register_pack` is idempotent, so requiring a pack more
160
+ than once is safe. `EventEngine::Definition.reset_packs!` clears the registry
161
+ (useful in tests).
162
+
93
163
  ### Emit the event (at runtime)
94
164
 
95
165
  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 +180,14 @@ Every `lead_created` event, from anywhere in the app, carries exactly the shape
110
180
  }
111
181
  ```
112
182
 
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). Until one is configured the default publisher raises, so wire it once at boot:
183
+ > **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).
184
+ >
185
+ > **With `event_engine` installed you wire nothing** — it registers `EventEngine::DefinitionPublisher` at boot and the helper just works.
186
+ >
187
+ > Without it, the default publisher raises `PublisherNotConfigured`. Assign your own — any object with `#publish(event_name, domain:, inputs:, **envelope)`:
114
188
  >
115
189
  > ```ruby
116
- > EventEngine::Definition.publisher = my_publisher # any object with #publish(event_name, **envelope)
190
+ > EventEngine::Definition.publisher = MyPublisher.new
117
191
  > ```
118
192
 
119
193
  ### More examples
@@ -161,7 +235,7 @@ A payload field must also have a `from:` that references a declared input, and e
161
235
 
162
236
  ### Inspecting the compiled schema
163
237
 
164
- Every definition compiles to a `Schema` value object:
238
+ Every definition compiles to an `EventEngine::EventDefinition::Schemas::Schema` value object:
165
239
 
166
240
  ```ruby
167
241
  schema = LeadCreated.schema
@@ -186,10 +260,18 @@ EventDefinition DSL registers as the publisher
186
260
  │ compile ────────────────────►
187
261
  ▼ reads the raw inputs via the
188
262
  generated helper ──publish(event)──► schema's from:/attr: mapping,
189
- + committed schema.json then dispatches, persists, brokers…
263
+ + committed schema.json then routes it to a processor
190
264
  ```
191
265
 
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` provides a real publisher adapter and assigns it to `EventEngine::Definition.publisher`, so calling a generated helper hands the event to the full runtime. Nothing in this gem knows how events are dispatched — that decision lives entirely in `event_engine`.
266
+ 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.
267
+
268
+ 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*.
269
+
270
+ ### Namespacing
271
+
272
+ Public API sits at `EventEngine::EventDefinition` (the DSL you subclass) and `EventEngine::Definition` (configuration, the publisher port, the pack registry).
273
+
274
+ 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
275
 
194
276
  ## Development
195
277
 
data/Rakefile CHANGED
@@ -6,3 +6,5 @@ require "minitest/test_task"
6
6
  Minitest::TestTask.create
7
7
 
8
8
  task default: :test
9
+
10
+ require "the_local/rake"
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 event_engine:definition:dump` build step in the README. These examples assume that step has already run, so they focus on defining the event and calling it.
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module EventEngine
4
4
  module Definition
5
- VERSION = "0.2.0"
5
+ VERSION = "0.4.0"
6
6
  end
7
7
  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