diff --git a/lib/ddtrace/configurable.rb b/lib/ddtrace/configurable.rb new file mode 100644 index 0000000000..8358c70fe8 --- /dev/null +++ b/lib/ddtrace/configurable.rb @@ -0,0 +1,52 @@ +module Datadog + InvalidOptionError = Class.new(StandardError) + # Configuratble provides configuration methods for a given class/module + module Configurable + IDENTITY = ->(x) { x } + + def self.included(base) + base.singleton_class.include(ClassMethods) + end + + # ClassMethods + module ClassMethods + def set_option(name, value) + name = name.to_sym + + invalid!(name) unless options.key?(name) + + options[name] = setters[name].call(value) + end + + def get_option(name) + invalid!(name) unless options.key?(name) + + options[name] + end + + def configure_pin(pin) + pin.config = Hash(pin.config).merge(options) + end + + private + + def option(name, opts = {}) + name = name.to_sym + setters[name] = opts.fetch(:setter, IDENTITY) + options[name] = opts[:default] + end + + def options + @options ||= {} + end + + def setters + @setters ||= {} + end + + def invalid!(name) + raise(InvalidOptionError, "#{self} doesn't have the option: #{name}") + end + end + end +end diff --git a/lib/ddtrace/contrib/base.rb b/lib/ddtrace/contrib/base.rb index 23c69272ed..63130a4be3 100644 --- a/lib/ddtrace/contrib/base.rb +++ b/lib/ddtrace/contrib/base.rb @@ -1,4 +1,5 @@ require 'ddtrace/registry' +require 'ddtrace/configurable' module Datadog module Contrib @@ -6,6 +7,7 @@ module Contrib module Base def self.included(base) base.include(Registry::Registerable) + base.include(Configurable) end end end diff --git a/test/configurable_test.rb b/test/configurable_test.rb new file mode 100644 index 0000000000..daff7aa512 --- /dev/null +++ b/test/configurable_test.rb @@ -0,0 +1,63 @@ +require 'ddtrace/configurable' + +module Datadog + class ConfigurableTest < Minitest::Test + def setup + @module = Module.new { include(Configurable) } + end + + def test_option_methods + assert_respond_to(@module, :set_option) + assert_respond_to(@module, :get_option) + assert_respond_to(@module, :configure_pin) + end + + def test_option_default + @module.class_eval do + option :foo, default: :bar + end + + assert_equal(:bar, @module.get_option(:foo)) + end + + def test_setting_an_option + @module.class_eval do + option :foo, default: :bar + end + + @module.set_option(:foo, 'baz!') + assert_equal('baz!', @module.get_option(:foo)) + end + + def test_custom_setter + @module.class_eval do + option :shout, setter: ->(v) { v.upcase } + end + + @module.set_option(:shout, 'loud') + assert_equal('LOUD', @module.get_option(:shout)) + end + + def test_pin_configuration + @module.class_eval do + option :foo + end + + pin = Pin.new(:test_service) + @module.set_option(:foo, :bar) + @module.configure_pin(pin) + + assert_equal(:bar, pin.config[:foo]) + end + + def test_invalid_option + assert_raises(InvalidOptionError) do + @module.set_option(:bad_option, 'foo') + end + + assert_raises(InvalidOptionError) do + @module.get_option(:bad_option) + end + end + end +end