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
data/Rakefile CHANGED
@@ -8,5 +8,4 @@ require "bundler/gem_tasks"
8
8
  task test: "app:test"
9
9
  task default: :test
10
10
 
11
- require "event_engine/the_local"
12
11
  require "the_local/rake"
@@ -0,0 +1,84 @@
1
+ require "digest"
2
+ require "json"
3
+
4
+ module EventEngine
5
+ class CatalogEntry < Struct.new(
6
+ :event_name,
7
+ :event_version,
8
+ :event_type,
9
+ :subject,
10
+ :domain,
11
+ :required_inputs,
12
+ :optional_inputs,
13
+ :payload_fields,
14
+ keyword_init: true
15
+ )
16
+
17
+ def self.from_h(hash)
18
+ h = hash.transform_keys(&:to_sym)
19
+
20
+ new(
21
+ event_name: h[:event_name]&.to_sym,
22
+ event_version: h[:event_version],
23
+ event_type: h[:event_type]&.to_sym,
24
+ subject: h[:subject]&.to_sym,
25
+ domain: h[:domain]&.to_sym,
26
+ required_inputs: Array(h[:required_inputs]).map(&:to_sym),
27
+ optional_inputs: Array(h[:optional_inputs]).map(&:to_sym),
28
+ payload_fields: Array(h[:payload_fields]).map { |field| payload_field_from_h(field) }
29
+ )
30
+ end
31
+
32
+ def self.payload_field_from_h(field)
33
+ f = field.transform_keys(&:to_sym)
34
+
35
+ {
36
+ name: f[:name]&.to_sym,
37
+ required: f[:required],
38
+ from: f[:from]&.to_sym,
39
+ attr: f[:attr]&.to_sym
40
+ }
41
+ end
42
+
43
+ def fingerprint
44
+ Digest::SHA256.hexdigest(canonical_representation)
45
+ end
46
+
47
+ def to_h
48
+ {
49
+ event_name: event_name,
50
+ event_version: event_version,
51
+ event_type: event_type,
52
+ subject: subject,
53
+ domain: domain,
54
+ required_inputs: required_inputs,
55
+ optional_inputs: optional_inputs,
56
+ payload_fields: payload_fields.map { |field| payload_field_h(field) },
57
+ fingerprint: fingerprint
58
+ }
59
+ end
60
+
61
+ private
62
+
63
+ def canonical_representation
64
+ {
65
+ event_name: event_name.to_s,
66
+ event_type: event_type.to_s,
67
+ required_inputs: required_inputs.map(&:to_s).sort,
68
+ optional_inputs: optional_inputs.map(&:to_s).sort,
69
+ payload_fields: payload_fields
70
+ .map { |h| h.transform_values { |v| v.to_s } }
71
+ .sort_by { |h| h[:name].to_s }
72
+ }.to_json
73
+ end
74
+
75
+ def payload_field_h(field)
76
+ {
77
+ name: field[:name],
78
+ from: field[:from],
79
+ attr: field[:attr],
80
+ required: field[:required]
81
+ }
82
+ end
83
+ end
84
+ end
@@ -1,20 +1,20 @@
1
1
  module EventEngine
2
- # Holds configuration options for EventEngine core.
3
- #
4
- # @example
5
- # EventEngine.configure do |config|
6
- # config.logger = Rails.logger
7
- # end
8
2
  class Configuration
9
- # @!attribute [rw] logger
10
- # Logger instance for EventEngine messages.
11
- # @return [Logger] defaults to +Rails.logger+
12
3
  attr_accessor :logger
13
4
 
14
5
  attr_accessor :metadata_defaults
15
6
 
7
+ attr_accessor :schema_path
8
+
9
+ attr_accessor :rules_path
10
+
11
+ attr_accessor :publisher_schema_paths
12
+
16
13
  def initialize
17
14
  @logger = defined?(Rails) ? Rails.logger : Logger.new($stdout)
15
+ @schema_path = "db/event_schema.json"
16
+ @rules_path = "config/event_rules.yml"
17
+ @publisher_schema_paths = []
18
18
  end
19
19
  end
20
20
  end
@@ -0,0 +1,22 @@
1
+ module EventEngine
2
+ class DefinitionPublisher
3
+ class EventNotInCatalogError < StandardError; end
4
+
5
+ def publish(event_name, domain:, inputs:, **envelope)
6
+ EventEngine.emit(event_name, domain: domain, inputs: inputs, **envelope)
7
+ rescue SchemaRegistry::UnknownEventError
8
+ raise EventNotInCatalogError, missing_event_message(event_name, domain)
9
+ end
10
+
11
+ private
12
+
13
+ def missing_event_message(event_name, domain)
14
+ <<~MSG
15
+ EventEngine has no schema for #{event_name} in domain #{domain.inspect}.
16
+
17
+ Its pack's schema.json is missing from the committed catalog. Rebuild it with:
18
+ bin/rails event_engine:catalog
19
+ MSG
20
+ end
21
+ end
22
+ end
@@ -1,7 +1,4 @@
1
1
  module EventEngine
2
- # A non-persisted, in-memory representation of an emitted event with a
3
- # symbol-keyed payload. Passed to subscribers' +#handle(event)+ at every
4
- # in-process level (1-3), and returned by the emitter for levels 1 and 2.
5
2
  Event = Struct.new(
6
3
  :event_name,
7
4
  :event_type,
@@ -18,11 +15,6 @@ module EventEngine
18
15
  :aggregate_version,
19
16
  keyword_init: true
20
17
  ) do
21
- # Builds an Event from a record responding to the same members (e.g. an
22
- # OutboxEvent), normalizing the payload to symbol keys.
23
- #
24
- # @param record [#payload]
25
- # @return [Event]
26
18
  def self.from(record)
27
19
  attrs = members.to_h { |member| [member, record.public_send(member)] }
28
20
  attrs[:payload] = attrs[:payload].to_h.transform_keys(&:to_sym)
@@ -1,13 +1,5 @@
1
1
  module EventEngine
2
- # Constructs an event payload hash from a schema and input data.
3
- # Validates that required inputs are present and no unknown inputs are passed.
4
2
  class EventBuilder
5
- # Builds an event attributes hash from schema and input data.
6
- #
7
- # @param schema [EventDefinition::Schema] the event schema
8
- # @param data [Hash] input data keyed by input name
9
- # @return [Hash] event attributes including :event_name, :event_type, :event_version, :payload
10
- # @raise [ArgumentError] if required inputs are missing or unknown inputs are provided
11
3
  def self.build(schema:, data:)
12
4
  validate_inputs!(schema, data)
13
5
 
@@ -1,87 +1,53 @@
1
1
  module EventEngine
2
- # Container for all event schemas, organized by event name and version.
3
- # This is the data structure loaded from the compiled +db/event_schema.rb+ file
4
- # and used by {SchemaRegistry} at runtime.
5
2
  class EventSchema
6
3
  class DuplicateEventNameError < StandardError; end
7
4
 
8
- # Creates an EventSchema using a block DSL (used by the schema file).
9
- #
10
- # @yieldparam schema [EventSchema]
11
- # @return [EventSchema]
12
- def self.define(&block)
13
- schema = new
14
- block.call(schema)
15
- schema
16
- end
17
-
18
5
  def initialize
19
6
  @schemas_by_event = {}
20
7
  @finalized = false
21
8
  end
22
9
 
23
- # Registers a schema for a specific event name and version.
24
- #
25
- # @param schema [EventDefinition::Schema]
26
- # @raise [FrozenError] if the schema has been finalized
27
10
  def register(schema)
28
11
  raise FrozenError, "EventSchema is finalized" if @finalized
29
- event_name = schema.event_name
12
+ key = key_for(schema.domain, schema.event_name)
30
13
  version = schema.event_version
31
14
 
32
- @schemas_by_event[event_name] ||= {}
33
- guard_duplicate_event_name!(@schemas_by_event[event_name][version], schema)
34
- @schemas_by_event[event_name][version] = schema
15
+ @schemas_by_event[key] ||= {}
16
+ guard_duplicate_event_name!(@schemas_by_event[key][version], schema)
17
+ @schemas_by_event[key][version] = schema
35
18
  end
36
19
 
37
20
  def guard_duplicate_event_name!(existing, incoming)
38
21
  return unless existing
39
22
 
40
23
  raise DuplicateEventNameError,
41
- "duplicate event_name #{incoming.event_name.inspect}: " \
42
- "already registered with domain #{existing.domain.inspect}, " \
43
- "cannot register again with domain #{incoming.domain.inspect}"
24
+ "duplicate (domain, event_name) " \
25
+ "(#{incoming.domain.inspect}, #{incoming.event_name.inspect}): " \
26
+ "already registered at version #{existing.event_version.inspect}"
44
27
  end
45
28
 
46
- # Returns all registered event names.
47
- #
48
- # @return [Array<Symbol>]
49
- def events
29
+ def events(domain: nil)
50
30
  @schemas_by_event.keys
31
+ .select { |(schema_domain, _name)| domain.nil? || schema_domain == domain }
32
+ .map { |(_domain, event_name)| event_name }
33
+ .uniq
51
34
  end
52
35
 
53
- # Returns sorted version numbers for a given event.
54
- #
55
- # @param event_name [Symbol]
56
- # @return [Array<Integer>]
57
- def versions_for(event_name)
58
- versions = @schemas_by_event[event_name]
59
- return [] unless versions
60
- versions.keys.sort
36
+ def versions_for(event_name, domain: nil)
37
+ version_sets_for(event_name, domain).flat_map(&:keys).uniq.sort
61
38
  end
62
39
 
63
- # Returns the schema for a specific event name and version.
64
- #
65
- # @param event_name [Symbol]
66
- # @param version [Integer]
67
- # @return [EventDefinition::Schema, nil]
68
- def schema_for(event_name, version)
69
- @schemas_by_event.dig(event_name, version)
40
+ def schema_for(event_name, version, domain: nil)
41
+ set = version_sets_for(event_name, domain).find { |versions| versions.key?(version) }
42
+ set && set[version]
70
43
  end
71
44
 
72
- # Returns the latest (highest version) schema for an event.
73
- #
74
- # @param event_name [Symbol]
75
- # @return [EventDefinition::Schema, nil]
76
- def latest_for(event_name)
77
- versions = @schemas_by_event[event_name]
78
- return nil unless versions && !versions.empty?
79
- versions[versions.keys.max]
45
+ def latest_for(event_name, domain: nil)
46
+ merged = version_sets_for(event_name, domain).reduce({}, :merge)
47
+ return nil if merged.empty?
48
+ merged[merged.keys.max]
80
49
  end
81
50
 
82
- # Freezes the schema, preventing further registrations.
83
- #
84
- # @return [void]
85
51
  def finalize!
86
52
  @finalized = true
87
53
  @schemas_by_event.each_value(&:freeze)
@@ -89,11 +55,20 @@ module EventEngine
89
55
  freeze
90
56
  end
91
57
 
92
- # Returns the internal hash of schemas keyed by event name and version.
93
- #
94
- # @return [Hash{Symbol => Hash{Integer => EventDefinition::Schema}}]
95
58
  def schemas_by_event
96
59
  @schemas_by_event
97
60
  end
61
+
62
+ private
63
+
64
+ def key_for(domain, event_name)
65
+ [domain, event_name]
66
+ end
67
+
68
+ def version_sets_for(event_name, domain = nil)
69
+ @schemas_by_event.select do |(schema_domain, name), _versions|
70
+ name == event_name && (domain.nil? || schema_domain == domain)
71
+ end.values
72
+ end
98
73
  end
99
74
  end
@@ -0,0 +1,19 @@
1
+ require "json"
2
+
3
+ module EventEngine
4
+ class EventSchemaJsonLoader
5
+ def self.load(path)
6
+ registry = SchemaRegistry.new
7
+ return registry unless File.exist?(path)
8
+
9
+ contents = File.read(path.to_s)
10
+ return registry if contents.strip.empty?
11
+
12
+ JSON.parse(contents).each do |attributes|
13
+ registry.register(CatalogEntry.from_h(attributes))
14
+ end
15
+
16
+ registry
17
+ end
18
+ end
19
+ end
@@ -4,14 +4,14 @@ module EventEngine
4
4
  @handlers = []
5
5
  end
6
6
 
7
- def register(handler, levels:)
8
- @handlers << { handler: handler, levels: levels }
7
+ def register(handler, process_types:)
8
+ @handlers << { handler: handler, process_types: process_types }
9
9
  end
10
10
 
11
11
  def dispatch(event)
12
12
  @handlers.each do |registration|
13
- levels = registration[:levels]
14
- registration[:handler].call(event) if levels == :all || levels.include?(event.process_type)
13
+ process_types = registration[:process_types]
14
+ registration[:handler].call(event) if process_types == :all || process_types.include?(event.process_type)
15
15
  end
16
16
  event
17
17
  end
@@ -0,0 +1,8 @@
1
+ module EventEngine
2
+ class InvalidRulesError < StandardError
3
+ def initialize(names)
4
+ super("the rules file names #{names.map(&:inspect).join(", ")}, " \
5
+ "but no processor is registered under #{names.one? ? "that name" : "those names"}")
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,39 @@
1
+ require "yaml"
2
+
3
+ module EventEngine
4
+ class ProcessingRules
5
+ def self.load(path)
6
+ return new unless File.exist?(path.to_s)
7
+
8
+ contents = YAML.safe_load_file(path.to_s) || {}
9
+
10
+ new(
11
+ default: contents["default"]&.to_sym,
12
+ packs: symbolize(contents["packs"]),
13
+ events: symbolize(contents["events"])
14
+ )
15
+ end
16
+
17
+ def self.symbolize(section)
18
+ (section || {}).to_h { |key, value| [ key.to_sym, value&.to_sym ] }
19
+ end
20
+
21
+ def initialize(default: nil, packs: {}, events: {})
22
+ @default = default
23
+ @packs = packs
24
+ @events = events
25
+ end
26
+
27
+ def for(event_name:, pack:)
28
+ @events[event_name] || @packs[pack] || @default
29
+ end
30
+
31
+ def any?
32
+ !@default.nil? || @packs.any? || @events.any?
33
+ end
34
+
35
+ def processor_names
36
+ ([ @default ] + @packs.values + @events.values).compact.uniq
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,19 @@
1
+ module EventEngine
2
+ class ProcessorRegistry
3
+ def initialize
4
+ @processors = {}
5
+ end
6
+
7
+ def register(name, processor)
8
+ @processors[name] = processor
9
+ end
10
+
11
+ def fetch(name)
12
+ @processors[name]
13
+ end
14
+
15
+ def clear!
16
+ @processors.clear
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,16 @@
1
+ module EventEngine
2
+ class ProcessorResolver
3
+ def initialize(rules)
4
+ @rules = rules
5
+ end
6
+
7
+ def routes?
8
+ @rules.any?
9
+ end
10
+
11
+ def resolve(event)
12
+ @rules.for(event_name: event.event_name, pack: event.domain) ||
13
+ raise(UnroutableEventError.new(event))
14
+ end
15
+ end
16
+ end
@@ -1,9 +1,59 @@
1
1
  module EventEngine
2
- class Railtie < Rails::Railtie
2
+ class Railtie < ::Rails::Railtie
3
3
  rake_tasks do
4
- load "tasks/event_engine_tasks.rake"
5
- load "tasks/event_engine_schema.rake"
6
- load "tasks/event_engine_schema_check.rake"
4
+ Dir[File.expand_path("../tasks/*.rake", __dir__)].sort.each { |task| load task }
5
+ end
6
+
7
+ initializer "event_engine.load_schema_and_helpers" do |app|
8
+ EventEngine.configuration.schema_path = Rails.root.join("db", "event_schema.json").to_s
9
+
10
+ app.config.after_initialize do
11
+ schema_path = Rails.root.join("db", "event_schema.json")
12
+ helpers_path = Rails.root.join("db", "event_engine_helpers.rb")
13
+
14
+ if File.exist?(schema_path)
15
+ Railtie.send(:boot!, schema_path: schema_path, helpers_path: helpers_path)
16
+ else
17
+ Railtie.send(:handle_missing_schema!, schema_path)
18
+ end
19
+
20
+ end
21
+ end
22
+
23
+ class << self
24
+ private
25
+
26
+ def boot!(schema_path:, helpers_path:)
27
+ EventEngine.register_definition_publisher!
28
+
29
+ EventEngine.boot_from_schema!(
30
+ schema_path: schema_path,
31
+ registry: EventEngine::SchemaRegistry.new
32
+ )
33
+
34
+ load helpers_path if File.exist?(helpers_path)
35
+ end
36
+
37
+ def handle_missing_schema!(schema_path)
38
+ if Rails.env.development? || Rails.env.test?
39
+ Rails.logger.warn(
40
+ "[EventEngine] Schema catalog not found at #{schema_path}. " \
41
+ "Commit it, or build it from your packs' schema.json with: " \
42
+ "bin/rails event_engine:catalog"
43
+ )
44
+ return
45
+ end
46
+
47
+ raise <<~MSG
48
+ EventEngine schema catalog missing.
49
+
50
+ Expected to find:
51
+ #{schema_path}
52
+
53
+ Commit the catalog, or build it from your packs' schema.json:
54
+ bin/rails event_engine:catalog
55
+ MSG
56
+ end
7
57
  end
8
58
  end
9
59
  end
@@ -0,0 +1,14 @@
1
+ require "yaml"
2
+
3
+ module EventEngine
4
+ class RulesFile
5
+ def self.sync(path:, event_names:)
6
+ existing = File.exist?(path.to_s) ? (YAML.safe_load_file(path.to_s) || {}) : {}
7
+ decided = existing["events"] || {}
8
+ events = event_names.to_h { |name| [ name.to_s, decided[name.to_s] ] }
9
+
10
+ File.write(path, YAML.dump(existing.merge("events" => events)))
11
+ path
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ require "json"
2
+
3
+ module EventEngine
4
+ class SchemaCatalogBuilder
5
+ def self.build(sources:, catalog_path:)
6
+ entries = sources.flat_map { |source| read(source) }
7
+
8
+ File.write(catalog_path, "#{JSON.pretty_generate(entries)}\n")
9
+ end
10
+
11
+ def self.read(source)
12
+ contents = File.read(source.to_s)
13
+ return [] if contents.strip.empty?
14
+
15
+ JSON.parse(contents)
16
+ end
17
+ end
18
+ end
@@ -1,53 +1,26 @@
1
1
  module EventEngine
2
- # In-memory registry holding all event schemas by name and version.
3
- # Loaded once at boot from the compiled schema file.
4
- #
5
- # @example Look up a schema
6
- # schema = registry.schema(:cow_fed)
7
- # schema.event_version #=> 2
8
- #
9
- # @example Look up a specific version
10
- # schema = registry.schema(:cow_fed, version: 1)
11
2
  class SchemaRegistry
12
- # Raised when looking up an event name or version that doesn't exist.
13
3
  class UnknownEventError < StandardError; end
14
4
 
15
- # Raised when the registry is accessed before loading or loaded twice.
16
5
  class RegistryFrozenError < StandardError; end
17
6
 
18
- # @param event_schema [EventSchema] initial schema (empty by default)
19
7
  def initialize(event_schema = EventSchema.new)
20
8
  @event_schema = event_schema
21
9
  @loaded = false
22
10
  end
23
11
 
24
- # Registers a schema into the underlying EventSchema store.
25
- #
26
- # @param schema [EventDefinition::Schema] the schema to register
27
12
  def register(schema)
28
13
  @event_schema.register(schema)
29
14
  end
30
15
 
31
- # Returns all registered event names.
32
- #
33
- # @return [Array<Symbol>]
34
16
  def events
35
17
  @event_schema.events
36
18
  end
37
19
 
38
- # Returns all version numbers for a given event.
39
- #
40
- # @param event_name [Symbol]
41
- # @return [Array<Integer>] sorted version numbers
42
- def versions_for(event_name)
43
- @event_schema.versions_for(event_name)
20
+ def versions_for(event_name, domain: nil)
21
+ @event_schema.versions_for(event_name, domain: domain)
44
22
  end
45
23
 
46
- # Populates the registry from a loaded EventSchema. Can only be called once.
47
- #
48
- # @param schema [EventSchema] the compiled schema
49
- # @return [self]
50
- # @raise [RegistryFrozenError] if already loaded
51
24
  def load_from_schema!(schema)
52
25
  raise RegistryFrozenError, "EventRegistry already loaded" if loaded?
53
26
  @event_schema = schema
@@ -56,30 +29,19 @@ module EventEngine
56
29
  self
57
30
  end
58
31
 
59
- # Clears the registry, allowing it to be reloaded.
60
- #
61
- # @return [void]
62
32
  def reset!
63
33
  @event_schema = {}
64
34
  @loaded = false
65
35
  end
66
36
 
67
- # Looks up a schema by event name and optional version.
68
- # Returns the latest version when no version is specified.
69
- #
70
- # @param event_name [Symbol]
71
- # @param version [Integer, nil] specific version, or nil for latest
72
- # @return [EventDefinition::Schema]
73
- # @raise [RegistryFrozenError] if registry is not loaded
74
- # @raise [UnknownEventError] if event or version is not found
75
- def schema(event_name, version: nil)
37
+ def schema(event_name, version: nil, domain: nil)
76
38
  raise RegistryFrozenError, "EventRegistry not loaded" unless loaded?
77
39
 
78
40
  schema =
79
41
  if version
80
- @event_schema.schema_for(event_name, version)
42
+ @event_schema.schema_for(event_name, version, domain: domain)
81
43
  else
82
- @event_schema.latest_for(event_name)
44
+ @event_schema.latest_for(event_name, domain: domain)
83
45
  end
84
46
 
85
47
  unless schema
@@ -90,31 +52,18 @@ module EventEngine
90
52
  schema
91
53
  end
92
54
 
93
- # Returns the latest schema version for an event.
94
- #
95
- # @param event_name [Symbol]
96
- # @return [EventDefinition::Schema, nil]
97
- def latest_for(event_name)
98
- @event_schema.latest_for(event_name)
55
+ def latest_for(event_name, domain: nil)
56
+ @event_schema.latest_for(event_name, domain: domain)
99
57
  end
100
58
 
101
- # Returns the underlying EventSchema store.
102
- #
103
- # @return [EventSchema]
104
59
  def event_schema
105
60
  @event_schema
106
61
  end
107
62
 
108
- # Freezes the underlying schema, preventing further modifications.
109
- #
110
- # @return [void]
111
63
  def finalize!
112
64
  @event_schema.finalize!
113
65
  end
114
66
 
115
- # Whether the registry has been loaded with schemas.
116
- #
117
- # @return [Boolean]
118
67
  def loaded?
119
68
  @loaded == true
120
69
  end
@@ -0,0 +1,8 @@
1
+ module EventEngine
2
+ class UnregisteredProcessorError < StandardError
3
+ def initialize(name, event)
4
+ super("the rule for event #{event.event_name.inspect} (pack #{event.domain.inspect}) " \
5
+ "names processor #{name.inspect}, but no processor is registered under that name")
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ module EventEngine
2
+ class UnroutableEventError < StandardError
3
+ def initialize(event)
4
+ super("no processing rule for event #{event.event_name.inspect} " \
5
+ "(pack #{event.domain.inspect}); declare it in the rules file " \
6
+ "under events, packs, or default")
7
+ end
8
+ end
9
+ end