Class: EcsRails::Registry

Inherits:
Object
  • Object
show all
Defined in:
lib/ecs_rails/registry.rb

Overview

Records which components each entity declares. See RFC-0002.

The registry is a process-wide singleton (EcsRails.registry) populated by the component DSL (RFC-0004) at class-load time, and read by generators, delegation and — later — systems.

Reload safety is the whole design constraint. In development Rails does not mutate a reloaded class in place: it removes the constant and autoloads a brand-new Class object under the same name. A registry that held class objects would pin the old, orphaned constants forever, and every lookup would hand back classes the rest of the app has already forgotten. So nothing here stores a Class: entries are keyed by class name, and names are resolved back to live constants via #constantize at read time.

Defined Under Namespace

Classes: Declaration

Instance Method Summary collapse

Constructor Details

#initializeRegistry

Returns a new instance of Registry.



86
87
88
# File 'lib/ecs_rails/registry.rb', line 86

def initialize
  clear!
end

Instance Method Details

#clear!self

Resets the registry. Used between tests and by the Railtie’s to_prepare.

Returns:

  • (self)


161
162
163
164
# File 'lib/ecs_rails/registry.rb', line 161

def clear!
  @declarations = {}
  self
end

#components_for(entity_class) ⇒ Array<Declaration>

The declarations for an entity, in declaration order.

Resolution is lazy, so this never raises for a stale entry; asking the returned Declaration for #component_class does. That is deliberate: a dangling name means the registry has drifted out of sync with the app, and silently dropping the entry would make generators emit an incomplete schema and delegation quietly stop working. #clear! is the supported way to drop entries, and the Railtie calls it on every reload.

Only the entity’s own declarations — inheritance is walked on read by DSL#component_declarations, not copied down here.

Parameters:

Returns:

  • (Array<Declaration>)

    its declarations; empty if none



138
139
140
141
# File 'lib/ecs_rails/registry.rb', line 138

def components_for(entity_class)
  declarations = @declarations[name_for(entity_class)]
  declarations ? declarations.dup : []
end

#entities_for(component_class) ⇒ Array<Class<EcsRails::Entity>>

Every entity class declaring this component, as live class objects.

Parameters:

Returns:

Raises:

  • (NameError)

    if a recorded entity constant has gone away



148
149
150
151
152
153
154
155
156
# File 'lib/ecs_rails/registry.rb', line 148

def entities_for(component_class)
  component_name = name_for(component_class)

  @declarations.each_value.with_object([]) do |declarations, entities|
    declarations.each do |declaration|
      entities << declaration.entity_class if declaration.component_class_name == component_name
    end
  end
end

#register(entity_class:, component_class:, options: {}) ⇒ Declaration

Records one declaration. Returns the Declaration.

Raises DuplicateComponent if this entity already declares this component — per ADR-0005 a component appears at most once per entity, and RFC-0004 relies on the raise to catch a doubled component line at class-load time.

Parameters:

  • entity_class (Class<EcsRails::Entity>)

    the declaring entity

  • component_class (Class<EcsRails::Component>)

    the declared component

  • options (Hash) (defaults to: {})

    only:/except: delegation options

Returns:

Raises:

  • (EcsRails::DuplicateComponent)

    if this entity already declares this component (ADR-0005)

  • (ArgumentError)

    if either class is anonymous, since the registry keys entries by class name



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/ecs_rails/registry.rb', line 104

def register(entity_class:, component_class:, options: {})
  entity_name = name_for(entity_class)
  component_name = name_for(component_class)

  declarations = (@declarations[entity_name] ||= [])

  if declarations.any? { |declaration| declaration.component_class_name == component_name }
    raise DuplicateComponent,
          "#{entity_name} already declares #{component_name}"
  end

  declaration = Declaration.new(
    entity_class_name: entity_name,
    component_class_name: component_name,
    options: options
  )
  declarations << declaration
  declaration
end

#restore(snapshot) ⇒ self

Replaces the declarations with a previously taken #snapshot.

Parameters:

  • snapshot (Hash)

    a value previously returned by #snapshot

Returns:

  • (self)

See Also:



183
184
185
186
# File 'lib/ecs_rails/registry.rb', line 183

def restore(snapshot)
  @declarations = snapshot.transform_values(&:dup)
  self
end

#snapshotHash

An opaque snapshot of the current declarations, for save/restore around a block that mutates the registry — chiefly tests that clear! the process-wide singleton and would otherwise wipe declarations that host/app classes made at load time. Declaration is frozen and holds only strings, so a shallow dup of the arrays is a safe, cheap copy.

Returns:

  • (Hash)

    an opaque snapshot to hand back to #restore

See Also:



174
175
176
# File 'lib/ecs_rails/registry.rb', line 174

def snapshot
  @declarations.transform_values(&:dup)
end