flip_fab 1.0.0 → 1.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.github/dependabot.yml +18 -0
- data/.github/workflows/dobby-actions.yml +29 -0
- data/.github/workflows/gem-publish.yml +46 -0
- data/.github/workflows/version-forget-me-not.yml +19 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +31 -0
- data/.rubocop_todo.yml +58 -0
- data/.ruby-version +1 -0
- data/.semaphore/semaphore.yml +49 -0
- data/CODEOWNERS +3 -0
- data/Gemfile +0 -12
- data/README.md +8 -2
- data/_pipeline/step_build_gem.sh +5 -0
- data/_pipeline/step_test_gem.sh +6 -0
- data/catalog-info.yaml +8 -0
- data/example/rails_app/Gemfile +4 -21
- data/example/rails_app/Rakefile +1 -1
- data/example/rails_app/app/assets/config/manifest.js +3 -0
- data/example/rails_app/app/assets/javascripts/application.js +0 -1
- data/example/rails_app/app/controllers/beavers_controller.rb +12 -13
- data/example/rails_app/bin/bundle +1 -1
- data/example/rails_app/bin/rails +1 -1
- data/example/rails_app/config/application.rb +2 -2
- data/example/rails_app/config/boot.rb +1 -1
- data/example/rails_app/config/environment.rb +1 -1
- data/example/rails_app/config/environments/development.rb +1 -1
- data/example/rails_app/config/environments/test.rb +2 -2
- data/example/rails_app/config/initializers/cookies_serializer.rb +1 -1
- data/example/rails_app/config.ru +1 -1
- data/example/rails_app/db/schema.rb +5 -8
- data/example/rails_app/spec/rails_helper.rb +2 -2
- data/example/rails_app/spec/spec_helper.rb +0 -1
- data/example/rails_app/test/controllers/beavers_controller_test.rb +12 -12
- data/example/rails_app/test/test_helper.rb +1 -1
- data/flip_fab.gemspec +11 -5
- data/lib/flip_fab/contextual_feature.rb +23 -19
- data/lib/flip_fab/cookie_persistence.rb +10 -17
- data/lib/flip_fab/feature.rb +2 -3
- data/lib/flip_fab/features_by_name.rb +5 -4
- data/lib/flip_fab/helper.rb +0 -1
- data/lib/flip_fab/persistence.rb +2 -3
- data/lib/flip_fab/version.rb +6 -1
- data/lib/flip_fab.rb +4 -4
- data/script/cibuild +10 -0
- data/spec/lib/flip_fab/contextual_feature_spec.rb +54 -57
- data/spec/lib/flip_fab/cookie_persistence.feature +3 -3
- data/spec/lib/flip_fab/cookie_persistence_spec.rb +36 -43
- data/spec/lib/flip_fab/feature_spec.rb +6 -9
- data/spec/lib/flip_fab/features_by_name_spec.rb +3 -6
- data/spec/lib/flip_fab/helper_spec.rb +35 -38
- data/spec/lib/flip_fab/persistence_spec.rb +2 -5
- data/spec/lib/flip_fab_spec.rb +11 -15
- data/spec/spec_helper.rb +47 -49
- data/spec/support/test_app.rb +2 -2
- data/spec/support/test_context.rb +1 -1
- data/spec/support/test_multiple_persistence.rb +2 -3
- data/spec/support/test_persistence.rb +2 -3
- data/spec/support/test_rack_context.rb +3 -3
- metadata +103 -11
- data/Gemfile.lock +0 -92
- data/example/rails_app/Gemfile.lock +0 -178
- data/example/rails_app/README.rdoc +0 -28
- data/example/rails_app/config/rabbit_feed.yml +0 -8
- data/example/rails_app/config/unicorn.rb +0 -4
data/flip_fab.gemspec
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
1
|
+
lib = File.expand_path('lib', __dir__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
require 'flip_fab/version'
|
5
4
|
|
@@ -8,13 +7,20 @@ Gem::Specification.new do |spec|
|
|
8
7
|
spec.version = FlipFab::VERSION
|
9
8
|
spec.authors = ['Simply Business']
|
10
9
|
spec.email = ['tech@simplybusiness.co.uk']
|
11
|
-
spec.description =
|
12
|
-
spec.summary =
|
10
|
+
spec.description = 'A gem providing persistent, per-user feature flipping to Rack applications.'
|
11
|
+
spec.summary = 'A gem providing persistent, per-user feature flipping to Rack applications.'
|
13
12
|
spec.homepage = 'https://github.com/simplybusiness/flip_fab'
|
14
13
|
spec.license = 'MIT'
|
15
14
|
|
16
|
-
spec.files = `git ls-files`.split(
|
15
|
+
spec.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
|
17
16
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
17
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
18
|
spec.require_paths = ['lib']
|
19
|
+
|
20
|
+
spec.add_development_dependency 'rack', '>=2.1.1'
|
21
|
+
spec.add_development_dependency 'rack-test'
|
22
|
+
spec.add_development_dependency 'rspec', '~> 3.5'
|
23
|
+
spec.add_development_dependency 'rutabaga', '~> 3.0'
|
24
|
+
spec.add_development_dependency 'simplycop', '~> 1.9'
|
25
|
+
spec.add_development_dependency 'timecop', '~> 0.8'
|
20
26
|
end
|
@@ -2,13 +2,19 @@ module FlipFab
|
|
2
2
|
class ContextualFeature
|
3
3
|
attr_reader :feature, :context
|
4
4
|
|
5
|
-
def initialize
|
5
|
+
def initialize(feature, context)
|
6
6
|
@feature = feature
|
7
7
|
@context = context
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
return unless overridden?
|
9
|
+
|
10
|
+
@state = override
|
11
|
+
persist
|
12
|
+
end
|
13
|
+
|
14
|
+
def as_json(options = {})
|
15
|
+
{
|
16
|
+
'feature' => feature.as_json(options)
|
17
|
+
}
|
12
18
|
end
|
13
19
|
|
14
20
|
def enabled?
|
@@ -27,16 +33,16 @@ module FlipFab
|
|
27
33
|
self.state = :disabled
|
28
34
|
end
|
29
35
|
|
30
|
-
def state=
|
31
|
-
raise "Invalid state provided: `#{value}`, possible states are :enabled, :disabled" unless %i
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
+
def state=(value)
|
37
|
+
raise "Invalid state provided: `#{value}`, possible states are :enabled, :disabled" unless %i[enabled disabled].include? value
|
38
|
+
return if overridden?
|
39
|
+
|
40
|
+
@state = value
|
41
|
+
persist
|
36
42
|
end
|
37
43
|
|
38
44
|
def persist
|
39
|
-
persistence_adapters.each{ |adapter| adapter.write state }
|
45
|
+
persistence_adapters.each { |adapter| adapter.write state }
|
40
46
|
end
|
41
47
|
|
42
48
|
private
|
@@ -46,11 +52,7 @@ module FlipFab
|
|
46
52
|
end
|
47
53
|
|
48
54
|
def state
|
49
|
-
@state ||=
|
50
|
-
state_from_context
|
51
|
-
else
|
52
|
-
default_state
|
53
|
-
end
|
55
|
+
@state ||= state_in_context? ? state_from_context : default_state
|
54
56
|
end
|
55
57
|
|
56
58
|
def state_in_context?
|
@@ -62,7 +64,7 @@ module FlipFab
|
|
62
64
|
end
|
63
65
|
|
64
66
|
def first_adapter_with_state
|
65
|
-
persistence_adapters.detect{|adapter| !adapter.read.nil?}
|
67
|
+
persistence_adapters.detect { |adapter| !adapter.read.nil? }
|
66
68
|
end
|
67
69
|
|
68
70
|
def default_state
|
@@ -71,8 +73,10 @@ module FlipFab
|
|
71
73
|
|
72
74
|
def override
|
73
75
|
return unless context.respond_to? :params
|
76
|
+
|
74
77
|
override = context.params[feature.name.to_s]
|
75
|
-
return unless %w
|
78
|
+
return unless %w[enabled disabled].include? override
|
79
|
+
|
76
80
|
override.to_sym
|
77
81
|
end
|
78
82
|
|
@@ -1,12 +1,11 @@
|
|
1
1
|
module FlipFab
|
2
2
|
class CookiePersistence < FlipFab::Persistence
|
3
|
+
COOKIE_PATH = '/'.freeze
|
4
|
+
COOKIE_DURATION_MONTHS = 12
|
5
|
+
# See: https://github.com/rails/rails/blob/b1124a2ac88778c0feb0157ac09367cbd204bf01/actionpack/lib/action_dispatch/middleware/cookies.rb#L214
|
6
|
+
DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
|
3
7
|
|
4
|
-
|
5
|
-
COOKIE_DURATION_MONTHS = 12
|
6
|
-
# See: https://github.com/rails/rails/blob/b1124a2ac88778c0feb0157ac09367cbd204bf01/actionpack/lib/action_dispatch/middleware/cookies.rb#L214
|
7
|
-
DOMAIN_REGEXP = /[^.]*\.([^.]*|..\...|...\...)$/
|
8
|
-
|
9
|
-
def initialize feature_name, context
|
8
|
+
def initialize(feature_name, context)
|
10
9
|
super
|
11
10
|
end
|
12
11
|
|
@@ -14,14 +13,10 @@ module FlipFab
|
|
14
13
|
value.to_sym unless value.nil?
|
15
14
|
end
|
16
15
|
|
17
|
-
def write
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
expires: cookie_expiration,
|
22
|
-
domain: cookie_domain,
|
23
|
-
path: COOKIE_PATH,
|
24
|
-
}
|
16
|
+
def write(state)
|
17
|
+
context.response.set_cookie key, value: state,
|
18
|
+
expires: cookie_expiration,
|
19
|
+
path: COOKIE_PATH
|
25
20
|
end
|
26
21
|
|
27
22
|
private
|
@@ -36,9 +31,7 @@ module FlipFab
|
|
36
31
|
|
37
32
|
# See: https://github.com/rails/rails/blob/b1124a2ac88778c0feb0157ac09367cbd204bf01/actionpack/lib/action_dispatch/middleware/cookies.rb#L286-L294
|
38
33
|
def top_level_domain
|
39
|
-
if (host !~ /^[\d.]+$/) && (host =~ DOMAIN_REGEXP)
|
40
|
-
$&
|
41
|
-
end
|
34
|
+
Regexp.last_match(0) if (host !~ /^[\d.]+$/) && (host =~ DOMAIN_REGEXP)
|
42
35
|
end
|
43
36
|
|
44
37
|
def cookie_expiration
|
data/lib/flip_fab/feature.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
module FlipFab
|
2
2
|
class Feature
|
3
|
-
|
4
3
|
attr_reader :name, :default, :persistence_adapters
|
5
4
|
|
6
|
-
def initialize
|
5
|
+
def initialize(name, options = {})
|
7
6
|
@name = name
|
8
7
|
@default = options[:default] || :disabled
|
9
8
|
@persistence_adapters = options[:persistence_adapters] || [CookiePersistence]
|
@@ -17,7 +16,7 @@ module FlipFab
|
|
17
16
|
!enabled?
|
18
17
|
end
|
19
18
|
|
20
|
-
def with_context
|
19
|
+
def with_context(context)
|
21
20
|
ContextualFeature.new self, context
|
22
21
|
end
|
23
22
|
end
|
@@ -4,17 +4,18 @@ module FlipFab
|
|
4
4
|
class FeaturesByName
|
5
5
|
extend Forwardable
|
6
6
|
|
7
|
-
def initialize
|
7
|
+
def initialize(features_by_name = {})
|
8
8
|
@features_by_name = features_by_name
|
9
9
|
end
|
10
10
|
|
11
|
-
def []
|
11
|
+
def [](name)
|
12
12
|
raise "no feature has been defined with the name: #{name}" if @features_by_name[name].nil?
|
13
|
+
|
13
14
|
@features_by_name[name]
|
14
15
|
end
|
15
16
|
|
16
|
-
def with_context
|
17
|
-
FeaturesByName.new Hash[@features_by_name.map{|name, feature| [name, (feature.with_context context)]}]
|
17
|
+
def with_context(context)
|
18
|
+
FeaturesByName.new Hash[@features_by_name.map { |name, feature| [name, (feature.with_context context)] }]
|
18
19
|
end
|
19
20
|
|
20
21
|
def_delegators :@features_by_name, :[]=, :clear, :count, :each
|
data/lib/flip_fab/helper.rb
CHANGED
data/lib/flip_fab/persistence.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
module FlipFab
|
2
2
|
class Persistence
|
3
|
-
|
4
3
|
attr_reader :feature_name, :context
|
5
4
|
|
6
|
-
def initialize
|
5
|
+
def initialize(feature_name, context)
|
7
6
|
@feature_name = feature_name
|
8
7
|
@context = context
|
9
8
|
end
|
@@ -12,7 +11,7 @@ module FlipFab
|
|
12
11
|
raise NotImplementedError
|
13
12
|
end
|
14
13
|
|
15
|
-
def write
|
14
|
+
def write(state)
|
16
15
|
raise NotImplementedError
|
17
16
|
end
|
18
17
|
end
|
data/lib/flip_fab/version.rb
CHANGED
data/lib/flip_fab.rb
CHANGED
@@ -6,19 +6,19 @@ require 'flip_fab/persistence'
|
|
6
6
|
require 'flip_fab/cookie_persistence'
|
7
7
|
|
8
8
|
module FlipFab
|
9
|
-
extend self
|
10
|
-
|
11
9
|
attr_reader :features
|
12
10
|
|
13
|
-
def define_feature
|
11
|
+
def define_feature(name, options = {})
|
14
12
|
@features ||= {}
|
15
13
|
@features[name] = Feature.new name, options
|
16
14
|
end
|
17
15
|
|
18
16
|
@features ||= FeaturesByName.new
|
17
|
+
|
18
|
+
module_function :features, :define_feature
|
19
19
|
end
|
20
20
|
|
21
21
|
if defined?(ActionController)
|
22
|
-
ActionController::Base.
|
22
|
+
ActionController::Base.include FlipFab::Helper
|
23
23
|
ActionController::Base.helper FlipFab::Helper
|
24
24
|
end
|
data/script/cibuild
ADDED
@@ -1,15 +1,20 @@
|
|
1
1
|
module FlipFab
|
2
|
-
describe ContextualFeature do
|
3
|
-
let(:override) {
|
2
|
+
describe ContextualFeature do # rubocop:disable Metrics/BlockLength
|
3
|
+
let(:override) {}
|
4
4
|
let(:default) { :disabled }
|
5
5
|
let(:persistence_adapters) { [TestPersistence] }
|
6
|
-
let(:feature) { Feature.new :example_feature,
|
7
|
-
let(:feature_states) {{ example_feature: :enabled }}
|
8
|
-
let(:context) { TestContext.new feature_states,
|
9
|
-
subject{ described_class.new feature, context }
|
6
|
+
let(:feature) { Feature.new :example_feature, default: default, persistence_adapters: persistence_adapters }
|
7
|
+
let(:feature_states) { { example_feature: :enabled } }
|
8
|
+
let(:context) { TestContext.new feature_states, 'example_feature' => override }
|
9
|
+
subject { described_class.new feature, context }
|
10
|
+
|
11
|
+
describe '.as_json' do
|
12
|
+
it 'returns only the feature' do
|
13
|
+
expect(subject.as_json.keys).to eq(['feature'])
|
14
|
+
end
|
15
|
+
end
|
10
16
|
|
11
17
|
describe '.new' do
|
12
|
-
|
13
18
|
it 'assigns the feature' do
|
14
19
|
expect(subject.feature).to eq(feature)
|
15
20
|
end
|
@@ -22,23 +27,22 @@ module FlipFab
|
|
22
27
|
let(:override) { 'disabled' }
|
23
28
|
|
24
29
|
it 'persists the override' do
|
25
|
-
expect{ subject }.to change{ feature_states }.from(
|
30
|
+
expect { subject }.to change { feature_states }.from(example_feature: :enabled).to(example_feature: :disabled)
|
26
31
|
end
|
27
32
|
|
28
33
|
context 'when the override provided is not one of enabled or disabled, it does not persist the override' do
|
29
34
|
let(:override) { '' }
|
30
35
|
|
31
36
|
it 'does not persist the override' do
|
32
|
-
expect{ subject }.not_to change{ feature_states }.from(
|
37
|
+
expect { subject }.not_to change { feature_states }.from(example_feature: :enabled)
|
33
38
|
end
|
34
39
|
end
|
35
40
|
end
|
36
41
|
end
|
37
42
|
|
38
43
|
describe '#enabled?' do
|
39
|
-
|
40
44
|
context 'when the feature is enabled in the adapter' do
|
41
|
-
let(:feature_states) {{ example_feature: :enabled }}
|
45
|
+
let(:feature_states) { { example_feature: :enabled } }
|
42
46
|
|
43
47
|
it 'returns true' do
|
44
48
|
expect(subject.enabled?).to be_truthy
|
@@ -54,7 +58,7 @@ module FlipFab
|
|
54
58
|
end
|
55
59
|
|
56
60
|
context 'when the feature is disabled in the adapter' do
|
57
|
-
let(:feature_states) {{ example_feature: :disabled }}
|
61
|
+
let(:feature_states) { { example_feature: :disabled } }
|
58
62
|
|
59
63
|
it 'returns false' do
|
60
64
|
expect(subject.enabled?).to be_falsey
|
@@ -62,7 +66,7 @@ module FlipFab
|
|
62
66
|
end
|
63
67
|
|
64
68
|
context 'when the feature is not specified in the adapter' do
|
65
|
-
let(:feature_states) {{ }
|
69
|
+
let(:feature_states) { {} }
|
66
70
|
|
67
71
|
context 'when the default is :enabled' do
|
68
72
|
let(:default) { :enabled }
|
@@ -85,7 +89,7 @@ module FlipFab
|
|
85
89
|
let(:persistence_adapters) { [TestPersistence, TestMultiplePersistence] }
|
86
90
|
|
87
91
|
context 'when the first adapter has enabled and the second adapter has nil' do
|
88
|
-
let(:feature_states) {{ example_feature: :enabled, different_example_feature: nil }}
|
92
|
+
let(:feature_states) { { example_feature: :enabled, different_example_feature: nil } }
|
89
93
|
|
90
94
|
it 'returns true' do
|
91
95
|
expect(subject.enabled?).to be_truthy
|
@@ -93,7 +97,7 @@ module FlipFab
|
|
93
97
|
end
|
94
98
|
|
95
99
|
context 'when the first adapter has nil and the second adapter has enabled' do
|
96
|
-
let(:feature_states) {{ example_feature: nil, different_example_feature: :enabled }}
|
100
|
+
let(:feature_states) { { example_feature: nil, different_example_feature: :enabled } }
|
97
101
|
|
98
102
|
it 'returns true' do
|
99
103
|
expect(subject.enabled?).to be_truthy
|
@@ -101,7 +105,7 @@ module FlipFab
|
|
101
105
|
end
|
102
106
|
|
103
107
|
context 'when the first adapter has disabled and the second adapter has enabled' do
|
104
|
-
let(:feature_states) {{ example_feature: :disabled, different_example_feature: :enabled }}
|
108
|
+
let(:feature_states) { { example_feature: :disabled, different_example_feature: :enabled } }
|
105
109
|
|
106
110
|
it 'returns false' do
|
107
111
|
expect(subject.enabled?).to be_falsey
|
@@ -111,9 +115,8 @@ module FlipFab
|
|
111
115
|
end
|
112
116
|
|
113
117
|
describe '#disabled?' do
|
114
|
-
|
115
118
|
context 'when #enabled? returns true' do
|
116
|
-
let(:feature_states) {{ example_feature: :enabled }}
|
119
|
+
let(:feature_states) { { example_feature: :enabled } }
|
117
120
|
|
118
121
|
it 'returns false' do
|
119
122
|
expect(subject.disabled?).to be_falsey
|
@@ -121,7 +124,7 @@ module FlipFab
|
|
121
124
|
end
|
122
125
|
|
123
126
|
context 'when #enabled? returns false' do
|
124
|
-
let(:feature_states) {{ example_feature: :disabled }}
|
127
|
+
let(:feature_states) { { example_feature: :disabled } }
|
125
128
|
|
126
129
|
it 'returns true' do
|
127
130
|
expect(subject.disabled?).to be_truthy
|
@@ -130,34 +133,30 @@ module FlipFab
|
|
130
133
|
end
|
131
134
|
|
132
135
|
describe '#state=' do
|
133
|
-
|
134
136
|
context 'when the provided value is not :enabled or :disabled' do
|
135
|
-
|
136
137
|
it 'raises' do
|
137
|
-
expect{ subject.state = '' }.to raise_error 'Invalid state provided: ``, possible states are :enabled, :disabled'
|
138
|
-
expect{ subject.state = 'enabled' }.to raise_error 'Invalid state provided: `enabled`, possible states are :enabled, :disabled'
|
138
|
+
expect { subject.state = '' }.to raise_error 'Invalid state provided: ``, possible states are :enabled, :disabled'
|
139
|
+
expect { subject.state = 'enabled' }.to raise_error 'Invalid state provided: `enabled`, possible states are :enabled, :disabled'
|
139
140
|
end
|
140
141
|
end
|
141
142
|
|
142
143
|
context 'when the provided value is :enabled or :disabled' do
|
143
|
-
|
144
144
|
it 'changes the state of the feature' do
|
145
|
-
expect{ subject.state = :disabled }.to change{subject.enabled?}.from(true).to(false)
|
146
|
-
expect{ subject.state = :enabled }.to change{subject.enabled?}.from(false).to(true)
|
145
|
+
expect { subject.state = :disabled }.to change { subject.enabled? }.from(true).to(false)
|
146
|
+
expect { subject.state = :enabled }.to change { subject.enabled? }.from(false).to(true)
|
147
147
|
end
|
148
148
|
end
|
149
149
|
end
|
150
150
|
|
151
151
|
describe '#enable' do
|
152
|
-
|
153
152
|
context 'when the state has been overridden' do
|
154
153
|
let(:override) { 'disabled' }
|
155
154
|
|
156
155
|
context 'and the persistence adapter has the opposite state' do
|
157
|
-
let(:feature_states) {{ example_feature: :disabled }}
|
156
|
+
let(:feature_states) { { example_feature: :disabled } }
|
158
157
|
|
159
158
|
it 'does not change the state of the feature' do
|
160
|
-
expect{subject.enable}.not_to change{subject.enabled?}.from(false)
|
159
|
+
expect { subject.enable }.not_to change { subject.enabled? }.from(false)
|
161
160
|
end
|
162
161
|
|
163
162
|
it 'does not persist the state in the adapter' do
|
@@ -169,14 +168,14 @@ module FlipFab
|
|
169
168
|
|
170
169
|
context 'when there are multiple persistence adapters' do
|
171
170
|
let(:persistence_adapters) { [TestPersistence, TestMultiplePersistence] }
|
172
|
-
let(:feature_states) {{ example_feature: :disabled, different_example_feature: :disabled }}
|
171
|
+
let(:feature_states) { { example_feature: :disabled, different_example_feature: :disabled } }
|
173
172
|
|
174
173
|
it 'changes the state of the feature' do
|
175
|
-
expect{subject.enable}.to change{subject.enabled?}.from(false).to(true)
|
174
|
+
expect { subject.enable }.to change { subject.enabled? }.from(false).to(true)
|
176
175
|
end
|
177
176
|
|
178
177
|
it 'persists the state in the adapters' do
|
179
|
-
expect{ subject.enable }.to change{ feature_states }.from(
|
178
|
+
expect { subject.enable }.to change { feature_states }.from(example_feature: :disabled, different_example_feature: :disabled).to(example_feature: :enabled, different_example_feature: :enabled)
|
180
179
|
end
|
181
180
|
end
|
182
181
|
|
@@ -184,37 +183,37 @@ module FlipFab
|
|
184
183
|
let(:persistence_adapters) { [TestPersistence] }
|
185
184
|
|
186
185
|
context 'and the persistence adapter has the same state' do
|
187
|
-
let(:feature_states) {{ example_feature: :enabled }}
|
186
|
+
let(:feature_states) { { example_feature: :enabled } }
|
188
187
|
|
189
188
|
it 'does not change the state of the feature' do
|
190
|
-
expect{subject.enable}.not_to change{subject.enabled?}.from(true)
|
189
|
+
expect { subject.enable }.not_to change { subject.enabled? }.from(true)
|
191
190
|
end
|
192
191
|
end
|
193
192
|
|
194
193
|
context 'and the persistence adapter has the opposite state' do
|
195
|
-
let(:feature_states) {{ example_feature: :disabled }}
|
194
|
+
let(:feature_states) { { example_feature: :disabled } }
|
196
195
|
|
197
196
|
it 'changes the state of the feature' do
|
198
|
-
expect{subject.enable}.to change{subject.enabled?}.from(false).to(true)
|
197
|
+
expect { subject.enable }.to change { subject.enabled? }.from(false).to(true)
|
199
198
|
end
|
200
199
|
|
201
200
|
it 'persists the state in the adapter' do
|
202
|
-
expect{ subject.enable }.to change{ feature_states }.from(
|
201
|
+
expect { subject.enable }.to change { feature_states }.from(example_feature: :disabled).to(example_feature: :enabled)
|
203
202
|
end
|
204
203
|
end
|
205
204
|
|
206
205
|
context 'and the persistence adapter has no state' do
|
207
|
-
let(:feature_states) {{ }
|
206
|
+
let(:feature_states) { {} }
|
208
207
|
|
209
208
|
context 'and the feature is disabled' do
|
210
209
|
let(:default) { :disabled }
|
211
210
|
|
212
211
|
it 'changes the state of the feature' do
|
213
|
-
expect{subject.enable}.to change{subject.enabled?}.from(false).to(true)
|
212
|
+
expect { subject.enable }.to change { subject.enabled? }.from(false).to(true)
|
214
213
|
end
|
215
214
|
|
216
215
|
it 'persists the state in the adapter' do
|
217
|
-
expect{ subject.enable }.to change{ feature_states }.from({
|
216
|
+
expect { subject.enable }.to change { feature_states }.from({}).to(example_feature: :enabled)
|
218
217
|
end
|
219
218
|
end
|
220
219
|
|
@@ -222,11 +221,11 @@ module FlipFab
|
|
222
221
|
let(:default) { :enabled }
|
223
222
|
|
224
223
|
it 'does not change the state of the feature' do
|
225
|
-
expect{subject.enable}.not_to change{subject.enabled?}.from(true)
|
224
|
+
expect { subject.enable }.not_to change { subject.enabled? }.from(true)
|
226
225
|
end
|
227
226
|
|
228
227
|
it 'persists the state in the adapter' do
|
229
|
-
expect{ subject.enable }.to change{ feature_states }.from({
|
228
|
+
expect { subject.enable }.to change { feature_states }.from({}).to(example_feature: :enabled)
|
230
229
|
end
|
231
230
|
end
|
232
231
|
end
|
@@ -238,7 +237,7 @@ module FlipFab
|
|
238
237
|
let(:default) { :enabled }
|
239
238
|
|
240
239
|
it 'does not change the state of the feature' do
|
241
|
-
expect{subject.enable}.not_to change{subject.enabled?}.from(true)
|
240
|
+
expect { subject.enable }.not_to change { subject.enabled? }.from(true)
|
242
241
|
end
|
243
242
|
end
|
244
243
|
|
@@ -246,24 +245,22 @@ module FlipFab
|
|
246
245
|
let(:default) { :disabled }
|
247
246
|
|
248
247
|
it 'changes the state of the feature' do
|
249
|
-
expect{subject.enable}.to change{subject.enabled?}.from(false).to(true)
|
248
|
+
expect { subject.enable }.to change { subject.enabled? }.from(false).to(true)
|
250
249
|
end
|
251
250
|
end
|
252
251
|
end
|
253
252
|
end
|
254
253
|
end
|
255
254
|
|
256
|
-
|
257
255
|
describe '#disable' do
|
258
|
-
|
259
256
|
context 'when the state has been overridden' do
|
260
257
|
let(:override) { 'enabled' }
|
261
258
|
|
262
259
|
context 'and the persistence adapter has the opposite state' do
|
263
|
-
let(:feature_states) {{ example_feature: :enabled }}
|
260
|
+
let(:feature_states) { { example_feature: :enabled } }
|
264
261
|
|
265
262
|
it 'does not change the state of the feature' do
|
266
|
-
expect{subject.disable}.not_to change{subject.disabled?}.from(false)
|
263
|
+
expect { subject.disable }.not_to change { subject.disabled? }.from(false)
|
267
264
|
end
|
268
265
|
|
269
266
|
it 'does not persist the state in the adapter' do
|
@@ -277,18 +274,18 @@ module FlipFab
|
|
277
274
|
let(:persistence_adapters) { [TestPersistence] }
|
278
275
|
|
279
276
|
context 'and the persistence adapter has the same state' do
|
280
|
-
let(:feature_states) {{ example_feature: :disabled }}
|
277
|
+
let(:feature_states) { { example_feature: :disabled } }
|
281
278
|
|
282
279
|
it 'does not change the state of the feature' do
|
283
|
-
expect{subject.disable}.not_to change{subject.disabled?}.from(true)
|
280
|
+
expect { subject.disable }.not_to change { subject.disabled? }.from(true)
|
284
281
|
end
|
285
282
|
end
|
286
283
|
|
287
284
|
context 'and the persistence adapter has the opposite state' do
|
288
|
-
let(:feature_states) {{ example_feature: :enabled }}
|
285
|
+
let(:feature_states) { { example_feature: :enabled } }
|
289
286
|
|
290
287
|
it 'changes the state of the feature' do
|
291
|
-
expect{subject.disable}.to change{subject.disabled?}.from(false).to(true)
|
288
|
+
expect { subject.disable }.to change { subject.disabled? }.from(false).to(true)
|
292
289
|
end
|
293
290
|
|
294
291
|
it 'persists the state in the adapter' do
|
@@ -298,13 +295,13 @@ module FlipFab
|
|
298
295
|
end
|
299
296
|
|
300
297
|
context 'and the persistence adapter has no state' do
|
301
|
-
let(:feature_states) {{ }
|
298
|
+
let(:feature_states) { {} }
|
302
299
|
|
303
300
|
context 'and the feature is enabled' do
|
304
301
|
let(:default) { :enabled }
|
305
302
|
|
306
303
|
it 'changes the state of the feature' do
|
307
|
-
expect{subject.disable}.to change{subject.disabled?}.from(false).to(true)
|
304
|
+
expect { subject.disable }.to change { subject.disabled? }.from(false).to(true)
|
308
305
|
end
|
309
306
|
|
310
307
|
it 'persists the state in the adapter' do
|
@@ -317,7 +314,7 @@ module FlipFab
|
|
317
314
|
let(:default) { :disabled }
|
318
315
|
|
319
316
|
it 'does not change the state of the feature' do
|
320
|
-
expect{subject.disable}.not_to change{subject.disabled?}.from(true)
|
317
|
+
expect { subject.disable }.not_to change { subject.disabled? }.from(true)
|
321
318
|
end
|
322
319
|
|
323
320
|
it 'persists the state in the adapter' do
|
@@ -334,7 +331,7 @@ module FlipFab
|
|
334
331
|
let(:default) { :disabled }
|
335
332
|
|
336
333
|
it 'does not change the state of the feature' do
|
337
|
-
expect{subject.disable}.not_to change{subject.disabled?}.from(true)
|
334
|
+
expect { subject.disable }.not_to change { subject.disabled? }.from(true)
|
338
335
|
end
|
339
336
|
end
|
340
337
|
|
@@ -342,7 +339,7 @@ module FlipFab
|
|
342
339
|
let(:default) { :enabled }
|
343
340
|
|
344
341
|
it 'changes the state of the feature' do
|
345
|
-
expect{subject.disable}.to change{subject.disabled?}.from(false).to(true)
|
342
|
+
expect { subject.disable }.to change { subject.disabled? }.from(false).to(true)
|
346
343
|
end
|
347
344
|
end
|
348
345
|
end
|
@@ -10,10 +10,10 @@ Feature: Persisting the feature state in a cookie
|
|
10
10
|
When I persist the feature state in a cookie
|
11
11
|
Then the cookie has the path '/'
|
12
12
|
|
13
|
-
Scenario Outline: The cookie should
|
13
|
+
Scenario Outline: The cookie populated should not contain domain
|
14
14
|
Given the host is '<host>'
|
15
15
|
When I persist the feature state in a cookie
|
16
|
-
Then the cookie
|
16
|
+
Then the cookie does not have domain '<cookie domain>'
|
17
17
|
|
18
18
|
Examples:
|
19
19
|
| host | cookie domain |
|
@@ -37,7 +37,7 @@ Feature: Persisting the feature state in a cookie
|
|
37
37
|
|
38
38
|
Scenario: The cookie should expire after 1 year
|
39
39
|
When I persist the feature state in a cookie
|
40
|
-
Then the cookie expires at 'Fri, 22 Jan 2016 15:26:31
|
40
|
+
Then the cookie expires at 'Fri, 22 Jan 2016 15:26:31 GMT'
|
41
41
|
|
42
42
|
Scenario Outline: The cookie's value should be the state of the feature
|
43
43
|
Given the state of the feature is '<feature state>'
|