class NxtRegistry::Registry

Attributes

accessor[R]
config[R]
configured[RW]
interface_defined[RW]
is_leaf[RW]
mutex[R]
name[R]
namespace[R]
options[R]
parent[R]
patterns[R]
store[R]

Public Class Methods

new(name = object_id.to_s, **options, &config) click to toggle source
# File lib/nxt_registry/registry.rb, line 3
def initialize(name = object_id.to_s, **options, &config)
  @options = options
  @name = name
  @parent = options[:parent]
  @is_leaf = true
  @namespace = build_namespace
  @config = config
  @store = {}
  @configured = false
  @patterns = []
  @config = config
  @mutex = Mutex.new

  configure(&config)
end

Public Instance Methods

[](key) click to toggle source
# File lib/nxt_registry/registry.rb, line 111
def [](key)
  resolve!(key)
end
[]=(key, value) click to toggle source
# File lib/nxt_registry/registry.rb, line 115
def []=(key, value)
  register(key, value)
end
allowed_keys(*keys) click to toggle source
# File lib/nxt_registry/registry.rb, line 58
def allowed_keys(*keys)
  @allowed_keys ||= []
  return @allowed_keys if keys.empty?

  @allowed_keys += keys.map { |key| transformed_key(key) }
end
Also aliased as: attrs
attr(key) click to toggle source
# File lib/nxt_registry/registry.rb, line 67
def attr(key)
  allowed_keys(key) # @deprecated
end
attrs(*keys)
Alias for: allowed_keys
configure(&block) click to toggle source
# File lib/nxt_registry/registry.rb, line 142
def configure(&block)
  setup_defaults(options)
  define_accessors
  define_interface
  allowed_keys(*Array(options.fetch(:allowed_keys, [])))
  required_keys(*Array(options.fetch(:required_keys, [])))

  if block.present?
    if block.arity == 1
      instance_exec(self, &block)
    else
      instance_exec(&block)
    end
  end

  validate_required_keys_given
  self.configured = true
end
exclude?(key) click to toggle source
# File lib/nxt_registry/registry.rb, line 131
def exclude?(key)
  store.exclude?(transformed_key(key))
end
fetch(key, *args, &block) click to toggle source
# File lib/nxt_registry/registry.rb, line 135
def fetch(key, *args, &block)
  key = matching_key(key)
  store.fetch(key, *args, &block)
end
include?(key) click to toggle source
# File lib/nxt_registry/registry.rb, line 127
def include?(key)
  store.include?(transformed_key(key))
end
inspect()
Alias for: to_s
key?(key) click to toggle source
# File lib/nxt_registry/registry.rb, line 123
def key?(key)
  store.key?(transformed_key(key))
end
keys() click to toggle source
# File lib/nxt_registry/registry.rb, line 119
def keys
  store.keys.map(&method(:transformed_key))
end
level(name, **options, &config) click to toggle source
# File lib/nxt_registry/registry.rb, line 22
def level(name, **options, &config)
  options = options.merge(parent: self)

  if is_a_blank?(default)
    self.is_leaf = false

    self.default = RegistryBuilder.new do
      Registry.new(name, **options, &config)
    end

    # Call the builder once to guarantee we do not create a registry with a broken setup
    default.call
  elsif default.is_a?(RegistryBuilder)
    raise ArgumentError, 'Multiple nestings on the same level'
  else
    raise ArgumentError, 'Default values cannot be defined on registries that nest others'
  end
end
register(key = Blank.new, value = Blank.new, **options, &block) click to toggle source
# File lib/nxt_registry/registry.rb, line 71
def register(key = Blank.new, value = Blank.new, **options, &block)
  if block_given?
    if is_a_blank?(value)
      registry(key, **options, &block)
    else
      raise_register_argument_error
    end
  else
    __register(key, value, raise_on_key_already_registered: true)
  end
end
register!(key = Blank.new, value = Blank.new, **options, &block) click to toggle source
# File lib/nxt_registry/registry.rb, line 83
def register!(key = Blank.new, value = Blank.new, **options, &block)
  if block_given?
    if is_a_blank?(value)
      registry!(key, **options, &block)
    else
      raise_register_argument_error
    end
  else
    __register(key, value, raise_on_key_already_registered: false)
  end
end
registry(name, **options, &config) click to toggle source
# File lib/nxt_registry/registry.rb, line 41
def registry(name, **options, &config)
  opts = conditionally_inherit_options(options)
  register(name, Registry.new(name, **opts, &config))
end
registry!(name, **options, &config) click to toggle source
# File lib/nxt_registry/registry.rb, line 46
def registry!(name, **options, &config)
  opts = conditionally_inherit_options(options)
  register!(name, Registry.new(name, **opts, &config))
end
required_keys(*keys) click to toggle source
# File lib/nxt_registry/registry.rb, line 51
def required_keys(*keys)
  @required_keys ||= []
  return @required_keys if keys.empty?

  @required_keys += keys.map { |key| transformed_key(key) }
end
resolve(*keys) click to toggle source
# File lib/nxt_registry/registry.rb, line 101
def resolve(*keys)
  keys.inject(self) do |current_registry, key|
    current_registry.send(:__resolve, key, raise_on_key_not_registered: false) || break
  end
end
resolve!(*keys) click to toggle source
# File lib/nxt_registry/registry.rb, line 95
def resolve!(*keys)
  keys.inject(self) do |current_registry, key|
    current_registry.send(:__resolve, key, raise_on_key_not_registered: true)
  end
end
to_h() click to toggle source
# File lib/nxt_registry/registry.rb, line 107
def to_h
  store
end
to_s() click to toggle source
# File lib/nxt_registry/registry.rb, line 161
def to_s
  "Registry[#{name}] -> #{store.to_s}"
end
Also aliased as: inspect

Private Instance Methods

__register(key, value, raise_on_key_already_registered: true) click to toggle source
# File lib/nxt_registry/registry.rb, line 189
def __register(key, value, raise_on_key_already_registered: true)
  mutex.synchronize do
    key = if key.is_a?(Regexp)
      patterns << key
      key
    else
      transformed_key(key)
    end

    raise ArgumentError, "Not allowed to register values in a registry that contains nested registries" unless is_leaf
    raise KeyError, "Keys are restricted to #{allowed_keys}" if key_not_allowed?(key)

    on_key_already_registered && on_key_already_registered.call(key) if store[key] && raise_on_key_already_registered

    store[key] = value
  end
end
__resolve(key, raise_on_key_not_registered: true) click to toggle source
# File lib/nxt_registry/registry.rb, line 207
def __resolve(key, raise_on_key_not_registered: true)

  key = transformed_key(key)

  value = if is_leaf?
    resolved_key = key_resolver.call(key)

    if store.key?(resolved_key)
      store.fetch(resolved_key)
    elsif (pattern = matching_pattern(resolved_key))
      store.fetch(pattern)
    else
      if is_a_blank?(default)
        return unless raise_on_key_not_registered

        on_key_not_registered && on_key_not_registered.call(key)
      else
        value = resolve_default(key)
        return value unless memoize

        store[key] ||= value
      end
    end
  else
    store[key] ||= default.call
  end

  value = call_or_value(value, key)

  resolver.call(value)
end
build_namespace() click to toggle source
# File lib/nxt_registry/registry.rb, line 360
def build_namespace
  parent ? name.to_s.prepend("#{parent.send(:namespace)}.") : name.to_s
end
call_or_value(value, key) click to toggle source
# File lib/nxt_registry/registry.rb, line 247
def call_or_value(value, key)
  return value unless call
  return value if value.is_a?(NxtRegistry::Registry)
  return value unless value.respond_to?(:call)

  args = [key, value]
  value.call(*args.take(value.arity))
end
conditionally_inherit_options(opts) click to toggle source
# File lib/nxt_registry/registry.rb, line 172
def conditionally_inherit_options(opts)
  base = opts.delete(:inherit_options) ? options : {}
  base.merge(opts).merge(parent: self)
end
define_accessors() click to toggle source
# File lib/nxt_registry/registry.rb, line 307
def define_accessors
  %w[default memoize call resolver key_resolver transform_keys on_key_already_registered on_key_not_registered].each do |attribute|
    define_singleton_method attribute do |value = Blank.new, &block|
      value = block if block

      if is_a_blank?(value)
        instance_variable_get("@#{attribute}")
      else
        options[attribute.to_sym] ||= value
        instance_variable_set("@#{attribute}", value)
      end
    end

    define_singleton_method "#{attribute}=" do |value|
      options[attribute.to_sym] ||= value
      instance_variable_set("@#{attribute}", value)
    end
  end
end
define_interface() click to toggle source
# File lib/nxt_registry/registry.rb, line 260
def define_interface
  return if interface_defined

  raise_invalid_accessor_name(accessor) if respond_to?(accessor.to_s)
  accessor_with_bang = "#{accessor}!"
  raise_invalid_accessor_name(accessor_with_bang) if respond_to?(accessor_with_bang)

  define_singleton_method accessor.to_s do |key = Blank.new, value = Blank.new|
    return self if is_a_blank?(key)

    key = transformed_key(key)

    if is_a_blank?(value)
      resolve(key)
    else
      register(key, value)
    end
  end

  define_singleton_method accessor_with_bang do |key = Blank.new, value = Blank.new|
    return self if is_a_blank?(key)

    key = transformed_key(key)

    if is_a_blank?(value)
      resolve!(key)
    else
      register!(key, value)
    end
  end

  self.interface_defined = true
end
initialize_copy(original) click to toggle source
Calls superclass method
# File lib/nxt_registry/registry.rb, line 376
def initialize_copy(original)
  super

  @mutex = Mutex.new
  containers = %i[store options]
  variables = %i[patterns required_keys allowed_keys namespace on_key_already_registered on_key_not_registered]

  containers.each { |c| instance_variable_set("@#{c}", original.send(c).deep_dup) }
  variables.each { |v| instance_variable_set("@#{v}", original.send(v).dup) }
end
is_a_blank?(value) click to toggle source
# File lib/nxt_registry/registry.rb, line 368
def is_a_blank?(value)
  value.is_a?(Blank)
end
is_leaf?() click to toggle source
# File lib/nxt_registry/registry.rb, line 185
def is_leaf?
  @is_leaf
end
key_not_allowed?(key) click to toggle source
# File lib/nxt_registry/registry.rb, line 327
def key_not_allowed?(key)
  return if allowed_keys.empty?

  allowed_keys.exclude?(transformed_key(key))
end
matching_key(key) click to toggle source
# File lib/nxt_registry/registry.rb, line 239
def matching_key(key)
  key = transformed_key(key)
  # if key is present it always wins over patterns
  return key if store.key?(key)

  matching_pattern(key) || key
end
matching_pattern(key) click to toggle source
# File lib/nxt_registry/registry.rb, line 256
def matching_pattern(key)
  patterns.find { |pattern| key.match?(pattern) }
end
raise_invalid_accessor_name(name) click to toggle source
# File lib/nxt_registry/registry.rb, line 372
def raise_invalid_accessor_name(name)
  raise ArgumentError, "#{self} already implements a method named: #{name}. Please choose a different accessor name"
end
raise_key_already_registered_error(key) click to toggle source
# File lib/nxt_registry/registry.rb, line 341
def raise_key_already_registered_error(key)
  raise NxtRegistry::Errors::KeyAlreadyRegisteredError, "Key '#{key}' already registered in registry '#{namespace}'"
end
raise_key_not_registered_error(key) click to toggle source
# File lib/nxt_registry/registry.rb, line 345
def raise_key_not_registered_error(key)
  raise NxtRegistry::Errors::KeyNotRegisteredError, "Key '#{key}' not registered in registry '#{namespace}'"
end
raise_register_argument_error() click to toggle source
# File lib/nxt_registry/registry.rb, line 364
def raise_register_argument_error
  raise ArgumentError, 'Either provide a key value pair or a block to register'
end
resolve_default(key) click to toggle source
# File lib/nxt_registry/registry.rb, line 333
def resolve_default(key)
  if call && default.respond_to?(:call)
    default.arity > 0 ? default.call(key) : default.call
  else
    default
  end
end
setup_defaults(options) click to toggle source
# File lib/nxt_registry/registry.rb, line 294
def setup_defaults(options)
  @default = options.fetch(:default) { Blank.new }
  @memoize = options.fetch(:memoize) { true }
  @call = options.fetch(:call) { true }
  @resolver = options.fetch(:resolver, ->(val) { val })
  @key_resolver = options.fetch(:key_resolver, ->(val) { val })
  @transform_keys = options.fetch(:transform_keys) { ->(key) { key.is_a?(Regexp) ? key : key.to_s } }
  @accessor = options.fetch(:accessor) { name }

  @on_key_already_registered = options.fetch(:on_key_already_registered) { ->(key) { raise_key_already_registered_error(key) } }
  @on_key_not_registered = options.fetch(:on_key_not_registered) { ->(key) { raise_key_not_registered_error(key) } }
end
transformed_key(key) click to toggle source
# File lib/nxt_registry/registry.rb, line 349
def transformed_key(key)
  @transformed_key ||= {}
  @transformed_key[key] ||= begin
    if transform_keys && !is_a_blank?(key)
      transform_keys.call(key)
    else
      key
    end
  end
end
validate_required_keys_given() click to toggle source
# File lib/nxt_registry/registry.rb, line 177
def validate_required_keys_given
  required_keys.each do |key|
    next if store.key?(key)

    raise Errors::RequiredKeyMissing, "Required key '#{key}' missing in #{self}"
  end
end