Skip to content

Commit

Permalink
Merge pull request #2020 from alphagov/add-feature-flagging
Browse files Browse the repository at this point in the history
Add Flipflop gem for feature-toggling
  • Loading branch information
mtaylorgds authored Jan 24, 2024
2 parents 07b39d7 + 8c85c76 commit bcde81e
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 0 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ gem "bootsnap", require: false
gem "bootstrap-kaminari-views"
gem "diffy"
gem "erubis"
gem "flipflop"
gem "gds-api-adapters"
gem "gds-sso"
gem "govspeak"
Expand Down
6 changes: 6 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ GEM
ruby2_keywords (>= 0.0.4)
faraday-net_http (3.0.2)
ffi (1.15.5)
flipflop (2.7.1)
activesupport (>= 4.0)
terminal-table (>= 1.8)
gds-api-adapters (91.1.0)
addressable
link_header
Expand Down Expand Up @@ -740,6 +743,8 @@ GEM
statsd-ruby (1.5.0)
strip_attributes (1.13.0)
activemodel (>= 3.0, < 8.0)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
terser (1.1.20)
execjs (>= 0.3.0, < 3)
thor (1.3.0)
Expand Down Expand Up @@ -789,6 +794,7 @@ DEPENDENCIES
diffy
erubis
factory_bot_rails
flipflop
gds-api-adapters
gds-sso
govspeak
Expand Down
13 changes: 13 additions & 0 deletions app/constraints/feature_constraint.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
class FeatureConstraint
def initialize(feature_name)
@feature_name = feature_name
end

def matches?(request)
if request.cookies.key?(@feature_name)
request.cookies[@feature_name] == "1"
else
Flipflop.enabled?(@feature_name.to_sym)
end
end
end
8 changes: 8 additions & 0 deletions config/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,14 @@

module Publisher
class Application < Rails::Application
# Before filter for Flipflop dashboard. Replace with a lambda or method name
# defined in ApplicationController to implement access control.
config.flipflop.dashboard_access_filter = nil

# By default, when set to `nil`, strategy loading errors are suppressed in test
# mode. Set to `true` to always raise errors, or `false` to always warn.
config.flipflop.raise_strategy_errors = nil

# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 7.0

Expand Down
8 changes: 8 additions & 0 deletions config/environments/development.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
require "active_support/core_ext/integer/time"

Rails.application.configure do
# Before filter for Flipflop dashboard. Replace with a lambda or method name
# defined in ApplicationController to implement access control.
config.flipflop.dashboard_access_filter = nil

# By default, when set to `nil`, strategy loading errors are suppressed in test
# mode. Set to `true` to always raise errors, or `false` to always warn.
config.flipflop.raise_strategy_errors = nil

# Settings specified here will take precedence over those in config/application.rb.

# In the development environment your application's code is reloaded any time
Expand Down
8 changes: 8 additions & 0 deletions config/environments/test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,14 @@
# and recreated between test runs. Don't rely on the data there!

Rails.application.configure do
# Before filter for Flipflop dashboard. Replace with a lambda or method name
# defined in ApplicationController to implement access control.
config.flipflop.dashboard_access_filter = nil

# By default, when set to `nil`, strategy loading errors are suppressed in test
# mode. Set to `true` to always raise errors, or `false` to always warn.
config.flipflop.raise_strategy_errors = nil

# Settings specified here will take precedence over those in config/application.rb.

# Turn false under Spring and add config.action_view.cache_template_loading = true.
Expand Down
11 changes: 11 additions & 0 deletions config/features.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Flipflop.configure do
# Strategies will be used in the order listed here.
strategy :cookie
strategy :default

if Rails.env.test?
feature :feature_for_tests,
default: true,
description: "A feature only used by tests; not to be used for any actual features."
end
end
1 change: 1 addition & 0 deletions config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,5 @@
get "/govuk-sitemap.xml" => "sitemap#index"

mount GovukAdminTemplate::Engine, at: "/style-guide"
mount Flipflop::Engine => "/flipflop"
end
65 changes: 65 additions & 0 deletions test/unit/constraints/feature_constraint_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
require "test_helper"

class FeatureConstraintTest < ActiveSupport::TestCase
context "Feature 'feature_for_tests' is enabled by default" do
setup do
test_strategy = Flipflop::FeatureSet.current.test!
test_strategy.switch!(:feature_for_tests, true)
end

should "match when a request cookie explicitly enables feature" do
request = stub(cookies: { "feature_for_tests" => "1" })

feature_constraint = FeatureConstraint.new("feature_for_tests")

assert_equal true, feature_constraint.matches?(request)
end

should "not match when a request cookie explicitly disables feature" do
request = stub(cookies: { "feature_for_tests" => "0" })

feature_constraint = FeatureConstraint.new("feature_for_tests")

assert_equal false, feature_constraint.matches?(request)
end

should "match when a request cookie does not override default feature status" do
request = stub(cookies: {})

feature_constraint = FeatureConstraint.new("feature_for_tests")

assert_equal true, feature_constraint.matches?(request)
end
end

context "Feature 'feature_for_tests' is disabled by default" do
setup do
test_strategy = Flipflop::FeatureSet.current.test!
test_strategy.switch!(:feature_for_tests, false)
end

should "match when a request cookie explicitly enables feature" do
request = stub(cookies: { "feature_for_tests" => "1" })

feature_constraint = FeatureConstraint.new("feature_for_tests")

assert_equal true, feature_constraint.matches?(request)
end

should "not match when a request cookie explicitly disables feature" do
request = stub(cookies: { "feature_for_tests" => "0" })

feature_constraint = FeatureConstraint.new("feature_for_tests")

assert_equal false, feature_constraint.matches?(request)
end

should "not match when a request cookie does not override default feature status" do
request = stub(cookies: {})

feature_constraint = FeatureConstraint.new("feature_for_tests")

assert_equal false, feature_constraint.matches?(request)
end
end
end

0 comments on commit bcde81e

Please sign in to comment.