Skip to content

Commit

Permalink
Merge pull request #760 from flippercloud/strict
Browse files Browse the repository at this point in the history
Add Strict adapter to ensure feature exists on get
  • Loading branch information
bkeepers authored Sep 21, 2023
2 parents 74ac06b + 944d174 commit d06da06
Show file tree
Hide file tree
Showing 4 changed files with 128 additions and 0 deletions.
18 changes: 18 additions & 0 deletions examples/strict.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
require 'bundler/setup'
require 'flipper'

adapter = Flipper::Adapters::Strict.new(Flipper::Adapters::Memory.new)
flipper = Flipper.new(adapter)

begin
puts "Checking :unknown_feature, which should raise an error."
flipper.enabled?(:unknown_feature)
warn "An error was not raised, but should have been"
exit 1
rescue Flipper::Adapters::Strict::NotFound => exception
puts "Ok, the exepcted error was raised: #{exception.message}"
end

puts "Flipper.add(:new_feature)"
flipper.add(:new_feature)
puts "Flipper.enabled?(:new_feature) => #{flipper.enabled?(:new_feature)}"
1 change: 1 addition & 0 deletions lib/flipper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ def groups_registry=(registry)
require 'flipper/adapter'
require 'flipper/adapters/memoizable'
require 'flipper/adapters/memory'
require 'flipper/adapters/strict'
require 'flipper/adapters/instrumented'
require 'flipper/configuration'
require 'flipper/dsl'
Expand Down
47 changes: 47 additions & 0 deletions lib/flipper/adapters/strict.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
module Flipper
module Adapters
# An adapter that ensures a feature exists before checking it.
class Strict
extend Forwardable
include ::Flipper::Adapter
attr_reader :name, :adapter, :handler

class NotFound < ::Flipper::Error
def initialize(name)
super "Could not find feature #{name.inspect}. Call `Flipper.add(#{name.inspect})` to create it."
end
end

HANDLERS = {
raise: ->(feature) { raise NotFound.new(feature.key) },
warn: ->(feature) { warn NotFound.new(feature.key).message },
noop: ->(_) { },
}

def_delegators :@adapter, :features, :get_all, :add, :remove, :clear, :enable, :disable

def initialize(adapter, handler = nil, &block)
@name = :strict
@adapter = adapter
@handler = block || HANDLERS.fetch(handler)
end

def get(feature)
assert_feature_exists(feature)
@adapter.get(feature)
end

def get_multi(features)
features.each { |feature| assert_feature_exists(feature) }
@adapter.get_multi(features)
end

private

def assert_feature_exists(feature)
@handler.call(feature) unless @adapter.features.include?(feature.key)
end

end
end
end
62 changes: 62 additions & 0 deletions spec/flipper/adapters/strict_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
RSpec.describe Flipper::Adapters::Strict do
let(:flipper) { Flipper.new(subject) }
let(:feature) { flipper[:unknown] }

it_should_behave_like 'a flipper adapter' do
subject { described_class.new(Flipper::Adapters::Memory.new, :noop) }
end

context "handler = :raise" do
subject { described_class.new(Flipper::Adapters::Memory.new, :raise) }

context "#get" do
it "raises an error for unknown feature" do
expect { subject.get(feature) }.to raise_error(Flipper::Adapters::Strict::NotFound)
end
end

context "#get_multi" do
it "raises an error for unknown feature" do
expect { subject.get_multi([feature]) }.to raise_error(Flipper::Adapters::Strict::NotFound)
end
end
end

context "handler = :warn" do
subject { described_class.new(Flipper::Adapters::Memory.new, :warn) }

context "#get" do
it "raises an error for unknown feature" do
expect(silence { subject.get(feature) }).to match(/Could not find feature "unknown"/)
end
end

context "#get_multi" do
it "raises an error for unknown feature" do
expect(silence { subject.get_multi([feature]) }).to match(/Could not find feature "unknown"/)
end
end
end

context "handler = Block" do
let(:unknown_features) { [] }
subject do
described_class.new(Flipper::Adapters::Memory.new) { |feature| unknown_features << feature.key}
end


context "#get" do
it "raises an error for unknown feature" do
subject.get(feature)
expect(unknown_features).to eq(["unknown"])
end
end

context "#get_multi" do
it "raises an error for unknown feature" do
subject.get_multi([flipper[:foo], flipper[:bar]])
expect(unknown_features).to eq(["foo", "bar"])
end
end
end
end

0 comments on commit d06da06

Please sign in to comment.