Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

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

  • Loading branch information...
commit 3f3db9cf4ef74d874421caece4dc954f1f394ebb 1 parent 4a03442
John Nunemaker authored
108 lib/flipper/adapter.rb
... ... @@ -1,108 +0,0 @@
1   -require 'flipper/adapters/decorator'
2   -require 'flipper/instrumenters/noop'
3   -
4   -module Flipper
5   - # Internal: Adapter wrapper that wraps vanilla adapter instances. Adds things
6   - # like local caching and instrumentation.
7   - #
8   - # So what is this local cache crap?
9   - #
10   - # The main goal of the local cache is to prevent multiple queries to an
11   - # adapter for the same key for a given amount of time (per request, per
12   - # background job, etc.).
13   - #
14   - # To facilitate with this, there is an included local cache middleware
15   - # that enables local caching for the length of a web request. The local
16   - # cache is enabled and cleared before each request and cleared and reset
17   - # to original value after each request.
18   - #
19   - # Examples
20   - #
21   - # To see an example adapter that this would wrap, checkout the [memory
22   - # adapter included with flipper](https://github.com/jnunemaker/flipper/blob/master/lib/flipper/adapters/memory.rb).
23   - class Adapter < Adapters::Decorator
24   - # Private: What adapter is being wrapped and will ultimately be used.
25   - attr_reader :adapter
26   -
27   - # Private: What is used to store the operation cache.
28   - attr_reader :local_cache
29   -
30   - # Private: What is instrumenting all the operations.
31   - attr_reader :instrumenter
32   -
33   - # Internal: Initializes a new adapter instance.
34   - #
35   - # adapter - Vanilla adapter instance to wrap. Just needs to respond to get,
36   - # enable and disable.
37   - #
38   - # options - The Hash of options.
39   - # :local_cache - Where to store the local cache data (default: {}).
40   - # Must respond to fetch(key, block), delete(key)
41   - # and clear.
42   - def initialize(adapter, options = {})
43   - @local_cache = options[:local_cache] || {}
44   - @instrumenter = options.fetch(:instrumenter, Flipper::Instrumenters::Noop)
45   - @adapter = Adapters::Instrumented.new(adapter, {
46   - :instrumenter => @instrumenter,
47   - })
48   -
49   - super @adapter
50   - end
51   -
52   - # Public: Turns local caching on/off.
53   - #
54   - # value - The Boolean that decides if local caching is on.
55   - def use_local_cache=(value)
56   - local_cache.clear
57   - @use_local_cache = value
58   - end
59   -
60   - # Public: Returns true for using local cache, false for not.
61   - def using_local_cache?
62   - @use_local_cache == true
63   - end
64   -
65   - # Public: Reads all keys for a given feature.
66   - def get(feature)
67   - if using_local_cache?
68   - local_cache.fetch(feature.name) {
69   - local_cache[feature.name] = super
70   - }
71   - else
72   - super
73   - end
74   - end
75   -
76   - # Public: Enable feature gate for thing.
77   - def enable(feature, gate, thing)
78   - result = super
79   -
80   - if using_local_cache?
81   - local_cache.delete(feature.name)
82   - end
83   -
84   - result
85   - end
86   -
87   - # Public: Disable feature gate for thing.
88   - def disable(feature, gate, thing)
89   - result = super
90   -
91   - if using_local_cache?
92   - local_cache.delete(feature.name)
93   - end
94   -
95   - result
96   - end
97   -
98   - # Public: Returns all the features that the adapter knows of.
99   - def features
100   - super
101   - end
102   -
103   - # Internal: Adds a known feature to the set of features.
104   - def add(feature)
105   - super
106   - end
107   - end
108   -end
11 lib/flipper/dsl.rb
... ... @@ -1,4 +1,5 @@
1   -require 'flipper/adapter'
  1 +require 'flipper/adapters/instrumented'
  2 +require 'flipper/adapters/memoized'
2 3 require 'flipper/instrumenters/noop'
3 4
4 5 module Flipper
@@ -16,7 +17,13 @@ class DSL
16 17 # :instrumenter - What should be used to instrument all the things.
17 18 def initialize(adapter, options = {})
18 19 @instrumenter = options.fetch(:instrumenter, Flipper::Instrumenters::Noop)
19   - @adapter = Flipper::Adapter.new(adapter, :instrumenter => @instrumenter)
  20 +
  21 + instrumented = Flipper::Adapters::Instrumented.new(adapter, {
  22 + :instrumenter => @instrumenter,
  23 + })
  24 + memoized = Flipper::Adapters::Memoized.new(instrumented)
  25 + @adapter = memoized
  26 +
20 27 @memoized_features = {}
21 28 end
22 29
1  lib/flipper/feature.rb
... ... @@ -1,4 +1,3 @@
1   -require 'flipper/adapter'
2 1 require 'flipper/errors'
3 2 require 'flipper/type'
4 3 require 'flipper/gate'
6 lib/flipper/middleware/local_cache.rb
@@ -9,13 +9,13 @@ def initialize(app, flipper)
9 9 end
10 10
11 11 def call(env)
12   - original = @flipper.adapter.using_local_cache?
13   - @flipper.adapter.use_local_cache = true
  12 + original = @flipper.adapter.memoizing?
  13 + @flipper.adapter.memoize = true
14 14
15 15 status, headers, body = @app.call(env)
16 16
17 17 body_proxy = Rack::BodyProxy.new(body) {
18   - @flipper.adapter.use_local_cache = original
  18 + @flipper.adapter.memoize = original
19 19 }
20 20
21 21 [status, headers, body_proxy]
134 spec/flipper/adapter_spec.rb
... ... @@ -1,134 +0,0 @@
1   -require 'helper'
2   -require 'flipper/adapter'
3   -require 'flipper/adapters/memory'
4   -require 'flipper/instrumenters/memory'
5   -
6   -describe Flipper::Adapter do
7   - let(:local_cache) { {} }
8   - let(:source) { {} }
9   - let(:adapter) { Flipper::Adapters::Memory.new(source) }
10   - let(:flipper) { Flipper.new(adapter) }
11   -
12   - subject { described_class.new(adapter, :local_cache => local_cache) }
13   -
14   - describe "#initialize" do
15   - it "wraps adapter with instrumentation" do
16   - instance = described_class.new(adapter)
17   - instance.adapter.should be_instance_of(Flipper::Adapters::Instrumented)
18   - end
19   -
20   - it "sets adapter name" do
21   - instance = described_class.new(adapter)
22   - instance.name.should be(:memory)
23   - end
24   -
25   - it "defaults instrumenter" do
26   - instance = described_class.new(adapter)
27   - instance.instrumenter.should be(Flipper::Instrumenters::Noop)
28   - end
29   -
30   - it "allows overriding instrumenter" do
31   - instrumenter = double('Instrumentor', :instrument => nil)
32   - instance = described_class.new(adapter, :instrumenter => instrumenter)
33   - instance.instrumenter.should be(instrumenter)
34   - instance.adapter.instrumenter.should be(instrumenter)
35   - end
36   - end
37   -
38   - describe "#use_local_cache=" do
39   - it "sets value" do
40   - subject.use_local_cache = true
41   - subject.using_local_cache?.should be_true
42   -
43   - subject.use_local_cache = false
44   - subject.using_local_cache?.should be_false
45   - end
46   -
47   - it "clears the local cache" do
48   - local_cache.should_receive(:clear)
49   - subject.use_local_cache = true
50   - end
51   - end
52   -
53   - describe "#using_local_cache?" do
54   - it "returns true if enabled" do
55   - subject.use_local_cache = true
56   - subject.using_local_cache?.should be_true
57   - end
58   -
59   - it "returns false if disabled" do
60   - subject.use_local_cache = false
61   - subject.using_local_cache?.should be_false
62   - end
63   - end
64   -
65   - context "with local cache enabled" do
66   - before do
67   - subject.use_local_cache = true
68   - end
69   -
70   - describe "#get" do
71   - before do
72   - @group = Flipper.register(:admins) { |thing| thing.admin? }
73   - @actor = Struct.new(:flipper_id).new('13')
74   - @actors = flipper.actors(20)
75   - @random = flipper.random(10)
76   - @feature = flipper[:stats]
77   -
78   - @feature.enable @group
79   - @feature.enable @actors
80   - @feature.enable @random
81   - @feature.enable @actor
82   -
83   - @result = subject.get(@feature)
84   - end
85   -
86   - it "returns hash of gate => value" do
87   - @result.should be_instance_of(Hash)
88   - @result[@feature.gate(:boolean)].should be_false
89   - @result[@feature.gate(:actor)].should eq(Set['13'])
90   - @result[@feature.gate(:group)].should eq(Set['admins'])
91   - @result[@feature.gate(:percentage_of_actors)].should eq('20')
92   - @result[@feature.gate(:percentage_of_random)].should eq('10')
93   - end
94   -
95   - it "memoizes adapter get value" do
96   - local_cache[@feature.name].should eq(@result)
97   - adapter.should_not_receive(:get)
98   - subject.get(@feature).should be(@result)
99   - end
100   - end
101   - end
102   -
103   - context "with local cache disabled" do
104   - before do
105   - subject.use_local_cache = false
106   - end
107   -
108   - describe "#get" do
109   - before do
110   - @group = Flipper.register(:admins) { |thing| thing.admin? }
111   - @actor = Struct.new(:flipper_id).new('13')
112   - @actors = flipper.actors(20)
113   - @random = flipper.random(10)
114   - @feature = flipper[:stats]
115   -
116   - @feature.enable @group
117   - @feature.enable @actors
118   - @feature.enable @random
119   - @feature.enable @actor
120   -
121   - @result = subject.get(@feature)
122   - end
123   -
124   - it "returns hash of gate => value" do
125   - @result.should be_instance_of(Hash)
126   - @result[@feature.gate(:boolean)].should be_false
127   - @result[@feature.gate(:actor)].should eq(Set['13'])
128   - @result[@feature.gate(:group)].should eq(Set['admins'])
129   - @result[@feature.gate(:percentage_of_actors)].should eq('20')
130   - @result[@feature.gate(:percentage_of_random)].should eq('10')
131   - end
132   - end
133   - end
134   -end
5 spec/flipper/dsl_spec.rb
@@ -9,10 +9,9 @@
9 9 let(:adapter) { Flipper::Adapters::Memory.new(source) }
10 10
11 11 describe "#initialize" do
12   - it "wraps adapter" do
  12 + it "sets adapter" do
13 13 dsl = described_class.new(adapter)
14   - dsl.adapter.should be_instance_of(Flipper::Adapter)
15   - dsl.adapter.adapter.should eq(adapter)
  14 + dsl.adapter.should_not be_nil
16 15 end
17 16
18 17 it "defaults instrumenter to noop" do
16 spec/flipper/middleware/local_cache_spec.rb
@@ -46,9 +46,9 @@
46 46 middleware = described_class.new app, flipper
47 47 body = middleware.call({}).last
48 48
49   - flipper.adapter.using_local_cache?.should be_true
  49 + flipper.adapter.memoizing?.should be_true
50 50 body.close
51   - flipper.adapter.using_local_cache?.should be_false
  51 + flipper.adapter.memoizing?.should be_false
52 52 end
53 53
54 54 it "clears local cache after body close" do
@@ -56,21 +56,21 @@
56 56 middleware = described_class.new app, flipper
57 57 body = middleware.call({}).last
58 58
59   - flipper.adapter.local_cache['hello'] = 'world'
  59 + flipper.adapter.cache['hello'] = 'world'
60 60 body.close
61   - flipper.adapter.local_cache.should be_empty
  61 + flipper.adapter.cache.should be_empty
62 62 end
63 63
64 64 it "clears the local cache with a successful request" do
65   - flipper.adapter.local_cache['hello'] = 'world'
  65 + flipper.adapter.cache['hello'] = 'world'
66 66 get '/'
67   - flipper.adapter.local_cache.should be_empty
  67 + flipper.adapter.cache.should be_empty
68 68 end
69 69
70 70 it "clears the local cache even when the request raises an error" do
71   - flipper.adapter.local_cache['hello'] = 'world'
  71 + flipper.adapter.cache['hello'] = 'world'
72 72 get '/fail' rescue nil
73   - flipper.adapter.local_cache.should be_empty
  73 + flipper.adapter.cache.should be_empty
74 74 end
75 75
76 76 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.