rollout 2.4.3 → 2.6.1
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/.circleci/config.yml +119 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +11 -0
- data/.travis.yml +5 -5
- data/Gemfile +3 -1
- data/README.md +29 -6
- data/Rakefile +5 -7
- data/lib/rollout/feature.rb +140 -0
- data/lib/rollout/logging.rb +199 -0
- data/lib/rollout/version.rb +3 -1
- data/lib/rollout.rb +73 -159
- data/rollout.gemspec +29 -23
- data/spec/rollout/feature_spec.rb +54 -0
- data/spec/rollout/logging_spec.rb +143 -0
- data/spec/rollout_spec.rb +196 -240
- data/spec/spec_helper.rb +21 -12
- metadata +66 -34
- data/.document +0 -5
- data/Appraisals +0 -7
- data/Gemfile.lock +0 -56
- data/gemfiles/redis_3.gemfile +0 -13
- data/gemfiles/redis_4.gemfile +0 -13
data/lib/rollout.rb
CHANGED
@@ -1,141 +1,30 @@
|
|
1
|
-
|
2
|
-
require "zlib"
|
3
|
-
require "set"
|
4
|
-
require "json"
|
1
|
+
# frozen_string_literal: true
|
5
2
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def initialize(name, string = nil, opts = {})
|
14
|
-
@options = opts
|
15
|
-
@name = name
|
16
|
-
|
17
|
-
if string
|
18
|
-
raw_percentage,raw_users,raw_groups,raw_data = string.split('|', 4)
|
19
|
-
@percentage = raw_percentage.to_f
|
20
|
-
@users = users_from_string(raw_users)
|
21
|
-
@groups = groups_from_string(raw_groups)
|
22
|
-
@data = raw_data.nil? || raw_data.strip.empty? ? {} : JSON.parse(raw_data)
|
23
|
-
else
|
24
|
-
clear
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def serialize
|
29
|
-
"#{@percentage}|#{@users.to_a.join(",")}|#{@groups.to_a.join(",")}|#{serialize_data}"
|
30
|
-
end
|
31
|
-
|
32
|
-
def add_user(user)
|
33
|
-
id = user_id(user)
|
34
|
-
@users << id unless @users.include?(id)
|
35
|
-
end
|
36
|
-
|
37
|
-
def remove_user(user)
|
38
|
-
@users.delete(user_id(user))
|
39
|
-
end
|
40
|
-
|
41
|
-
def add_group(group)
|
42
|
-
@groups << group.to_sym unless @groups.include?(group.to_sym)
|
43
|
-
end
|
44
|
-
|
45
|
-
def remove_group(group)
|
46
|
-
@groups.delete(group.to_sym)
|
47
|
-
end
|
48
|
-
|
49
|
-
def clear
|
50
|
-
@groups = groups_from_string("")
|
51
|
-
@users = users_from_string("")
|
52
|
-
@percentage = 0
|
53
|
-
@data = {}
|
54
|
-
end
|
55
|
-
|
56
|
-
def active?(rollout, user)
|
57
|
-
if user
|
58
|
-
id = user_id(user)
|
59
|
-
user_in_percentage?(id) ||
|
60
|
-
user_in_active_users?(id) ||
|
61
|
-
user_in_active_group?(user, rollout)
|
62
|
-
else
|
63
|
-
@percentage == 100
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def user_in_active_users?(user)
|
68
|
-
@users.include?(user_id(user))
|
69
|
-
end
|
70
|
-
|
71
|
-
def to_hash
|
72
|
-
{
|
73
|
-
percentage: @percentage,
|
74
|
-
groups: @groups,
|
75
|
-
users: @users
|
76
|
-
}
|
77
|
-
end
|
78
|
-
|
79
|
-
private
|
80
|
-
def user_id(user)
|
81
|
-
if user.is_a?(Integer) || user.is_a?(String)
|
82
|
-
user.to_s
|
83
|
-
else
|
84
|
-
user.send(id_user_by).to_s
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
def id_user_by
|
89
|
-
@options[:id_user_by] || :id
|
90
|
-
end
|
91
|
-
|
92
|
-
def user_in_percentage?(user)
|
93
|
-
Zlib.crc32(user_id_for_percentage(user)) < RAND_BASE * @percentage
|
94
|
-
end
|
95
|
-
|
96
|
-
def user_id_for_percentage(user)
|
97
|
-
if @options[:randomize_percentage]
|
98
|
-
user_id(user).to_s + @name.to_s
|
99
|
-
else
|
100
|
-
user_id(user)
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
def user_in_active_group?(user, rollout)
|
105
|
-
@groups.any? do |g|
|
106
|
-
rollout.active_in_group?(g, user)
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def serialize_data
|
111
|
-
return "" unless @data.is_a? Hash
|
3
|
+
require 'rollout/feature'
|
4
|
+
require 'rollout/logging'
|
5
|
+
require 'rollout/version'
|
6
|
+
require 'zlib'
|
7
|
+
require 'set'
|
8
|
+
require 'json'
|
9
|
+
require 'observer'
|
112
10
|
|
113
|
-
|
114
|
-
|
11
|
+
class Rollout
|
12
|
+
include Observable
|
115
13
|
|
116
|
-
|
117
|
-
users = (raw_users || "").split(",").map(&:to_s)
|
118
|
-
if @options[:use_sets]
|
119
|
-
users.to_set
|
120
|
-
else
|
121
|
-
users
|
122
|
-
end
|
123
|
-
end
|
14
|
+
RAND_BASE = (2**32 - 1) / 100.0
|
124
15
|
|
125
|
-
|
126
|
-
groups = (raw_groups || "").split(",").map(&:to_sym)
|
127
|
-
if @options[:use_sets]
|
128
|
-
groups.to_set
|
129
|
-
else
|
130
|
-
groups
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
16
|
+
attr_reader :options, :storage
|
134
17
|
|
135
18
|
def initialize(storage, opts = {})
|
136
19
|
@storage = storage
|
137
20
|
@options = opts
|
138
|
-
@groups = { all:
|
21
|
+
@groups = { all: ->(_user) { true } }
|
22
|
+
|
23
|
+
extend(Logging) if opts[:logging]
|
24
|
+
end
|
25
|
+
|
26
|
+
def groups
|
27
|
+
@groups.keys
|
139
28
|
end
|
140
29
|
|
141
30
|
def activate(feature)
|
@@ -145,16 +34,18 @@ class Rollout
|
|
145
34
|
end
|
146
35
|
|
147
36
|
def deactivate(feature)
|
148
|
-
with_feature(feature)
|
149
|
-
f.clear
|
150
|
-
end
|
37
|
+
with_feature(feature, &:clear)
|
151
38
|
end
|
152
39
|
|
153
40
|
def delete(feature)
|
154
|
-
features = (@storage.get(features_key) ||
|
41
|
+
features = (@storage.get(features_key) || '').split(',')
|
155
42
|
features.delete(feature.to_s)
|
156
|
-
@storage.set(features_key, features.join(
|
43
|
+
@storage.set(features_key, features.join(','))
|
157
44
|
@storage.del(key(feature))
|
45
|
+
|
46
|
+
if respond_to?(:logging)
|
47
|
+
logging.delete(feature)
|
48
|
+
end
|
158
49
|
end
|
159
50
|
|
160
51
|
def set(feature, desired_state)
|
@@ -193,20 +84,20 @@ class Rollout
|
|
193
84
|
|
194
85
|
def activate_users(feature, users)
|
195
86
|
with_feature(feature) do |f|
|
196
|
-
users.each{|user| f.add_user(user)}
|
87
|
+
users.each { |user| f.add_user(user) }
|
197
88
|
end
|
198
89
|
end
|
199
90
|
|
200
91
|
def deactivate_users(feature, users)
|
201
92
|
with_feature(feature) do |f|
|
202
|
-
users.each{|user| f.remove_user(user)}
|
93
|
+
users.each { |user| f.remove_user(user) }
|
203
94
|
end
|
204
95
|
end
|
205
96
|
|
206
97
|
def set_users(feature, users)
|
207
98
|
with_feature(feature) do |f|
|
208
99
|
f.users = []
|
209
|
-
users.each{|user| f.add_user(user)}
|
100
|
+
users.each { |user| f.add_user(user) }
|
210
101
|
end
|
211
102
|
end
|
212
103
|
|
@@ -216,7 +107,7 @@ class Rollout
|
|
216
107
|
|
217
108
|
def active?(feature, user = nil)
|
218
109
|
feature = get(feature)
|
219
|
-
feature.active?(
|
110
|
+
feature.active?(user)
|
220
111
|
end
|
221
112
|
|
222
113
|
def user_in_active_users?(feature, user = nil)
|
@@ -242,12 +133,12 @@ class Rollout
|
|
242
133
|
|
243
134
|
def active_in_group?(group, user)
|
244
135
|
f = @groups[group.to_sym]
|
245
|
-
f
|
136
|
+
f&.call(user)
|
246
137
|
end
|
247
138
|
|
248
139
|
def get(feature)
|
249
140
|
string = @storage.get(key(feature))
|
250
|
-
Feature.new(feature, string, @options)
|
141
|
+
Feature.new(feature, state: string, rollout: self, options: @options)
|
251
142
|
end
|
252
143
|
|
253
144
|
def set_feature_data(feature, data)
|
@@ -263,29 +154,37 @@ class Rollout
|
|
263
154
|
end
|
264
155
|
|
265
156
|
def multi_get(*features)
|
266
|
-
|
267
|
-
|
157
|
+
return [] if features.empty?
|
158
|
+
|
159
|
+
feature_keys = features.map { |feature| key(feature) }
|
160
|
+
|
161
|
+
@storage
|
162
|
+
.mget(*feature_keys)
|
163
|
+
.map
|
164
|
+
.with_index do |string, index|
|
165
|
+
Feature.new(features[index], state: string, rollout: self, options: @options)
|
166
|
+
end
|
268
167
|
end
|
269
168
|
|
270
169
|
def features
|
271
|
-
(@storage.get(features_key) ||
|
170
|
+
(@storage.get(features_key) || '').split(',').map(&:to_sym)
|
272
171
|
end
|
273
172
|
|
274
173
|
def feature_states(user = nil)
|
275
|
-
features.each_with_object({}) do |f, hash|
|
276
|
-
hash[f] = active?(
|
174
|
+
multi_get(*features).each_with_object({}) do |f, hash|
|
175
|
+
hash[f.name] = f.active?(user)
|
277
176
|
end
|
278
177
|
end
|
279
178
|
|
280
179
|
def active_features(user = nil)
|
281
|
-
features.select do |f|
|
282
|
-
active?(
|
283
|
-
end
|
180
|
+
multi_get(*features).select do |f|
|
181
|
+
f.active?(user)
|
182
|
+
end.map(&:name)
|
284
183
|
end
|
285
184
|
|
286
185
|
def clear!
|
287
186
|
features.each do |feature|
|
288
|
-
with_feature(feature
|
187
|
+
with_feature(feature, &:clear)
|
289
188
|
@storage.del(key(feature))
|
290
189
|
end
|
291
190
|
|
@@ -293,7 +192,28 @@ class Rollout
|
|
293
192
|
end
|
294
193
|
|
295
194
|
def exists?(feature)
|
296
|
-
|
195
|
+
# since redis-rb v4.2, `#exists?` replaces `#exists` which now returns integer value instead of boolean
|
196
|
+
# https://github.com/redis/redis-rb/pull/918
|
197
|
+
if @storage.respond_to?(:exists?)
|
198
|
+
@storage.exists?(key(feature))
|
199
|
+
else
|
200
|
+
@storage.exists(key(feature))
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
def with_feature(feature)
|
205
|
+
f = get(feature)
|
206
|
+
|
207
|
+
if count_observers > 0
|
208
|
+
before = f.deep_clone
|
209
|
+
yield(f)
|
210
|
+
save(f)
|
211
|
+
changed
|
212
|
+
notify_observers(:update, before, f)
|
213
|
+
else
|
214
|
+
yield(f)
|
215
|
+
save(f)
|
216
|
+
end
|
297
217
|
end
|
298
218
|
|
299
219
|
private
|
@@ -303,17 +223,11 @@ class Rollout
|
|
303
223
|
end
|
304
224
|
|
305
225
|
def features_key
|
306
|
-
|
307
|
-
end
|
308
|
-
|
309
|
-
def with_feature(feature)
|
310
|
-
f = get(feature)
|
311
|
-
yield(f)
|
312
|
-
save(f)
|
226
|
+
'feature:__features__'
|
313
227
|
end
|
314
228
|
|
315
229
|
def save(feature)
|
316
230
|
@storage.set(key(feature.name), feature.serialize)
|
317
|
-
@storage.set(features_key, (features | [feature.name.to_sym]).join(
|
231
|
+
@storage.set(features_key, (features | [feature.name.to_sym]).join(','))
|
318
232
|
end
|
319
233
|
end
|
data/rollout.gemspec
CHANGED
@@ -1,27 +1,33 @@
|
|
1
|
-
#
|
2
|
-
$:.push File.expand_path("../lib", __FILE__)
|
3
|
-
require "rollout/version"
|
1
|
+
# frozen_string_literal: true
|
4
2
|
|
5
|
-
|
6
|
-
|
7
|
-
s.version = Rollout::VERSION
|
8
|
-
s.authors = ["James Golick"]
|
9
|
-
s.email = ["jamesgolick@gmail.com"]
|
10
|
-
s.description = "Feature flippers with redis."
|
11
|
-
s.summary = "Feature flippers with redis."
|
12
|
-
s.homepage = "https://github.com/FetLife/rollout"
|
13
|
-
s.license = "MIT"
|
3
|
+
$LOAD_PATH.push File.expand_path('lib', __dir__)
|
4
|
+
require 'rollout/version'
|
14
5
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'rollout'
|
8
|
+
spec.version = Rollout::VERSION
|
9
|
+
spec.authors = ['James Golick']
|
10
|
+
spec.email = ['jamesgolick@gmail.com']
|
11
|
+
spec.description = 'Feature flippers with redis.'
|
12
|
+
spec.summary = 'Feature flippers with redis.'
|
13
|
+
spec.homepage = 'https://github.com/FetLife/rollout'
|
14
|
+
spec.license = 'MIT'
|
19
15
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
16
|
+
spec.files = `git ls-files`.split("\n")
|
17
|
+
spec.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
spec.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.required_ruby_version = '>= 2.3'
|
22
|
+
|
23
|
+
spec.add_dependency 'observer'
|
24
|
+
spec.add_dependency 'redis', '>= 4.0', '< 6'
|
25
|
+
|
26
|
+
spec.add_development_dependency 'rake'
|
27
|
+
spec.add_development_dependency 'bundler', '>= 1.17'
|
28
|
+
spec.add_development_dependency 'pry'
|
29
|
+
spec.add_development_dependency 'rspec', '~> 3.13'
|
30
|
+
spec.add_development_dependency 'rspec_junit_formatter', '~> 0.6'
|
31
|
+
spec.add_development_dependency 'rubocop', '~> 0.71'
|
32
|
+
spec.add_development_dependency 'simplecov', '0.17'
|
27
33
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Rollout::Feature" do
|
4
|
+
let(:rollout) { Rollout.new($redis) }
|
5
|
+
|
6
|
+
describe "#add_user" do
|
7
|
+
it "ids a user using id_user_by" do
|
8
|
+
user = double("User", email: "test@test.com")
|
9
|
+
feature = Rollout::Feature.new(:chat, state: nil, rollout: rollout, options: { id_user_by: :email })
|
10
|
+
feature.add_user(user)
|
11
|
+
expect(user).to have_received :email
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#initialize" do
|
16
|
+
describe "when string does not exist" do
|
17
|
+
it 'clears feature attributes when string is not given' do
|
18
|
+
feature = Rollout::Feature.new(:chat, rollout: rollout)
|
19
|
+
expect(feature.groups).to be_empty
|
20
|
+
expect(feature.users).to be_empty
|
21
|
+
expect(feature.percentage).to eq 0
|
22
|
+
expect(feature.data).to eq({})
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'clears feature attributes when string is nil' do
|
26
|
+
feature = Rollout::Feature.new(:chat, state: nil, rollout: rollout)
|
27
|
+
expect(feature.groups).to be_empty
|
28
|
+
expect(feature.users).to be_empty
|
29
|
+
expect(feature.percentage).to eq 0
|
30
|
+
expect(feature.data).to eq({})
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'clears feature attributes when string is empty string' do
|
34
|
+
feature = Rollout::Feature.new(:chat, state: "", rollout: rollout)
|
35
|
+
expect(feature.groups).to be_empty
|
36
|
+
expect(feature.users).to be_empty
|
37
|
+
expect(feature.percentage).to eq 0
|
38
|
+
expect(feature.data).to eq({})
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "when there is no data" do
|
42
|
+
it 'sets @data to empty hash' do
|
43
|
+
feature = Rollout::Feature.new(:chat, state: "0||", rollout: rollout)
|
44
|
+
expect(feature.data).to eq({})
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'sets @data to empty hash' do
|
48
|
+
feature = Rollout::Feature.new(:chat, state: "||| ", rollout: rollout)
|
49
|
+
expect(feature.data).to eq({})
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'Rollout::Logging' do
|
4
|
+
let(:rollout) { Rollout.new($redis, logging: logging) }
|
5
|
+
let(:logging) { true }
|
6
|
+
let(:feature) { :foo }
|
7
|
+
|
8
|
+
it 'logs changes' do
|
9
|
+
expect(rollout.logging.last_event(feature)).to be_nil
|
10
|
+
|
11
|
+
rollout.activate_percentage(feature, 50)
|
12
|
+
|
13
|
+
expect(rollout.logging.updated_at(feature)).to_not be_nil
|
14
|
+
|
15
|
+
first_event = rollout.logging.last_event(feature)
|
16
|
+
|
17
|
+
expect(first_event.name).to eq 'update'
|
18
|
+
expect(first_event.data).to eq(before: { percentage: 0 }, after: { percentage: 50 })
|
19
|
+
|
20
|
+
rollout.activate_percentage(feature, 75)
|
21
|
+
|
22
|
+
second_event = rollout.logging.last_event(feature)
|
23
|
+
|
24
|
+
expect(second_event.name).to eq 'update'
|
25
|
+
expect(second_event.data).to eq(before: { percentage: 50 }, after: { percentage: 75 })
|
26
|
+
|
27
|
+
rollout.activate_group(feature, :hipsters)
|
28
|
+
|
29
|
+
third_event = rollout.logging.last_event(feature)
|
30
|
+
|
31
|
+
expect(third_event.name).to eq 'update'
|
32
|
+
expect(third_event.data).to eq(before: { groups: [] }, after: { groups: ['hipsters'] })
|
33
|
+
|
34
|
+
expect(rollout.logging.events(feature)).to eq [first_event, second_event, third_event]
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'logging data changes' do
|
38
|
+
it 'logs changes' do
|
39
|
+
expect(rollout.logging.last_event(feature)).to be_nil
|
40
|
+
|
41
|
+
rollout.set_feature_data(feature, description: "foo")
|
42
|
+
|
43
|
+
event = rollout.logging.last_event(feature)
|
44
|
+
|
45
|
+
expect(event).not_to be_nil
|
46
|
+
expect(event.name).to eq 'update'
|
47
|
+
expect(event.data).to eq(before: { "data.description": nil }, after: { "data.description": "foo" })
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'no logging' do
|
52
|
+
let(:logging) { nil }
|
53
|
+
|
54
|
+
it 'doesnt even respond to logging' do
|
55
|
+
expect(rollout).not_to respond_to :logging
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'history truncation' do
|
60
|
+
let(:logging) { { history_length: 1 } }
|
61
|
+
|
62
|
+
it 'logs changes' do
|
63
|
+
expect(rollout.logging.last_event(feature)).to be_nil
|
64
|
+
|
65
|
+
rollout.activate_percentage(feature, 25)
|
66
|
+
|
67
|
+
first_event = rollout.logging.last_event(feature)
|
68
|
+
|
69
|
+
expect(first_event.name).to eq 'update'
|
70
|
+
expect(first_event.data).to eq(before: { percentage: 0 }, after: { percentage: 25 })
|
71
|
+
|
72
|
+
rollout.activate_percentage(feature, 30)
|
73
|
+
|
74
|
+
second_event = rollout.logging.last_event(feature)
|
75
|
+
|
76
|
+
expect(second_event.name).to eq 'update'
|
77
|
+
expect(second_event.data).to eq(before: { percentage: 25 }, after: { percentage: 30 })
|
78
|
+
|
79
|
+
expect(rollout.logging.events(feature)).to eq [second_event]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'with context' do
|
84
|
+
let(:current_user) { double(nickname: 'lester') }
|
85
|
+
|
86
|
+
it 'adds context to the event' do
|
87
|
+
rollout.logging.with_context(actor: current_user.nickname) do
|
88
|
+
rollout.activate_percentage(feature, 25)
|
89
|
+
end
|
90
|
+
|
91
|
+
event = rollout.logging.last_event(feature)
|
92
|
+
|
93
|
+
expect(event.name).to eq 'update'
|
94
|
+
expect(event.data).to eq(before: { percentage: 0 }, after: { percentage: 25 })
|
95
|
+
expect(event.context).to eq(actor: current_user.nickname)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
context 'global logs' do
|
100
|
+
let(:logging) { { global: true } }
|
101
|
+
let(:feature_foo) { 'foo' }
|
102
|
+
let(:feature_bar) { 'bar' }
|
103
|
+
|
104
|
+
it 'logs changes' do
|
105
|
+
expect(rollout.logging.last_event(feature_foo)).to be_nil
|
106
|
+
|
107
|
+
rollout.activate_percentage(feature_foo, 25)
|
108
|
+
|
109
|
+
event_foo = rollout.logging.last_event(feature_foo)
|
110
|
+
|
111
|
+
expect(event_foo.feature).to eq feature_foo
|
112
|
+
expect(event_foo.name).to eq 'update'
|
113
|
+
expect(event_foo.data).to eq(before: { percentage: 0 }, after: { percentage: 25 })
|
114
|
+
|
115
|
+
expect(rollout.logging.events(feature_foo)).to eq [event_foo]
|
116
|
+
|
117
|
+
rollout.activate_percentage(feature_bar, 30)
|
118
|
+
|
119
|
+
event_bar = rollout.logging.last_event(feature_bar)
|
120
|
+
|
121
|
+
expect(event_bar.feature).to eq feature_bar
|
122
|
+
expect(event_bar.name).to eq 'update'
|
123
|
+
expect(event_bar.data).to eq(before: { percentage: 0 }, after: { percentage: 30 })
|
124
|
+
|
125
|
+
expect(rollout.logging.events(feature_bar)).to eq [event_bar]
|
126
|
+
|
127
|
+
expect(rollout.logging.global_events).to eq [event_foo, event_bar]
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
context 'no logging for block' do
|
132
|
+
it 'doesnt log' do
|
133
|
+
rollout.logging.without do
|
134
|
+
rollout.activate_percentage(feature, 25)
|
135
|
+
end
|
136
|
+
|
137
|
+
event = rollout.logging.last_event(feature)
|
138
|
+
|
139
|
+
expect(event).to be_nil
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|