Class: Async::Matrix::Schema::Registry

Inherits:
Object
  • Object
show all
Defined in:
lib/async/matrix/schema/registry.rb

Overview

Loads and indexes Matrix event schemas from the official matrix-org/matrix-spec YAML files bundled in data/matrix-spec/event-schemas/schema/.

Schemas are loaded lazily on first access and cached for the lifetime of the process. Each schema is a JSONSchemer::Schema instance that resolves relative $ref paths (e.g. core-event-schema/room_event.yaml) via a file-based resolver.

registry = Async::Matrix::Schema::Registry.instance
registry["m.room.message"]                         # => JSONSchemer::Schema
registry.variant("m.room.message", "m.text")       # => JSONSchemer::Schema
registry.event_types                                # => ["m.accepted_terms", ...]

Constant Summary collapse

MATRIX_FORMATS =

Custom format validators for Matrix-specific format hints.

{
	"mx-user-id"        => ->(value, _schema) { value.is_a?(String) && value.match?(/\A@[^:]+:.+\z/) },
	"mx-room-id"        => ->(value, _schema) { value.is_a?(String) && value.match?(/\A![^:]+:.+\z/) },
	"mx-event-id"       => ->(value, _schema) { value.is_a?(String) && value.match?(/\A\$/) },
	"mx-room-alias"     => ->(value, _schema) { value.is_a?(String) && value.match?(/\A#[^:]+:.+\z/) },
	"mx-server-name"    => ->(value, _schema) { value.is_a?(String) && !value.empty? },
	"mx-unpadded-base64" => ->(value, _schema) { value.is_a?(String) && value.match?(/\A[A-Za-z0-9+\/]*\z/) },
}.freeze
GEM_ROOT =
File.expand_path("../../../..", __dir__)
SCHEMA_DIR =
File.join(GEM_ROOT, "data", "matrix-spec", "event-schemas", "schema")

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeRegistry

Returns a new instance of Registry.



54
55
56
57
58
# File 'lib/async/matrix/schema/registry.rb', line 54

def initialize
	@schemas = {}
	@variants = {}
	@loaded = false
end

Class Method Details

.instanceObject

Returns the singleton Registry instance, creating it on first access.



44
45
46
# File 'lib/async/matrix/schema/registry.rb', line 44

def instance
	@instance ||= new
end

.reset!Object

Reset the singleton (useful for testing).



49
50
51
# File 'lib/async/matrix/schema/registry.rb', line 49

def reset!
	@instance = nil
end

Instance Method Details

#[](event_type) ⇒ Object

Look up a schema by Matrix event type (e.g. “m.room.message”). Returns a JSONSchemer::Schema or nil if no schema exists for that type.



62
63
64
65
# File 'lib/async/matrix/schema/registry.rb', line 62

def [](event_type)
	load_schemas! unless @loaded
	@schemas[event_type]
end

#content_properties(event_type) ⇒ Object

Content properties defined by the schema for a given event type. Returns an array of property name strings, or empty array if unknown.



125
126
127
128
129
130
# File 'lib/async/matrix/schema/registry.rb', line 125

def content_properties(event_type)
	schema = self[event_type]
	return [] unless schema

	extract_content_properties(schema.value)
end

#event_typesArray<String>

All known base event types (excludes variant subtypes).

Returns:

  • (Array<String>)

    sorted event type strings



76
77
78
79
# File 'lib/async/matrix/schema/registry.rb', line 76

def event_types
	load_schemas! unless @loaded
	@schemas.keys.sort
end

#sizeObject

Total number of schemas loaded (base + variants).



89
90
91
92
# File 'lib/async/matrix/schema/registry.rb', line 89

def size
	load_schemas! unless @loaded
	@schemas.size + @variants.size
end

#valid?(event_hash) ⇒ Boolean

Boolean validation.

Returns:

  • (Boolean)


119
120
121
# File 'lib/async/matrix/schema/registry.rb', line 119

def valid?(event_hash)
	validate(event_hash).empty?
end

#validate(event_hash) ⇒ Object

Validate an event hash against its schema. Returns an array of error hashes (empty if valid). If no schema exists for the event type, returns empty (lenient).



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/async/matrix/schema/registry.rb', line 97

def validate(event_hash)
	event_type = event_hash["type"]
	return [] unless event_type

	base = self[event_type]
	return [] unless base

	errors = base.validate(event_hash).to_a

	# Also validate against variant if applicable
	variant_key = detect_variant_key(event_type, event_hash)
	if variant_key
		variant_schema = variant(event_type, variant_key)
		if variant_schema
			errors.concat(variant_schema.validate(event_hash).to_a)
		end
	end

	errors
end

#variant(event_type, subtype) ⇒ Object

Look up a variant schema (e.g. variant(“m.room.message”, “m.text”)). Returns a JSONSchemer::Schema or nil.



69
70
71
72
# File 'lib/async/matrix/schema/registry.rb', line 69

def variant(event_type, subtype)
	load_schemas! unless @loaded
	@variants[[event_type, subtype]]
end

#variant_typesArray<Array(String, String)>

All known variant keys as [event_type, subtype] pairs.

Returns:

  • (Array<Array(String, String)>)


83
84
85
86
# File 'lib/async/matrix/schema/registry.rb', line 83

def variant_types
	load_schemas! unless @loaded
	@variants.keys.sort
end