Skip to content

Conversation

@Zaimwa9
Copy link
Contributor

@Zaimwa9 Zaimwa9 commented Oct 27, 2025

No description provided.

@Zaimwa9 Zaimwa9 requested a review from a team as a code owner October 27, 2025 14:36
@Zaimwa9 Zaimwa9 requested review from khvn26 and removed request for a team October 27, 2025 14:36
@Zaimwa9 Zaimwa9 changed the base branch from main to feat/evaluation-context-mappers October 27, 2025 14:36
@Zaimwa9 Zaimwa9 marked this pull request as draft October 27, 2025 14:36
Comment on lines 84 to 91
'Integer' => ->(v) {
i = v.to_i;
i.to_s == v.to_s ? i : v
},
'Float' => ->(v) {
f = v.to_f;
f.to_s == v.to_s ? f : v
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implementation is problematic and was fixed in incoming PR.
This would end up coerciting 123abc to 123abc 🔴

Comment on lines 215 to 222
# Simple JSONPath implementation - handle basic cases
path_parts = json_path.sub('$.', '').split('.')
current = context

path_parts.each do |part|
return nil unless current.is_a?(Hash)

current = current[part.to_sym]
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Proper JSON path implementation done in following PR

@Zaimwa9 Zaimwa9 marked this pull request as ready for review October 29, 2025 09:41
@Zaimwa9 Zaimwa9 changed the title feat: get evaluation get result [draft] feat: get evaluation get result Oct 29, 2025
Copy link
Member

@khvn26 khvn26 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Leaving some comments as a first pass.

I'd really like to see which engine tests fail for this PR — if it's something to better pursue locally rather than in CI, let me know, @Zaimwa9.


# Returns { segments: EvaluationResultSegments; segmentOverrides: Record<string, SegmentOverride>; }
def evaluate_segments(evaluation_context)
return [], {} if evaluation_context[:identity].nil? || evaluation_context[:segments].nil?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm insisting that if evaluation_context[:identity].nil? part should happen in the mapper layer. We're not supposed to prevent identity-less evaluation in the engine, we're maintaining compatibility with /v1/flags for the get_environment_flags SDK interface.

#
# @param context [Hash] Evaluation context containing identity and segment definitions
# @return [Array<Hash>] Array of segments that the identity matches
def get_identity_segments_from_context(context)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: For complete correctness:

Suggested change
def get_identity_segments_from_context(context)
def get_segments_from_context(context)

As stated previously, we're not shutting the door for identity-less contexts.

name: segment[:name]
}

result[:metadata] = segment[:metadata].dup if segment[:metadata]
Copy link
Member

@khvn26 khvn26 Oct 30, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we forced to use dup here? Does a simple result[:metadata] = segment[:metadata] imply clone?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I’ve read, assignment does not imply a clone — it just copies the reference to the same object. Since this struct is created fresh for every request, I think we can remove the dup here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

Comment on lines +83 to +87
evaluated = if has_override
{ value: final_feature[:value], reason: nil }
else
evaluate_feature_value(final_feature, get_identity_key(evaluation_context))
end
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This probably reproduces the bug existing in the reference implementation, covered by test_multivariate__segment_override__expected_allocation.

Can we add tests to CI so we don't have to guess?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's not part of this sprint?

Copy link
Member

@khvn26 khvn26 Nov 5, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you're confusing with Flagsmith/flagsmith-python-client#172 (and other similar issues in Flagsmith/flagsmith#5977)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it is — I don't think we should ship with that bug. The test covering it is part of engine-test-data anyway.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As far as I remember, I raised this during the standup to fix it in Rust, but we decided not to include it in this sprint. Is this bug present in all the SDKs?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We’re not releasing without the fix, but it can be included in a separate pull request. As far as I remember, that was the decision — please correct me if I’m mistaken.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed offline, there's no reason not to inlclude the fix in this PR.


# Set reason
flag_result[:reason] = evaluated[:reason] ||
get_targeting_match_reason({ type: 'SEGMENT', override: segment_override })
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure why we need this additional level of misdirection with match_object and get_targeting_match_reason utility, when evaluate_feature_value and get_multivariate_feature_value could've simply set the reason then and there? Am I missing something?


# returns boolean
def higher_priority?(priority_a, priority_b)
(priority_a || Float::INFINITY) < (priority_b || Float::INFINITY)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Maybe have a WEAKEST_PRIORITY constant set to Float::INFINITY.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see it addressed in #89; feel free to resolve or integrate the changes here.

end

# returns boolean
def higher_priority?(priority_a, priority_b)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
def higher_priority?(priority_a, priority_b)
def stronger_priority?(priority_a, priority_b)

Or maybe even

Suggested change
def higher_priority?(priority_a, priority_b)
def is_stronger_priority?(priority_a, priority_b)

Comment on lines +233 to +236
def non_primitive?(value)
return false if value.nil?

value.is_a?(Hash) || value.is_a?(Array)
Copy link
Member

@khvn26 khvn26 Nov 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems this is used in a negative boolean expression. For readability's sake, I'd prefer to have a is_primitive? method instead.

# @return [Boolean] True if the condition matches
def traits_match_segment_condition_from_context(condition, segment_key, context)
if condition[:operator] == PERCENTAGE_SPLIT
context_value_key = get_context_value(condition[:property], context) || get_identity_key_from_context(context)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe optimise this by memorising the key in the context itself, like Python engine does.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants