flip_fab 1.0.0 → 1.0.16
Sign up to get free protection for your applications and to get access to all the features.
- 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>'
|