Skip to content
Browse files

Kill Flipper::Adapter. Just use decorators in dsl.

  • Loading branch information...
1 parent 4a03442 commit 3f3db9cf4ef74d874421caece4dc954f1f394ebb @jnunemaker committed Feb 15, 2013
View
108 lib/flipper/adapter.rb
@@ -1,108 +0,0 @@
-require 'flipper/adapters/decorator'
-require 'flipper/instrumenters/noop'
-
-module Flipper
- # Internal: Adapter wrapper that wraps vanilla adapter instances. Adds things
- # like local caching and instrumentation.
- #
- # So what is this local cache crap?
- #
- # The main goal of the local cache is to prevent multiple queries to an
- # adapter for the same key for a given amount of time (per request, per
- # background job, etc.).
- #
- # To facilitate with this, there is an included local cache middleware
- # that enables local caching for the length of a web request. The local
- # cache is enabled and cleared before each request and cleared and reset
- # to original value after each request.
- #
- # Examples
- #
- # 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 < Adapters::Decorator
- # Private: What adapter is being wrapped and will ultimately be used.
- attr_reader :adapter
-
- # Private: What is used to store the operation cache.
- attr_reader :local_cache
-
- # Private: What is instrumenting all the operations.
- attr_reader :instrumenter
-
- # Internal: Initializes a new adapter instance.
- #
- # adapter - Vanilla adapter instance to wrap. Just needs to respond to get,
- # enable and disable.
- #
- # options - The Hash of options.
- # :local_cache - Where to store the local cache data (default: {}).
- # Must respond to fetch(key, block), delete(key)
- # and clear.
- def initialize(adapter, options = {})
- @local_cache = options[:local_cache] || {}
- @instrumenter = options.fetch(:instrumenter, Flipper::Instrumenters::Noop)
- @adapter = Adapters::Instrumented.new(adapter, {
- :instrumenter => @instrumenter,
- })
-
- super @adapter
- end
-
- # Public: Turns local caching on/off.
- #
- # value - The Boolean that decides if local caching is on.
- def use_local_cache=(value)
- local_cache.clear
- @use_local_cache = value
- end
-
- # Public: Returns true for using local cache, false for not.
- def using_local_cache?
- @use_local_cache == true
- end
-
- # Public: Reads all keys for a given feature.
- def get(feature)
- if using_local_cache?
- local_cache.fetch(feature.name) {
- local_cache[feature.name] = super
- }
- else
- super
- end
- end
-
- # Public: Enable feature gate for thing.
- def enable(feature, gate, thing)
- result = super
-
- if using_local_cache?
- local_cache.delete(feature.name)
- end
-
- result
- end
-
- # Public: Disable feature gate for thing.
- def disable(feature, gate, thing)
- result = super
-
- if using_local_cache?
- local_cache.delete(feature.name)
- end
-
- result
- end
-
- # Public: Returns all the features that the adapter knows of.
- def features
- super
- end
-
- # Internal: Adds a known feature to the set of features.
- def add(feature)
- super
- end
- end
-end
View
11 lib/flipper/dsl.rb
@@ -1,4 +1,5 @@
-require 'flipper/adapter'
+require 'flipper/adapters/instrumented'
+require 'flipper/adapters/memoized'
require 'flipper/instrumenters/noop'
module Flipper
@@ -16,7 +17,13 @@ class DSL
# :instrumenter - What should be used to instrument all the things.
def initialize(adapter, options = {})
@instrumenter = options.fetch(:instrumenter, Flipper::Instrumenters::Noop)
- @adapter = Flipper::Adapter.new(adapter, :instrumenter => @instrumenter)
+
+ instrumented = Flipper::Adapters::Instrumented.new(adapter, {
+ :instrumenter => @instrumenter,
+ })
+ memoized = Flipper::Adapters::Memoized.new(instrumented)
+ @adapter = memoized
+
@memoized_features = {}
end
View
1 lib/flipper/feature.rb
@@ -1,4 +1,3 @@
-require 'flipper/adapter'
require 'flipper/errors'
require 'flipper/type'
require 'flipper/gate'
View
6 lib/flipper/middleware/local_cache.rb
@@ -9,13 +9,13 @@ def initialize(app, flipper)
end
def call(env)
- original = @flipper.adapter.using_local_cache?
- @flipper.adapter.use_local_cache = true
+ original = @flipper.adapter.memoizing?
+ @flipper.adapter.memoize = true
status, headers, body = @app.call(env)
body_proxy = Rack::BodyProxy.new(body) {
- @flipper.adapter.use_local_cache = original
+ @flipper.adapter.memoize = original
}
[status, headers, body_proxy]
View
134 spec/flipper/adapter_spec.rb
@@ -1,134 +0,0 @@
-require 'helper'
-require 'flipper/adapter'
-require 'flipper/adapters/memory'
-require 'flipper/instrumenters/memory'
-
-describe Flipper::Adapter do
- let(:local_cache) { {} }
- let(:source) { {} }
- let(:adapter) { Flipper::Adapters::Memory.new(source) }
- let(:flipper) { Flipper.new(adapter) }
-
- subject { described_class.new(adapter, :local_cache => local_cache) }
-
- describe "#initialize" do
- it "wraps adapter with instrumentation" do
- instance = described_class.new(adapter)
- instance.adapter.should be_instance_of(Flipper::Adapters::Instrumented)
- end
-
- it "sets adapter name" do
- instance = described_class.new(adapter)
- instance.name.should be(:memory)
- end
-
- it "defaults instrumenter" do
- instance = described_class.new(adapter)
- instance.instrumenter.should be(Flipper::Instrumenters::Noop)
- end
-
- it "allows overriding instrumenter" do
- 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
-
- describe "#use_local_cache=" do
- it "sets value" do
- subject.use_local_cache = true
- subject.using_local_cache?.should be_true
-
- subject.use_local_cache = false
- subject.using_local_cache?.should be_false
- end
-
- it "clears the local cache" do
- local_cache.should_receive(:clear)
- subject.use_local_cache = true
- end
- end
-
- describe "#using_local_cache?" do
- it "returns true if enabled" do
- subject.use_local_cache = true
- subject.using_local_cache?.should be_true
- end
-
- it "returns false if disabled" do
- subject.use_local_cache = false
- subject.using_local_cache?.should be_false
- end
- end
-
- context "with local cache enabled" do
- before do
- subject.use_local_cache = true
- end
-
- describe "#get" do
- before do
- @group = Flipper.register(:admins) { |thing| thing.admin? }
- @actor = Struct.new(:flipper_id).new('13')
- @actors = flipper.actors(20)
- @random = flipper.random(10)
- @feature = flipper[:stats]
-
- @feature.enable @group
- @feature.enable @actors
- @feature.enable @random
- @feature.enable @actor
-
- @result = subject.get(@feature)
- end
-
- it "returns hash of gate => value" do
- @result.should be_instance_of(Hash)
- @result[@feature.gate(:boolean)].should be_false
- @result[@feature.gate(:actor)].should eq(Set['13'])
- @result[@feature.gate(:group)].should eq(Set['admins'])
- @result[@feature.gate(:percentage_of_actors)].should eq('20')
- @result[@feature.gate(:percentage_of_random)].should eq('10')
- end
-
- it "memoizes adapter get value" do
- local_cache[@feature.name].should eq(@result)
- adapter.should_not_receive(:get)
- subject.get(@feature).should be(@result)
- end
- end
- end
-
- context "with local cache disabled" do
- before do
- subject.use_local_cache = false
- end
-
- describe "#get" do
- before do
- @group = Flipper.register(:admins) { |thing| thing.admin? }
- @actor = Struct.new(:flipper_id).new('13')
- @actors = flipper.actors(20)
- @random = flipper.random(10)
- @feature = flipper[:stats]
-
- @feature.enable @group
- @feature.enable @actors
- @feature.enable @random
- @feature.enable @actor
-
- @result = subject.get(@feature)
- end
-
- it "returns hash of gate => value" do
- @result.should be_instance_of(Hash)
- @result[@feature.gate(:boolean)].should be_false
- @result[@feature.gate(:actor)].should eq(Set['13'])
- @result[@feature.gate(:group)].should eq(Set['admins'])
- @result[@feature.gate(:percentage_of_actors)].should eq('20')
- @result[@feature.gate(:percentage_of_random)].should eq('10')
- end
- end
- end
-end
View
5 spec/flipper/dsl_spec.rb
@@ -9,10 +9,9 @@
let(:adapter) { Flipper::Adapters::Memory.new(source) }
describe "#initialize" do
- it "wraps adapter" do
+ it "sets adapter" do
dsl = described_class.new(adapter)
- dsl.adapter.should be_instance_of(Flipper::Adapter)
- dsl.adapter.adapter.should eq(adapter)
+ dsl.adapter.should_not be_nil
end
it "defaults instrumenter to noop" do
View
16 spec/flipper/middleware/local_cache_spec.rb
@@ -46,31 +46,31 @@
middleware = described_class.new app, flipper
body = middleware.call({}).last
- flipper.adapter.using_local_cache?.should be_true
+ flipper.adapter.memoizing?.should be_true
body.close
- flipper.adapter.using_local_cache?.should be_false
+ flipper.adapter.memoizing?.should be_false
end
it "clears local cache after body close" do
app = lambda { |env| [200, {}, []] }
middleware = described_class.new app, flipper
body = middleware.call({}).last
- flipper.adapter.local_cache['hello'] = 'world'
+ flipper.adapter.cache['hello'] = 'world'
body.close
- flipper.adapter.local_cache.should be_empty
+ flipper.adapter.cache.should be_empty
end
it "clears the local cache with a successful request" do
- flipper.adapter.local_cache['hello'] = 'world'
+ flipper.adapter.cache['hello'] = 'world'
get '/'
- flipper.adapter.local_cache.should be_empty
+ flipper.adapter.cache.should be_empty
end
it "clears the local cache even when the request raises an error" do
- flipper.adapter.local_cache['hello'] = 'world'
+ flipper.adapter.cache['hello'] = 'world'
get '/fail' rescue nil
- flipper.adapter.local_cache.should be_empty
+ flipper.adapter.cache.should be_empty
end
it "caches getting a feature for duration of request" do

0 comments on commit 3f3db9c

Please sign in to comment.
Something went wrong with that request. Please try again.