Skip to content
This repository has been archived by the owner on Nov 8, 2024. It is now read-only.

Commit

Permalink
add logic to support semver comparisons
Browse files Browse the repository at this point in the history
  • Loading branch information
vpai committed Mar 6, 2024
1 parent cd026c2 commit 69de5ef
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 3 deletions.
46 changes: 44 additions & 2 deletions lib/eppo_client/rules.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,12 @@ def evaluate_condition(subject_attributes, condition)
when OperatorType::NOT_ONE_OF
!condition.value.map(&:downcase).include?(subject_value.to_s.downcase)
else
subject_value.is_a?(Numeric) && evaluate_numeric_condition(subject_value, condition)
# Numeric operator: value could be numeric or semver.
if subject_value.is_a?(Numeric)
evaluate_numeric_condition(subject_value, condition)
elsif valid_semver?(subject_value)
compare_semver(subject_value, condition.value, condition.operator)
end
end
end
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
Expand All @@ -82,6 +87,43 @@ def evaluate_numeric_condition(subject_value, condition)
end
# rubocop:enable Metrics/MethodLength

# rubocop:disable Metrics/MethodLength
def compare_semver(attribute_value, condition_value, operator)

Check notice

Code scanning / Rubocop

A complexity metric that is strongly correlated to the number of test cases needed to validate a method. Note

Metrics/CyclomaticComplexity: Cyclomatic complexity for compare_semver is too high. [7/6]
return false unless valid_semver?(attribute_value) && valid_semver?(condition_value)

Check notice

Code scanning / Rubocop

Favor modifier if/unless usage when you have a single-line body. Note

Style/IfUnlessModifier: Modifier form of unless makes the line too long.

Check notice

Code scanning / Rubocop

Limit lines to 80 characters. Note

Layout/LineLength: Line is too long. [88/80]

case operator
when OperatorType::GT
Gem::Version.new(attribute_value) > Gem::Version.new(condition_value)
when OperatorType::GTE
Gem::Version.new(attribute_value) >= Gem::Version.new(condition_value)
when OperatorType::LT
Gem::Version.new(attribute_value) < Gem::Version.new(condition_value)
when OperatorType::LTE
Gem::Version.new(attribute_value) <= Gem::Version.new(condition_value)
else
false
end
end
# rubocop:enable Metrics/MethodLength

# The semver format follows MAJOR.MINOR.PATCH, optionally including pre-release

Check notice

Code scanning / Rubocop

Limit lines to 80 characters. Note

Layout/LineLength: Line is too long. [81/80]
# and build metadata.
#
# Valid semver strings examples (method returns `true`):
# - Basic SemVer: "2.0.0"
# - Pre-release Version: "1.0.0-alpha"
# - Pre-release w/ Multiple Identifiers: "1.0.0-alpha.1"
# - Build Metadata: "1.0.0+20130313144700"
# - Pre-release & Build Metadata: "1.0.0-rc.1+build.1"
#
# Invalid examples (method returns `false`):
# - "1.0", "01.0.0", "1.0.0.0", "1.0b.0"
# - "1.0.0 beta", "1.0.0$", "1.0.0+build@", ".1.0", ""
def valid_semver?(string)
semver_regex = /\A\d+\.\d+\.\d+(?:-[0-9A-Za-z-.]+)?(?:\+[0-9A-Za-z-.]+)?\z/
!!string.match(semver_regex)

Check notice

Code scanning / Rubocop

Checks for uses of double negation (!!). Note

Style/DoubleNegation: Avoid the use of double negation (!!).
end

module_function :find_matching_rule, :matches_rule, :evaluate_condition,
:evaluate_numeric_condition
:evaluate_numeric_condition, :valid_semver?, :compare_semver
end
2 changes: 1 addition & 1 deletion lib/eppo_client/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module EppoClient
VERSION = '0.2.3'
VERSION = '0.2.4'
end
14 changes: 14 additions & 0 deletions spec/rules_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,20 @@
expect(EppoClient.find_matching_rule({ 'age' => '99' }, [numeric_rule])).to be_nil
end

it 'tests find matching rule for semver string' do
semver_greater_than_condition = EppoClient::Condition.new(
operator: EppoClient::OperatorType::GTE, value: '1.0.0', attribute: 'version'

Check notice

Code scanning / Rubocop

Limit lines to 80 characters. Note test

Layout/LineLength: Line is too long. [83/80]
)
semver_less_than_condition = EppoClient::Condition.new(
operator: EppoClient::OperatorType::LTE, value: '2.0.0', attribute: 'version'

Check notice

Code scanning / Rubocop

Limit lines to 80 characters. Note test

Layout/LineLength: Line is too long. [83/80]
)
semver_rule = EppoClient::Rule.new(allocation_key: 'allocation', conditions: [semver_less_than_condition, semver_greater_than_condition])

Check notice

Code scanning / Rubocop

Limit lines to 80 characters. Note test

Layout/LineLength: Line is too long. [141/80]

expect(EppoClient.find_matching_rule({ 'version' => '1.1.0' }, [semver_rule])).to be(semver_rule)

Check notice

Code scanning / Rubocop

Limit lines to 80 characters. Note test

Layout/LineLength: Line is too long. [101/80]
expect(EppoClient.find_matching_rule({ 'version' => '2.0.0' }, [semver_rule])).to be(semver_rule)

Check notice

Code scanning / Rubocop

Limit lines to 80 characters. Note test

Layout/LineLength: Line is too long. [101/80]
expect(EppoClient.find_matching_rule({ 'version' => '2.1.0' }, [semver_rule])).to be_nil

Check notice

Code scanning / Rubocop

Limit lines to 80 characters. Note test

Layout/LineLength: Line is too long. [92/80]
end

it 'tests find matching rule with numeric value and regex' do
condition = EppoClient::Condition.new(
operator: EppoClient::OperatorType::MATCHES, value: '[0-9]+', attribute: 'age'
Expand Down

0 comments on commit 69de5ef

Please sign in to comment.