diff --git a/lib/splitclient-rb.rb b/lib/splitclient-rb.rb index b2fd8c99..d76f8218 100644 --- a/lib/splitclient-rb.rb +++ b/lib/splitclient-rb.rb @@ -94,6 +94,7 @@ require 'splitclient-rb/engine/matchers/equal_to_semver_matcher' require 'splitclient-rb/engine/matchers/greater_than_or_equal_to_semver_matcher' require 'splitclient-rb/engine/matchers/less_than_or_equal_to_semver_matcher' +require 'splitclient-rb/engine/matchers/between_semver_matcher' require 'splitclient-rb/engine/evaluator/splitter' require 'splitclient-rb/engine/impressions/noop_unique_keys_tracker' require 'splitclient-rb/engine/impressions/unique_keys_tracker' diff --git a/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb b/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb new file mode 100644 index 00000000..a38a2801 --- /dev/null +++ b/lib/splitclient-rb/engine/matchers/between_semver_matcher.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +module SplitIoClient + class BetweenSemverMatcher < Matcher + MATCHER_TYPE = 'BETWEEN_SEMVER' + + attr_reader :attribute + + def initialize(attribute, start_value, end_value, logger, validator) + super(logger) + @validator = validator + @attribute = attribute + @semver_start = SplitIoClient::Semver.build(start_value, logger) + @semver_end = SplitIoClient::Semver.build(end_value, logger) + @logger = logger + end + + def match?(args) + @logger.log_if_debug('[BetweenSemverMatcher] evaluating value and attributes.') + return false unless @validator.valid_matcher_arguments(args) && args[:attributes][@attribute.to_sym].is_a?(String) + + value_to_match = SplitIoClient::Semver.build(args[:attributes][@attribute.to_sym], @logger) + unless !value_to_match.nil? && !@semver_start.nil? && !@semver_end.nil? + @logger.log_if_debug('betweenStringMatcherData is required for BETWEEN_SEMVER matcher type') + return false + end + matches = ([0, -1].include?(@semver_start.compare(value_to_match)) && + [0, 1].include?(@semver_end.compare(value_to_match))) + @logger.log_if_debug("[BetweenMatcher] #{value_to_match} matches -> #{matches}") + matches + end + end +end diff --git a/lib/splitclient-rb/engine/matchers/semver.rb b/lib/splitclient-rb/engine/matchers/semver.rb index ac9b7a58..92d9a6a8 100644 --- a/lib/splitclient-rb/engine/matchers/semver.rb +++ b/lib/splitclient-rb/engine/matchers/semver.rb @@ -8,10 +8,6 @@ class Semver attr_reader :major, :minor, :patch, :pre_release, :is_stable, :old_version - # - # Class Initializer - # - # @param version [String] raw version as read from splitChanges response. def initialize(version) @major = 0 @minor = 0 @@ -22,6 +18,19 @@ def initialize(version) parse end + # + # Class builder + # + # @param version [String] raw version as read from splitChanges response. + # + # @return [type] Semver instance + def self.build(version, logger) + new(version) + rescue RuntimeError => e + logger.warn("Failed to parse Semver data: #{e}") + nil + end + # # Check if there is any metadata characters in self._old_version. # diff --git a/lib/splitclient-rb/engine/parser/condition.rb b/lib/splitclient-rb/engine/parser/condition.rb index cd99225b..cefec1da 100644 --- a/lib/splitclient-rb/engine/parser/condition.rb +++ b/lib/splitclient-rb/engine/parser/condition.rb @@ -213,6 +213,15 @@ def matcher_less_than_or_equal_to_semver(params) ) end + def matcher_between_semver(params) + BetweenSemverMatcher.new( + params[:matcher][:keySelector][:attribute], + params[:matcher][:betweenStringMatcherData][:start], + params[:matcher][:betweenStringMatcherData][:end], + @config.split_logger, @config.split_validator + ) + end + # # @return [object] the negate value for this condition def negate diff --git a/spec/engine/matchers/matches_between_semver_matcher_spec.rb b/spec/engine/matchers/matches_between_semver_matcher_spec.rb new file mode 100644 index 00000000..1fa7d0f8 --- /dev/null +++ b/spec/engine/matchers/matches_between_semver_matcher_spec.rb @@ -0,0 +1,41 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe SplitIoClient::BetweenSemverMatcher do + let(:raw) { { + 'negate': false, + 'matcherType': 'BETWEEN_SEMVER', + 'betweenStringMatcherData': {"start": "2.1.8", "end": "2.1.11"} +} } + let(:config) { SplitIoClient::SplitConfig.new } + + it 'initilized params' do + matcher = described_class.new("version", raw[:betweenStringMatcherData][:start], raw[:betweenStringMatcherData][:end], config.split_logger, config.split_validator) + expect(matcher.attribute).to eq("version") + semver_start = matcher.instance_variable_get(:@semver_start) + expect(semver_start.instance_variable_get(:@old_version)).to eq("2.1.8") + semver_end = matcher.instance_variable_get(:@semver_end) + expect(semver_end.instance_variable_get(:@old_version)).to eq("2.1.11") + end + + it 'matches' do + matcher = described_class.new("version", raw[:betweenStringMatcherData][:start], raw[:betweenStringMatcherData][:end], config.split_logger, config.split_validator) + expect(matcher.match?(:attributes=>{"version": "2.1.8+rc"})).to eq(true) + expect(matcher.match?(:attributes=>{"version": "2.1.9"})).to eq(true) + expect(matcher.match?(:attributes=>{"version": "2.1.11-rc12"})).to eq(true) + end + + it 'does not match' do + matcher = described_class.new("version", raw[:betweenStringMatcherData][:start], raw[:betweenStringMatcherData][:end], config.split_logger, config.split_validator) + expect(matcher.match?(:attributes=>{"version": "2.1.5"})).to eq(false) + expect(matcher.match?(:attributes=>{"version": "2.1.12-rc1"})).to eq(false) + end + + it 'invalid attribute' do + matcher = described_class.new("version", raw[:betweenStringMatcherData][:start], raw[:betweenStringMatcherData][:end], config.split_logger, config.split_validator) + expect(matcher.match?(:attributes=>{"version": 2.1})).to eq(false) + expect(matcher.match?(:attributes=>{"version": nil})).to eq(false) + end + +end