mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-02-27 01:43:22 +00:00
Merge pull request #6516 from coopdevs/per-user-feature-toggles
Refactor FeatureToggle to toggle depending on user
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
module ApplicationHelper
|
||||
def feature?(feature)
|
||||
OpenFoodNetwork::FeatureToggle.enabled? feature
|
||||
def feature?(feature, user = nil)
|
||||
OpenFoodNetwork::FeatureToggle.enabled?(feature, user)
|
||||
end
|
||||
|
||||
def ng_form_for(name, *args, &block)
|
||||
|
||||
@@ -1,21 +1,81 @@
|
||||
module OpenFoodNetwork
|
||||
# This feature toggles implementation provides two mechanisms to conditionally enable features.
|
||||
#
|
||||
# You can provide an ENV var with the prefix `OFN_FEATURE_` and query it using the
|
||||
# `ApplicationHelper#feature?` helper method. For instance, providing the ENV var
|
||||
# `OFN_FEATURE_NEW_SHINNY_FEATURE` you could then query it from view as follows:
|
||||
#
|
||||
# - if feature? :new_shiny_feature
|
||||
# = render "new_shiny_feature"
|
||||
#
|
||||
# Alternatively, you can choose which users have the feature toggled on. To do that you need to
|
||||
# register the feature and its users from an initializer like:
|
||||
#
|
||||
# require 'open_food_network/feature_toggle'
|
||||
# OpenFoodNetwork::FeatureToggle.enable(:new_shiny_feature, ['ofn@example.com'])
|
||||
#
|
||||
# Note, however, that it'd be better to read the user emails from an ENV var provisioned with
|
||||
# ofn-install:
|
||||
#
|
||||
# require 'open_food_network/feature_toggle'
|
||||
# OpenFoodNetwork::FeatureToggle.enable(:new_shiny_feature, ENV['PRODUCT_TEAM'])
|
||||
#
|
||||
# You can then check it from a view like:
|
||||
#
|
||||
# - if feature? :new_shiny_feature, spree_current_user
|
||||
# = render "new_shiny_feature"
|
||||
#
|
||||
class FeatureToggle
|
||||
def self.enabled?(feature_name)
|
||||
true?(env_variable_value(feature_name))
|
||||
def self.enabled?(feature_name, user = nil)
|
||||
new.enabled?(feature_name, user)
|
||||
end
|
||||
|
||||
def self.enable(feature_name, user_emails)
|
||||
Thread.current[:features] ||= {}
|
||||
Thread.current[:features][feature_name] = Feature.new(user_emails)
|
||||
end
|
||||
|
||||
def initialize
|
||||
@features = Thread.current[:features]
|
||||
end
|
||||
|
||||
def enabled?(feature_name, user)
|
||||
if user.present?
|
||||
feature = features[feature_name]
|
||||
feature.enabled?(user)
|
||||
else
|
||||
true?(env_variable_value(feature_name))
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.env_variable_value(feature_name)
|
||||
attr_reader :features
|
||||
|
||||
def env_variable_value(feature_name)
|
||||
ENV.fetch(env_variable_name(feature_name), nil)
|
||||
end
|
||||
|
||||
def self.env_variable_name(feature_name)
|
||||
def env_variable_name(feature_name)
|
||||
"OFN_FEATURE_#{feature_name.to_s.upcase}"
|
||||
end
|
||||
|
||||
def self.true?(value)
|
||||
def true?(value)
|
||||
value.to_s.casecmp("true").zero?
|
||||
end
|
||||
end
|
||||
|
||||
class Feature
|
||||
def initialize(users = [])
|
||||
@users = users
|
||||
end
|
||||
|
||||
def enabled?(user)
|
||||
users.include?(user.email)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
attr_reader :users
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,30 +1,52 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'open_food_network/feature_toggle'
|
||||
require 'spec_helper'
|
||||
|
||||
module OpenFoodNetwork
|
||||
describe FeatureToggle do
|
||||
it "returns true when feature is on" do
|
||||
stub_foo("true")
|
||||
expect(FeatureToggle.enabled?(:foo)).to be true
|
||||
context 'when users are not specified' do
|
||||
it "returns true when feature is on" do
|
||||
stub_foo("true")
|
||||
expect(FeatureToggle.enabled?(:foo)).to be true
|
||||
end
|
||||
|
||||
it "returns false when feature is off" do
|
||||
stub_foo("false")
|
||||
expect(FeatureToggle.enabled?(:foo)).to be false
|
||||
end
|
||||
|
||||
it "returns false when feature is unspecified" do
|
||||
stub_foo("maybe")
|
||||
expect(FeatureToggle.enabled?(:foo)).to be false
|
||||
end
|
||||
|
||||
it "returns false when feature is undefined" do
|
||||
expect(FeatureToggle.enabled?(:foo)).to be false
|
||||
end
|
||||
|
||||
def stub_foo(value)
|
||||
allow(ENV).to receive(:fetch).with("OFN_FEATURE_FOO", nil).and_return(value)
|
||||
end
|
||||
end
|
||||
|
||||
it "returns false when feature is off" do
|
||||
stub_foo("false")
|
||||
expect(FeatureToggle.enabled?(:foo)).to be false
|
||||
end
|
||||
context 'when specifying users' do
|
||||
let(:user) { build(:user) }
|
||||
|
||||
it "returns false when feature is unspecified" do
|
||||
stub_foo("maybe")
|
||||
expect(FeatureToggle.enabled?(:foo)).to be false
|
||||
end
|
||||
context 'and the feature is enabled for them' do
|
||||
before { FeatureToggle.enable(:foo, [user.email]) }
|
||||
|
||||
it "returns false when feature is undefined" do
|
||||
expect(FeatureToggle.enabled?(:foo)).to be false
|
||||
end
|
||||
it 'returns true' do
|
||||
expect(FeatureToggle.enabled?(:foo, user)).to eq(true)
|
||||
end
|
||||
end
|
||||
|
||||
def stub_foo(value)
|
||||
allow(ENV).to receive(:fetch).with("OFN_FEATURE_FOO", nil).and_return(value)
|
||||
context 'and the feature is disabled for them' do
|
||||
before { FeatureToggle.enable(:foo, []) }
|
||||
|
||||
it 'returns false' do
|
||||
expect(FeatureToggle.enabled?(:foo, user)).to eq(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user