event_engine 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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +55 -0
  3. data/README.md +210 -399
  4. data/Rakefile +0 -1
  5. data/lib/event_engine/catalog_entry.rb +84 -0
  6. data/lib/event_engine/configuration.rb +9 -9
  7. data/lib/event_engine/definition_publisher.rb +22 -0
  8. data/lib/event_engine/event.rb +0 -8
  9. data/lib/event_engine/event_builder.rb +0 -8
  10. data/lib/event_engine/event_schema.rb +32 -57
  11. data/lib/event_engine/event_schema_json_loader.rb +19 -0
  12. data/lib/event_engine/handler_registry.rb +4 -4
  13. data/lib/event_engine/invalid_rules_error.rb +8 -0
  14. data/lib/event_engine/processing_rules.rb +39 -0
  15. data/lib/event_engine/processor_registry.rb +19 -0
  16. data/lib/event_engine/processor_resolver.rb +16 -0
  17. data/lib/event_engine/railtie.rb +54 -4
  18. data/lib/event_engine/rules_file.rb +14 -0
  19. data/lib/event_engine/schema_catalog_builder.rb +18 -0
  20. data/lib/event_engine/schema_registry.rb +7 -58
  21. data/lib/event_engine/unregistered_processor_error.rb +8 -0
  22. data/lib/event_engine/unroutable_event_error.rb +9 -0
  23. data/lib/event_engine/unrouted_events_error.rb +9 -0
  24. data/lib/event_engine/version.rb +1 -1
  25. data/lib/event_engine.rb +117 -123
  26. data/lib/tasks/event_engine_catalog.rake +15 -6
  27. data/lib/tasks/event_engine_rules.rake +10 -0
  28. data/the_local/agents/event_engine-develop.md +154 -0
  29. data/the_local/agents/event_engine-info.md +82 -0
  30. data/the_local/agents/event_engine-install.md +99 -0
  31. data/the_local/interface.yml +27 -0
  32. metadata +45 -48
  33. data/app/assets/config/event_engine_manifest.js +0 -1
  34. data/app/assets/stylesheets/event_engine/application.css +0 -15
  35. data/app/controllers/event_engine/application_controller.rb +0 -4
  36. data/app/helpers/event_engine/application_helper.rb +0 -4
  37. data/app/jobs/event_engine/application_job.rb +0 -4
  38. data/app/mailers/event_engine/application_mailer.rb +0 -6
  39. data/app/models/event_engine/application_record.rb +0 -5
  40. data/app/views/layouts/event_engine/application.html.erb +0 -15
  41. data/config/routes.rb +0 -2
  42. data/lib/event_engine/definition_loader.rb +0 -26
  43. data/lib/event_engine/dsl_compiler.rb +0 -50
  44. data/lib/event_engine/engine.rb +0 -56
  45. data/lib/event_engine/event_definition/inputs.rb +0 -43
  46. data/lib/event_engine/event_definition/payloads.rb +0 -47
  47. data/lib/event_engine/event_definition/schemas.rb +0 -158
  48. data/lib/event_engine/event_definition/validation.rb +0 -18
  49. data/lib/event_engine/event_definition.rb +0 -76
  50. data/lib/event_engine/event_schema_dumper.rb +0 -13
  51. data/lib/event_engine/event_schema_loader.rb +0 -37
  52. data/lib/event_engine/event_schema_merger.rb +0 -62
  53. data/lib/event_engine/event_schema_writer.rb +0 -47
  54. data/lib/event_engine/lifecycle_definition.rb +0 -86
  55. data/lib/event_engine/process_type.rb +0 -26
  56. data/lib/event_engine/reference/guide.md +0 -129
  57. data/lib/event_engine/reference.rb +0 -16
  58. data/lib/event_engine/schema_catalog.rb +0 -50
  59. data/lib/event_engine/schema_compatibility.rb +0 -50
  60. data/lib/event_engine/schema_diff.rb +0 -35
  61. data/lib/event_engine/schema_drift_guard.rb +0 -38
  62. data/lib/event_engine/subject_registry.rb +0 -40
  63. data/lib/event_engine/the_local/agents/event_engine-develop.md +0 -142
  64. data/lib/event_engine/the_local/agents/event_engine-info.md +0 -140
  65. data/lib/event_engine/the_local/agents/event_engine-install.md +0 -140
  66. data/lib/event_engine/the_local.rb +0 -55
  67. data/lib/generators/event_engine/install_generator.rb +0 -31
  68. data/lib/generators/event_engine/templates/event_schema.rb +0 -10
  69. data/lib/generators/event_engine/templates/initializer.rb +0 -4
  70. data/lib/tasks/event_engine_schema.rake +0 -82
  71. data/lib/tasks/event_engine_schema_check.rake +0 -20
  72. data/lib/tasks/event_engine_tasks.rake +0 -4
@@ -1,158 +0,0 @@
1
- module EventEngine
2
- class EventDefinition
3
- # Provides schema generation and fingerprinting for event definitions.
4
- module Schemas
5
- def self.included(base)
6
- base.extend ClassMethods
7
- end
8
-
9
- # Immutable representation of a compiled event schema.
10
- # Holds the event identity, inputs, and payload field definitions.
11
- # Used both at development time (compilation) and runtime (registry).
12
- class Schema < Struct.new(
13
- :event_name,
14
- :event_version,
15
- :event_type,
16
- :process_type,
17
- :subject,
18
- :domain,
19
- :required_inputs,
20
- :optional_inputs,
21
- :payload_fields,
22
- keyword_init: true
23
- )
24
-
25
- # Returns a SHA256 fingerprint of the schema's canonical representation.
26
- # Used to detect schema changes and trigger version bumps.
27
- #
28
- # @return [String] hex-encoded SHA256 digest
29
- def fingerprint
30
- Digest::SHA256.hexdigest(
31
- canonical_representation
32
- )
33
- end
34
-
35
- # Serializes the schema to a Ruby source string for the schema file.
36
- #
37
- # @return [String]
38
- def to_ruby
39
- <<~RUBY.strip
40
- EventEngine::EventDefinition::Schema.new(
41
- event_name: #{event_name.inspect},
42
- event_version: #{event_version.inspect},
43
- event_type: #{event_type.inspect},
44
- process_type: #{process_type.inspect},
45
- subject: #{subject.inspect},
46
- domain: #{domain.inspect},
47
- required_inputs: #{required_inputs.inspect},
48
- optional_inputs: #{optional_inputs.inspect},
49
- payload_fields: [#{payload_fields.map { |h| ruby_hash(h) }.join(", ")}]
50
- )
51
- RUBY
52
- end
53
-
54
- private
55
-
56
- def canonical_representation
57
- {
58
- event_name: event_name.to_s,
59
- event_type: event_type.to_s,
60
- required_inputs: required_inputs.map(&:to_s).sort,
61
- optional_inputs: optional_inputs.map(&:to_s).sort,
62
- payload_fields: payload_fields
63
- .map { |h| h.transform_values { |v| v.to_s } }
64
- .sort_by { |h| h[:name].to_s }
65
- }.to_json
66
- end
67
-
68
- def ruby_hash(hash)
69
- inner = hash.map { |k, v| "#{k}: #{v.inspect}" }.join(", ")
70
- "{#{inner}}"
71
- end
72
- end
73
-
74
- module ClassMethods
75
- # Builds and returns a {Schema} from this definition's DSL declarations.
76
- #
77
- # @return [Schema]
78
- # @raise [ArgumentError] if the definition has validation errors
79
- def schema
80
- errors = schema_errors
81
- raise ArgumentError, errors.join(", ") if errors.any?
82
-
83
- required = inputs.select { |_, v| v== :required }.keys
84
- optional = inputs.select { |_, v| v== :optional }.keys
85
-
86
- Schema.new(
87
- event_name: @event_name,
88
- event_type: @event_type,
89
- process_type: @process_type,
90
- subject: @subject,
91
- domain: @domain,
92
- required_inputs: required,
93
- optional_inputs: optional,
94
- payload_fields: payload_fields
95
- )
96
- end
97
-
98
- # Returns validation errors for this definition, if any.
99
- #
100
- # @return [Array<String>]
101
- def schema_errors
102
- errors = []
103
- validate_identity(errors)
104
- validate_process_type(errors)
105
- validate_payload_fields(errors)
106
- errors
107
- end
108
-
109
- # Whether this definition has a valid schema (no errors).
110
- #
111
- # @return [Boolean]
112
- def valid_schema?
113
- schema_errors.empty?
114
- end
115
-
116
- private
117
-
118
- def validate_identity(errors)
119
- errors << "event_name is required" unless @event_name
120
- errors << "event_type is required" unless @event_type
121
- end
122
-
123
- def validate_process_type(errors)
124
- return if @process_type.nil? || ProcessType.known?(@process_type)
125
- errors << "process_type is unknown: #{@process_type.inspect}"
126
- end
127
-
128
- def validate_payload_fields(errors)
129
- seen = {}
130
-
131
- payload_fields.each do |field|
132
- name = field[:name]
133
-
134
- if seen[name]
135
- errors << "duplicate payload field: #{name}"
136
- end
137
-
138
- if RESERVED_PAYLOAD_FIELDS.include?(name)
139
- errors << "payload field uses reserved name: #{name}"
140
- end
141
-
142
- if field[:from].nil?
143
- errors << "payload field #{name} must have a from:"
144
- end
145
-
146
- unless inputs.key?(field[:from])
147
- errors << "payload field #{name} references unknown input: #{field[:from]}"
148
- end
149
-
150
- # attr: is optional - when omitted, input value is used directly (passthrough)
151
-
152
- seen[name] = true
153
- end
154
- end
155
- end
156
- end
157
- end
158
- end
@@ -1,18 +0,0 @@
1
- module EventEngine
2
- class EventDefinition
3
- module Validation
4
- def validate_inputs!(inputs)
5
- declared = self.class.inputs
6
- provided = inputs.keys.map(&:to_sym)
7
-
8
- return if declared.empty?
9
-
10
- missing = declared - provided
11
- raise ArgumentError, "missing input: #{missing.join(', ')}" if missing.any?
12
-
13
- extra = provided - declared
14
- raise ArgumentError, "undeclared input: #{extra.join(', ')}" if extra.any?
15
- end
16
- end
17
- end
18
- end
@@ -1,76 +0,0 @@
1
- require "event_engine/event_definition/inputs"
2
- require "event_engine/event_definition/payloads"
3
- require "event_engine/event_definition/validation"
4
- require "event_engine/event_definition/schemas"
5
-
6
- module EventEngine
7
- # Base class for defining events using the EventEngine DSL.
8
- #
9
- # Subclass this to declare an event's name, type, inputs, and payload fields.
10
- # Definitions are compiled into a schema file at development time and are
11
- # not used at runtime.
12
- #
13
- # @example Define an event
14
- # class CowFed < EventEngine::EventDefinition
15
- # input :cow
16
- # optional_input :farmer
17
- #
18
- # event_name :cow_fed
19
- # event_type :domain
20
- #
21
- # required_payload :weight, from: :cow, attr: :weight
22
- # optional_payload :farmer_name, from: :farmer, attr: :name
23
- # end
24
- class EventDefinition
25
- # Payload field names reserved by the outbox schema.
26
- RESERVED_PAYLOAD_FIELDS = %i[
27
- event_name
28
- event_type
29
- event_version
30
- occurred_at
31
- created_at
32
- updated_at
33
- published_at
34
- metadata
35
- idempotency_key
36
- attempts
37
- dead_lettered_at
38
- aggregate_type
39
- aggregate_id
40
- aggregate_version
41
- ].freeze
42
-
43
- include Inputs
44
- include Payloads
45
- include Validation
46
- include Schemas
47
-
48
- class << self
49
- # Sets the event name for this definition.
50
- #
51
- # @param value [Symbol] the event name (e.g. +:cow_fed+)
52
- def event_name(value)
53
- @event_name = value
54
- end
55
-
56
- # Sets the event type for this definition.
57
- #
58
- # @param value [Symbol] the event type (e.g. +:domain+, +:integration+)
59
- def event_type(value)
60
- @event_type = value
61
- end
62
-
63
- def process_type(value)
64
- @process_type = value
65
- end
66
-
67
- def subject(value)
68
- @subject = value
69
- end
70
-
71
- def domain(value)
72
- @domain = value
73
- end
74
- end
75
- end
76
- end
@@ -1,13 +0,0 @@
1
- module EventEngine
2
- class EventSchemaDumper
3
- def self.dump!(definitions:, path:)
4
- compiled_schema = DslCompiler.compile(definitions)
5
- compiled_schema.finalize!
6
-
7
- loaded_schema = EventSchemaLoader.load(path)
8
- merged_schema = EventSchemaMerger.merge(compiled_schema, loaded_schema)
9
-
10
- EventSchemaWriter.write(path, merged_schema)
11
- end
12
- end
13
- end
@@ -1,37 +0,0 @@
1
- module EventEngine
2
- class EventSchemaLoader
3
- def self.load(path)
4
- registry = SchemaRegistry.new
5
- return registry unless File.exist?(path)
6
-
7
- contents = File.read(path.to_s)
8
- return registry if contents.strip.empty?
9
-
10
- sandbox = Module.new
11
- sandbox.const_set(:EventEngine, EventEngine)
12
-
13
- schema =
14
- sandbox.module_eval(contents, path.to_s)
15
-
16
- unless schema.is_a?(EventEngine::EventSchema)
17
- raise <<~MSG
18
- Invalid EventEngine schema file.
19
-
20
- Expected #{path} to return an EventSchema from:
21
- EventEngine::EventSchema.define { ... }
22
-
23
- But got:
24
- #{schema.inspect}
25
- MSG
26
- end
27
-
28
- schema.schemas_by_event.each_value do |versions|
29
- versions.each_value do |s|
30
- registry.register(s)
31
- end
32
- end
33
-
34
- registry
35
- end
36
- end
37
- end
@@ -1,62 +0,0 @@
1
- module EventEngine
2
- class EventSchemaMerger
3
- def self.merge(compiled_registry, file_registry)
4
- merged = EventSchema.new
5
-
6
- file_loaded_schema = file_registry.event_schema
7
-
8
- file_loaded_schema.events.each do |event|
9
- file_loaded_schema.versions_for(event).each do |version|
10
- merged.register(file_loaded_schema.schema_for(event, version))
11
- end
12
- end
13
-
14
- # Merge compiled schemas
15
- compiled_registry.events.each do |event|
16
- compiled_schema = compiled_registry.latest_for(event)
17
-
18
- existing_versions = merged.versions_for(event)
19
- latest_version = existing_versions.max
20
- latest_schema = latest_version && merged.schema_for(event, latest_version)
21
-
22
- if no_schema_change?(latest_schema, compiled_schema)
23
- next
24
- end
25
-
26
- new_version = version(latest_version)
27
- new_schema = compiled_schema.dup
28
- new_schema.event_version = new_version
29
-
30
- merged.register(new_schema)
31
- end
32
-
33
- merged.finalize!
34
-
35
- merged
36
- end
37
-
38
- def self.changed?(compiled_registry, file_registry)
39
- compiled_registry.events.any? do |event|
40
- compiled_schema = compiled_registry.latest_for(event)
41
-
42
- existing_versions = file_registry.versions_for(event)
43
- latest_version = existing_versions.max
44
- latest_schema = latest_version && file_registry.schema(event, version: latest_version)
45
-
46
- # New event entirely
47
- return true unless latest_schema
48
-
49
- # Fingerprint mismatch means a new version would be created
50
- latest_schema.fingerprint != compiled_schema.fingerprint
51
- end
52
- end
53
-
54
- def self.no_schema_change?(latest_schema, compiled_schema)
55
- latest_schema && latest_schema.fingerprint == compiled_schema.fingerprint
56
- end
57
-
58
- def self.version(latest_version)
59
- (latest_version || 0) + 1
60
- end
61
- end
62
- end
@@ -1,47 +0,0 @@
1
- module EventEngine
2
- class EventSchemaWriter
3
- HEADER = <<~RUBY.freeze
4
- # This file is authoritative in production.
5
- # It is generated from EventDefinitions via:
6
- #
7
- # bin/rails event_engine:schema:dump
8
- #
9
- # Do not edit manually.
10
-
11
- RUBY
12
-
13
- def self.write(path, event_schema)
14
- schemas =
15
- event_schema
16
- .schemas_by_event
17
- .flat_map { |_event, versions| versions.values }
18
- .sort_by { |s| [s.event_name.to_s, s.event_version] }
19
-
20
- File.open(path, "w") do |io|
21
- io.write(HEADER)
22
- io.write("EventEngine::EventSchema.define do |schema|\n")
23
-
24
- schemas.each do |definition|
25
- write_definition(io, definition)
26
- end
27
-
28
- io.write("end\n")
29
- end
30
- end
31
-
32
- def self.write_definition(io, definition)
33
- io.write(" schema.register(\n")
34
- indent(io, 4) { definition.to_ruby }
35
- io.write(" )\n")
36
- end
37
-
38
- def self.indent(io, spaces)
39
- padding = " " * spaces
40
- yield.each_line do |line|
41
- io.write(padding)
42
- io.write(line)
43
- io.write("\n")
44
- end
45
- end
46
- end
47
- end
@@ -1,86 +0,0 @@
1
- require "event_engine/event_definition"
2
-
3
- module EventEngine
4
- class LifecycleDefinition
5
- include EventDefinition::Inputs
6
- include EventDefinition::Payloads
7
-
8
- class << self
9
- def subject(value)
10
- @subject = value
11
- end
12
-
13
- def event_type(value)
14
- @event_type = value
15
- end
16
-
17
- def process_type(value)
18
- @process_type = value
19
- end
20
-
21
- def lifecycle(*verbs)
22
- @verbs = verbs
23
- end
24
-
25
- def on(verb, &block)
26
- verb_overrides[verb] = block
27
- end
28
-
29
- def verb_overrides
30
- @verb_overrides ||= {}
31
- end
32
-
33
- def generated_events
34
- @generated_events ||= Array(@verbs).map { |verb| build_event(verb) }
35
- end
36
-
37
- def materialize_all!
38
- subclasses.flat_map(&:generated_events)
39
- end
40
-
41
- def declared_subject
42
- @subject
43
- end
44
-
45
- def declared_event_type
46
- @event_type
47
- end
48
-
49
- def declared_process_type
50
- @process_type
51
- end
52
-
53
- private
54
-
55
- def build_event(verb)
56
- template = self
57
- name = :"#{template.declared_subject}_#{verb}"
58
-
59
- Class.new(EventDefinition) do
60
- event_name name
61
- event_type template.declared_event_type
62
-
63
- define_singleton_method(:inspect) { "EventEngine::LifecycleDefinition(#{name})" }
64
- define_singleton_method(:to_s) { inspect }
65
- subject template.declared_subject
66
- process_type template.declared_process_type if template.declared_process_type
67
-
68
- template.inputs.each do |name, kind|
69
- kind == :required ? input(name) : optional_input(name)
70
- end
71
-
72
- template.payload_fields.each do |field|
73
- if field[:required]
74
- required_payload field[:name], from: field[:from], attr: field[:attr]
75
- else
76
- optional_payload field[:name], from: field[:from], attr: field[:attr]
77
- end
78
- end
79
-
80
- override = template.verb_overrides[verb]
81
- class_eval(&override) if override
82
- end
83
- end
84
- end
85
- end
86
- end
@@ -1,26 +0,0 @@
1
- module EventEngine
2
- module ProcessType
3
- ALL = %i[inline background durable broker telemetry sourced].freeze
4
-
5
- PROCESSORS = {
6
- inline: :subscribers,
7
- background: :subscribers,
8
- durable: :delivery,
9
- broker: :delivery,
10
- telemetry: :telemetry,
11
- sourced: :sourcing
12
- }.freeze
13
-
14
- def self.all
15
- ALL
16
- end
17
-
18
- def self.processor_for(type)
19
- PROCESSORS[type]
20
- end
21
-
22
- def self.known?(type)
23
- ALL.include?(type)
24
- end
25
- end
26
- end
@@ -1,129 +0,0 @@
1
- ## EventEngine
2
-
3
- > **DO NOT** explore the event_engine gem source code. This reference is the
4
- > complete user-facing API, embedded verbatim into every event_engine local so
5
- > their guidance never drifts. Keep it the single source of truth.
6
-
7
- EventEngine is a Rails engine for defining domain events as declarative classes,
8
- compiling them to a committed schema, emitting them through generated helpers, and
9
- dispatching them to registered handlers. Core builds and routes events; it ships no
10
- handlers of its own. Durable delivery, an event store, and ready-made subscriber
11
- classes are separate companion gems (`event_engine-delivery`, `event_engine-store`,
12
- `event_engine-subscribers`) — this reference covers core only.
13
-
14
- ### What it offers
15
-
16
- **Define events** — subclass `EventEngine::EventDefinition` in `app/event_definitions/`:
17
-
18
- ```ruby
19
- class CowFed < EventEngine::EventDefinition
20
- event_name :cow_fed # the event's identity (required)
21
- event_type :domain # classification, e.g. :domain (required)
22
- process_type :durable # routing type (optional; set it explicitly)
23
-
24
- input :cow # a required input
25
- optional_input :farmer # an optional input
26
-
27
- required_payload :weight, from: :cow, attr: :weight
28
- optional_payload :farmer_name, from: :farmer, attr: :name
29
- end
30
- ```
31
-
32
- | DSL method | Purpose |
33
- |---|---|
34
- | `event_name(:symbol)` | The event's identity; becomes `EventEngine.<name>`. Required. |
35
- | `event_type(:symbol)` | Classification, e.g. `:domain`. Required. |
36
- | `process_type(:symbol)` | Routing type (optional). One of the six values below. |
37
- | `input(:name)` / `optional_input(:name)` | Inputs the emit helper must / may receive. |
38
- | `required_payload(name, from:, attr: nil)` | Payload field; `from:` names an input, `attr:` is the method read on it (`nil` passes the input through). |
39
- | `optional_payload(name, from:, attr: nil)` | Same, but omitted when the source input is nil. |
40
-
41
- Duplicate input names raise `ArgumentError`; payload `from:` must reference a
42
- declared input.
43
-
44
- **process_type** — core stamps this symbol onto every emitted event but does not act
45
- on it. Which handlers receive an event is decided by each handler's `levels:`. The
46
- values:
47
-
48
- | value | intent |
49
- |---|---|
50
- | `:inline` | handled in-process, synchronously |
51
- | `:background` | handled in-process, via a background job |
52
- | `:durable` | handled when a durable outbox drains |
53
- | `:broker` | published to an external transport |
54
- | `:telemetry` | metrics / observability handlers |
55
- | `:sourced` | an append-only event store |
56
-
57
- The companion gems register the handlers that give `:durable`, `:broker`, `:sourced`,
58
- etc. their behavior; core just routes to whatever is registered. If `process_type`
59
- is omitted it is `nil` — set it explicitly so routing intent is clear.
60
-
61
- **Emit events** — booting installs an `EventEngine.<event_name>` helper per event:
62
-
63
- ```ruby
64
- EventEngine.cow_fed(
65
- cow: cow, farmer: farmer, # declared inputs, by name
66
- occurred_at: Time.current, # optional, defaults to now
67
- metadata: { request_id: "abc" }, # optional
68
- idempotency_key: "…", # optional, defaults to a UUID
69
- aggregate_type: "Cow", aggregate_id: cow.id, aggregate_version: 1,
70
- event_version: 1 # optional, defaults to the latest schema version
71
- )
72
- ```
73
-
74
- Missing a required input, or passing an unknown one, raises `ArgumentError`. The
75
- event's `payload` is symbol-keyed.
76
-
77
- **Register handlers** — a handler is any object responding to `call(event)`:
78
-
79
- ```ruby
80
- EventEngine.register_handler(handler, levels: [:inline, :durable]) # or levels: :all
81
- EventEngine.dispatch(event) # fan an event out (emit helpers call this)
82
- EventEngine.reset_handlers! # clear all handlers
83
- ```
84
-
85
- Handlers run synchronously in registration order; if one raises, the rest don't run.
86
- Keep handlers idempotent.
87
-
88
- **Configure** — `config/initializers/event_engine.rb`, logger only:
89
-
90
- ```ruby
91
- EventEngine.configure { |config| config.logger = Rails.logger }
92
- ```
93
-
94
- **Schema workflow** — definitions compile to a committed `db/event_schema.rb`, which
95
- is authoritative at boot:
96
-
97
- ```bash
98
- bin/rails event_engine:schema:dump # compile definitions → db/event_schema.rb
99
- bin/rails event_engine:schema_check # CI: fail if definitions drift from the file
100
- ```
101
-
102
- A new event is version 1; changing an event's identity or payload bumps its version.
103
- Changing only `process_type` does not bump the version.
104
-
105
- ### Install
106
-
107
- 1. Add the gem and install: `gem "event_engine"`, then `bundle install`.
108
- 2. Run `bin/rails g event_engine:install` — creates `db/event_schema.rb` and
109
- `config/initializers/event_engine.rb`.
110
- 3. Define events as classes in `app/event_definitions/`.
111
- 4. Run `bin/rails event_engine:schema:dump` and commit `db/event_schema.rb`.
112
- 5. Set `config.logger` in the initializer if you want something other than the default.
113
-
114
- Durable delivery, an event store, and prebuilt subscriber classes are separate gems
115
- (`event_engine-delivery`, `event_engine-store`, `event_engine-subscribers`); add them
116
- when you need them and follow their own setup.
117
-
118
- ### EventEngine conventions
119
-
120
- - Define one `EventDefinition` class per event in `app/event_definitions/`; never
121
- hand-build event hashes.
122
- - Build payloads from inputs with `required_payload`/`optional_payload`; don't pass
123
- raw payload hashes to the emit helper.
124
- - Always set `process_type` explicitly so routing intent is clear.
125
- - Emit only through the generated `EventEngine.<event_name>` helpers, passing the
126
- declared inputs.
127
- - Re-run `event_engine:schema:dump` and commit `db/event_schema.rb` after any
128
- definition change; keep `event_engine:schema_check` green in CI.
129
- - Keep handlers and subscribers idempotent.
@@ -1,16 +0,0 @@
1
- module EventEngine
2
- # Single source of truth for the EventEngine API reference. The companion
3
- # Claude Code subagents (and any future doc generator) read from here so they
4
- # can never disagree about how the gem is used.
5
- module Reference
6
- DIR = File.expand_path("reference", __dir__)
7
-
8
- def self.content
9
- read("guide.md")
10
- end
11
-
12
- def self.read(name)
13
- File.read(File.join(DIR, name)).chomp
14
- end
15
- end
16
- end