flipper 0.12.2 → 0.24.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.codeclimate.yml +1 -0
- data/.github/workflows/ci.yml +62 -0
- data/.github/workflows/examples.yml +67 -0
- data/.rspec +1 -0
- data/Changelog.md +263 -3
- data/Dockerfile +1 -1
- data/Gemfile +13 -16
- data/README.md +59 -56
- data/Rakefile +10 -2
- data/docker-compose.yml +37 -34
- data/docs/DockerCompose.md +0 -1
- data/docs/README.md +1 -0
- data/docs/images/banner.jpg +0 -0
- data/examples/api/basic.ru +19 -0
- data/examples/api/custom_memoized.ru +37 -0
- data/examples/api/memoized.ru +43 -0
- data/examples/basic.rb +1 -13
- data/examples/configuring_default.rb +2 -6
- data/examples/dsl.rb +13 -25
- data/examples/enabled_for_actor.rb +8 -16
- data/examples/group.rb +3 -7
- data/examples/group_dynamic_lookup.rb +5 -20
- data/examples/group_with_members.rb +4 -15
- data/examples/importing.rb +1 -1
- data/examples/individual_actor.rb +2 -6
- data/examples/instrumentation.rb +1 -3
- data/examples/instrumentation_last_accessed_at.rb +37 -0
- data/examples/memoizing.rb +35 -0
- data/examples/percentage_of_actors.rb +6 -17
- data/examples/percentage_of_actors_enabled_check.rb +7 -11
- data/examples/percentage_of_actors_group.rb +5 -19
- data/examples/percentage_of_time.rb +3 -7
- data/flipper.gemspec +2 -1
- data/lib/flipper/actor.rb +4 -0
- data/lib/flipper/adapter.rb +7 -42
- data/lib/flipper/adapters/dual_write.rb +61 -0
- data/lib/flipper/adapters/failover.rb +83 -0
- data/lib/flipper/adapters/http/client.rb +23 -1
- data/lib/flipper/adapters/http/error.rb +19 -1
- data/lib/flipper/adapters/http.rb +32 -28
- data/lib/flipper/adapters/instrumented.rb +20 -19
- data/lib/flipper/adapters/memoizable.rb +8 -16
- data/lib/flipper/adapters/memory.rb +24 -95
- data/lib/flipper/adapters/operation_logger.rb +19 -2
- data/lib/flipper/adapters/pstore.rb +12 -4
- data/lib/flipper/adapters/read_only.rb +2 -0
- data/lib/flipper/adapters/sync/feature_synchronizer.rb +118 -0
- data/lib/flipper/adapters/sync/interval_synchronizer.rb +49 -0
- data/lib/flipper/adapters/sync/synchronizer.rb +63 -0
- data/lib/flipper/adapters/sync.rb +91 -0
- data/lib/flipper/configuration.rb +33 -7
- data/lib/flipper/dsl.rb +22 -5
- data/lib/flipper/errors.rb +17 -2
- data/lib/flipper/feature.rb +9 -4
- data/lib/flipper/gate_values.rb +1 -0
- data/lib/flipper/identifier.rb +17 -0
- data/lib/flipper/instrumenters/memory.rb +18 -2
- data/lib/flipper/metadata.rb +5 -0
- data/lib/flipper/middleware/memoizer.rb +31 -18
- data/lib/flipper/middleware/setup_env.rb +13 -3
- data/lib/flipper/railtie.rb +47 -0
- data/lib/flipper/spec/shared_adapter_specs.rb +27 -1
- data/lib/flipper/test/shared_adapter_test.rb +29 -2
- data/lib/flipper/type.rb +0 -7
- data/lib/flipper/typecast.rb +2 -0
- data/lib/flipper/types/actor.rb +8 -2
- data/lib/flipper/types/boolean.rb +2 -0
- data/lib/flipper/types/group.rb +8 -1
- data/lib/flipper/types/percentage.rb +2 -0
- data/lib/flipper/version.rb +1 -1
- data/lib/flipper.rb +22 -5
- data/spec/flipper/actor_spec.rb +10 -2
- data/spec/flipper/adapter_spec.rb +2 -5
- data/spec/flipper/adapters/dual_write_spec.rb +69 -0
- data/spec/flipper/adapters/failover_spec.rb +129 -0
- data/spec/flipper/adapters/http_spec.rb +114 -11
- data/spec/flipper/adapters/instrumented_spec.rb +2 -3
- data/spec/flipper/adapters/memoizable_spec.rb +0 -3
- data/spec/flipper/adapters/memory_spec.rb +21 -5
- data/spec/flipper/adapters/operation_logger_spec.rb +9 -3
- data/spec/flipper/adapters/pstore_spec.rb +0 -2
- data/spec/flipper/adapters/read_only_spec.rb +0 -1
- data/spec/flipper/adapters/sync/feature_synchronizer_spec.rb +208 -0
- data/spec/flipper/adapters/sync/interval_synchronizer_spec.rb +33 -0
- data/spec/flipper/adapters/sync/synchronizer_spec.rb +88 -0
- data/spec/flipper/adapters/sync_spec.rb +200 -0
- data/spec/flipper/configuration_spec.rb +20 -3
- data/spec/flipper/dsl_spec.rb +24 -5
- data/spec/flipper/feature_check_context_spec.rb +2 -4
- data/spec/flipper/feature_spec.rb +30 -10
- data/spec/flipper/gate_spec.rb +0 -2
- data/spec/flipper/gate_values_spec.rb +0 -1
- data/spec/flipper/gates/actor_spec.rb +0 -2
- data/spec/flipper/gates/boolean_spec.rb +0 -2
- data/spec/flipper/gates/group_spec.rb +0 -2
- data/spec/flipper/gates/percentage_of_actors_spec.rb +0 -2
- data/spec/flipper/gates/percentage_of_time_spec.rb +0 -2
- data/spec/flipper/identifier_spec.rb +13 -0
- data/spec/flipper/instrumentation/log_subscriber_spec.rb +0 -2
- data/spec/flipper/instrumentation/statsd_subscriber_spec.rb +0 -2
- data/spec/flipper/instrumenters/memory_spec.rb +18 -1
- data/spec/flipper/instrumenters/noop_spec.rb +14 -9
- data/spec/flipper/middleware/memoizer_spec.rb +94 -39
- data/spec/flipper/middleware/setup_env_spec.rb +23 -5
- data/spec/flipper/railtie_spec.rb +73 -0
- data/spec/flipper/registry_spec.rb +0 -1
- data/spec/flipper/typecast_spec.rb +0 -1
- data/spec/flipper/types/actor_spec.rb +0 -1
- data/spec/flipper/types/boolean_spec.rb +0 -1
- data/spec/flipper/types/group_spec.rb +22 -1
- data/spec/flipper/types/percentage_of_actors_spec.rb +0 -1
- data/spec/flipper/types/percentage_of_time_spec.rb +0 -1
- data/spec/flipper/types/percentage_spec.rb +0 -1
- data/spec/{integration_spec.rb → flipper_integration_spec.rb} +0 -2
- data/spec/flipper_spec.rb +30 -1
- data/spec/{helper.rb → spec_helper.rb} +5 -3
- data/spec/support/descriptions.yml +1 -0
- data/spec/support/spec_helpers.rb +22 -2
- data/test/adapters/memory_test.rb +0 -1
- data/test/test_helper.rb +2 -1
- data/{test → test_rails}/helper.rb +1 -1
- metadata +48 -25
- data/.rubocop.yml +0 -48
- data/.rubocop_todo.yml +0 -199
- data/docs/Adapters.md +0 -125
- data/docs/Caveats.md +0 -4
- data/docs/Gates.md +0 -167
- data/docs/Instrumentation.md +0 -27
- data/docs/Optimization.md +0 -119
- data/docs/api/README.md +0 -849
- data/docs/http/README.md +0 -35
- data/docs/read-only/README.md +0 -22
- data/examples/example_setup.rb +0 -8
@@ -0,0 +1,33 @@
|
|
1
|
+
require "flipper/adapters/sync/interval_synchronizer"
|
2
|
+
|
3
|
+
RSpec.describe Flipper::Adapters::Sync::IntervalSynchronizer do
|
4
|
+
let(:events) { [] }
|
5
|
+
let(:synchronizer) { -> { events << now } }
|
6
|
+
let(:interval) { 10 }
|
7
|
+
let(:now) { subject.send(:now) }
|
8
|
+
|
9
|
+
subject { described_class.new(synchronizer, interval: interval) }
|
10
|
+
|
11
|
+
it 'synchronizes on first call' do
|
12
|
+
expect(events.size).to be(0)
|
13
|
+
subject.call
|
14
|
+
expect(events.size).to be(1)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "only invokes wrapped synchronizer every interval seconds" do
|
18
|
+
subject.call
|
19
|
+
events.clear
|
20
|
+
|
21
|
+
# move time to one millisecond less than last sync + interval
|
22
|
+
1.upto(interval) do |i|
|
23
|
+
allow(subject).to receive(:now).and_return(now + i - 1)
|
24
|
+
subject.call
|
25
|
+
end
|
26
|
+
expect(events.size).to be(0)
|
27
|
+
|
28
|
+
# move time to last sync + interval in milliseconds
|
29
|
+
allow(subject).to receive(:now).and_return(now + interval)
|
30
|
+
subject.call
|
31
|
+
expect(events.size).to be(1)
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require "flipper/adapters/memory"
|
2
|
+
require "flipper/instrumenters/memory"
|
3
|
+
require "flipper/adapters/sync/synchronizer"
|
4
|
+
|
5
|
+
RSpec.describe Flipper::Adapters::Sync::Synchronizer do
|
6
|
+
let(:local) { Flipper::Adapters::Memory.new }
|
7
|
+
let(:remote) { Flipper::Adapters::Memory.new }
|
8
|
+
let(:local_flipper) { Flipper.new(local) }
|
9
|
+
let(:remote_flipper) { Flipper.new(remote) }
|
10
|
+
let(:instrumenter) { Flipper::Instrumenters::Memory.new }
|
11
|
+
|
12
|
+
subject { described_class.new(local, remote, instrumenter: instrumenter) }
|
13
|
+
|
14
|
+
it "instruments call" do
|
15
|
+
subject.call
|
16
|
+
expect(instrumenter.events_by_name("synchronizer_exception.flipper").size).to be(0)
|
17
|
+
|
18
|
+
events = instrumenter.events_by_name("synchronizer_call.flipper")
|
19
|
+
expect(events.size).to be(1)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "raises errors by default" do
|
23
|
+
exception = StandardError.new
|
24
|
+
expect(remote).to receive(:get_all).and_raise(exception)
|
25
|
+
|
26
|
+
expect { subject.call }.to raise_error(exception)
|
27
|
+
end
|
28
|
+
|
29
|
+
context "when raise disabled" do
|
30
|
+
subject do
|
31
|
+
options = {
|
32
|
+
instrumenter: instrumenter,
|
33
|
+
raise: false,
|
34
|
+
}
|
35
|
+
described_class.new(local, remote, options)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "does not raise, but instruments exceptions for visibility" do
|
39
|
+
exception = StandardError.new
|
40
|
+
expect(remote).to receive(:get_all).and_raise(exception)
|
41
|
+
|
42
|
+
expect { subject.call }.not_to raise_error
|
43
|
+
|
44
|
+
events = instrumenter.events_by_name("synchronizer_exception.flipper")
|
45
|
+
expect(events.size).to be(1)
|
46
|
+
|
47
|
+
event = events[0]
|
48
|
+
expect(event.payload[:exception]).to eq(exception)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe '#call' do
|
53
|
+
it 'returns nothing' do
|
54
|
+
expect(subject.call).to be(nil)
|
55
|
+
expect(instrumenter.events_by_name("synchronizer_exception.flipper").size).to be(0)
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'syncs each remote feature to local' do
|
59
|
+
remote_flipper.enable(:search)
|
60
|
+
remote_flipper.enable_percentage_of_time(:logging, 10)
|
61
|
+
|
62
|
+
subject.call
|
63
|
+
expect(instrumenter.events_by_name("synchronizer_exception.flipper").size).to be(0)
|
64
|
+
|
65
|
+
expect(local_flipper[:search].boolean_value).to eq(true)
|
66
|
+
expect(local_flipper[:logging].percentage_of_time_value).to eq(10)
|
67
|
+
expect(local_flipper.features.map(&:key).sort).to eq(%w(logging search))
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'adds features in remote that are not in local' do
|
71
|
+
remote_flipper.add(:search)
|
72
|
+
|
73
|
+
subject.call
|
74
|
+
expect(instrumenter.events_by_name("synchronizer_exception.flipper").size).to be(0)
|
75
|
+
|
76
|
+
expect(local_flipper.features.map(&:key)).to eq(["search"])
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'removes features in local that are not in remote' do
|
80
|
+
local_flipper.add(:stats)
|
81
|
+
|
82
|
+
subject.call
|
83
|
+
expect(instrumenter.events_by_name("synchronizer_exception.flipper").size).to be(0)
|
84
|
+
|
85
|
+
expect(local_flipper.features.map(&:key)).to eq([])
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,200 @@
|
|
1
|
+
require 'flipper/adapters/sync'
|
2
|
+
require 'flipper/adapters/operation_logger'
|
3
|
+
require 'active_support/notifications'
|
4
|
+
|
5
|
+
RSpec.describe Flipper::Adapters::Sync do
|
6
|
+
let(:local_adapter) do
|
7
|
+
Flipper::Adapters::OperationLogger.new Flipper::Adapters::Memory.new
|
8
|
+
end
|
9
|
+
let(:remote_adapter) do
|
10
|
+
Flipper::Adapters::OperationLogger.new Flipper::Adapters::Memory.new
|
11
|
+
end
|
12
|
+
let(:local) { Flipper.new(local_adapter) }
|
13
|
+
let(:remote) { Flipper.new(remote_adapter) }
|
14
|
+
let(:sync) { Flipper.new(subject) }
|
15
|
+
|
16
|
+
subject do
|
17
|
+
described_class.new(local_adapter, remote_adapter, interval: 1)
|
18
|
+
end
|
19
|
+
|
20
|
+
it_should_behave_like 'a flipper adapter'
|
21
|
+
|
22
|
+
context 'when local has never been synced' do
|
23
|
+
it 'syncs boolean' do
|
24
|
+
remote.enable(:search)
|
25
|
+
expect(sync[:search].boolean_value).to be(true)
|
26
|
+
expect(subject.features.sort).to eq(%w(search))
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'syncs actor' do
|
30
|
+
actor = Flipper::Actor.new("User;1000")
|
31
|
+
remote.enable_actor(:search, actor)
|
32
|
+
expect(sync[:search].actors_value).to eq(Set[actor.flipper_id])
|
33
|
+
expect(subject.features.sort).to eq(%w(search))
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'syncs group' do
|
37
|
+
remote.enable_group(:search, :staff)
|
38
|
+
expect(sync[:search].groups_value).to eq(Set["staff"])
|
39
|
+
expect(subject.features.sort).to eq(%w(search))
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'syncs percentage of actors' do
|
43
|
+
remote.enable_percentage_of_actors(:search, 25)
|
44
|
+
expect(sync[:search].percentage_of_actors_value).to eq(25)
|
45
|
+
expect(subject.features.sort).to eq(%w(search))
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'syncs percentage of time' do
|
49
|
+
remote.enable_percentage_of_time(:search, 15)
|
50
|
+
expect(sync[:search].percentage_of_time_value).to eq(15)
|
51
|
+
expect(subject.features.sort).to eq(%w(search))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'enables boolean locally when remote feature boolean enabled' do
|
56
|
+
remote.disable(:search)
|
57
|
+
local.disable(:search)
|
58
|
+
remote.enable(:search)
|
59
|
+
subject # initialize forces sync
|
60
|
+
expect(local[:search].boolean_value).to be(true)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'disables boolean locally when remote feature disabled' do
|
64
|
+
remote.enable(:search)
|
65
|
+
local.enable(:search)
|
66
|
+
remote.disable(:search)
|
67
|
+
subject # initialize forces sync
|
68
|
+
expect(local[:search].boolean_value).to be(false)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'adds local actor when remote actor is added' do
|
72
|
+
actor = Flipper::Actor.new("User;235")
|
73
|
+
remote.enable_actor(:search, actor)
|
74
|
+
subject # initialize forces sync
|
75
|
+
expect(local[:search].actors_value).to eq(Set[actor.flipper_id])
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'removes local actor when remote actor is removed' do
|
79
|
+
actor = Flipper::Actor.new("User;235")
|
80
|
+
remote.enable_actor(:search, actor)
|
81
|
+
local.enable_actor(:search, actor)
|
82
|
+
remote.disable(:search, actor)
|
83
|
+
subject # initialize forces sync
|
84
|
+
expect(local[:search].actors_value).to eq(Set.new)
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'adds local group when remote group is added' do
|
88
|
+
group = Flipper::Types::Group.new(:staff)
|
89
|
+
remote.enable_group(:search, group)
|
90
|
+
subject # initialize forces sync
|
91
|
+
expect(local[:search].groups_value).to eq(Set["staff"])
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'removes local group when remote group is removed' do
|
95
|
+
group = Flipper::Types::Group.new(:staff)
|
96
|
+
remote.enable_group(:search, group)
|
97
|
+
local.enable_group(:search, group)
|
98
|
+
remote.disable(:search, group)
|
99
|
+
subject # initialize forces sync
|
100
|
+
expect(local[:search].groups_value).to eq(Set.new)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'updates percentage of actors when remote is updated' do
|
104
|
+
remote.enable_percentage_of_actors(:search, 10)
|
105
|
+
local.enable_percentage_of_actors(:search, 10)
|
106
|
+
remote.enable_percentage_of_actors(:search, 15)
|
107
|
+
subject # initialize forces sync
|
108
|
+
expect(local[:search].percentage_of_actors_value).to eq(15)
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'updates percentage of time when remote is updated' do
|
112
|
+
remote.enable_percentage_of_time(:search, 10)
|
113
|
+
local.enable_percentage_of_time(:search, 10)
|
114
|
+
remote.enable_percentage_of_time(:search, 15)
|
115
|
+
subject # initialize forces sync
|
116
|
+
expect(local[:search].percentage_of_time_value).to eq(15)
|
117
|
+
end
|
118
|
+
|
119
|
+
context 'when local and remote match' do
|
120
|
+
it 'does not update boolean enabled' do
|
121
|
+
local.enable(:search)
|
122
|
+
remote.enable(:search)
|
123
|
+
local_adapter.reset
|
124
|
+
subject # initialize forces sync
|
125
|
+
expect(local_adapter.count(:enable)).to be(0)
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'does not update boolean disabled' do
|
129
|
+
local.disable(:search)
|
130
|
+
remote.disable(:search)
|
131
|
+
local_adapter.reset
|
132
|
+
subject # initialize forces sync
|
133
|
+
expect(local_adapter.count(:disable)).to be(0)
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'does not update actors' do
|
137
|
+
actor = Flipper::Actor.new("User;235")
|
138
|
+
local.enable_actor(:search, actor)
|
139
|
+
remote.enable_actor(:search, actor)
|
140
|
+
local_adapter.reset
|
141
|
+
subject # initialize forces sync
|
142
|
+
expect(local_adapter.count(:enable)).to be(0)
|
143
|
+
expect(local_adapter.count(:disable)).to be(0)
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'does not update groups' do
|
147
|
+
group = Flipper::Types::Group.new(:staff)
|
148
|
+
local.enable_group(:search, group)
|
149
|
+
remote.enable_group(:search, group)
|
150
|
+
local_adapter.reset
|
151
|
+
subject # initialize forces sync
|
152
|
+
expect(local_adapter.count(:enable)).to be(0)
|
153
|
+
expect(local_adapter.count(:disable)).to be(0)
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'does not update percentage of actors' do
|
157
|
+
local.enable_percentage_of_actors(:search, 10)
|
158
|
+
remote.enable_percentage_of_actors(:search, 10)
|
159
|
+
local_adapter.reset
|
160
|
+
subject # initialize forces sync
|
161
|
+
expect(local_adapter.count(:enable)).to be(0)
|
162
|
+
expect(local_adapter.count(:disable)).to be(0)
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'does not update percentage of time' do
|
166
|
+
local.enable_percentage_of_time(:search, 10)
|
167
|
+
remote.enable_percentage_of_time(:search, 10)
|
168
|
+
local_adapter.reset
|
169
|
+
subject # initialize forces sync
|
170
|
+
expect(local_adapter.count(:enable)).to be(0)
|
171
|
+
expect(local_adapter.count(:disable)).to be(0)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'synchronizes for #features' do
|
176
|
+
expect(subject).to receive(:synchronize)
|
177
|
+
subject.features
|
178
|
+
end
|
179
|
+
|
180
|
+
it 'synchronizes for #get' do
|
181
|
+
expect(subject).to receive(:synchronize)
|
182
|
+
subject.get sync[:search]
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'synchronizes for #get_multi' do
|
186
|
+
expect(subject).to receive(:synchronize)
|
187
|
+
subject.get_multi [sync[:search]]
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'synchronizes for #get_all' do
|
191
|
+
expect(subject).to receive(:synchronize)
|
192
|
+
subject.get_all
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'does not raise sync exceptions' do
|
196
|
+
exception = StandardError.new
|
197
|
+
expect(remote_adapter).to receive(:get_all).and_raise(exception)
|
198
|
+
expect { subject.get_all }.not_to raise_error
|
199
|
+
end
|
200
|
+
end
|
@@ -1,14 +1,31 @@
|
|
1
|
-
require 'helper'
|
2
1
|
require 'flipper/configuration'
|
3
2
|
|
4
3
|
RSpec.describe Flipper::Configuration do
|
4
|
+
describe '#adapter' do
|
5
|
+
it 'returns instance using Memory adapter' do
|
6
|
+
expect(subject.adapter).to be_a(Flipper::Adapters::Memory)
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'can be set' do
|
10
|
+
instance = Flipper::Adapters::Memory.new
|
11
|
+
expect(subject.adapter).not_to be(instance)
|
12
|
+
subject.adapter { instance }
|
13
|
+
expect(subject.adapter).to be(instance)
|
14
|
+
# All adapters are wrapped in Memoizable
|
15
|
+
expect(subject.default.adapter.adapter).to be(instance)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
5
19
|
describe '#default' do
|
6
|
-
it '
|
7
|
-
expect
|
20
|
+
it 'returns instance using Memory adapter' do
|
21
|
+
expect(subject.default).to be_a(Flipper::DSL)
|
22
|
+
# All adapters are wrapped in Memoizable
|
23
|
+
expect(subject.default.adapter.adapter).to be_a(Flipper::Adapters::Memory)
|
8
24
|
end
|
9
25
|
|
10
26
|
it 'can be set default' do
|
11
27
|
instance = Flipper.new(Flipper::Adapters::Memory.new)
|
28
|
+
expect(subject.default).not_to be(instance)
|
12
29
|
subject.default { instance }
|
13
30
|
expect(subject.default).to be(instance)
|
14
31
|
end
|
data/spec/flipper/dsl_spec.rb
CHANGED
@@ -1,6 +1,4 @@
|
|
1
|
-
require 'helper'
|
2
1
|
require 'flipper/dsl'
|
3
|
-
require 'flipper/adapters/memory'
|
4
2
|
|
5
3
|
RSpec.describe Flipper::DSL do
|
6
4
|
subject { described_class.new(adapter) }
|
@@ -8,9 +6,19 @@ RSpec.describe Flipper::DSL do
|
|
8
6
|
let(:adapter) { Flipper::Adapters::Memory.new }
|
9
7
|
|
10
8
|
describe '#initialize' do
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
context 'when using default memoize strategy' do
|
10
|
+
it 'wraps the given adapter with Flipper::Adapters::Memoizable' do
|
11
|
+
dsl = described_class.new(adapter)
|
12
|
+
expect(dsl.adapter.class).to be(Flipper::Adapters::Memoizable)
|
13
|
+
expect(dsl.adapter.adapter).to be(adapter)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when disabling memoization' do
|
18
|
+
it 'uses the given adapter directly' do
|
19
|
+
dsl = described_class.new(adapter, memoize: false)
|
20
|
+
expect(dsl.adapter).to be(adapter)
|
21
|
+
end
|
14
22
|
end
|
15
23
|
|
16
24
|
it 'defaults instrumenter to noop' do
|
@@ -313,6 +321,17 @@ RSpec.describe Flipper::DSL do
|
|
313
321
|
end
|
314
322
|
end
|
315
323
|
|
324
|
+
describe '#exist?' do
|
325
|
+
it 'returns true if the feature is added in adapter' do
|
326
|
+
subject.add(:stats)
|
327
|
+
expect(subject.exist?(:stats)).to be(true)
|
328
|
+
end
|
329
|
+
|
330
|
+
it 'returns false if the feature is NOT added in adapter' do
|
331
|
+
expect(subject.exist?(:stats)).to be(false)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
316
335
|
describe '#remove' do
|
317
336
|
it 'removes the feature' do
|
318
337
|
subject.adapter.add(subject[:stats])
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'helper'
|
2
|
-
|
3
1
|
RSpec.describe Flipper::FeatureCheckContext do
|
4
2
|
let(:feature_name) { :new_profiles }
|
5
3
|
let(:values) { Flipper::GateValues.new({}) }
|
@@ -41,8 +39,8 @@ RSpec.describe Flipper::FeatureCheckContext do
|
|
41
39
|
end
|
42
40
|
|
43
41
|
it 'knows actors_value' do
|
44
|
-
args = options.merge(values: Flipper::GateValues.new(actors: Set['User
|
45
|
-
expect(described_class.new(args).actors_value).to eq(Set['User
|
42
|
+
args = options.merge(values: Flipper::GateValues.new(actors: Set['User;1']))
|
43
|
+
expect(described_class.new(args).actors_value).to eq(Set['User;1'])
|
46
44
|
end
|
47
45
|
|
48
46
|
it 'knows groups_value' do
|
@@ -1,6 +1,4 @@
|
|
1
|
-
require 'helper'
|
2
1
|
require 'flipper/feature'
|
3
|
-
require 'flipper/adapters/memory'
|
4
2
|
require 'flipper/instrumenters/memory'
|
5
3
|
|
6
4
|
RSpec.describe Flipper::Feature do
|
@@ -97,6 +95,17 @@ RSpec.describe Flipper::Feature do
|
|
97
95
|
end
|
98
96
|
end
|
99
97
|
|
98
|
+
describe '#exist?' do
|
99
|
+
it 'returns true if feature is added in adapter' do
|
100
|
+
subject.add
|
101
|
+
expect(subject.exist?).to be(true)
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'returns false if feature is NOT added in adapter' do
|
105
|
+
expect(subject.exist?).to be(false)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
100
109
|
describe '#remove' do
|
101
110
|
it 'removes feature from adapter' do
|
102
111
|
adapter.add(subject)
|
@@ -232,6 +241,17 @@ RSpec.describe Flipper::Feature do
|
|
232
241
|
expect(event.payload[:result]).not_to be_nil
|
233
242
|
end
|
234
243
|
|
244
|
+
it 'is recorded for exist?' do
|
245
|
+
subject.exist?
|
246
|
+
|
247
|
+
event = instrumenter.events.last
|
248
|
+
expect(event).not_to be_nil
|
249
|
+
expect(event.name).to eq('feature_operation.flipper')
|
250
|
+
expect(event.payload[:feature_name]).to eq(:search)
|
251
|
+
expect(event.payload[:operation]).to eq(:exist?)
|
252
|
+
expect(event.payload[:result]).not_to be_nil
|
253
|
+
end
|
254
|
+
|
235
255
|
it 'is recorded for remove' do
|
236
256
|
subject.remove
|
237
257
|
|
@@ -338,19 +358,19 @@ RSpec.describe Flipper::Feature do
|
|
338
358
|
end
|
339
359
|
|
340
360
|
it 'returns :on' do
|
341
|
-
expect(subject.state).to be(:
|
361
|
+
expect(subject.state).to be(:conditional)
|
342
362
|
end
|
343
363
|
|
344
|
-
it 'returns
|
345
|
-
expect(subject.on?).to be(
|
364
|
+
it 'returns false for on?' do
|
365
|
+
expect(subject.on?).to be(false)
|
346
366
|
end
|
347
367
|
|
348
368
|
it 'returns false for off?' do
|
349
369
|
expect(subject.off?).to be(false)
|
350
370
|
end
|
351
371
|
|
352
|
-
it 'returns
|
353
|
-
expect(subject.conditional?).to be(
|
372
|
+
it 'returns true for conditional?' do
|
373
|
+
expect(subject.conditional?).to be(true)
|
354
374
|
end
|
355
375
|
end
|
356
376
|
|
@@ -509,12 +529,12 @@ RSpec.describe Flipper::Feature do
|
|
509
529
|
|
510
530
|
context 'when one or more actors are enabled' do
|
511
531
|
before do
|
512
|
-
subject.enable Flipper::Types::Actor.new(Flipper::Actor.new('User
|
513
|
-
subject.enable Flipper::Types::Actor.new(Flipper::Actor.new('User
|
532
|
+
subject.enable Flipper::Types::Actor.new(Flipper::Actor.new('User;5'))
|
533
|
+
subject.enable Flipper::Types::Actor.new(Flipper::Actor.new('User;22'))
|
514
534
|
end
|
515
535
|
|
516
536
|
it 'returns set of actor ids' do
|
517
|
-
expect(subject.actors_value).to eq(Set.new(['User
|
537
|
+
expect(subject.actors_value).to eq(Set.new(['User;5', 'User;22']))
|
518
538
|
end
|
519
539
|
end
|
520
540
|
end
|
data/spec/flipper/gate_spec.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'flipper/identifier'
|
2
|
+
|
3
|
+
RSpec.describe Flipper::Identifier do
|
4
|
+
describe '#flipper_id' do
|
5
|
+
class User < Struct.new(:id)
|
6
|
+
include Flipper::Identifier
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'uses class name and id' do
|
10
|
+
expect(User.new(5).flipper_id).to eq('User;5')
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'helper'
|
2
1
|
require 'flipper/instrumenters/memory'
|
3
2
|
|
4
3
|
RSpec.describe Flipper::Instrumenters::Memory do
|
@@ -22,5 +21,23 @@ RSpec.describe Flipper::Instrumenters::Memory do
|
|
22
21
|
event = described_class::Event.new(name, payload, block_result)
|
23
22
|
expect(instrumenter.events).to eq([event])
|
24
23
|
end
|
24
|
+
|
25
|
+
context 'when an error is raised' do
|
26
|
+
subject do
|
27
|
+
instrumenter.instrument(:name) { raise IOError }
|
28
|
+
end
|
29
|
+
|
30
|
+
let(:instrumenter) { described_class.new }
|
31
|
+
|
32
|
+
it 'captures and propagates the error' do
|
33
|
+
expect { subject }.to raise_error(IOError)
|
34
|
+
|
35
|
+
expect(instrumenter.events.count).to be 1
|
36
|
+
|
37
|
+
payload = instrumenter.events[0].payload
|
38
|
+
expect(payload.keys).to include(:exception, :exception_object)
|
39
|
+
expect(payload[:exception_object]).to be_a IOError
|
40
|
+
end
|
41
|
+
end
|
25
42
|
end
|
26
43
|
end
|
@@ -1,21 +1,26 @@
|
|
1
|
-
require 'helper'
|
2
|
-
require 'flipper/instrumenters/noop'
|
3
|
-
|
4
1
|
RSpec.describe Flipper::Instrumenters::Noop do
|
5
2
|
describe '.instrument' do
|
6
3
|
context 'with name' do
|
7
4
|
it 'yields block' do
|
8
|
-
|
9
|
-
|
10
|
-
|
5
|
+
expect { |block|
|
6
|
+
described_class.instrument(:foo, &block)
|
7
|
+
}.to yield_control
|
11
8
|
end
|
12
9
|
end
|
13
10
|
|
14
11
|
context 'with name and payload' do
|
12
|
+
let(:payload) { { pay: :load } }
|
13
|
+
|
15
14
|
it 'yields block' do
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
expect { |block|
|
16
|
+
described_class.instrument(:foo, payload, &block)
|
17
|
+
}.to yield_control
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'yields the payload' do
|
21
|
+
described_class.instrument(:foo, payload) do |block_payload|
|
22
|
+
expect(block_payload).to eq payload
|
23
|
+
end
|
19
24
|
end
|
20
25
|
end
|
21
26
|
end
|