Skip to content

Commit 80c207f

Browse files
committed
updated matcher and evaluator
1 parent 4bc3716 commit 80c207f

6 files changed

Lines changed: 144 additions & 19 deletions

File tree

lib/splitclient-rb/engine/matchers/rule_based_segment_matcher.rb

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,11 @@ module SplitIoClient
77
class RuleBasedSegmentMatcher < Matcher
88
MATCHER_TYPE = 'IN_RULE_BASED_SEGMENT'
99

10-
def initialize(rule_based_segments_repository, segments_repository, segment_name, config, evaluator)
10+
def initialize(segments_repository, rule_based_segments_repository, segment_name, config)
1111
super(config.logger)
1212
@rule_based_segments_repository = rule_based_segments_repository
1313
@segments_repository = segments_repository
1414
@segment_name = segment_name
15-
@evaluator = evaluator
1615
@config = config
1716
end
1817

@@ -28,18 +27,40 @@ def match?(args)
2827

2928
return false if rule_based_segment[:excluded][:keys].include?([args[:value]])
3029

31-
return false if @segments_repository.contains?(rule_based_segment[:excluded][:segments])
30+
rule_based_segment[:excluded][:segments].each do |segment|
31+
return false if segment[:type] == 'standard' and @segments_repository.in_segment?(segment[:name], args[:value])
32+
33+
if segment[:type] == 'rule-based'
34+
return true if match_rbs(@rule_based_segments_repository.get_rule_based_segment(segment[:name])[:conditions], args)
35+
end
36+
end
3237

3338
matches = false
3439
rule_based_segment[:conditions].each do |c|
3540
condition = SplitIoClient::Condition.new(c, @config)
3641
next if condition.empty?
3742

38-
matches = Helpers::EvaluatorHelper.matcher_type(condition, @segments_repository).match?(args)
43+
matches = Helpers::EvaluatorHelper.matcher_type(condition, @segments_repository, @rule_based_segments_repository).match?(args)
3944
end
4045
@logger.debug("[InRuleSegmentMatcher] #{@segment_name} is in rule based segment -> #{matches}")
4146

4247
matches
4348
end
49+
50+
private
51+
52+
def match_rbs(conditions, args)
53+
conditions.each do |condition|
54+
next if condition.empty?
55+
56+
return true if Helpers::EvaluatorHelper::matcher_type(SplitIoClient::Condition.new(condition, @config),
57+
@segments_repository, @rule_based_segments_repository).match?(
58+
matching_key: args[:matching_key],
59+
bucketing_key: args[:value],
60+
attributes: args[:attributes]
61+
)
62+
end
63+
return false
64+
end
4465
end
4566
end

lib/splitclient-rb/engine/parser/condition.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,13 @@ def matcher_in_list_semver(params)
230230
)
231231
end
232232

233+
def matcher_in_rule_based_segment(params)
234+
matcher = params[:matcher]
235+
segment_name = matcher[:userDefinedSegmentMatcherData] && matcher[:userDefinedSegmentMatcherData][:segmentName]
236+
237+
RuleBasedSegmentMatcher.new(params[:segments_repository], params[:rule_based_segments_repository], segment_name, @config)
238+
end
239+
233240
#
234241
# @return [object] the negate value for this condition
235242
def negate

lib/splitclient-rb/engine/parser/evaluator.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module SplitIoClient
22
module Engine
33
module Parser
44
class Evaluator
5-
def initialize(segments_repository, splits_repository, config)
5+
def initialize(segments_repository, splits_repository, rb_segment_repository, config)
66
@splits_repository = splits_repository
77
@segments_repository = segments_repository
88
@config = config
@@ -59,7 +59,7 @@ def match(split, keys, attributes)
5959
in_rollout = true
6060
end
6161

62-
condition_matched = Helpers::EvaluatorHelper::matcher_type(condition, @segments_repository).match?(
62+
condition_matched = Helpers::EvaluatorHelper::matcher_type(condition, @segments_repository, @rb_segment_repository).match?(
6363
matching_key: keys[:matching_key],
6464
bucketing_key: keys[:bucketing_key],
6565
evaluator: self,

lib/splitclient-rb/helpers/evaluator_helper.rb

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@
33
module SplitIoClient
44
module Helpers
55
class EvaluatorHelper
6-
def self.matcher_type(condition, segments_repository)
6+
def self.matcher_type(condition, segments_repository, rb_segment_repository)
77
matchers = []
8-
98
segments_repository.adapter.pipelined do
109
condition.matchers.each do |matcher|
1110
matchers << if matcher[:negate]
1211
condition.negation_matcher(matcher_instance(matcher[:matcherType], condition, matcher))
1312
else
14-
matcher_instance(matcher[:matcherType], condition, matcher, segments_repository)
13+
matcher_instance(matcher[:matcherType], condition, matcher, segments_repository, rb_segment_repository)
1514
end
1615
end
1716
end
@@ -25,10 +24,10 @@ def self.matcher_type(condition, segments_repository)
2524
final_matcher
2625
end
2726

28-
def self.matcher_instance(type, condition, matcher, segments_repository)
27+
def self.matcher_instance(type, condition, matcher, segments_repository, rb_segment_repository)
2928
condition.send(
3029
"matcher_#{type.downcase}",
31-
matcher: matcher, segments_repository: segments_repository
30+
matcher: matcher, segments_repository: segments_repository, rule_based_segments_repository: rb_segment_repository
3231
)
3332
end
3433
end

spec/engine/matchers/rule_based_segment_matcher_spec.rb

Lines changed: 104 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,31 +8,62 @@
88
let(:flag_sets_repository) {SplitIoClient::Cache::Repositories::MemoryFlagSetsRepository.new([])}
99
let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new([])}
1010
let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(config, flag_sets_repository, flag_set_filter) }
11-
let(:evaluator) { SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, true) }
12-
1311

1412
context '#string_type' do
1513
it 'is not string type matcher' do
16-
expect(described_class.new(nil, nil, nil, config, nil).string_type?).to be false
14+
expect(described_class.new(nil, nil, nil, config).string_type?).to be false
1715
end
1816
end
1917

2018
context 'test_matcher' do
2119
it 'return false if excluded key is passed' do
2220
rbs_repositoy = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config)
2321
rbs_repositoy.update([{name: 'foo', trafficTypeName: 'tt_name_1', conditions: [], excluded: {keys: ['key1'], segments: []}}], [], -1)
24-
matcher = described_class.new(rbs_repositoy, segments_repository, 'foo', config, nil)
22+
matcher = described_class.new(segments_repository, rbs_repositoy, 'foo', config)
2523
expect(matcher.match?(value: 'key1')).to be false
2624
end
2725

2826
it 'return false if excluded segment is passed' do
2927
rbs_repositoy = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config)
28+
evaluator = SplitIoClient::Engine::Parser::Evaluator.new(segments_repository, splits_repository, rbs_repositoy, true)
3029
segments_repository.add_to_segment({:name => 'segment1', :added => [], :removed => []})
31-
rbs_repositoy.update([{name: 'foo', trafficTypeName: 'tt_name_1', conditions: [], excluded: {keys: ['key1'], segments: ['segment1']}}], [], -1)
32-
matcher = described_class.new(rbs_repositoy, segments_repository, 'foo', config, nil)
30+
rbs_repositoy.update([{:name => 'foo', :trafficTypeName => 'tt_name_1', :conditions => [], :excluded => {:keys => ['key1'], :segments => [{:name => 'segment1', :type => 'standard'}]}}], [], -1)
31+
matcher = described_class.new(segments_repository, rbs_repositoy, 'foo', config)
3332
expect(matcher.match?(value: 'key2')).to be false
3433
end
3534

35+
it 'return true if excluded rb segment is matched' do
36+
rbs_repositoy = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config)
37+
rbs = {:name => 'sample_rule_based_segment', :trafficTypeName => 'tt_name_1', :conditions => [], :excluded => {:keys => [], :segments => [{:name => 'no_excludes', :type => 'rule-based'}]}}
38+
rbs2 = {:name => 'no_excludes', :trafficTypeName => 'tt_name_1',
39+
:conditions => [{
40+
:matcherGroup => {
41+
:combiner => "AND",
42+
:matchers => [
43+
{
44+
:keySelector => {
45+
:trafficType => "user",
46+
:attribute => "email"
47+
},
48+
:matcherType => "ENDS_WITH",
49+
:negate => false,
50+
:whitelistMatcherData => {
51+
:whitelist => [
52+
"@split.io"
53+
]
54+
}
55+
}
56+
]
57+
}
58+
}
59+
], :excluded => {:keys => [], :segments => []}}
60+
61+
rbs_repositoy.update([rbs, rbs2], [], -1)
62+
matcher = described_class.new(segments_repository, rbs_repositoy, 'sample_rule_based_segment', config)
63+
expect(matcher.match?(value: 'bilal@split.io', attributes: {'email': 'bilal@split.io'})).to be true
64+
expect(matcher.match?(value: 'bilal', attributes: {'email': 'bilal'})).to be false
65+
end
66+
3667
it 'return true if condition matches' do
3768
rule_based_segment = { :name => 'corge', :trafficTypeName => 'tt_name_5',
3869
:excluded => {:keys => [], :segments => []},
@@ -58,9 +89,75 @@
5889

5990
rbs_repositoy = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config)
6091
rbs_repositoy.update([rule_based_segment], [], -1)
61-
matcher = described_class.new(rbs_repositoy, segments_repository, 'corge', config, evaluator)
92+
matcher = described_class.new(segments_repository, rbs_repositoy, 'corge', config)
6293
expect(matcher.match?({:matching_key => 'user', :attributes => {}})).to be false
6394
expect(matcher.match?({:matching_key => 'k1', :attributes => {}})).to be true
6495
end
96+
97+
it 'return true if dependent rb segment matches' do
98+
rbs_repositoy = SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(config)
99+
rbs = {
100+
:changeNumber => 5,
101+
:name => "dependent_rbs",
102+
:status => "ACTIVE",
103+
:trafficTypeName => "user",
104+
:excluded =>{:keys =>["mauro@split.io","gaston@split.io"],:segments =>[]},
105+
:conditions => [
106+
{
107+
:matcherGroup => {
108+
:combiner => "AND",
109+
:matchers => [
110+
{
111+
:keySelector => {
112+
:trafficType => "user",
113+
:attribute => "email"
114+
},
115+
:matcherType => "ENDS_WITH",
116+
:negate => false,
117+
:whitelistMatcherData => {
118+
:whitelist => [
119+
"@split.io"
120+
]
121+
}
122+
}
123+
]
124+
}
125+
}
126+
]}
127+
rbs2 = {
128+
:changeNumber => 5,
129+
:name => "sample_rule_based_segment",
130+
:status => "ACTIVE",
131+
:trafficTypeName => "user",
132+
:excluded => {
133+
:keys => [],
134+
:segments => []
135+
},
136+
:conditions => [
137+
{
138+
:conditionType => "ROLLOUT",
139+
:matcherGroup => {
140+
:combiner => "AND",
141+
:matchers => [
142+
{
143+
:keySelector => {
144+
:trafficType => "user"
145+
},
146+
:matcherType => "IN_RULE_BASED_SEGMENT",
147+
:negate => false,
148+
:userDefinedSegmentMatcherData => {
149+
:segmentName => "dependent_rbs"
150+
}
151+
}
152+
]
153+
}
154+
}
155+
]
156+
}
157+
rbs_repositoy.update([rbs, rbs2], [], -1)
158+
matcher = described_class.new(segments_repository, rbs_repositoy, 'sample_rule_based_segment', config)
159+
expect(matcher.match?(value: 'bilal@split.io', attributes: {'email': 'bilal@split.io'})).to be true
160+
expect(matcher.match?(value: 'bilal', attributes: {'email': 'bilal'})).to be false
161+
end
65162
end
66163
end

spec/engine/parser/evaluator_spec.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44

55
describe SplitIoClient::Engine::Parser::Evaluator do
66
let(:segments_repository) { SplitIoClient::Cache::Repositories::SegmentsRepository.new(@default_config) }
7+
let(:rule_based_segments_repository) { SplitIoClient::Cache::Repositories::RuleBasedSegmentsRepository.new(@default_config) }
78
let(:flag_sets_repository) {SplitIoClient::Cache::Repositories::MemoryFlagSetsRepository.new([])}
89
let(:flag_set_filter) {SplitIoClient::Cache::Filter::FlagSetsFilter.new([])}
910
let(:splits_repository) { SplitIoClient::Cache::Repositories::SplitsRepository.new(@default_config, flag_sets_repository, flag_set_filter) }
10-
let(:evaluator) { described_class.new(segments_repository, splits_repository, true) }
11+
let(:evaluator) { described_class.new(segments_repository, splits_repository, rule_based_segments_repository, true) }
1112

1213
let(:killed_split) { { killed: true, defaultTreatment: 'default' } }
1314
let(:archived_split) { { status: 'ARCHIVED' } }

0 commit comments

Comments
 (0)