require 'active_support/core_ext/class/subclasses' module Nunes class Adapter # Private: Wraps a given object with the correct adapter/decorator. # # client - The thing to be wrapped. # # Returns Nunes::Adapter instance. def self.wrap(client) raise ArgumentError, "client cannot be nil" if client.nil? return client if client.is_a?(self) adapter = adapters.detect { |adapter| adapter.wraps?(client) } if adapter.nil? raise ArgumentError, "I have no clue how to wrap what you've given me (#{client.inspect})" end adapter.new(client) end # Private def self.wraps?(client) client.respond_to?(:increment) && client.respond_to?(:timing) end # Private def self.adapters [Nunes::Adapter, *subclasses] end # Private attr_reader :client # Internal: Sets the client for the adapter. # # client - The thing being adapted to a simple interface. def initialize(client) @client = client end # Internal: Increment a metric by a value. Override in subclass if client # interface does not match. def increment(metric, value = 1) @client.increment prepare(metric), value end # Internal: Record a metric's duration. Override in subclass if client # interface does not match. def timing(metric, duration) @client.timing prepare(metric), duration end # Private: What Ruby uses to separate namespaces. ReplaceRegex = /[^a-z0-9\-_]+/i # Private: The default metric namespace separator. Separator = ".".freeze # Private Nothing = "".freeze # Private: Prepare a metric name before it is sent to the adapter's client. def prepare(metric, replacement = Separator) escaped = Regexp.escape(replacement) replace_begin_end_regex = /\A#{escaped}|#{escaped}\Z/ metric = metric.to_s.gsub(ReplaceRegex, replacement) metric.squeeze!(replacement) metric.gsub!(replace_begin_end_regex, Nothing) metric end end end