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 (#16)
Browse files Browse the repository at this point in the history
  • Loading branch information
vpai authored Mar 9, 2024
1 parent cd026c2 commit cd47f76
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 11 deletions.
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@
source 'http://rubygems.org'

gemspec

gem 'semver2', '~> 3.4', '>= 3.4.2'
20 changes: 12 additions & 8 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
eppo-server-sdk (0.2.3)
eppo-server-sdk (0.2.4)
concurrent-ruby (~> 1.1, >= 1.1.9)
faraday (~> 2.7, >= 2.7.1)
faraday-retry (~> 2.0, >= 2.0.0)
Expand All @@ -12,18 +12,20 @@ GEM
addressable (2.8.1)
public_suffix (>= 2.0.2, < 6.0)
ast (2.4.2)
concurrent-ruby (1.1.10)
concurrent-ruby (1.2.3)
crack (0.4.5)
rexml
diff-lcs (1.5.0)
faraday (2.7.2)
faraday-net_http (>= 2.0, < 3.1)
ruby2_keywords (>= 0.0.4)
faraday-net_http (3.0.2)
faraday-retry (2.0.0)
faraday (2.9.0)
faraday-net_http (>= 2.0, < 3.2)
faraday-net_http (3.1.0)
net-http
faraday-retry (2.2.0)
faraday (~> 2.0)
hashdiff (1.0.1)
jaro_winkler (1.5.6)
net-http (0.4.1)
uri
parallel (1.22.1)
parser (3.2.0.0)
ast (~> 2.4.1)
Expand Down Expand Up @@ -53,8 +55,9 @@ GEM
ruby-progressbar (~> 1.7)
unicode-display_width (>= 1.4.0, < 2.0)
ruby-progressbar (1.11.0)
ruby2_keywords (0.0.5)
semver2 (3.4.2)
unicode-display_width (1.8.0)
uri (0.13.0)
webmock (3.18.1)
addressable (>= 2.8.0)
crack (>= 0.3.2)
Expand All @@ -69,6 +72,7 @@ DEPENDENCIES
rake (~> 13.0, >= 13.0.6)
rspec (~> 3.12, >= 3.12.0)
rubocop (~> 0.82.0)
semver2 (~> 3.4, >= 3.4.2)
webmock (~> 3.18, >= 3.18.1)

BUNDLED WITH
Expand Down
36 changes: 34 additions & 2 deletions lib/eppo_client/rules.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# frozen_string_literal: true

require 'semver'

# The helper module for rules
module EppoClient
module OperatorType
Expand Down Expand Up @@ -60,7 +62,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 +89,31 @@ def evaluate_numeric_condition(subject_value, condition)
end
# rubocop:enable Metrics/MethodLength

# rubocop:disable Metrics/MethodLength
def compare_semver(attribute_value, condition_value, operator)
unless valid_semver?(attribute_value) && valid_semver?(condition_value)
return false
end

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

def valid_semver?(string)
!SemVer.parse(string).nil?
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
36 changes: 36 additions & 0 deletions spec/rules_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
rule_with_empty_conditions = EppoClient::Rule.new(allocation_key: 'allocation', conditions: [])

# rubocop:disable Metrics/BlockLength
# rubocop:disable Layout/LineLength
describe EppoClient::Rule do
it 'tests find_matching_rule_when_no_rules_match' do
subject_attributes = { 'age' => 20, 'country' => 'US' }
Expand Down Expand Up @@ -44,6 +45,40 @@
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'
)
semver_less_than_condition = EppoClient::Condition.new(
operator: EppoClient::OperatorType::LTE, value: '2.0.0', attribute: 'version'
)
semver_rule = EppoClient::Rule.new(
allocation_key: 'allocation',
conditions: [semver_less_than_condition, semver_greater_than_condition]
)

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

it 'tests find matching rule for semver string, ensuring it is not interpreted lexographically' do
semver_greater_than_condition = EppoClient::Condition.new(
operator: EppoClient::OperatorType::GTE, value: '1.2.3', attribute: 'version'
)

semver_less_than_condition = EppoClient::Condition.new(
operator: EppoClient::OperatorType::LTE, value: '1.15.0', attribute: 'version'
)

semver_rule = EppoClient::Rule.new(
allocation_key: 'allocation',
conditions: [semver_less_than_condition, semver_greater_than_condition]
)

expect(EppoClient.find_matching_rule({ 'version' => '1.12.0' }, [semver_rule])).to be(semver_rule)
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 Expand Up @@ -138,3 +173,4 @@
end
end
# rubocop:enable Metrics/BlockLength
# rubocop:enable Layout/LineLength

0 comments on commit cd47f76

Please sign in to comment.