prefab-cloud-ruby 0.20.0 → 0.21.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc.sample +3 -0
  3. data/.github/workflows/ruby.yml +4 -0
  4. data/.gitmodules +3 -0
  5. data/Gemfile +12 -12
  6. data/Gemfile.lock +16 -14
  7. data/README.md +1 -1
  8. data/Rakefile +13 -14
  9. data/VERSION +1 -1
  10. data/lib/prefab/auth_interceptor.rb +2 -1
  11. data/lib/prefab/cancellable_interceptor.rb +8 -7
  12. data/lib/prefab/client.rb +33 -24
  13. data/lib/prefab/config_client.rb +55 -66
  14. data/lib/prefab/config_loader.rb +7 -114
  15. data/lib/prefab/config_resolver.rb +27 -57
  16. data/lib/prefab/config_value_unwrapper.rb +23 -0
  17. data/lib/prefab/criteria_evaluator.rb +96 -0
  18. data/lib/prefab/errors/invalid_api_key_error.rb +1 -1
  19. data/lib/prefab/feature_flag_client.rb +13 -145
  20. data/lib/prefab/internal_logger.rb +6 -5
  21. data/lib/prefab/local_config_parser.rb +110 -0
  22. data/lib/prefab/logger_client.rb +26 -31
  23. data/lib/prefab/murmer3.rb +3 -4
  24. data/lib/prefab/noop_cache.rb +5 -7
  25. data/lib/prefab/noop_stats.rb +2 -3
  26. data/lib/prefab/options.rb +11 -9
  27. data/lib/prefab/ratelimit_client.rb +11 -13
  28. data/lib/prefab/sse_logger.rb +3 -2
  29. data/lib/prefab/weighted_value_resolver.rb +42 -0
  30. data/lib/prefab/yaml_config_parser.rb +32 -0
  31. data/lib/prefab-cloud-ruby.rb +7 -2
  32. data/lib/prefab_pb.rb +49 -43
  33. data/lib/prefab_services_pb.rb +0 -1
  34. data/prefab-cloud-ruby.gemspec +28 -19
  35. data/test/.prefab.unit_tests.config.yaml +3 -2
  36. data/test/integration_test.rb +98 -0
  37. data/test/integration_test_helpers.rb +37 -0
  38. data/test/test_client.rb +32 -31
  39. data/test/test_config_client.rb +21 -20
  40. data/test/test_config_loader.rb +48 -37
  41. data/test/test_config_resolver.rb +312 -135
  42. data/test/test_config_value_unwrapper.rb +83 -0
  43. data/test/test_criteria_evaluator.rb +533 -0
  44. data/test/test_feature_flag_client.rb +35 -347
  45. data/test/test_helper.rb +18 -14
  46. data/test/test_integration.rb +33 -0
  47. data/test/test_local_config_parser.rb +78 -0
  48. data/test/test_logger.rb +47 -46
  49. data/test/test_weighted_value_resolver.rb +65 -0
  50. metadata +24 -27
  51. data/lib/prefab/config_helper.rb +0 -31
  52. data/run_test_harness_server.sh +0 -8
  53. data/test/harness_server.rb +0 -64
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yaml'
4
3
  module Prefab
5
4
  class ConfigLoader
6
5
  attr_reader :highwater_mark
@@ -19,21 +18,19 @@ module Prefab
19
18
  @api_config.each_key do |k|
20
19
  rtn[k] = @api_config[k]
21
20
  end
22
- rtn = rtn.merge(@local_overrides)
23
- rtn
21
+ rtn.merge(@local_overrides)
24
22
  end
25
23
 
26
24
  def set(config, source)
27
25
  # don't overwrite newer values
28
- if @api_config[config.key] && @api_config[config.key][:config].id >= config.id
29
- return
30
- end
26
+ return if @api_config[config.key] && @api_config[config.key][:config].id >= config.id
31
27
 
32
28
  if config.rows.empty?
33
29
  @api_config.delete(config.key)
34
30
  else
35
31
  if @api_config[config.key]
36
- @base_client.log_internal Logger::DEBUG, "Replace #{config.key} with value from #{source} #{ @api_config[config.key][:config].id} -> #{config.id}"
32
+ @base_client.log_internal Logger::DEBUG,
33
+ "Replace #{config.key} with value from #{source} #{@api_config[config.key][:config].id} -> #{config.id}"
37
34
  end
38
35
  @api_config[config.key] = { source: source, config: config }
39
36
  end
@@ -56,7 +53,7 @@ module Prefab
56
53
 
57
54
  def load_classpath_config
58
55
  classpath_dir = @prefab_options.prefab_config_classpath_dir
59
- rtn = load_glob(File.join(classpath_dir, ".prefab.default.config.yaml"))
56
+ rtn = load_glob(File.join(classpath_dir, '.prefab.default.config.yaml'))
60
57
  @prefab_options.prefab_envs.each do |env|
61
58
  rtn = rtn.merge load_glob(File.join(classpath_dir, ".prefab.#{env}.config.yaml"))
62
59
  end
@@ -65,7 +62,7 @@ module Prefab
65
62
 
66
63
  def load_local_overrides
67
64
  override_dir = @prefab_options.prefab_config_override_dir
68
- rtn = load_glob(File.join(override_dir, ".prefab.overrides.config.yaml"))
65
+ rtn = load_glob(File.join(override_dir, '.prefab.default.config.yaml'))
69
66
  @prefab_options.prefab_envs.each do |env|
70
67
  rtn = rtn.merge load_glob(File.join(override_dir, ".prefab.#{env}.config.yaml"))
71
68
  end
@@ -75,113 +72,9 @@ module Prefab
75
72
  def load_glob(glob)
76
73
  rtn = {}
77
74
  Dir.glob(glob).each do |file|
78
- @base_client.log_internal Logger::INFO, "Load #{file}"
79
- yaml = load(file)
80
- yaml.each do |k, v|
81
- load_kv(k, v, rtn, file)
82
- end
75
+ Prefab::YAMLConfigParser.new(file, @base_client).merge(rtn)
83
76
  end
84
77
  rtn
85
78
  end
86
-
87
- def load_kv(k, v, rtn, file)
88
- if v.class == Hash
89
- if v['feature_flag']
90
- rtn[k] = feature_flag_config(file, k, v)
91
- else
92
- v.each do |nest_k, nest_v|
93
- nested_key = "#{k}.#{nest_k}"
94
- nested_key = k if nest_k == "_"
95
- load_kv(nested_key, nest_v, rtn, file)
96
- end
97
- end
98
- else
99
- rtn[k] = {
100
- source: file,
101
- match: "default",
102
- config: Prefab::Config.new(
103
- key: k,
104
- rows: [
105
- Prefab::ConfigRow.new(value: Prefab::ConfigValue.new(value_from(k, v)))
106
- ]
107
- )
108
- }
109
- end
110
- end
111
-
112
- def load(filename)
113
- if File.exist? filename
114
- @base_client.log_internal Logger::INFO, "Load #{filename}"
115
- YAML.load_file(filename)
116
- else
117
- @base_client.log_internal Logger::INFO, "No file #{filename}"
118
- {}
119
- end
120
- end
121
-
122
- def value_from(key, raw)
123
- case raw
124
- when String
125
- if key.start_with? Prefab::LoggerClient::BASE_KEY
126
- prefab_log_level_resolve = Prefab::LogLevel.resolve(raw.upcase.to_sym) || Prefab::LogLevel::NOT_SET_LOG_LEVEL
127
- { log_level: prefab_log_level_resolve }
128
- else
129
- { string: raw }
130
- end
131
- when Integer
132
- { int: raw }
133
- when TrueClass, FalseClass
134
- { bool: raw }
135
- when Float
136
- { double: raw }
137
- end
138
- end
139
-
140
- def feature_flag_config(file, key, value)
141
- criteria = Prefab::Criteria.new(operator: 'ALWAYS_TRUE')
142
-
143
- if value['criteria']
144
- criteria = Prefab::Criteria.new(criteria_values(value['criteria']))
145
- end
146
-
147
- row = Prefab::ConfigRow.new(
148
- value: Prefab::ConfigValue.new(
149
- feature_flag: Prefab::FeatureFlag.new(
150
- active: true,
151
- inactive_variant_idx: -1, # not supported
152
- rules: [
153
- Prefab::Rule.new(
154
- variant_weights: [
155
- Prefab::VariantWeight.new(variant_idx: 0, weight: 1000)
156
- ],
157
- criteria: criteria
158
- )
159
- ]
160
- )
161
- )
162
- )
163
-
164
- unless value.has_key?('value')
165
- raise Prefab::Error, "Feature flag config `#{key}` #{file} must have a `value`"
166
- end
167
-
168
- {
169
- source: file,
170
- match: key,
171
- config: Prefab::Config.new(
172
- key: key,
173
- variants: [Prefab::FeatureFlagVariant.new(value_from(key, value['value']))],
174
- rows: [row]
175
- )
176
- }
177
- end
178
-
179
- def criteria_values(criteria_hash)
180
- if RUBY_VERSION < '2.7'
181
- criteria_hash.transform_keys(&:to_sym)
182
- else
183
- criteria_hash
184
- end
185
- end
186
79
  end
187
80
  end
@@ -1,17 +1,16 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Prefab
3
4
  class ConfigResolver
4
- include Prefab::ConfigHelper
5
- NAMESPACE_DELIMITER = "."
6
-
7
5
  attr_accessor :project_env_id # this will be set by the config_client when it gets an API response
8
6
 
9
7
  def initialize(base_client, config_loader)
10
8
  @lock = Concurrent::ReadWriteLock.new
11
9
  @local_store = {}
12
- @namespace = base_client.options.namespace
10
+ @additional_properties = { Prefab::CriteriaEvaluator::NAMESPACE_KEY => base_client.options.namespace }
13
11
  @config_loader = config_loader
14
- @project_env_id = 0
12
+ @project_env_id = 0 # we don't know this yet, it is set from the API results
13
+ @base_client = base_client
15
14
  make_local
16
15
  end
17
16
 
@@ -22,82 +21,53 @@ module Prefab
22
21
  v = @local_store[k]
23
22
  elements = [k.slice(0..49).ljust(50)]
24
23
  if v.nil?
25
- elements << "tombstone"
24
+ elements << 'tombstone'
26
25
  else
27
- value = v[:value]
28
- elements << value_of(value).to_s.slice(0..34).ljust(35)
29
- elements << value_of(value).class.to_s.slice(0..6).ljust(7)
26
+ config = evaluate(v[:config], {})
27
+ value = Prefab::ConfigValueUnwrapper.unwrap(config, k, {})
28
+ elements << value.to_s.slice(0..34).ljust(35)
29
+ elements << value.class.to_s.slice(0..6).ljust(7)
30
30
  elements << "Match: #{v[:match]}".slice(0..29).ljust(30)
31
31
  elements << "Source: #{v[:source]}"
32
32
  end
33
- str += elements.join(" | ") << "\n"
33
+ str += elements.join(' | ') << "\n"
34
34
  end
35
35
  end
36
36
  str
37
37
  end
38
38
 
39
- def get(property)
40
- config = _get(property)
41
- config ? value_of(config[:value]) : nil
42
- end
39
+ def raw(key)
40
+ via_key = @local_store[key]
43
41
 
44
- def get_config(property)
45
- config = _get(property)
46
- config ? config[:config] : nil
42
+ via_key ? via_key[:config] : nil
47
43
  end
48
44
 
49
- def _get(key)
45
+ def get(key, lookup_key, properties = {})
50
46
  @lock.with_read_lock do
51
- @local_store[key]
47
+ raw_config = raw(key)
48
+
49
+ return nil unless raw_config
50
+
51
+ evaluate(raw(key), lookup_key, properties)
52
52
  end
53
53
  end
54
54
 
55
+ def evaluate(config, lookup_key, properties = {})
56
+ props = properties.merge(@additional_properties).merge(Prefab::CriteriaEvaluator::LOOKUP_KEY => lookup_key)
57
+
58
+ Prefab::CriteriaEvaluator.new(config,
59
+ project_env_id: @project_env_id, resolver: self, base_client: @base_client).evaluate(props)
60
+ end
61
+
55
62
  def update
56
63
  make_local
57
64
  end
58
65
 
59
66
  private
60
67
 
61
- # Should client a.b.c see key in namespace a.b? yes
62
- # Should client a.b.c see key in namespace a.b.c? yes
63
- # Should client a.b.c see key in namespace a.b.d? no
64
- # Should client a.b.c see key in namespace ""? yes
65
- #
66
- def starts_with_ns?(key_namespace, client_namespace)
67
- zipped = key_namespace.split(NAMESPACE_DELIMITER).zip(client_namespace.split(NAMESPACE_DELIMITER))
68
- mapped = zipped.map do |k, c|
69
- (k.nil? || k.empty?) || k == c
70
- end
71
- [mapped.all?, mapped.size]
72
- end
73
-
74
68
  def make_local
75
- store = {}
76
- @config_loader.calc_config.each do |key, config_resolver_obj|
77
- config = config_resolver_obj[:config]
78
- sortable = config.rows.map do |row|
79
- if row.project_env_id != 0
80
- if row.project_env_id == @project_env_id
81
- if !row.namespace.empty?
82
- (starts_with, count) = starts_with_ns?(row.namespace, @namespace)
83
- # rubocop:disable BlockNesting
84
- { sortable: 2 + count, match: "nm:#{row.namespace}", value: row.value, config: config} if starts_with
85
- else
86
- { sortable: 1, match: "env:#{row.project_env_id}", value: row.value, config: config}
87
- end
88
- end
89
- else
90
- match = config_resolver_obj[:match] || "default"
91
- { sortable: 0, match: match, value: row.value, config: config}
92
- end
93
- end.compact
94
- to_store = sortable.sort_by { |h| h[:sortable] }.last
95
- to_store[:source] = config_resolver_obj[:source]
96
- store[key] = to_store
97
- end
98
-
99
69
  @lock.with_write_lock do
100
- @local_store = store
70
+ @local_store = @config_loader.calc_config
101
71
  end
102
72
  end
103
73
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Prefab
4
+ class ConfigValueUnwrapper
5
+ def self.unwrap(config_value, config_key, properties)
6
+ return nil unless config_value
7
+
8
+ case config_value.type
9
+ when :int, :string, :double, :bool, :log_level
10
+ config_value.public_send(config_value.type)
11
+ when :string_list
12
+ config_value.string_list.values
13
+ when :weighted_values
14
+ lookup_key = properties[Prefab::CriteriaEvaluator::LOOKUP_KEY]
15
+ weights = config_value.weighted_values.weighted_values
16
+ value = Prefab::WeightedValueResolver.new(weights, config_key, lookup_key).resolve
17
+ unwrap(value.value, config_key, properties)
18
+ else
19
+ raise "Unknown type: #{config_value.type}"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Prefab
4
+ class CriteriaEvaluator
5
+ LOOKUP_KEY = 'LOOKUP'
6
+ NAMESPACE_KEY = 'NAMESPACE'
7
+ NO_MATCHING_ROWS = [].freeze
8
+
9
+ def initialize(config, project_env_id:, resolver:, base_client:)
10
+ @config = config
11
+ @project_env_id = project_env_id
12
+ @resolver = resolver
13
+ @base_client = base_client
14
+ end
15
+
16
+ def evaluate(properties)
17
+ # TODO: optimize this and perhaps do it elsewhere
18
+ props = properties.transform_keys(&:to_s)
19
+
20
+ matching_environment_row_values.each do |conditional_value|
21
+ return conditional_value.value if all_criteria_match?(conditional_value, props)
22
+ end
23
+
24
+ default_row_values.each do |conditional_value|
25
+ return conditional_value.value if all_criteria_match?(conditional_value, props)
26
+ end
27
+
28
+ nil
29
+ end
30
+
31
+ def all_criteria_match?(conditional_value, props)
32
+ conditional_value.criteria.all? do |criterion|
33
+ evaluate_criteron(criterion, props)
34
+ end
35
+ end
36
+
37
+ def evaluate_criteron(criterion, properties)
38
+ value_from_properties = properties[criterion.property_name]
39
+
40
+ case criterion.operator
41
+ when :LOOKUP_KEY_IN, :PROP_IS_ONE_OF
42
+ matches?(criterion, value_from_properties, properties)
43
+ when :LOOKUP_KEY_NOT_IN, :PROP_IS_NOT_ONE_OF
44
+ !matches?(criterion, value_from_properties, properties)
45
+ when :IN_SEG
46
+ in_segment?(criterion, properties)
47
+ when :NOT_IN_SEG
48
+ !in_segment?(criterion, properties)
49
+ when :PROP_ENDS_WITH_ONE_OF
50
+ return false unless value_from_properties
51
+
52
+ criterion.value_to_match.string_list.values.any? do |ending|
53
+ value_from_properties.end_with?(ending)
54
+ end
55
+ when :PROP_DOES_NOT_END_WITH_ONE_OF
56
+ return true unless value_from_properties
57
+
58
+ criterion.value_to_match.string_list.values.none? do |ending|
59
+ value_from_properties.end_with?(ending)
60
+ end
61
+ when :HIERARCHICAL_MATCH
62
+ value_from_properties.start_with?(criterion.value_to_match.string)
63
+ when :ALWAYS_TRUE
64
+ true
65
+ else
66
+ @base_client.log.info("Unknown Operator: #{criterion.operator}")
67
+ false
68
+ end
69
+ end
70
+
71
+ private
72
+
73
+ def matching_environment_row_values
74
+ @config.rows.find { |row| row.project_env_id == @project_env_id }&.values || NO_MATCHING_ROWS
75
+ end
76
+
77
+ def default_row_values
78
+ @config.rows.find { |row| row.project_env_id != @project_env_id }&.values || NO_MATCHING_ROWS
79
+ end
80
+
81
+ def in_segment?(criterion, properties)
82
+ @resolver.get(criterion.value_to_match.string, properties[LOOKUP_KEY], properties).bool
83
+ end
84
+
85
+ def matches?(criterion, value_from_properties, properties)
86
+ criterion_value_or_values = Prefab::ConfigValueUnwrapper.unwrap(criterion.value_to_match, @config.key, properties)
87
+
88
+ case criterion_value_or_values
89
+ when Google::Protobuf::RepeatedField
90
+ criterion_value_or_values.include?(value_from_properties)
91
+ else
92
+ criterion_value_or_values == value_from_properties
93
+ end
94
+ end
95
+ end
96
+ end
@@ -5,7 +5,7 @@ module Prefab
5
5
  class InvalidApiKeyError < Prefab::Error
6
6
  def initialize(key)
7
7
  if key.nil? || key.empty?
8
- message = "No API key. Set PREFAB_API_KEY env var or use PREFAB_DATASOURCES=LOCAL_ONLY"
8
+ message = 'No API key. Set PREFAB_API_KEY env var or use PREFAB_DATASOURCES=LOCAL_ONLY'
9
9
 
10
10
  super(message)
11
11
  else
@@ -1,176 +1,44 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Prefab
3
4
  class FeatureFlagClient
4
- include Prefab::ConfigHelper
5
- MAX_32_FLOAT = 4294967294.0
6
-
7
5
  def initialize(base_client)
8
6
  @base_client = base_client
9
7
  end
10
8
 
11
- def upsert(feature_name, feature_obj)
12
- @base_client.config_client.upsert(feature_name, Prefab::ConfigValue.new(feature_flag: feature_obj))
13
- end
14
-
15
9
  def feature_is_on?(feature_name)
16
10
  feature_is_on_for?(feature_name, nil)
17
11
  end
18
12
 
19
13
  def feature_is_on_for?(feature_name, lookup_key, attributes: {})
20
- @base_client.stats.increment("prefab.featureflag.on", tags: ["feature:#{feature_name}"])
14
+ @base_client.stats.increment('prefab.featureflag.on', tags: ["feature:#{feature_name}"])
21
15
 
22
- return is_on?(_get(feature_name, lookup_key, attributes, default: false))
23
- end
24
-
25
- def get(feature_name, lookup_key=nil, attributes={}, default: false)
26
- variant = _get(feature_name, lookup_key, attributes, default: default)
16
+ variant = @base_client.config_client.get(feature_name, false, attributes, lookup_key)
27
17
 
28
- value_of_variant_or_nil(variant, default)
18
+ is_on?(variant)
29
19
  end
30
20
 
31
- private
21
+ def get(feature_name, lookup_key = nil, attributes = {}, default: false)
22
+ value = _get(feature_name, lookup_key, attributes)
32
23
 
33
- def value_of_variant_or_nil(variant_maybe, default)
34
- if variant_maybe.nil?
35
- default != Prefab::Client::NO_DEFAULT_PROVIDED ? default : nil
36
- else
37
- value_of_variant(variant_maybe)
38
- end
24
+ value.nil? ? default : value
39
25
  end
40
26
 
41
- def _get(feature_name, lookup_key=nil, attributes={}, default:)
42
- feature_obj = @base_client.config_client.get(feature_name, default)
43
- config_obj = @base_client.config_client.get_config_obj(feature_name)
44
-
45
- return nil if feature_obj.nil? || config_obj.nil?
46
-
47
- if feature_obj == !!feature_obj
48
- return feature_obj
49
- end
27
+ private
50
28
 
51
- variants = config_obj.variants
52
- get_variant(feature_name, lookup_key, attributes, feature_obj, variants)
29
+ def _get(feature_name, lookup_key = nil, attributes = {})
30
+ @base_client.config_client.get(feature_name, nil, attributes, lookup_key)
53
31
  end
54
32
 
55
33
  def is_on?(variant)
56
- if variant.nil?
57
- return false
58
- end
34
+ return false if variant.nil?
59
35
 
60
- if variant == !!variant
61
- return variant
62
- end
36
+ return variant if variant == !!variant
63
37
 
64
38
  variant.bool
65
- rescue
39
+ rescue StandardError
66
40
  @base_client.log.info("is_on? methods only work for boolean feature flags variants. This feature flags variant is '#{variant}'. Returning false")
67
41
  false
68
42
  end
69
-
70
- def get_variant(feature_name, lookup_key, attributes, feature_obj, variants)
71
- if !feature_obj.active
72
- return get_variant_obj(variants, feature_obj.inactive_variant_idx)
73
- end
74
-
75
- #default to inactive
76
- variant_weights = [Prefab::VariantWeight.new(variant_idx: feature_obj.inactive_variant_idx, weight: 1)]
77
-
78
- # if rules.match
79
- feature_obj.rules.each do |rule|
80
- if criteria_match?(rule.criteria, lookup_key, attributes)
81
- variant_weights = rule.variant_weights
82
- break
83
- end
84
- end
85
-
86
- percent_through_distribution = rand()
87
- if lookup_key
88
- percent_through_distribution = get_user_pct(feature_name, lookup_key)
89
- end
90
-
91
- variant_idx = get_variant_idx_from_weights(variant_weights, percent_through_distribution, feature_name)
92
-
93
- return get_variant_obj(variants, variant_idx)
94
- end
95
-
96
- def get_variant_obj(variants, idx)
97
- # our array is 0 based, but the idx are 1 based so the protos are clearly set
98
- return variants[idx - 1] if variants.length >= idx
99
- nil
100
- end
101
-
102
- def get_variant_idx_from_weights(variant_weights, percent_through_distribution, feature_name)
103
- distrubution_space = variant_weights.inject(0) { |sum, v| sum + v.weight }
104
- bucket = distrubution_space * percent_through_distribution
105
- sum = 0
106
- variant_weights.each do |variant_weight|
107
- if bucket < sum + variant_weight.weight
108
- return variant_weight.variant_idx
109
- else
110
- sum += variant_weight.weight
111
- end
112
- end
113
- # variants didn't add up to 100%
114
- @base_client.log.info("Variants of #{feature_name} did not add to 100%")
115
- return variant_weights.last.variant_idx
116
- end
117
-
118
- def get_user_pct(feature, lookup_key)
119
- to_hash = "#{feature}#{lookup_key}"
120
- int_value = Murmur3.murmur3_32(to_hash)
121
- int_value / MAX_32_FLOAT
122
- end
123
-
124
- def criteria_match?(criteria, lookup_key, attributes)
125
- case criteria.operator
126
- when :ALWAYS_TRUE
127
- true
128
- when :LOOKUP_KEY_IN
129
- criteria.values.include?(lookup_key)
130
- when :LOOKUP_KEY_NOT_IN
131
- !criteria.values.include?(lookup_key)
132
- when :IN_SEG
133
- segment_matches?(criteria.values, lookup_key, attributes)
134
- when :NOT_IN_SEG
135
- !segment_matches?(criteria.values, lookup_key, attributes)
136
- when :PROP_IS_ONE_OF
137
- criteria.values.include?(attribute_value(attributes, criteria.property))
138
- when :PROP_IS_NOT_ONE_OF
139
- !criteria.values.include?(attribute_value(attributes, criteria.property))
140
- when :PROP_ENDS_WITH_ONE_OF
141
- criteria.values.any? { |value| attribute_value(attributes, criteria.property)&.end_with?(value) }
142
- when :PROP_DOES_NOT_END_WITH_ONE_OF
143
- criteria.values.none? { |value| attribute_value(attributes, criteria.property)&.end_with?(value) }
144
- else
145
- @base_client.log.info("Unknown Operator: #{criteria.operator}")
146
- false
147
- end
148
- end
149
-
150
- def attribute_value(attributes, property)
151
- attributes[property] || attributes[property.to_sym]
152
- end
153
-
154
- # evaluate each segment key and return whether any match
155
- # there should be an associated segment available as a standard config obj
156
- def segment_matches?(segment_keys, lookup_key, attributes)
157
- segment_keys.any? do |segment_key|
158
- segment = @base_client.config_client.get(segment_key)
159
- if segment.nil?
160
- @base_client.log.info("Missing Segment")
161
- false
162
- else
163
- segment_match?(segment, lookup_key, attributes)
164
- end
165
- end
166
- end
167
-
168
- # does a given segment match?
169
- def segment_match?(segment, lookup_key, attributes)
170
- segment.criterion.any? do |criteria|
171
- criteria_match?(criteria, lookup_key, attributes)
172
- end
173
- end
174
43
  end
175
44
  end
176
-
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Prefab
3
4
  class InternalLogger < Logger
4
5
  def initialize(path, logger)
@@ -6,23 +7,23 @@ module Prefab
6
7
  @logger = logger
7
8
  end
8
9
 
9
- def debug(progname = nil, &block)
10
+ def debug(progname = nil)
10
11
  @logger.log_internal yield, @path, progname, DEBUG
11
12
  end
12
13
 
13
- def info(progname = nil, &block)
14
+ def info(progname = nil)
14
15
  @logger.log_internal yield, @path, progname, INFO
15
16
  end
16
17
 
17
- def warn(progname = nil, &block)
18
+ def warn(progname = nil)
18
19
  @logger.log_internal yield, @path, progname, WARN
19
20
  end
20
21
 
21
- def error(progname = nil, &block)
22
+ def error(progname = nil)
22
23
  @logger.log_internal yield, @path, progname, ERROR
23
24
  end
24
25
 
25
- def fatal(progname = nil, &block)
26
+ def fatal(progname = nil)
26
27
  @logger.log_internal yield, @path, progname, FATAL
27
28
  end
28
29
  end