featurevisor 0.3.0 → 1.1.0
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 +4 -4
- data/README.md +234 -88
- data/bin/cli.rb +9 -4
- data/bin/commands/assess_distribution.rb +34 -15
- data/bin/commands/benchmark.rb +65 -63
- data/bin/commands/test.rb +74 -96
- data/lib/featurevisor/child_instance.rb +22 -24
- data/lib/featurevisor/datafile_reader.rb +18 -13
- data/lib/featurevisor/emitter.rb +2 -2
- data/lib/featurevisor/evaluate.rb +16 -23
- data/lib/featurevisor/events.rb +5 -4
- data/lib/featurevisor/instance.rb +225 -39
- data/lib/featurevisor/logger.rb +0 -7
- data/lib/featurevisor/modules.rb +184 -0
- data/lib/featurevisor/version.rb +1 -1
- data/lib/featurevisor.rb +4 -1
- metadata +28 -3
- data/lib/featurevisor/hooks.rb +0 -159
|
@@ -3,8 +3,6 @@
|
|
|
3
3
|
module Featurevisor
|
|
4
4
|
# Child instance class for managing child contexts and sticky features
|
|
5
5
|
class ChildInstance
|
|
6
|
-
attr_reader :parent, :context, :sticky, :emitter
|
|
7
|
-
|
|
8
6
|
# Initialize a new child instance
|
|
9
7
|
# @param options [Hash] Child instance options
|
|
10
8
|
# @option options [Instance] :parent Parent instance
|
|
@@ -94,8 +92,8 @@ module Featurevisor
|
|
|
94
92
|
**context
|
|
95
93
|
},
|
|
96
94
|
{
|
|
97
|
-
|
|
98
|
-
|
|
95
|
+
**options,
|
|
96
|
+
__featurevisor_child_sticky: @sticky
|
|
99
97
|
}
|
|
100
98
|
)
|
|
101
99
|
end
|
|
@@ -113,8 +111,8 @@ module Featurevisor
|
|
|
113
111
|
**context
|
|
114
112
|
},
|
|
115
113
|
{
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
**options,
|
|
115
|
+
__featurevisor_child_sticky: @sticky
|
|
118
116
|
}
|
|
119
117
|
)
|
|
120
118
|
end
|
|
@@ -134,8 +132,8 @@ module Featurevisor
|
|
|
134
132
|
**context
|
|
135
133
|
},
|
|
136
134
|
{
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
**options,
|
|
136
|
+
__featurevisor_child_sticky: @sticky
|
|
139
137
|
}
|
|
140
138
|
)
|
|
141
139
|
end
|
|
@@ -155,8 +153,8 @@ module Featurevisor
|
|
|
155
153
|
**context
|
|
156
154
|
},
|
|
157
155
|
{
|
|
158
|
-
|
|
159
|
-
|
|
156
|
+
**options,
|
|
157
|
+
__featurevisor_child_sticky: @sticky
|
|
160
158
|
}
|
|
161
159
|
)
|
|
162
160
|
end
|
|
@@ -176,8 +174,8 @@ module Featurevisor
|
|
|
176
174
|
**context
|
|
177
175
|
},
|
|
178
176
|
{
|
|
179
|
-
|
|
180
|
-
|
|
177
|
+
**options,
|
|
178
|
+
__featurevisor_child_sticky: @sticky
|
|
181
179
|
}
|
|
182
180
|
)
|
|
183
181
|
end
|
|
@@ -197,8 +195,8 @@ module Featurevisor
|
|
|
197
195
|
**context
|
|
198
196
|
},
|
|
199
197
|
{
|
|
200
|
-
|
|
201
|
-
|
|
198
|
+
**options,
|
|
199
|
+
__featurevisor_child_sticky: @sticky
|
|
202
200
|
}
|
|
203
201
|
)
|
|
204
202
|
end
|
|
@@ -218,8 +216,8 @@ module Featurevisor
|
|
|
218
216
|
**context
|
|
219
217
|
},
|
|
220
218
|
{
|
|
221
|
-
|
|
222
|
-
|
|
219
|
+
**options,
|
|
220
|
+
__featurevisor_child_sticky: @sticky
|
|
223
221
|
}
|
|
224
222
|
)
|
|
225
223
|
end
|
|
@@ -239,8 +237,8 @@ module Featurevisor
|
|
|
239
237
|
**context
|
|
240
238
|
},
|
|
241
239
|
{
|
|
242
|
-
|
|
243
|
-
|
|
240
|
+
**options,
|
|
241
|
+
__featurevisor_child_sticky: @sticky
|
|
244
242
|
}
|
|
245
243
|
)
|
|
246
244
|
end
|
|
@@ -260,8 +258,8 @@ module Featurevisor
|
|
|
260
258
|
**context
|
|
261
259
|
},
|
|
262
260
|
{
|
|
263
|
-
|
|
264
|
-
|
|
261
|
+
**options,
|
|
262
|
+
__featurevisor_child_sticky: @sticky
|
|
265
263
|
}
|
|
266
264
|
)
|
|
267
265
|
end
|
|
@@ -281,8 +279,8 @@ module Featurevisor
|
|
|
281
279
|
**context
|
|
282
280
|
},
|
|
283
281
|
{
|
|
284
|
-
|
|
285
|
-
|
|
282
|
+
**options,
|
|
283
|
+
__featurevisor_child_sticky: @sticky
|
|
286
284
|
}
|
|
287
285
|
)
|
|
288
286
|
end
|
|
@@ -300,8 +298,8 @@ module Featurevisor
|
|
|
300
298
|
},
|
|
301
299
|
feature_keys,
|
|
302
300
|
{
|
|
303
|
-
|
|
304
|
-
|
|
301
|
+
**options,
|
|
302
|
+
__featurevisor_child_sticky: @sticky
|
|
305
303
|
}
|
|
306
304
|
)
|
|
307
305
|
end
|
|
@@ -5,7 +5,7 @@ require "json"
|
|
|
5
5
|
module Featurevisor
|
|
6
6
|
# DatafileReader class for reading and processing Featurevisor datafiles
|
|
7
7
|
class DatafileReader
|
|
8
|
-
attr_reader :schema_version, :revision, :segments, :features, :logger, :regex_cache
|
|
8
|
+
attr_reader :schema_version, :revision, :featurevisor_version, :segments, :features, :logger, :regex_cache
|
|
9
9
|
|
|
10
10
|
# Initialize a new DatafileReader
|
|
11
11
|
# @param options [Hash] Options hash containing datafile and logger
|
|
@@ -17,6 +17,7 @@ module Featurevisor
|
|
|
17
17
|
|
|
18
18
|
@schema_version = datafile[:schemaVersion]
|
|
19
19
|
@revision = datafile[:revision]
|
|
20
|
+
@featurevisor_version = datafile[:featurevisorVersion]
|
|
20
21
|
@segments = (datafile[:segments] || {}).transform_keys(&:to_sym)
|
|
21
22
|
@features = (datafile[:features] || {}).transform_keys(&:to_sym)
|
|
22
23
|
|
|
@@ -65,6 +66,16 @@ module Featurevisor
|
|
|
65
66
|
@schema_version
|
|
66
67
|
end
|
|
67
68
|
|
|
69
|
+
def get_datafile
|
|
70
|
+
{
|
|
71
|
+
schemaVersion: @schema_version,
|
|
72
|
+
revision: @revision,
|
|
73
|
+
featurevisorVersion: @featurevisor_version,
|
|
74
|
+
segments: @segments,
|
|
75
|
+
features: @features
|
|
76
|
+
}.compact
|
|
77
|
+
end
|
|
78
|
+
|
|
68
79
|
# Get a segment by key
|
|
69
80
|
# @param segment_key [String] Segment key to retrieve
|
|
70
81
|
# @return [Hash, nil] Segment data or nil if not found
|
|
@@ -172,15 +183,11 @@ module Featurevisor
|
|
|
172
183
|
end
|
|
173
184
|
|
|
174
185
|
if conditions.is_a?(Hash) && conditions[:not] && conditions[:not].is_a?(Array)
|
|
175
|
-
return conditions[:not]
|
|
176
|
-
all_conditions_are_matched({ and: conditions[:not] }, context) == false
|
|
177
|
-
end
|
|
186
|
+
return !all_conditions_are_matched({ and: conditions[:not] }, context)
|
|
178
187
|
end
|
|
179
188
|
|
|
180
189
|
if conditions.is_a?(Hash) && conditions["not"] && conditions["not"].is_a?(Array)
|
|
181
|
-
return conditions["not"]
|
|
182
|
-
all_conditions_are_matched({ "and" => conditions["not"] }, context) == false
|
|
183
|
-
end
|
|
190
|
+
return !all_conditions_are_matched({ "and" => conditions["not"] }, context)
|
|
184
191
|
end
|
|
185
192
|
|
|
186
193
|
|
|
@@ -237,15 +244,11 @@ module Featurevisor
|
|
|
237
244
|
end
|
|
238
245
|
|
|
239
246
|
if group_segments[:not] && group_segments[:not].is_a?(Array)
|
|
240
|
-
return group_segments[:not]
|
|
241
|
-
all_segments_are_matched({ and: group_segments[:not] }, context) == false
|
|
242
|
-
end
|
|
247
|
+
return !all_segments_are_matched({ and: group_segments[:not] }, context)
|
|
243
248
|
end
|
|
244
249
|
|
|
245
250
|
if group_segments["not"] && group_segments["not"].is_a?(Array)
|
|
246
|
-
return group_segments["not"]
|
|
247
|
-
all_segments_are_matched({ "and" => group_segments["not"] }, context) == false
|
|
248
|
-
end
|
|
251
|
+
return !all_segments_are_matched({ "and" => group_segments["not"] }, context)
|
|
249
252
|
end
|
|
250
253
|
|
|
251
254
|
|
|
@@ -351,4 +354,6 @@ module Featurevisor
|
|
|
351
354
|
segments
|
|
352
355
|
end
|
|
353
356
|
end
|
|
357
|
+
|
|
358
|
+
private_constant :DatafileReader
|
|
354
359
|
end
|
data/lib/featurevisor/emitter.rb
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Featurevisor
|
|
4
4
|
# Event names for the emitter
|
|
5
|
-
EVENT_NAMES = %w[datafile_set context_set sticky_set].freeze
|
|
5
|
+
EVENT_NAMES = %w[datafile_set context_set sticky_set error].freeze
|
|
6
6
|
|
|
7
7
|
# Event emitter class for handling event subscriptions and triggers
|
|
8
8
|
class Emitter
|
|
@@ -42,7 +42,7 @@ module Featurevisor
|
|
|
42
42
|
|
|
43
43
|
return unless listeners
|
|
44
44
|
|
|
45
|
-
listeners.each do |listener|
|
|
45
|
+
listeners.dup.each do |listener|
|
|
46
46
|
begin
|
|
47
47
|
listener.call(details)
|
|
48
48
|
rescue => err
|
|
@@ -36,20 +36,17 @@ module Featurevisor
|
|
|
36
36
|
# Evaluation module for feature flag evaluation
|
|
37
37
|
module Evaluate
|
|
38
38
|
|
|
39
|
-
# Evaluate with
|
|
39
|
+
# Evaluate with modules
|
|
40
40
|
# @param options [Hash] Evaluation options
|
|
41
41
|
# @return [Hash] Evaluation result
|
|
42
|
-
def self.
|
|
42
|
+
def self.evaluate_with_modules(options)
|
|
43
43
|
begin
|
|
44
|
-
|
|
45
|
-
hooks = hooks_manager.get_all
|
|
44
|
+
modules_manager = options[:modules_manager]
|
|
46
45
|
|
|
47
|
-
# Run before
|
|
46
|
+
# Run before modules
|
|
48
47
|
result_options = options
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
result_options = hook.call_before(result_options)
|
|
52
|
-
end
|
|
48
|
+
if modules_manager
|
|
49
|
+
result_options = modules_manager.run_before_modules(result_options)
|
|
53
50
|
end
|
|
54
51
|
|
|
55
52
|
# Evaluate
|
|
@@ -69,11 +66,9 @@ module Featurevisor
|
|
|
69
66
|
evaluation[:variable_value] = options[:default_variable_value]
|
|
70
67
|
end
|
|
71
68
|
|
|
72
|
-
# Run after
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
evaluation = hook.call_after(evaluation, result_options)
|
|
76
|
-
end
|
|
69
|
+
# Run after modules
|
|
70
|
+
if modules_manager
|
|
71
|
+
evaluation = modules_manager.run_after_modules(evaluation, result_options)
|
|
77
72
|
end
|
|
78
73
|
|
|
79
74
|
evaluation
|
|
@@ -108,9 +103,7 @@ module Featurevisor
|
|
|
108
103
|
logger = options[:logger]
|
|
109
104
|
datafile_reader = options[:datafile_reader]
|
|
110
105
|
sticky = options[:sticky]
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
hooks = hooks_manager.get_all
|
|
106
|
+
modules_manager = options[:modules_manager]
|
|
114
107
|
evaluation = nil
|
|
115
108
|
|
|
116
109
|
begin
|
|
@@ -422,24 +415,24 @@ module Featurevisor
|
|
|
422
415
|
logger: logger
|
|
423
416
|
})
|
|
424
417
|
|
|
425
|
-
# Run bucket key
|
|
426
|
-
bucket_key =
|
|
418
|
+
# Run bucket key modules
|
|
419
|
+
bucket_key = modules_manager.run_bucket_key_modules({
|
|
427
420
|
feature_key: feature_key,
|
|
428
421
|
context: context,
|
|
429
422
|
bucket_by: feature[:bucketBy],
|
|
430
423
|
bucket_key: bucket_key
|
|
431
|
-
})
|
|
424
|
+
}) if modules_manager
|
|
432
425
|
|
|
433
426
|
# bucketValue
|
|
434
427
|
bucket_value = Featurevisor::Bucketer.get_bucketed_number(bucket_key)
|
|
435
428
|
|
|
436
|
-
# Run bucket value
|
|
437
|
-
bucket_value =
|
|
429
|
+
# Run bucket value modules
|
|
430
|
+
bucket_value = modules_manager.run_bucket_value_modules({
|
|
438
431
|
feature_key: feature_key,
|
|
439
432
|
bucket_key: bucket_key,
|
|
440
433
|
context: context,
|
|
441
434
|
bucket_value: bucket_value
|
|
442
|
-
})
|
|
435
|
+
}) if modules_manager
|
|
443
436
|
|
|
444
437
|
matched_traffic = nil
|
|
445
438
|
matched_allocation = nil
|
data/lib/featurevisor/events.rb
CHANGED
|
@@ -24,7 +24,7 @@ module Featurevisor
|
|
|
24
24
|
# @param previous_reader [DatafileReader] Previous datafile reader
|
|
25
25
|
# @param new_reader [DatafileReader] New datafile reader
|
|
26
26
|
# @return [Hash] Event parameters
|
|
27
|
-
def self.get_params_for_datafile_set_event(previous_reader, new_reader)
|
|
27
|
+
def self.get_params_for_datafile_set_event(previous_reader, new_reader, replace = false)
|
|
28
28
|
previous_revision = previous_reader.get_revision
|
|
29
29
|
previous_feature_keys = previous_reader.get_feature_keys
|
|
30
30
|
|
|
@@ -67,9 +67,10 @@ module Featurevisor
|
|
|
67
67
|
|
|
68
68
|
{
|
|
69
69
|
revision: new_revision,
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
features: all_affected_features
|
|
70
|
+
previousRevision: previous_revision,
|
|
71
|
+
revisionChanged: previous_revision != new_revision,
|
|
72
|
+
features: all_affected_features,
|
|
73
|
+
replaced: replace
|
|
73
74
|
}
|
|
74
75
|
end
|
|
75
76
|
end
|