mirror of
https://github.com/meineerde/rackstash.git
synced 2026-01-31 17:27:13 +00:00
Allow to define conditionals on built filters
Now you can define optional filters which only run if some condition is true or false. This can be used to e.g. update fields depending on some tags being present in the event.
This commit is contained in:
parent
a475a5c6fc
commit
de6b60925a
@ -58,21 +58,51 @@ module Rackstash
|
||||
# we then create a filter object as before. When giving an object which
|
||||
# responds to `call` already (e.g. a `Proc`, we return it unchanged,
|
||||
# ignoring any additional passed `args`.
|
||||
# @param only_if [#call, nil] An optional condition defining whether the
|
||||
# filter should be applied, usually given as a `Proc` object. Before
|
||||
# evaluating the newly created filter object, we first call the given
|
||||
# proc with the event as its argument. The filter is applied only if the
|
||||
# proc returns a truethy value.
|
||||
# @param not_if [#call, nil] An optional condition defining whether the
|
||||
# filter should not be applied, usually given as a `Proc` object. Before
|
||||
# evaluating the newly created filter object, we first call the given
|
||||
# proc with the event as its argument. The filter is not applied if the
|
||||
# proc returns a truethy value.
|
||||
# @param args [Array] an optional list of arguments which is passed to the
|
||||
# initializer for the new filter object.
|
||||
# @param kwargs [Hash] an optional list of keyword arguments which are
|
||||
# passed to the initializer for the new filter object.
|
||||
# @raise [TypeError] if we can not create a new filter object from the
|
||||
# given `filter_spec`, usually because it is an unsupported type
|
||||
# @raise [KeyError] if we could not find a filter class in the registry
|
||||
# for the specified class name
|
||||
# @return [Object] a new filter object
|
||||
def build(filter_spec, *args, &block)
|
||||
def build(filter_spec, *args, only_if: nil, not_if: nil, **kwargs, &block)
|
||||
case filter_spec
|
||||
when ->(filter) { filter.respond_to?(:call) }
|
||||
filter_spec
|
||||
else
|
||||
registry[filter_spec].new(*args, &block)
|
||||
args << kwargs unless kwargs.empty?
|
||||
|
||||
filter = registry[filter_spec].new(*args, &block)
|
||||
conditional_filter(filter, only_if: only_if, not_if: not_if)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def conditional_filter(filter, only_if: nil, not_if: nil)
|
||||
return filter if only_if.nil? && not_if.nil?
|
||||
|
||||
conditional = Module.new do
|
||||
define_method(:call) do |event|
|
||||
return event if only_if && !only_if.call(event)
|
||||
return event if not_if && not_if.call(event)
|
||||
super(event)
|
||||
end
|
||||
end
|
||||
filter.extend(conditional)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -56,6 +56,83 @@ describe Rackstash::Filter do
|
||||
expect(described_class.build(filter, :ignored, 42)).to equal filter
|
||||
end
|
||||
|
||||
context 'with conditionals' do
|
||||
let(:event) { Object.new }
|
||||
|
||||
it 'applies the only_if conditional for new filters' do
|
||||
only_if = -> {}
|
||||
filter = described_class.build(filter_name, only_if: only_if)
|
||||
|
||||
expect(only_if).to receive(:call).and_return false
|
||||
expect { filter.call({}) }.not_to raise_error
|
||||
end
|
||||
|
||||
it 'applies the not_if conditional for new filters' do
|
||||
not_if = -> {}
|
||||
filter = described_class.build(filter_name, not_if: not_if)
|
||||
|
||||
expect(not_if).to receive(:call).and_return true
|
||||
expect(filter.call(event)).to equal event
|
||||
end
|
||||
|
||||
it 'applies both conditionals for new filters' do
|
||||
only_if = -> {}
|
||||
not_if = -> {}
|
||||
|
||||
filter = described_class.build(filter_name, only_if: only_if, not_if: not_if)
|
||||
|
||||
expect(only_if).to receive(:call).and_return true
|
||||
expect(not_if).to receive(:call).and_return false
|
||||
expect(filter.call(event)).to eql 'filtered'
|
||||
end
|
||||
|
||||
it 'keeps the class hierarchy unchanged' do
|
||||
filter = described_class.build(filter_name, only_if: ->(event){ false })
|
||||
|
||||
expect(filter).to be_instance_of(filter_class)
|
||||
end
|
||||
|
||||
it 'ignores the conditional for existing filters' do
|
||||
filter = filter_class.new
|
||||
only_if = -> {}
|
||||
|
||||
expect(described_class.build(filter, only_if: only_if))
|
||||
.to equal filter
|
||||
|
||||
expect(only_if).not_to receive(:call)
|
||||
expect(described_class.build(filter, only_if: only_if).call(event))
|
||||
.to eql 'filtered'
|
||||
end
|
||||
|
||||
it 'passes keyword arguments to the initializer' do
|
||||
filter_class.class_eval do
|
||||
def initialize(mandatory:)
|
||||
@mandatory = mandatory
|
||||
end
|
||||
|
||||
attr_reader :mandatory
|
||||
end
|
||||
|
||||
filter = described_class.build(filter_name, only_if: ->{}, mandatory: 'foo')
|
||||
expect(filter.mandatory).to eql 'foo'
|
||||
end
|
||||
end
|
||||
|
||||
context 'without conditionals' do
|
||||
it 'passes keyword arguments to the initializer' do
|
||||
filter_class.class_eval do
|
||||
def initialize(mandatory:)
|
||||
@mandatory = mandatory
|
||||
end
|
||||
|
||||
attr_reader :mandatory
|
||||
end
|
||||
|
||||
filter = described_class.build(filter_name, mandatory: 'foo')
|
||||
expect(filter.mandatory).to eql 'foo'
|
||||
end
|
||||
end
|
||||
|
||||
it 'raises a TypeError with invalid spec types' do
|
||||
expect { described_class.build(123) }
|
||||
.to raise_error(TypeError, '123 can not be used to describe filters')
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user