1
0
mirror of https://github.com/meineerde/rackstash.git synced 2026-02-01 01:37:12 +00:00

Build a new Filter from a String or Symbol

This commit is contained in:
Holger Just 2017-08-05 22:50:42 +02:00
parent 9e23267c76
commit f0c65524a7
2 changed files with 117 additions and 0 deletions

View File

@ -23,5 +23,54 @@ module Rackstash
# A filter can be any object responding to `call`, e.g. a Proc or a concrete
# class inside this module.
module Filters
# Create a new filter instance from the specified class and the given
# arguments. The class can be given as an actual class or as the name of a
# filter in which case we are resolving it to a class defined inside the
# {Rackstash::Filters} namespace.
#
# @param klass [Class, Symbol, String] a description of the class from which
# we are creating a new filter object. When giving a `Class`, we are using
# it as is. When giving a `String` or `Symbol`, we are determining the
# associated class from the {Rackstash::Filters} module and create an
# instance of that.
# @param args [Array] an optional list of arguments which is passed to the
# initializer for the new filter object.
# @raise [TypeError] if we can not create a new Filter object from `class`
# @raise [NameError] if we could not find a filter class for the specified
# class name
# @return [Object] a new filter object
def self.build(klass, *args, &block)
case klass
when Class
klass.new(*args, &block)
when Symbol, String
filter_class_name = klass.to_s
.sub(/^[a-z\d]*/) { $&.capitalize }
.gsub(/(?:_)([a-z\d]*)/) { $1.capitalize }
.to_sym
filter_class = const_get(filter_class_name, false)
filter_class.new(*args, &block)
else
raise TypeError, "Can not build filter for #{klass.inspect}"
end
end
# @return [Hash<Symbol => Class>] a Hash with names of filters and their
# respective classes which can be used with {Filters.build} to create a
# new filter object
def self.known
constants.each_with_object({}) do |const, known|
filter_class = const_get(const, false)
next unless filter_class.is_a?(Class)
filter_class_name = const.to_s
.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
.gsub(/([a-z\d])([A-Z])/,'\1_\2')
.downcase
.to_sym
known[filter_class_name] = filter_class
end
end
end
end

View File

@ -0,0 +1,68 @@
# 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 'securerandom'
require 'rackstash/filters'
describe Rackstash::Filters do
let(:filter_class) { Class.new }
let(:random) { SecureRandom.hex(6) }
let(:filter_class_name) { :"FilterClass#{random}" }
around(:each) do |example|
described_class.const_set(filter_class_name, filter_class)
example.run
described_class.send(:remove_const, filter_class_name)
end
describe '.build' do
it 'builds a filter from a class' do
args = ['arg1', foo: 'bar']
expect(filter_class).to receive(:new).with(*args)
described_class.build(filter_class, *args)
end
it 'builds a filter from a Symbol' do
args = ['arg1', foo: 'bar']
expect(filter_class).to receive(:new).with(*args)
described_class.build(:"filter_class#{random}", *args)
end
it 'builds a filter from a String' do
args = ['arg1', foo: 'bar']
expect(filter_class).to receive(:new).with(*args)
described_class.build("filter_class#{random}", *args)
end
it 'raises a TypeError with different arguments' do
expect { described_class.build(123) }.to raise_error(TypeError)
expect { described_class.build(nil) }.to raise_error(TypeError)
expect { described_class.build(true) }.to raise_error(TypeError)
end
end
describe '.known' do
it 'returns a Hash with known Filters' do
expect(described_class.known).not_to be_empty
expect(described_class.known.keys).to all(
be_a(Symbol)
.and match(/\A[a-z0-9_]+\z/)
)
expect(described_class.known.values).to all be_a(Class)
end
it 'includes Filter classes' do
expect(described_class.known[:"filter_class#{random}"]).to equal filter_class
end
end
end