Skip to content

Commit 3b729fc

Browse files
committed
feat: linter
1 parent cbbd3c2 commit 3b729fc

File tree

3 files changed

+95
-89
lines changed

3 files changed

+95
-89
lines changed

lib/flagsmith/engine/evaluation/core.rb

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -43,15 +43,11 @@ def evaluate_segments(evaluation_context)
4343
end
4444

4545
# Returns Record<string: override.name, SegmentOverride>
46-
def process_segment_overrides(identity_segments)
46+
def process_segment_overrides(identity_segments) # rubocop:disable Metrics/MethodLength
4747
segment_overrides = {}
4848

4949
identity_segments.each do |segment|
50-
next unless segment[:overrides]
51-
52-
overrides_list = segment[:overrides].is_a?(Array) ? segment[:overrides] : []
53-
54-
overrides_list.each do |override|
50+
Array(segment[:overrides]).each do |override|
5551
next unless should_apply_override(override, segment_overrides)
5652

5753
segment_overrides[override[:name]] = {
@@ -66,33 +62,15 @@ def process_segment_overrides(identity_segments)
6662

6763
# returns EvaluationResultFlags<Metadata>
6864
def evaluate_features(evaluation_context, segment_overrides)
69-
flags = {}
65+
identity_key = get_identity_key(evaluation_context)
7066

71-
(evaluation_context[:features] || {}).each_value do |feature|
67+
(evaluation_context[:features] || {}).each_with_object({}) do |(_, feature), flags|
7268
segment_override = segment_overrides[feature[:name]]
7369
final_feature = segment_override ? segment_override[:feature] : feature
74-
has_override = !segment_override.nil?
75-
76-
# Evaluate feature value
77-
evaluated = evaluate_feature_value(final_feature, get_identity_key(evaluation_context))
78-
79-
# Build flag result
80-
flag_result = {
81-
name: final_feature[:name],
82-
enabled: final_feature[:enabled],
83-
value: evaluated[:value]
84-
}
85-
86-
# Add metadata if present
87-
flag_result[:metadata] = final_feature[:metadata] if final_feature[:metadata]
8870

89-
# Set reason
90-
flag_result[:reason] = evaluated[:reason] ||
91-
(has_override ? "#{TARGETING_REASON_TARGETING_MATCH}; segment=#{segment_override[:segment_name]}" : TARGETING_REASON_DEFAULT)
71+
flag_result = build_flag_result(final_feature, identity_key, segment_override)
9272
flags[final_feature[:name].to_sym] = flag_result
9373
end
94-
95-
flags
9674
end
9775

9876
# Returns {value: any; reason?: string}
@@ -107,19 +85,19 @@ def get_multivariate_feature_value(feature, identity_key)
10785
percentage_value = hashed_percentage_for_object_ids([feature[:key], identity_key])
10886
sorted_variants = (feature[:variants] || []).sort_by { |v| v[:priority] || WEAKEST_PRIORITY }
10987

88+
variant = find_matching_variant(sorted_variants, percentage_value)
89+
variant || { value: feature[:value], reason: nil }
90+
end
91+
92+
def find_matching_variant(sorted_variants, percentage_value)
11093
start_percentage = 0
11194
sorted_variants.each do |variant|
11295
limit = start_percentage + variant[:weight]
113-
if start_percentage <= percentage_value && percentage_value < limit
114-
return {
115-
value: variant[:value],
116-
reason: "#{TARGETING_REASON_SPLIT}; weight=#{variant[:weight]}"
117-
}
118-
end
96+
return { value: variant[:value], reason: "#{TARGETING_REASON_SPLIT}; weight=#{variant[:weight]}" } if start_percentage <= percentage_value && percentage_value < limit
97+
11998
start_percentage = limit
12099
end
121-
122-
{ value: feature[:value], reason: nil }
100+
nil
123101
end
124102

125103
# returns boolean
@@ -130,6 +108,20 @@ def should_apply_override(override, existing_overrides)
130108

131109
private
132110

111+
def build_flag_result(feature, identity_key, segment_override)
112+
evaluated = evaluate_feature_value(feature, identity_key)
113+
114+
flag_result = {
115+
name: feature[:name],
116+
enabled: feature[:enabled],
117+
value: evaluated[:value],
118+
reason: evaluated[:reason] || (segment_override ? "#{TARGETING_REASON_TARGETING_MATCH}; segment=#{segment_override[:segment_name]}" : TARGETING_REASON_DEFAULT)
119+
}
120+
121+
flag_result[:metadata] = feature[:metadata] if feature[:metadata]
122+
flag_result
123+
end
124+
133125
# Extract identity key from evaluation context
134126
#
135127
# @param evaluation_context [Hash] The evaluation context

lib/flagsmith/engine/segments/evaluator.rb

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ module Flagsmith
1010
module Engine
1111
module Segments
1212
# Evaluator methods
13-
module Evaluator
13+
module Evaluator # rubocop:disable Metrics/ModuleLength
1414
include Flagsmith::Engine::Segments::Constants
1515
include Flagsmith::Engine::Utils::HashFunc
1616

@@ -144,30 +144,30 @@ def evaluate_sub_rules_from_context(rule, segment_key, context)
144144
# @param context [Hash] The evaluation context
145145
# @return [Boolean] True if the condition matches
146146
def traits_match_segment_condition_from_context(condition, segment_key, context)
147-
if condition[:operator] == PERCENTAGE_SPLIT
148-
context_value_key = get_context_value(condition[:property], context) || get_identity_key_from_context(context)
149-
hashed_percentage = hashed_percentage_for_object_ids([segment_key, context_value_key])
150-
return hashed_percentage <= condition[:value].to_f
151-
end
152-
147+
return handle_percentage_split(condition, segment_key, context) if condition[:operator] == PERCENTAGE_SPLIT
153148
return false if condition[:property].nil?
154149

155150
trait_value = get_trait_value(condition[:property], context)
151+
evaluate_trait_condition(condition, trait_value)
152+
end
153+
154+
def handle_percentage_split(condition, segment_key, context)
155+
context_value_key = get_context_value(condition[:property], context) || get_identity_key_from_context(context)
156+
hashed_percentage = hashed_percentage_for_object_ids([segment_key, context_value_key])
157+
hashed_percentage <= condition[:value].to_f
158+
end
156159

160+
def evaluate_trait_condition(condition, trait_value)
157161
return !trait_value.nil? if condition[:operator] == IS_SET
158162
return trait_value.nil? if condition[:operator] == IS_NOT_SET
159-
160-
unless trait_value.nil?
161-
# Reuse existing Condition class logic
162-
condition_obj = Flagsmith::Engine::Segments::Condition.new(
163-
operator: condition[:operator],
164-
value: condition[:value],
165-
property: condition[:property]
166-
)
167-
return condition_obj.match_trait_value?(trait_value)
168-
end
169-
170-
false
163+
return false if trait_value.nil?
164+
165+
condition_obj = Flagsmith::Engine::Segments::Condition.new(
166+
operator: condition[:operator],
167+
value: condition[:value],
168+
property: condition[:property]
169+
)
170+
condition_obj.match_trait_value?(trait_value)
171171
end
172172

173173
# Evaluate rule conditions based on type (ALL/ANY/NONE)

lib/flagsmith/engine/segments/models.rb

Lines changed: 49 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -54,41 +54,48 @@ def initialize(operator:, value:, property: nil)
5454
@property = property
5555
end
5656

57-
def match_trait_value?(trait_value) # rubocop:disable Metrics/MethodLength
58-
if @value.is_a?(String) && @value.match?(/:semver$/)
59-
begin
60-
trait_value = Semantic::Version.new(trait_value.to_s.gsub(/:semver$/, ''))
61-
rescue ArgumentError, Semantic::Version::ValidationFailed => _e
62-
return false
63-
end
64-
end
57+
def match_trait_value?(trait_value)
58+
trait_value = parse_semver_trait_value(trait_value)
59+
return false if trait_value.nil?
6560

6661
return match_in_value(trait_value) if @operator == IN
6762
return match_modulo_value(trait_value) if @operator == MODULO
6863
return MATCHING_FUNCTIONS[REGEX]&.call(trait_value, @value) if @operator == REGEX
6964

65+
match_with_type_conversion(trait_value)
66+
end
67+
68+
def parse_semver_trait_value(trait_value)
69+
return trait_value unless @value.is_a?(String) && @value.match?(/:semver$/)
70+
71+
Semantic::Version.new(trait_value.to_s.gsub(/:semver$/, ''))
72+
rescue ArgumentError, Semantic::Version::ValidationFailed
73+
nil
74+
end
75+
76+
def match_with_type_conversion(trait_value)
7077
type_as_trait_value = format_to_type_of(trait_value)
7178
formatted_value = type_as_trait_value ? type_as_trait_value.call(@value) : @value
72-
7379
MATCHING_FUNCTIONS[operator]&.call(trait_value, formatted_value)
7480
end
7581

76-
def format_to_type_of(input) # rubocop:disable Metrics/AbcSize
77-
{
78-
'String' => ->(v) { v.to_s },
79-
'Semantic::Version' => ->(v) { Semantic::Version.new(v.to_s.gsub(/:semver$/, '')) },
80-
# Double check this is the desired behavior between SDKs
81-
'TrueClass' => ->(v) { ['True', 'true', 'TRUE', true, 1, '1'].include?(v) },
82-
'FalseClass' => ->(v) { !['False', 'false', 'FALSE', false].include?(v) },
83-
'Integer' => lambda { |v|
84-
i = v.to_i
85-
i.to_s == v.to_s ? i : v
86-
},
87-
'Float' => lambda { |v|
88-
f = v.to_f
89-
f.to_s == v.to_s ? f : v
90-
}
91-
}[input.class.to_s]
82+
TYPE_CONVERTERS = {
83+
'String' => ->(v) { v.to_s },
84+
'Semantic::Version' => ->(v) { Semantic::Version.new(v.to_s.gsub(/:semver$/, '')) },
85+
'TrueClass' => ->(v) { ['True', 'true', 'TRUE', true, 1, '1'].include?(v) },
86+
'FalseClass' => ->(v) { !['False', 'false', 'FALSE', false].include?(v) },
87+
'Integer' => lambda { |v|
88+
i = v.to_i
89+
i.to_s == v.to_s ? i : v
90+
},
91+
'Float' => lambda { |v|
92+
f = v.to_f
93+
f.to_s == v.to_s ? f : v
94+
}
95+
}.freeze
96+
97+
def format_to_type_of(input)
98+
TYPE_CONVERTERS[input.class.to_s]
9299
end
93100

94101
def match_modulo_value(trait_value)
@@ -98,18 +105,25 @@ def match_modulo_value(trait_value)
98105
false
99106
end
100107

101-
def match_in_value(trait_value) # rubocop:disable Metrics/AbcSize
102-
return false if trait_value.nil? || trait_value.is_a?(TrueClass) || trait_value.is_a?(FalseClass) || ([true, false].include? trait_value)
103-
108+
def match_in_value(trait_value)
109+
return false if invalid_in_value?(trait_value)
104110
return @value.include?(trait_value.to_s) if @value.is_a?(Array)
105111

106-
if @value.is_a?(String)
107-
begin
108-
parsed = JSON.parse(@value)
109-
return parsed.include?(trait_value.to_s) if parsed.is_a?(Array)
110-
rescue JSON::ParserError
111-
end
112-
end
112+
parse_and_match_string_value(trait_value)
113+
end
114+
115+
def invalid_in_value?(trait_value)
116+
trait_value.nil? || [TrueClass, FalseClass].any? { |klass| trait_value.is_a?(klass) }
117+
end
118+
119+
def parse_and_match_string_value(trait_value) # rubocop:disable Metrics/AbcSize
120+
return @value.to_s.split(',').include?(trait_value.to_s) unless @value.is_a?(String)
121+
122+
parsed = JSON.parse(@value)
123+
return parsed.include?(trait_value.to_s) if parsed.is_a?(Array)
124+
125+
@value.to_s.split(',').include?(trait_value.to_s)
126+
rescue JSON::ParserError
113127
@value.to_s.split(',').include?(trait_value.to_s)
114128
end
115129

0 commit comments

Comments
 (0)