featurevisor 0.1.1 → 0.3.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.
@@ -53,12 +53,12 @@ module Featurevisor
53
53
  if value.is_a?(Array) && (context_value_from_path.is_a?(String) || context_value_from_path.is_a?(Numeric) || context_value_from_path.nil?)
54
54
  # Check if the attribute key actually exists in the context
55
55
  key_exists = context.key?(attribute.to_sym) || context.key?(attribute.to_s)
56
-
56
+
57
57
  # If key doesn't exist, notIn should fail (return false), in should also fail
58
58
  if !key_exists
59
59
  return false
60
60
  end
61
-
61
+
62
62
  value_in_context = context_value_from_path.to_s
63
63
 
64
64
  if operator == "in"
@@ -183,6 +183,8 @@ module Featurevisor
183
183
  end
184
184
  end
185
185
 
186
+
187
+
186
188
  if conditions.is_a?(Array)
187
189
  return conditions.all? { |c| all_conditions_are_matched(c, context) }
188
190
  end
@@ -245,6 +247,8 @@ module Featurevisor
245
247
  all_segments_are_matched({ "and" => group_segments["not"] }, context) == false
246
248
  end
247
249
  end
250
+
251
+
248
252
  end
249
253
 
250
254
  if group_segments.is_a?(Array)
@@ -17,7 +17,8 @@ module Featurevisor
17
17
  VARIABLE_NOT_FOUND = "variable_not_found" # variable's schema is not defined in the feature
18
18
  VARIABLE_DEFAULT = "variable_default" # default variable value used
19
19
  VARIABLE_DISABLED = "variable_disabled" # feature is disabled, and variable's disabledValue is used
20
- VARIABLE_OVERRIDE = "variable_override" # variable overridden from inside a variation
20
+ VARIABLE_OVERRIDE_VARIATION = "variable_override_variation" # variable overridden from inside a variation
21
+ VARIABLE_OVERRIDE_RULE = "variable_override_rule" # variable overridden from inside a rule
21
22
 
22
23
  # Common
23
24
  NO_MATCH = "no_match" # no rules matched
@@ -55,14 +56,14 @@ module Featurevisor
55
56
  evaluation = evaluate(result_options)
56
57
 
57
58
  # Default: variation
58
- if options[:default_variation_value] &&
59
+ if options.key?(:default_variation_value) &&
59
60
  evaluation[:type] == "variation" &&
60
61
  evaluation[:variation_value].nil?
61
62
  evaluation[:variation_value] = options[:default_variation_value]
62
63
  end
63
64
 
64
65
  # Default: variable
65
- if options[:default_variable_value] &&
66
+ if options.key?(:default_variable_value) &&
66
67
  evaluation[:type] == "variable" &&
67
68
  evaluation[:variable_value].nil?
68
69
  evaluation[:variable_value] = options[:default_variable_value]
@@ -132,10 +133,10 @@ module Featurevisor
132
133
  if type == "variable"
133
134
  if feature && variable_key &&
134
135
  feature[:variablesSchema] &&
135
- (feature[:variablesSchema][variable_key] || feature[:variablesSchema][variable_key.to_sym])
136
- variable_schema = feature[:variablesSchema][variable_key] || feature[:variablesSchema][variable_key.to_sym]
136
+ has_key?(feature[:variablesSchema], variable_key)
137
+ variable_schema = fetch_with_symbol_key(feature[:variablesSchema], variable_key)
137
138
 
138
- if variable_schema[:disabledValue]
139
+ if variable_schema.key?(:disabledValue)
139
140
  # disabledValue: <value>
140
141
  evaluation = {
141
142
  type: type,
@@ -179,8 +180,8 @@ module Featurevisor
179
180
  end
180
181
 
181
182
  # Sticky
182
- if sticky && (sticky[feature_key] || sticky[feature_key.to_sym])
183
- sticky_feature = sticky[feature_key] || sticky[feature_key.to_sym]
183
+ if sticky && has_key?(sticky, feature_key)
184
+ sticky_feature = fetch_with_symbol_key(sticky, feature_key)
184
185
 
185
186
  # flag
186
187
  if type == "flag" && sticky_feature.key?(:enabled)
@@ -201,7 +202,7 @@ module Featurevisor
201
202
  if type == "variation"
202
203
  variation_value = sticky_feature[:variation]
203
204
 
204
- if variation_value
205
+ unless variation_value.nil?
205
206
  evaluation = {
206
207
  type: type,
207
208
  feature_key: feature_key,
@@ -219,8 +220,8 @@ module Featurevisor
219
220
  if type == "variable" && variable_key
220
221
  variables = sticky_feature[:variables]
221
222
 
222
- if variables && (variables[variable_key] || variables[variable_key.to_sym])
223
- variable_value = variables[variable_key] || variables[variable_key.to_sym]
223
+ if variables && has_key?(variables, variable_key)
224
+ variable_value = fetch_with_symbol_key(variables, variable_key)
224
225
  evaluation = {
225
226
  type: type,
226
227
  feature_key: feature_key,
@@ -261,8 +262,8 @@ module Featurevisor
261
262
  variable_schema = nil
262
263
 
263
264
  if variable_key
264
- if feature[:variablesSchema] && (feature[:variablesSchema][variable_key] || feature[:variablesSchema][variable_key.to_sym])
265
- variable_schema = feature[:variablesSchema][variable_key] || feature[:variablesSchema][variable_key.to_sym]
265
+ if feature[:variablesSchema] && has_key?(feature[:variablesSchema], variable_key)
266
+ variable_schema = fetch_with_symbol_key(feature[:variablesSchema], variable_key)
266
267
  end
267
268
 
268
269
  # variable schema not found
@@ -343,8 +344,8 @@ module Featurevisor
343
344
  end
344
345
 
345
346
  # variable
346
- if variable_key && force[:variables] && (force[:variables][variable_key] || force[:variables][variable_key.to_sym])
347
- variable_value = force[:variables][variable_key] || force[:variables][variable_key.to_sym]
347
+ if variable_key && force[:variables] && has_key?(force[:variables], variable_key)
348
+ variable_value = fetch_with_symbol_key(force[:variables], variable_key)
348
349
  evaluation = {
349
350
  type: type,
350
351
  feature_key: feature_key,
@@ -385,8 +386,8 @@ module Featurevisor
385
386
 
386
387
  required_variation_value = nil
387
388
 
388
- if required_variation_evaluation[:variation_value]
389
- required_variation_value = required_variation_evaluation[:variation_value]
389
+ if has_key?(required_variation_evaluation, :variation_value)
390
+ required_variation_value = fetch_with_symbol_key(required_variation_evaluation, :variation_value)
390
391
  elsif required_variation_evaluation[:variation]
391
392
  required_variation_value = required_variation_evaluation[:variation][:value]
392
393
  end
@@ -601,10 +602,50 @@ module Featurevisor
601
602
  # variable
602
603
  if type == "variable" && variable_key
603
604
  # override from rule
605
+ if matched_traffic &&
606
+ matched_traffic[:variableOverrides] &&
607
+ has_key?(matched_traffic[:variableOverrides], variable_key)
608
+ overrides = fetch_with_symbol_key(matched_traffic[:variableOverrides], variable_key)
609
+
610
+ override_index = overrides.find_index do |o|
611
+ if o[:conditions]
612
+ conditions = o[:conditions].is_a?(String) && o[:conditions] != "*" ? JSON.parse(o[:conditions]) : o[:conditions]
613
+ datafile_reader.all_conditions_are_matched(conditions, context)
614
+ elsif o[:segments]
615
+ segments = datafile_reader.parse_segments_if_stringified(o[:segments])
616
+ datafile_reader.all_segments_are_matched(segments, context)
617
+ else
618
+ false
619
+ end
620
+ end
621
+
622
+ unless override_index.nil?
623
+ override = overrides[override_index]
624
+
625
+ evaluation = {
626
+ type: type,
627
+ feature_key: feature_key,
628
+ reason: Featurevisor::EvaluationReason::VARIABLE_OVERRIDE_RULE,
629
+ bucket_key: bucket_key,
630
+ bucket_value: bucket_value,
631
+ rule_key: matched_traffic[:key],
632
+ traffic: matched_traffic,
633
+ variable_key: variable_key,
634
+ variable_schema: variable_schema,
635
+ variable_value: override[:value],
636
+ variable_override_index: override_index
637
+ }
638
+
639
+ logger.debug("variable override from rule", evaluation)
640
+
641
+ return evaluation
642
+ end
643
+ end
644
+
604
645
  if matched_traffic &&
605
646
  matched_traffic[:variables] &&
606
- (matched_traffic[:variables][variable_key] || matched_traffic[:variables][variable_key.to_sym])
607
- variable_value = matched_traffic[:variables][variable_key] || matched_traffic[:variables][variable_key.to_sym]
647
+ has_key?(matched_traffic[:variables], variable_key)
648
+ variable_value = fetch_with_symbol_key(matched_traffic[:variables], variable_key)
608
649
  evaluation = {
609
650
  type: type,
610
651
  feature_key: feature_key,
@@ -637,81 +678,38 @@ module Featurevisor
637
678
  if variation_value && feature[:variations].is_a?(Array)
638
679
  variation = feature[:variations].find { |v| v[:value] == variation_value }
639
680
 
640
- if variation && variation[:variableOverrides] && (variation[:variableOverrides][variable_key] || variation[:variableOverrides][variable_key.to_sym])
641
- overrides = variation[:variableOverrides][variable_key] || variation[:variableOverrides][variable_key.to_sym]
681
+ if variation && variation[:variableOverrides] && has_key?(variation[:variableOverrides], variable_key)
682
+ overrides = fetch_with_symbol_key(variation[:variableOverrides], variable_key)
642
683
 
643
- logger.debug("checking variableOverrides", {
644
- feature_key: feature_key,
645
- variable_key: variable_key,
646
- overrides: overrides,
647
- context: context
648
- })
649
-
650
- override = overrides.find do |o|
651
- logger.debug("evaluating override", {
652
- feature_key: feature_key,
653
- variable_key: variable_key,
654
- override: o,
655
- context: context
656
- })
657
-
658
- result = if o[:conditions]
659
- matched = datafile_reader.all_conditions_are_matched(
660
- o[:conditions].is_a?(String) && o[:conditions] != "*" ?
661
- JSON.parse(o[:conditions]) : o[:conditions],
662
- context
663
- )
664
- logger.debug("conditions match result", {
665
- feature_key: feature_key,
666
- variable_key: variable_key,
667
- conditions: o[:conditions],
668
- matched: matched
669
- })
670
- matched
684
+ override_index = overrides.find_index do |o|
685
+ if o[:conditions]
686
+ conditions = o[:conditions].is_a?(String) && o[:conditions] != "*" ? JSON.parse(o[:conditions]) : o[:conditions]
687
+ datafile_reader.all_conditions_are_matched(conditions, context)
671
688
  elsif o[:segments]
672
689
  segments = datafile_reader.parse_segments_if_stringified(o[:segments])
673
- matched = datafile_reader.all_segments_are_matched(segments, context)
674
- logger.debug("segments match result", {
675
- feature_key: feature_key,
676
- variable_key: variable_key,
677
- segments: o[:segments],
678
- parsed_segments: segments,
679
- matched: matched
680
- })
681
- matched
690
+ datafile_reader.all_segments_are_matched(segments, context)
682
691
  else
683
- logger.debug("override has no conditions or segments", {
684
- feature_key: feature_key,
685
- variable_key: variable_key,
686
- override: o
687
- })
688
692
  false
689
693
  end
690
-
691
- logger.debug("override evaluation result", {
692
- feature_key: feature_key,
693
- variable_key: variable_key,
694
- result: result
695
- })
696
-
697
- result
698
694
  end
699
695
 
700
- if override
696
+ unless override_index.nil?
697
+ override = overrides[override_index]
701
698
  evaluation = {
702
699
  type: type,
703
700
  feature_key: feature_key,
704
- reason: Featurevisor::EvaluationReason::VARIABLE_OVERRIDE,
701
+ reason: Featurevisor::EvaluationReason::VARIABLE_OVERRIDE_VARIATION,
705
702
  bucket_key: bucket_key,
706
703
  bucket_value: bucket_value,
707
704
  rule_key: matched_traffic&.[](:key),
708
705
  traffic: matched_traffic,
709
706
  variable_key: variable_key,
710
707
  variable_schema: variable_schema,
711
- variable_value: override[:value]
708
+ variable_value: override[:value],
709
+ variable_override_index: override_index
712
710
  }
713
711
 
714
- logger.debug("variable override", evaluation)
712
+ logger.debug("variable override from variation", evaluation)
715
713
 
716
714
  return evaluation
717
715
  end
@@ -719,8 +717,8 @@ module Featurevisor
719
717
 
720
718
  if variation &&
721
719
  variation[:variables] &&
722
- (variation[:variables][variable_key] || variation[:variables][variable_key.to_sym])
723
- variable_value = variation[:variables][variable_key] || variation[:variables][variable_key.to_sym]
720
+ has_key?(variation[:variables], variable_key)
721
+ variable_value = fetch_with_symbol_key(variation[:variables], variable_key)
724
722
  evaluation = {
725
723
  type: type,
726
724
  feature_key: feature_key,
@@ -814,5 +812,20 @@ module Featurevisor
814
812
  evaluation
815
813
  end
816
814
  end
815
+
816
+ def self.fetch_with_symbol_key(obj, key)
817
+ return obj[key] if obj.is_a?(Hash) && obj.key?(key)
818
+
819
+ symbol_key = key.to_sym
820
+ return obj[symbol_key] if obj.is_a?(Hash) && obj.key?(symbol_key)
821
+
822
+ nil
823
+ end
824
+
825
+ def self.has_key?(obj, key)
826
+ return false unless obj.is_a?(Hash)
827
+
828
+ obj.key?(key) || obj.key?(key.to_sym)
829
+ end
817
830
  end
818
831
  end
@@ -42,7 +42,7 @@ module Featurevisor
42
42
 
43
43
  if options[:datafile]
44
44
  @datafile_reader = Featurevisor::DatafileReader.new(
45
- datafile: options[:datafile].is_a?(String) ? JSON.parse(options[:datafile]) : options[:datafile],
45
+ datafile: options[:datafile].is_a?(String) ? JSON.parse(options[:datafile], symbolize_names: true) : options[:datafile],
46
46
  logger: @logger
47
47
  )
48
48
  end
@@ -61,7 +61,7 @@ module Featurevisor
61
61
  def set_datafile(datafile)
62
62
  begin
63
63
  new_datafile_reader = Featurevisor::DatafileReader.new(
64
- datafile: datafile.is_a?(String) ? JSON.parse(datafile) : datafile,
64
+ datafile: datafile.is_a?(String) ? JSON.parse(datafile, symbolize_names: true) : datafile,
65
65
  logger: @logger
66
66
  )
67
67
 
@@ -1,3 +1,3 @@
1
1
  module Featurevisor
2
- VERSION = "0.1.1"
2
+ VERSION = "0.3.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: featurevisor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fahad Heylaal