mirror of
https://github.com/meineerde/rackstash.git
synced 2025-12-19 15:01:12 +00:00
Add abstract adapter and adapter registry
An ada pert wraps a log device (e.g. a file, an underlying logger, ...) and provides an uniform interface to write the encoded log event to its final target. By using a registry, we can create the required adapter instance for a provided log device automatically.
This commit is contained in:
parent
f4e85f7013
commit
05f0faeedc
145
lib/rackstash/adapters.rb
Normal file
145
lib/rackstash/adapters.rb
Normal file
@ -0,0 +1,145 @@
|
|||||||
|
# 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 'uri'
|
||||||
|
|
||||||
|
module Rackstash
|
||||||
|
module Adapters
|
||||||
|
class << self
|
||||||
|
# Register a concrete adapter class which can be instanciated with a
|
||||||
|
# certain log device (e.g. a file name, an IO object, a URL specifying a
|
||||||
|
# log target, ...). With the provided `matchers`, a class can describe
|
||||||
|
# which kind of log devices are suitable to be used with it:
|
||||||
|
#
|
||||||
|
# * `String` with only lower-case characters - When passing a string with
|
||||||
|
# only lower-case characters, we register it as the scheme of a URL.
|
||||||
|
# When retrieving an adapter for a URL in {[]}, we check if the URL's
|
||||||
|
# scheme matches this string.
|
||||||
|
# * `String` with other characters - When passing a string that doesn't
|
||||||
|
# look like a URL scheme, we assume it to represent a class name. When
|
||||||
|
# retrieving a matching adapter for a device, we check if the name of
|
||||||
|
# the device's class matches this string. This can be used to register
|
||||||
|
# an adapter for a device which might not be loaded (yet), e.g. from an
|
||||||
|
# external gem. If possible, you should register the adapter for the
|
||||||
|
# actual class instead of its name.
|
||||||
|
# * `Symbol` - When passing a symbol, we check if the resolved device
|
||||||
|
# responds to an equally named method.
|
||||||
|
# * An object responding to the `===` method - When retrieving an adapter
|
||||||
|
# for a device, we are comparing the matcher object to the device. This
|
||||||
|
# is the same comparison as done in a `case ... when` statement in Ruby.
|
||||||
|
# Usually, the matcher object is either a class or module (in which case
|
||||||
|
# we check if the device object inherits from the matcher) or a proc,
|
||||||
|
# accepting an object as its first parameter. When checking this
|
||||||
|
# matcher, the proc gets called with the device as its parameter. If the
|
||||||
|
# proc returns a truethy value, we use it to build the adapter instance.
|
||||||
|
#
|
||||||
|
# @param adapter_class [Class] a concrete adapter class
|
||||||
|
# @param matchers [Array<String, Symbol, #===>] a list of specifications
|
||||||
|
# for log devices the `adapter_class` can forward logs to.
|
||||||
|
# @raise [TypeError] if the passed adapter_class is not a class
|
||||||
|
# inheriting from {Adapters::Adapter}
|
||||||
|
# @return [Class] the `adapter_class`
|
||||||
|
def register(adapter_class, *matchers)
|
||||||
|
unless adapter_class.is_a?(Class) && adapter_class < Adapters::Adapter
|
||||||
|
raise TypeError, 'adapter_class must be a class and inherit from ' +
|
||||||
|
'Rackstash::Adapters::Adapter'
|
||||||
|
end
|
||||||
|
|
||||||
|
matchers.flatten.each do |matcher|
|
||||||
|
case matcher
|
||||||
|
when String
|
||||||
|
matcher = matcher.to_s
|
||||||
|
if matcher =~ /\A[a-z0-9]+\z/
|
||||||
|
# If the matcher is a lower-case string, we assume it is a URL
|
||||||
|
# scheme.
|
||||||
|
adapter_schemes[matcher.downcase] = adapter_class
|
||||||
|
else
|
||||||
|
# If it starts with a upper-case characters, we assume it is a
|
||||||
|
# class name.
|
||||||
|
|
||||||
|
# Since we use `compare_by_identity` for types, we need to ensure
|
||||||
|
# that we can override existing class names.
|
||||||
|
adapter_types.delete_if { |key, _value| key == matcher }
|
||||||
|
adapter_types[matcher] = adapter_class
|
||||||
|
end
|
||||||
|
when Symbol, ->(o) { o.respond_to?(:===) }
|
||||||
|
adapter_types[matcher] = adapter_class
|
||||||
|
else
|
||||||
|
# Should not be reached by "normal" objects since `Object` already
|
||||||
|
# responds to `===` (which is the same as `==` by default)
|
||||||
|
raise TypeError, "unknown matcher: #{matcher.inspect}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
adapter_class
|
||||||
|
end
|
||||||
|
|
||||||
|
# Try to build an adapter instance from the passed `device`. If the
|
||||||
|
# `device` is already an {Adapter}, it is returned unchanged. If not, we
|
||||||
|
# attempt to identify a suitable adapter class from the {register}ed
|
||||||
|
# classes and return a new adapter instance.
|
||||||
|
#
|
||||||
|
# if no suitable adapter can be found, we raise an `ArgumentError`.
|
||||||
|
#
|
||||||
|
# @param device [Adapters::Adapter, Object] a log device which should be
|
||||||
|
# wrapped in an {Adapter}. If it is already an adapter, the `device` is
|
||||||
|
# returned unchanged.
|
||||||
|
# @raise [ArgumentError] if no suitable adapter could be found for the
|
||||||
|
# provided `device`
|
||||||
|
# @return [Adapters::Adapter] the resolved adapter instance
|
||||||
|
def [](device)
|
||||||
|
return device if device.is_a?(Adapters::Adapter)
|
||||||
|
|
||||||
|
adapter = adapter_by_uri(device)
|
||||||
|
adapter ||= adapter_by_type(device)
|
||||||
|
|
||||||
|
unless adapter
|
||||||
|
raise ArgumentError, "No log adapter found for #{device.inspect}"
|
||||||
|
end
|
||||||
|
adapter
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def adapter_by_uri(uri)
|
||||||
|
uri = URI(uri) rescue return
|
||||||
|
scheme = uri.scheme || uri.opaque
|
||||||
|
|
||||||
|
return unless scheme
|
||||||
|
adapter_class = adapter_schemes.fetch(scheme.to_s.downcase) { return }
|
||||||
|
|
||||||
|
if adapter_class.respond_to?(:from_uri)
|
||||||
|
adapter_class.from_uri(uri)
|
||||||
|
else
|
||||||
|
adapter_class.new(uri)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def adapter_by_type(device)
|
||||||
|
adapter_types.each do |type, adapter_class|
|
||||||
|
suitable =
|
||||||
|
if type.is_a?(Symbol)
|
||||||
|
device.respond_to?(type)
|
||||||
|
elsif type.is_a?(String)
|
||||||
|
device.class.ancestors.any? { |klass| type == klass.name }
|
||||||
|
else
|
||||||
|
type === device
|
||||||
|
end
|
||||||
|
|
||||||
|
return adapter_class.new(device) if suitable
|
||||||
|
end
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def adapter_schemes
|
||||||
|
@adapter_schemes ||= {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def adapter_types
|
||||||
|
@adapter_types ||= {}.compare_by_identity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
99
lib/rackstash/adapters/adapter.rb
Normal file
99
lib/rackstash/adapters/adapter.rb
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
# 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 'rackstash/adapters'
|
||||||
|
require 'rackstash/encoders/json'
|
||||||
|
|
||||||
|
module Rackstash
|
||||||
|
module Adapters
|
||||||
|
# The Adapter wraps a raw external log device like a file, an IO object like
|
||||||
|
# `STDOUT`, the system's syslog or even the connection to a TCP server with
|
||||||
|
# a common interface. At the end of a {Flow}, it is responsible to finally
|
||||||
|
# store the filtered and encoded log event.
|
||||||
|
#
|
||||||
|
# Each concrete adapter can register itself so that it can be used to wrap
|
||||||
|
# any compatible log device with {Rackstash::Adapters.[]}.
|
||||||
|
#
|
||||||
|
# @abstract Subclasses need to override at least {#write_single} to
|
||||||
|
# implement a concrete log adapter.
|
||||||
|
class Adapter
|
||||||
|
# Register the current class as an adapter for the provided matchers.
|
||||||
|
#
|
||||||
|
# This is a convenience method intended to be used by sub-classes of this
|
||||||
|
# abstract parent class to register themselves as adapters.
|
||||||
|
#
|
||||||
|
# @param matchers [Array<String, Symbol, #===>] a list of specifications
|
||||||
|
# for log devices the current adapter can forward logs to.
|
||||||
|
# @return [self]
|
||||||
|
# @see Adapter.register
|
||||||
|
def self.register_for(*matchers)
|
||||||
|
Rackstash::Adapters.register(self, *matchers)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Create a new adapter instance.
|
||||||
|
#
|
||||||
|
# Usually, this method is overwritten by child classes to accept a
|
||||||
|
# suitable log device which will be used to write log lines to. When
|
||||||
|
# registering the adapter class, {Rackstash::Adapters.[]} will call
|
||||||
|
# {initialize} with a single argument: the log device.
|
||||||
|
def initialize(*)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Close the underlying log device if supported by it.
|
||||||
|
#
|
||||||
|
# This method needs to be overwritten in child classes. By default, this
|
||||||
|
# method does nothing.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
def close
|
||||||
|
end
|
||||||
|
|
||||||
|
# Close and re-open the underlying log device if supported by it.
|
||||||
|
#
|
||||||
|
# This method needs to be overwritten in child classes. By default, this
|
||||||
|
# method does nothing.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
def reopen
|
||||||
|
end
|
||||||
|
|
||||||
|
# Write a log line to the log device. This method is called by the flow
|
||||||
|
# with a formatted log event.
|
||||||
|
#
|
||||||
|
# @param log [Object] the encoded log event
|
||||||
|
# @return [nil]
|
||||||
|
def write(log)
|
||||||
|
write_single(log)
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
# Write a single log line to the log device.
|
||||||
|
#
|
||||||
|
# This method needs to be overwritten in child classes to write the
|
||||||
|
# encoded log event to the adapter's device. By default, this method
|
||||||
|
# raises a `NotImplementedError`.
|
||||||
|
#
|
||||||
|
# @param log [Object] the encoded log event
|
||||||
|
# @return [void]
|
||||||
|
# @raise NotImplementedError
|
||||||
|
def write_single(log)
|
||||||
|
raise NotImplementedError, 'write needs to be implemented in a sub class'
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Helper method to ensure that a log line passed to {#write} is a string
|
||||||
|
# that ends in a newline character by mutating the object is required.
|
||||||
|
#
|
||||||
|
# @param line [#to_s] a log line
|
||||||
|
# @return [String] a string with a trailing newline character (`"\n"`)
|
||||||
|
def normalize_line(line)
|
||||||
|
line = line.to_s
|
||||||
|
line << "\n".freeze unless line.end_with?("\n".freeze)
|
||||||
|
line
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
45
spec/rackstash/adapters/adapter_spec.rb
Normal file
45
spec/rackstash/adapters/adapter_spec.rb
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
# 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/adapters/adapter'
|
||||||
|
|
||||||
|
describe Rackstash::Adapters::Adapter do
|
||||||
|
let(:adapter) { Rackstash::Adapters::Adapter.new }
|
||||||
|
|
||||||
|
describe '#initialize' do
|
||||||
|
it 'accepts any arguments' do
|
||||||
|
Rackstash::Adapters::Adapter.new
|
||||||
|
Rackstash::Adapters::Adapter.new(:foo)
|
||||||
|
Rackstash::Adapters::Adapter.new(123, [:foo])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#close' do
|
||||||
|
it 'does nothing' do
|
||||||
|
expect(adapter.close).to be nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#reopen' do
|
||||||
|
it 'does nothing' do
|
||||||
|
expect(adapter.reopen).to be nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#write' do
|
||||||
|
it 'calls write_single' do
|
||||||
|
expect(adapter).to receive(:write_single).with('a log line')
|
||||||
|
adapter.write('a log line')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#write_single' do
|
||||||
|
it 'is not implemented in the abstract base class' do
|
||||||
|
expect { adapter.write('something') }.to raise_error(NotImplementedError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
258
spec/rackstash/adapters_spec.rb
Normal file
258
spec/rackstash/adapters_spec.rb
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
# 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/adapters'
|
||||||
|
|
||||||
|
describe Rackstash::Adapters do
|
||||||
|
around(:each) do |example|
|
||||||
|
types = Rackstash::Adapters.send(:adapter_types)
|
||||||
|
schemes = Rackstash::Adapters.send(:adapter_schemes)
|
||||||
|
|
||||||
|
Rackstash::Adapters.instance_variable_set(:@adapter_types, nil)
|
||||||
|
Rackstash::Adapters.instance_variable_set(:@adapter_schemes, nil)
|
||||||
|
|
||||||
|
example.run
|
||||||
|
|
||||||
|
Rackstash::Adapters.instance_variable_set(:@adapter_types, types)
|
||||||
|
Rackstash::Adapters.instance_variable_set(:@adapter_schemes, schemes)
|
||||||
|
end
|
||||||
|
|
||||||
|
let(:adapter) {
|
||||||
|
Class.new(Rackstash::Adapters::Adapter) do
|
||||||
|
def self.from_uri(*args)
|
||||||
|
new(*args)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
||||||
|
describe '#register' do
|
||||||
|
it 'can register a class' do
|
||||||
|
expect {
|
||||||
|
Rackstash::Adapters.register adapter, Class.new
|
||||||
|
Rackstash::Adapters.register adapter, String
|
||||||
|
Rackstash::Adapters.register adapter, Numeric
|
||||||
|
Rackstash::Adapters.register adapter, Integer
|
||||||
|
}.to change { Rackstash::Adapters.send(:adapter_types).size }
|
||||||
|
.from(0).to(4)
|
||||||
|
expect(Rackstash::Adapters.send(:adapter_schemes).size).to eql 0
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'can register a class name (upper-case String)' do
|
||||||
|
expect {
|
||||||
|
Rackstash::Adapters.register adapter, '❤'
|
||||||
|
Rackstash::Adapters.register adapter, ''
|
||||||
|
Rackstash::Adapters.register adapter, 'Hello::World'
|
||||||
|
}.to change { Rackstash::Adapters.send(:adapter_types).size }
|
||||||
|
.from(0).to(3)
|
||||||
|
|
||||||
|
# Registering 'Hello::World' a second time overwrites the first one
|
||||||
|
expect {
|
||||||
|
Rackstash::Adapters.register(adapter, 'Hello::World')
|
||||||
|
}.not_to change { Rackstash::Adapters.send(:adapter_types).size }
|
||||||
|
|
||||||
|
expect(Rackstash::Adapters.send(:adapter_schemes).size).to eql 0
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'can register a method name (symbol)' do
|
||||||
|
expect {
|
||||||
|
Rackstash::Adapters.register adapter, :foo
|
||||||
|
}.to change { Rackstash::Adapters.send(:adapter_types).size }
|
||||||
|
.from(0).to(1)
|
||||||
|
expect(Rackstash::Adapters.send(:adapter_schemes).size).to eql 0
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'can register a proc' do
|
||||||
|
expect {
|
||||||
|
Rackstash::Adapters.register adapter, ->(o) { o.respond_to?(:write) }
|
||||||
|
Rackstash::Adapters.register adapter, -> {}
|
||||||
|
}.to change { Rackstash::Adapters.send(:adapter_types).size }
|
||||||
|
.from(0).to(2)
|
||||||
|
expect(Rackstash::Adapters.send(:adapter_schemes).size).to eql 0
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'can register a scheme (lower-case String)' do
|
||||||
|
expect {
|
||||||
|
Rackstash::Adapters.register adapter, 'customscheme'
|
||||||
|
}.to change { Rackstash::Adapters.send(:adapter_schemes).size }
|
||||||
|
.from(0).to(1)
|
||||||
|
expect(Rackstash::Adapters.send(:adapter_types).size).to eql 0
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'rejects invalid adapter classes' do
|
||||||
|
expect { Rackstash::Adapters.register nil, :foo }
|
||||||
|
.to raise_error(TypeError)
|
||||||
|
expect { Rackstash::Adapters.register Class.new, :foo }
|
||||||
|
.to raise_error(TypeError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#[]' do
|
||||||
|
context 'with a registered class' do
|
||||||
|
let(:device_class) { Class.new }
|
||||||
|
|
||||||
|
before do
|
||||||
|
Rackstash::Adapters.register adapter, device_class
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates an adapter if the class was found' do
|
||||||
|
device = device_class.new
|
||||||
|
|
||||||
|
expect(device_class).to receive(:===).with(device).and_call_original
|
||||||
|
expect(adapter).to receive(:new).with(device).and_call_original
|
||||||
|
expect(Rackstash::Adapters[device]).to be_an Rackstash::Adapters::Adapter
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates an adapter if any parent class was found' do
|
||||||
|
inherited_device = Class.new(device_class).new
|
||||||
|
|
||||||
|
expect(device_class).to receive(:===).with(inherited_device).and_call_original
|
||||||
|
expect(adapter).to receive(:new).with(inherited_device).and_call_original
|
||||||
|
expect(Rackstash::Adapters[inherited_device]).to be_an Rackstash::Adapters::Adapter
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises if no class was found' do
|
||||||
|
expect(adapter).to_not receive(:new)
|
||||||
|
expect { Rackstash::Adapters['foo'] }.to raise_error(ArgumentError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a registered class name' do
|
||||||
|
before do
|
||||||
|
class SpecDevice; end
|
||||||
|
class InheritedSpecDevice < SpecDevice; end
|
||||||
|
|
||||||
|
Rackstash::Adapters.register adapter, 'SpecDevice'
|
||||||
|
end
|
||||||
|
|
||||||
|
after do
|
||||||
|
Object.send :remove_const, :InheritedSpecDevice
|
||||||
|
Object.send :remove_const, :SpecDevice
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates an adapter if the class was found' do
|
||||||
|
device = SpecDevice.new
|
||||||
|
|
||||||
|
expect(adapter).to receive(:new).with(device).and_call_original
|
||||||
|
expect(Rackstash::Adapters[device]).to be_an Rackstash::Adapters::Adapter
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates an adapter if any parent class was found' do
|
||||||
|
inherited_device = InheritedSpecDevice.new
|
||||||
|
|
||||||
|
expect(adapter).to receive(:new).with(inherited_device).and_call_original
|
||||||
|
expect(Rackstash::Adapters[inherited_device]).to be_an Rackstash::Adapters::Adapter
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises if no class was found' do
|
||||||
|
expect(adapter).to_not receive(:new)
|
||||||
|
expect { Rackstash::Adapters['foo'] }.to raise_error(ArgumentError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a registered symbol' do
|
||||||
|
before do
|
||||||
|
Rackstash::Adapters.register adapter, :foo
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates an adapter if it responds to the registered method' do
|
||||||
|
device = Struct.new(:foo).new('foo')
|
||||||
|
|
||||||
|
expect(adapter).to receive(:new).with(device).and_call_original
|
||||||
|
expect(Rackstash::Adapters[device]).to be_an Rackstash::Adapters::Adapter
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises if it does not respond to the registered method' do
|
||||||
|
device = Struct.new(:bar).new('bar')
|
||||||
|
|
||||||
|
expect(adapter).to_not receive(:new)
|
||||||
|
expect { Rackstash::Adapters[device] }.to raise_error(ArgumentError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a registered proc' do
|
||||||
|
let(:device) { Object.new }
|
||||||
|
|
||||||
|
it 'creates an adapter if the proc returns true' do
|
||||||
|
checker = proc { true }
|
||||||
|
Rackstash::Adapters.register adapter, checker
|
||||||
|
|
||||||
|
expect(checker).to receive(:===).with(device).and_call_original
|
||||||
|
expect(adapter).to receive(:new).with(device).and_call_original
|
||||||
|
expect(Rackstash::Adapters[device]).to be_an Rackstash::Adapters::Adapter
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not create an adapter if the proc returns false' do
|
||||||
|
checker = proc { false }
|
||||||
|
Rackstash::Adapters.register adapter, checker
|
||||||
|
|
||||||
|
expect(checker).to receive(:===).with(device).and_call_original
|
||||||
|
expect(adapter).to_not receive(:new)
|
||||||
|
expect { Rackstash::Adapters[device] }.to raise_error(ArgumentError)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with a registered scheme' do
|
||||||
|
before do
|
||||||
|
Rackstash::Adapters.register adapter, 'dummy'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates an adapter from the scheme' do
|
||||||
|
raw_uri = 'dummy://example.com'
|
||||||
|
expect(adapter).to receive(:from_uri).with(URI(raw_uri)).and_call_original
|
||||||
|
expect(Rackstash::Adapters[raw_uri]).to be_an Rackstash::Adapters::Adapter
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'creates an adapter from a URI' do
|
||||||
|
uri = URI('dummy://example.com')
|
||||||
|
expect(adapter).to receive(:from_uri).with(uri).and_call_original
|
||||||
|
expect(Rackstash::Adapters[uri]).to be_an Rackstash::Adapters::Adapter
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises if no scheme was found' do
|
||||||
|
expect(adapter).to_not receive(:new)
|
||||||
|
expect(adapter).to_not receive(:from_uri)
|
||||||
|
expect { Rackstash::Adapters['unknown://example.com'] }
|
||||||
|
.to raise_error(ArgumentError)
|
||||||
|
expect { Rackstash::Adapters[URI('unknown://example.com')] }
|
||||||
|
.to raise_error(ArgumentError)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'and a registered class' do
|
||||||
|
before do
|
||||||
|
Rackstash::Adapters.register adapter, Object
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'falls though on invalid URI' do
|
||||||
|
invalid_uri = '::'
|
||||||
|
|
||||||
|
expect(adapter).to_not receive(:from_uri)
|
||||||
|
# from the fallback
|
||||||
|
expect(adapter).to receive(:new).with(invalid_uri).and_call_original
|
||||||
|
expect(Rackstash::Adapters[invalid_uri]).to be_an Rackstash::Adapters::Adapter
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'falls though if no scheme was found' do
|
||||||
|
unknown_uri = 'unknown://example.com'
|
||||||
|
|
||||||
|
expect(adapter).to_not receive(:from_uri)
|
||||||
|
expect(adapter).to receive(:new).with(unknown_uri).and_call_original
|
||||||
|
expect(Rackstash::Adapters[unknown_uri]).to be_an Rackstash::Adapters::Adapter
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with an existing adapter object' do
|
||||||
|
it 'just returns the object' do
|
||||||
|
adapter_instance = adapter.new
|
||||||
|
Rackstash::Adapters.register adapter, Object
|
||||||
|
|
||||||
|
expect(adapter).to_not receive(:new)
|
||||||
|
expect(Rackstash::Adapters[adapter_instance]).to equal adapter_instance
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Loading…
x
Reference in New Issue
Block a user