Class: AttrJson::AttributeDefinition::Registry

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

Overview

Attached to a class to record the json attributes registered, with either AttrJson::Record or AttrJson::Model.

Think of it as mostly like a hash keyed by attribute name, value an AttributeDefinition.

It is expected to be used by AttrJson::Record and AttrJson::Model, you shouldn't need to interact with it directly.

It is intentionally immutable to make it harder to accidentally mutate a registry shared with superclass in a class_attribute, instead of properly assigning a new modified registry.

self.some_registry_attribute = self.some_registry_attribute.with(
   attr_definition_1, attr_definition_2
)
# => Returns a NEW AttributeDefinition object

All references in code to "definition" are to a AttrJson::AttributeDefinition instance.

Instance Method Summary collapse

Constructor Details

#initialize(hash = {}) ⇒ Registry

Returns a new instance of Registry.



27
28
29
30
31
32
33
34
# File 'lib/attr_json/attribute_definition/registry.rb', line 27

def initialize(hash = {})
  @name_to_definition = hash.dup
  @store_key_to_definition = {}

  @name_to_definition.values.each { |d| store_key_index!(d) }

  @container_attributes_registered = Hash.new { Set.new }
end

Instance Method Details

#[](key) ⇒ Object



40
41
42
# File 'lib/attr_json/attribute_definition/registry.rb', line 40

def [](key)
  @name_to_definition[key.to_sym]
end

#attribute_namesObject

Returns all registered attributes as an array of symbols



64
65
66
# File 'lib/attr_json/attribute_definition/registry.rb', line 64

def attribute_names
  @name_to_definition.keys
end

#container_attribute_registered?(attribute_name:, model:) ⇒ Boolean

Returns:

  • (Boolean)


104
105
106
# File 'lib/attr_json/attribute_definition/registry.rb', line 104

def container_attribute_registered?(attribute_name:, model:)
   @container_attributes_registered[attribute_name.to_sym].include?(model)
end

#container_attributesObject



68
69
70
# File 'lib/attr_json/attribute_definition/registry.rb', line 68

def container_attributes
  @store_key_to_definition.keys.collect { |s| AttrJson.efficient_to_s(s) }
end

#definitionsObject



58
59
60
61
# File 'lib/attr_json/attribute_definition/registry.rb', line 58

def definitions
  # Since we are immutable, we can cache this to avoid allocation in a hot spot
  @stored_definitions ||= @name_to_definition.values
end

#fetch(key, *args, &block) ⇒ Object



36
37
38
# File 'lib/attr_json/attribute_definition/registry.rb', line 36

def fetch(key, *args, &block)
  @name_to_definition.fetch(key.to_sym, *args, &block)
end

#has_attribute?(key) ⇒ Boolean

Returns:

  • (Boolean)


44
45
46
# File 'lib/attr_json/attribute_definition/registry.rb', line 44

def has_attribute?(key)
  @name_to_definition.has_key?(key.to_sym)
end

#register_container_attribute(attribute_name:, model:) ⇒ Object

We need to lazily set the container type only the FIRST time

While also avoiding this triggering ActiveRecord to actually go to DB, we don't want DB connection forced on boot, that's a problem for many apps, including that may not have a DB connection in initial development. (#type_for_attribute forces DB connection)

AND we need to call container attriubte on SUB-CLASS AGAIN, iff sub-class has any of it's own new registrations, to make sure we get the right type in sub-class!

So we just keep track of whether we've registered ourselves, so we can first time we need to.

While current implementation is simple, this has ended up a bit fragile, a different API that doesn't require us to do this implicitly lazily might be preferred! But this is what we got for now.



100
101
102
# File 'lib/attr_json/attribute_definition/registry.rb', line 100

def register_container_attribute(attribute_name:, model:)
  @container_attributes_registered[attribute_name.to_sym] << model
end

#store_key_lookup(container_attribute, store_key) ⇒ Object

Can return nil if none found.



53
54
55
56
# File 'lib/attr_json/attribute_definition/registry.rb', line 53

def store_key_lookup(container_attribute, store_key)
  @store_key_to_definition[AttrJson.efficient_to_s(container_attribute)] &&
    @store_key_to_definition[AttrJson.efficient_to_s(container_attribute)][AttrJson.efficient_to_s(store_key)]
end

#type_for_attribute(key) ⇒ Object



48
49
50
# File 'lib/attr_json/attribute_definition/registry.rb', line 48

def type_for_attribute(key)
  self[key].type
end

#with(*definitions) ⇒ Object

This is how you register additional definitions, as a non-mutating return-a-copy operation.



74
75
76
77
78
79
80
# File 'lib/attr_json/attribute_definition/registry.rb', line 74

def with(*definitions)
  self.class.new(@name_to_definition).tap do |copied|
    definitions.each do |defin|
      copied.add!(defin)
    end
  end
end