mirror of
https://github.com/meineerde/rackstash.git
synced 2026-01-31 17:27:13 +00:00
Add Rackstash::ClassRegistry to register filters and encoders
Using the ClassRegistry, we can register short names for filter and encoder classes so that they can be specified in a shorter and more readable way during creation of the logger.
This commit is contained in:
parent
1b2dd09000
commit
01593a9b8b
114
lib/rackstash/class_registry.rb
Normal file
114
lib/rackstash/class_registry.rb
Normal file
@ -0,0 +1,114 @@
|
||||
# frozen_string_literal: true
|
||||
#
|
||||
# Copyright 2017 Holger Just
|
||||
#
|
||||
# This software may be modified and distributed under the terms
|
||||
# of the MIT license. See the LICENSE.txt file for details.
|
||||
|
||||
module Rackstash
|
||||
class ClassRegistry
|
||||
include ::Enumerable
|
||||
|
||||
# @return [String] the human-readable singular name of the registered
|
||||
# objects. It is used to build more useful error messages.
|
||||
attr_reader :object_type
|
||||
|
||||
# @param object_type [#to_s] the human-readable singular name of the
|
||||
# registered objects. It is used to build more useful error messages.
|
||||
def initialize(object_type = 'class')
|
||||
@object_type = object_type.to_s
|
||||
@registry = {}
|
||||
end
|
||||
|
||||
# Retrieve the registered class for a given name. If the argument is already
|
||||
# a class, we return it unchanged.
|
||||
#
|
||||
# @param spec [Class,String,Symbol] Either a class (in which case it is
|
||||
# returned directly) or the name of a registered class.
|
||||
# @raise [KeyError] when giving a `String` or `Symbol` but no registered
|
||||
# class was found for it
|
||||
# @raise [TypeError] when giving an invalid object
|
||||
# @return [Class] the registered class (when giving a `String` or `Symbol`)
|
||||
# or the given class (when giving a `Class`)
|
||||
def [](spec)
|
||||
case spec
|
||||
when Class
|
||||
spec
|
||||
when String, Symbol, ->(s) { s.respond_to?(:to_sym) }
|
||||
@registry.fetch(spec.to_sym) do
|
||||
raise KeyError, "No #{@object_type} was registered for #{spec.inspect}"
|
||||
end
|
||||
else
|
||||
raise TypeError, "#{spec.inspect} can not be used to describe #{@object_type}s"
|
||||
end
|
||||
end
|
||||
|
||||
# Register a class for the given name.
|
||||
#
|
||||
# @param name [String, Symbol] the name at which the class should be
|
||||
# registered
|
||||
# @param registered_class [Class] the class to register at `name`
|
||||
# @raise [TypeError] if `name` is not a `String` or `Symbol`, or if
|
||||
# `registered_class` is not a `Class`
|
||||
# @return [Class] the `registered_class`
|
||||
def []=(name, registered_class)
|
||||
unless registered_class.is_a?(Class)
|
||||
raise TypeError, 'Can only register class objects'
|
||||
end
|
||||
|
||||
case name
|
||||
when String, Symbol
|
||||
@registry[name.to_sym] = registered_class
|
||||
else
|
||||
raise TypeError, "Can not use #{name.inspect} to register a #{@object_type} class"
|
||||
end
|
||||
registered_class
|
||||
end
|
||||
|
||||
# Remove all registered classes
|
||||
#
|
||||
# @return [self]
|
||||
def clear
|
||||
@registry.clear
|
||||
self
|
||||
end
|
||||
|
||||
# Calls the given block once for each name in `self`, passing the name and
|
||||
# the registered class as parameters.
|
||||
#
|
||||
# An `Enumerator` is returned if no block is given.
|
||||
#
|
||||
# @yield [name, registered_class] calls the given block once for each name
|
||||
# @yieldparam name [Symbol] the name of the registered class
|
||||
# @yieldparam registered_class [Class] the registered class
|
||||
# @return [Enumerator, self] `self` if a block was given or an `Enumerator`
|
||||
# if no block was given.
|
||||
def each
|
||||
return enum_for(__method__) unless block_given?
|
||||
@registry.each_pair do |name, registered_class|
|
||||
yield name, registered_class
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
# Prevents further modifications to `self`. A `RuntimeError` will be raised
|
||||
# if modification is attempted. There is no way to unfreeze a frozen object.
|
||||
#
|
||||
# @return [self]
|
||||
def freeze
|
||||
@registry.freeze
|
||||
super
|
||||
end
|
||||
|
||||
# @return [::Array<Symbol>] a new array populated with all registered names
|
||||
def names
|
||||
@registry.keys
|
||||
end
|
||||
|
||||
# @return [Hash<Symbol=>Class>] a new `Hash` containing all registered
|
||||
# names and classes
|
||||
def to_h
|
||||
@registry.dup
|
||||
end
|
||||
end
|
||||
end
|
||||
153
spec/rackstash/class_registry_spec.rb
Normal file
153
spec/rackstash/class_registry_spec.rb
Normal file
@ -0,0 +1,153 @@
|
||||
# frozen_string_literal: true
|
||||
#
|
||||
# Copyright 2017 Holger Just
|
||||
#
|
||||
# This software may be modified and distributed under the terms
|
||||
# of the MIT license. See the LICENSE.txt file for details.
|
||||
|
||||
require 'spec_helper'
|
||||
|
||||
require 'rackstash/class_registry'
|
||||
|
||||
describe Rackstash::ClassRegistry do
|
||||
let(:registry) { described_class.new('value') }
|
||||
let(:klass) { Class.new}
|
||||
|
||||
describe '#initialize' do
|
||||
it 'sets the object_type' do
|
||||
expect(registry.object_type).to eql 'value'
|
||||
end
|
||||
end
|
||||
|
||||
describe '#[]' do
|
||||
before do
|
||||
registry[:class] = klass
|
||||
end
|
||||
|
||||
it 'returns the class for a String' do
|
||||
expect(registry['class']).to equal klass
|
||||
end
|
||||
|
||||
it 'returns the class for a Symbol' do
|
||||
expect(registry[:class]).to equal klass
|
||||
end
|
||||
|
||||
it 'returns an actual class' do
|
||||
expect(registry[klass]).to equal klass
|
||||
expect(registry[String]).to equal String
|
||||
end
|
||||
|
||||
it 'raises a KeyError on unknown names' do
|
||||
expect { registry[:unknown] }
|
||||
.to raise_error(KeyError, 'No value was registered for :unknown')
|
||||
expect { registry['invalid'] }
|
||||
.to raise_error(KeyError, 'No value was registered for "invalid"')
|
||||
end
|
||||
|
||||
it 'raises a TypeError on invalid names' do
|
||||
expect { registry[0] }
|
||||
.to raise_error(TypeError, '0 can not be used to describe values')
|
||||
expect { registry[nil] }
|
||||
.to raise_error(TypeError, 'nil can not be used to describe values')
|
||||
expect { registry[true] }
|
||||
.to raise_error(TypeError, 'true can not be used to describe values')
|
||||
end
|
||||
end
|
||||
|
||||
describe '[]=' do
|
||||
it 'registers a class at the given name' do
|
||||
registry[:name] = klass
|
||||
registry[:alias] = klass
|
||||
|
||||
expect(registry[:name]).to equal klass
|
||||
expect(registry[:alias]).to equal klass
|
||||
end
|
||||
|
||||
it 'rejects invalid names' do
|
||||
expect { registry[0] = Class.new }
|
||||
.to raise_error(TypeError, 'Can not use 0 to register a value class')
|
||||
expect { registry[nil] = Class.new }
|
||||
.to raise_error(TypeError, 'Can not use nil to register a value class')
|
||||
expect { registry[String] = Class.new }
|
||||
.to raise_error(TypeError, 'Can not use String to register a value class')
|
||||
end
|
||||
|
||||
it 'rejects invalid values' do
|
||||
expect { registry[:foo] = 123 }
|
||||
.to raise_error(TypeError, 'Can only register class objects')
|
||||
expect { registry[:foo] = -> { :foo } }
|
||||
.to raise_error(TypeError, 'Can only register class objects')
|
||||
expect { registry[:nil] = nil }
|
||||
.to raise_error(TypeError, 'Can only register class objects')
|
||||
end
|
||||
end
|
||||
|
||||
describe '#clear' do
|
||||
it 'removes all registrations' do
|
||||
registry[:class] = klass
|
||||
expect(registry[:class]).to equal klass
|
||||
|
||||
expect(registry.clear).to equal registry
|
||||
expect { registry[:class] }.to raise_error(KeyError)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#each' do
|
||||
it 'yield each registered pait' do
|
||||
registry['name'] = klass
|
||||
registry[:alias] = klass
|
||||
|
||||
expect { |b| registry.each(&b) }
|
||||
.to yield_successive_args([:name, klass], [:alias, klass])
|
||||
end
|
||||
|
||||
it 'returns the registry if a block was provided' do
|
||||
registry['name'] = klass
|
||||
expect(registry.each {}).to equal registry
|
||||
end
|
||||
|
||||
it 'returns an Enumerator if no block was provided' do
|
||||
registry['name'] = klass
|
||||
expect(registry.each).to be_instance_of Enumerator
|
||||
end
|
||||
end
|
||||
|
||||
describe '#freeze' do
|
||||
it 'freezes the object' do
|
||||
expect(registry.freeze).to equal registry
|
||||
expect(registry).to be_frozen
|
||||
end
|
||||
|
||||
it 'denies all further changes' do
|
||||
registry.freeze
|
||||
expect { registry[:name] = klass }.to raise_error(RuntimeError)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#names' do
|
||||
it 'returns all registered names' do
|
||||
registry['name'] = klass
|
||||
registry[:alias] = klass
|
||||
|
||||
expect(registry.names).to eql [:name, :alias]
|
||||
end
|
||||
end
|
||||
|
||||
describe '#to_h' do
|
||||
it 'returns a Hash containing all registrations' do
|
||||
registry['name'] = klass
|
||||
registry[:alias] = klass
|
||||
|
||||
expect(registry.to_h).to eql(name: klass, alias: klass)
|
||||
end
|
||||
|
||||
it 'returns a copy of the internal data' do
|
||||
registry['name'] = klass
|
||||
|
||||
hash = registry.to_h
|
||||
hash[:alias] = klass
|
||||
|
||||
expect { registry[:alias] }.to raise_error(KeyError)
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
x
Reference in New Issue
Block a user