diff --git a/lib/flipper/adapter.rb b/lib/flipper/adapter.rb index 831b00ea6..e59364371 100644 --- a/lib/flipper/adapter.rb +++ b/lib/flipper/adapter.rb @@ -2,8 +2,7 @@ module Flipper # Internal: Adapter wrapper that wraps vanilla adapter instances. Adds things - # like local caching and convenience methods for adding/reading features from - # the adapter. + # like local caching and instrumentation. # # So what is this local cache crap? # @@ -21,43 +20,13 @@ module Flipper # To see an example adapter that this would wrap, checkout the [memory # adapter included with flipper](https://github.com/jnunemaker/flipper/blob/master/lib/flipper/adapters/memory.rb). class Adapter - # Private: The name of instrumentation events. - InstrumentationName = "adapter_operation.#{InstrumentationNamespace}" - - # Internal: Wraps vanilla adapter instance for use internally in flipper. - # - # object - Either an instance of Flipper::Adapter or a vanilla adapter instance - # - # Examples - # - # adapter = Flipper::Adapters::Memory.new - # instance = Flipper::Adapter.new(adapter) - # - # Flipper::Adapter.wrap(instance) - # # => Flipper::Adapter instance - # - # Flipper::Adapter.wrap(adapter) - # # => Flipper::Adapter instance - # - # Returns Flipper::Adapter instance - def self.wrap(object, options = {}) - if object.is_a?(Flipper::Adapter) - object - else - new(object, options) - end - end - # Private: What adapter is being wrapped and will ultimately be used. attr_reader :adapter - # Private: The name of the adapter. Based on the class name. - attr_reader :name - - # Private: What is used to store the local cache. + # Private: What is used to store the operation cache. attr_reader :local_cache - # Private: What is used to instrument all the things. + # Private: What is instrumenting all the operations. attr_reader :instrumenter # Internal: Initializes a new adapter instance. @@ -69,12 +38,12 @@ def self.wrap(object, options = {}) # :local_cache - Where to store the local cache data (default: {}). # Must respond to fetch(key, block), delete(key) # and clear. - # :instrumenter - What to use to instrument all the things. - # def initialize(adapter, options = {}) - @adapter = adapter @local_cache = options[:local_cache] || {} @instrumenter = options.fetch(:instrumenter, Flipper::Instrumenters::Noop) + @adapter = Adapters::Instrumented.new(adapter, { + :instrumenter => @instrumenter, + }) end # Public: Turns local caching on/off. @@ -97,31 +66,18 @@ def name # Public: Reads all keys for a given feature. def get(feature) - payload = { - :operation => :get, - :adapter_name => name, - :feature_name => feature.name, - } - if using_local_cache? local_cache.fetch(feature.name) { - local_cache[feature.name] = instrument_operation(:get, payload, feature) + local_cache[feature.name] = @adapter.get(feature) } else - instrument_operation(:get, payload, feature) + @adapter.get(feature) end end # Public: Enable feature gate for thing. def enable(feature, gate, thing) - payload = { - :operation => :enable, - :adapter_name => name, - :feature_name => feature.name, - :gate_name => gate.name, - } - - result = instrument_operation(:enable, payload, feature, gate, thing) + result = @adapter.enable(feature, gate, thing) if using_local_cache? local_cache.delete(feature.name) @@ -132,14 +88,7 @@ def enable(feature, gate, thing) # Public: Disable feature gate for thing. def disable(feature, gate, thing) - payload = { - :operation => :disable, - :adapter_name => name, - :feature_name => feature.name, - :gate_name => gate.name, - } - - result = instrument_operation(:disable, payload, feature, gate, thing) + result = @adapter.disable(feature, gate, thing) if using_local_cache? local_cache.delete(feature.name) @@ -150,30 +99,12 @@ def disable(feature, gate, thing) # Public: Returns all the features that the adapter knows of. def features - payload = { - :operation => :features, - :adapter_name => name, - } - - instrument_operation :features, payload + @adapter.features end # Internal: Adds a known feature to the set of features. def add(feature) - payload = { - :operation => :add, - :adapter_name => name, - :feature_name => feature.name, - } - - instrument_operation :add, payload, feature - end - - # Private: Instruments operation with payload. - def instrument_operation(operation, payload = {}, *args) - @instrumenter.instrument(InstrumentationName, payload) { |payload| - payload[:result] = @adapter.send(operation, *args) - } + @adapter.add(feature) end end end diff --git a/lib/flipper/dsl.rb b/lib/flipper/dsl.rb index c7c5db8bc..6719dc5a6 100644 --- a/lib/flipper/dsl.rb +++ b/lib/flipper/dsl.rb @@ -16,7 +16,7 @@ class DSL # :instrumenter - What should be used to instrument all the things. def initialize(adapter, options = {}) @instrumenter = options.fetch(:instrumenter, Flipper::Instrumenters::Noop) - @adapter = Adapter.wrap(adapter, :instrumenter => @instrumenter) + @adapter = Flipper::Adapter.new(adapter, :instrumenter => @instrumenter) @memoized_features = {} end diff --git a/spec/flipper/adapter_spec.rb b/spec/flipper/adapter_spec.rb index 3e524bc9b..a64e0193e 100644 --- a/spec/flipper/adapter_spec.rb +++ b/spec/flipper/adapter_spec.rb @@ -11,60 +11,10 @@ subject { described_class.new(adapter, :local_cache => local_cache) } - describe ".wrap" do - context "with Flipper::Adapter instance" do - before do - @result = described_class.wrap(subject) - end - - it "returns same Flipper::Adapter instance" do - @result.should equal(subject) - end - - it "wraps adapter that instance was wrapping" do - @result.adapter.should be(subject.adapter) - end - end - - context "with adapter instance" do - before do - @result = described_class.wrap(adapter) - end - - it "returns Flipper::Adapter instance" do - @result.should be_instance_of(described_class) - end - - it "wraps adapter" do - @result.adapter.should be(adapter) - end - end - - context "with adapter instance and options" do - let(:instrumenter) { double('Instrumentor') } - - before do - @result = described_class.wrap(adapter, :instrumenter => instrumenter) - end - - it "returns Flipper::Adapter instance" do - @result.should be_instance_of(described_class) - end - - it "wraps adapter" do - @result.adapter.should be(adapter) - end - - it "passes options to initialization" do - @result.instrumenter.should be(instrumenter) - end - end - end - describe "#initialize" do - it "sets adapter" do + it "wraps adapter with instrumentation" do instance = described_class.new(adapter) - instance.adapter.should be(adapter) + instance.adapter.should be_instance_of(Flipper::Adapters::Instrumented) end it "sets adapter name" do @@ -81,6 +31,7 @@ instrumenter = double('Instrumentor', :instrument => nil) instance = described_class.new(adapter, :instrumenter => instrumenter) instance.instrumenter.should be(instrumenter) + instance.adapter.instrumenter.should be(instrumenter) end end @@ -180,76 +131,4 @@ end end end - - describe "instrumentation" do - let(:instrumenter) { Flipper::Instrumenters::Memory.new } - let(:feature) { flipper[:stats] } - let(:gate) { feature.gate(:percentage_of_actors) } - let(:thing) { flipper.actors(22) } - - subject { - described_class.new(adapter, :instrumenter => instrumenter) - } - - it "is recorded for get" do - result = subject.get(feature) - - event = instrumenter.events.last - event.should_not be_nil - event.name.should eq('adapter_operation.flipper') - event.payload[:operation].should eq(:get) - event.payload[:adapter_name].should eq(:memory) - event.payload[:feature_name].should eq(:stats) - event.payload[:result].should be(result) - end - - it "is recorded for enable" do - result = subject.enable(feature, gate, thing) - - event = instrumenter.events.last - event.should_not be_nil - event.name.should eq('adapter_operation.flipper') - event.payload[:operation].should eq(:enable) - event.payload[:adapter_name].should eq(:memory) - event.payload[:feature_name].should eq(:stats) - event.payload[:gate_name].should eq(:percentage_of_actors) - event.payload[:result].should be(result) - end - - it "is recorded for disable" do - result = subject.disable(feature, gate, thing) - - event = instrumenter.events.last - event.should_not be_nil - event.name.should eq('adapter_operation.flipper') - event.payload[:operation].should eq(:disable) - event.payload[:adapter_name].should eq(:memory) - event.payload[:feature_name].should eq(:stats) - event.payload[:gate_name].should eq(:percentage_of_actors) - event.payload[:result].should be(result) - end - - it "is recorded for add" do - result = subject.add(feature) - - event = instrumenter.events.last - event.should_not be_nil - event.name.should eq('adapter_operation.flipper') - event.payload[:operation].should eq(:add) - event.payload[:adapter_name].should eq(:memory) - event.payload[:feature_name].should eq(:stats) - event.payload[:result].should be(result) - end - - it "is recorded for features" do - result = subject.features - - event = instrumenter.events.last - event.should_not be_nil - event.name.should eq('adapter_operation.flipper') - event.payload[:operation].should eq(:features) - event.payload[:adapter_name].should eq(:memory) - event.payload[:result].should be(result) - end - end end