splitclient-rb 4.2.2 → 4.3.0.canary.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.txt +9 -0
  3. data/Detailed-README.md +21 -0
  4. data/NEWS +5 -0
  5. data/exe/splitio +6 -2
  6. data/lib/splitclient-rb.rb +3 -0
  7. data/lib/splitclient-rb/cache/adapters/memory_adapters/map_adapter.rb +4 -0
  8. data/lib/splitclient-rb/cache/adapters/memory_adapters/queue_adapter.rb +5 -0
  9. data/lib/splitclient-rb/cache/adapters/redis_adapter.rb +6 -0
  10. data/lib/splitclient-rb/cache/repositories/metrics/memory_repository.rb +6 -0
  11. data/lib/splitclient-rb/cache/repositories/metrics/redis_repository.rb +6 -0
  12. data/lib/splitclient-rb/cache/repositories/metrics_repository.rb +1 -1
  13. data/lib/splitclient-rb/cache/repositories/repository.rb +1 -1
  14. data/lib/splitclient-rb/cache/repositories/segments_repository.rb +4 -0
  15. data/lib/splitclient-rb/cache/repositories/splits_repository.rb +4 -0
  16. data/lib/splitclient-rb/cache/routers/impression_router.rb +52 -0
  17. data/lib/splitclient-rb/cache/senders/impressions_sender.rb +10 -4
  18. data/lib/splitclient-rb/cache/senders/metrics_sender.rb +1 -1
  19. data/lib/splitclient-rb/cache/stores/segment_store.rb +1 -1
  20. data/lib/splitclient-rb/cache/stores/split_store.rb +1 -1
  21. data/lib/splitclient-rb/clients/split_client.rb +55 -8
  22. data/lib/splitclient-rb/engine/matchers/attribute_matcher.rb +20 -0
  23. data/lib/splitclient-rb/engine/matchers/between_matcher.rb +3 -3
  24. data/lib/splitclient-rb/engine/matchers/contains_matcher.rb +1 -3
  25. data/lib/splitclient-rb/engine/matchers/ends_with_matcher.rb +1 -3
  26. data/lib/splitclient-rb/engine/matchers/equal_to_boolean_matcher.rb +1 -3
  27. data/lib/splitclient-rb/engine/matchers/equal_to_matcher.rb +3 -3
  28. data/lib/splitclient-rb/engine/matchers/greater_than_or_equal_to_matcher.rb +3 -3
  29. data/lib/splitclient-rb/engine/matchers/less_than_or_equal_to_matcher.rb +3 -3
  30. data/lib/splitclient-rb/engine/matchers/matches_string_matcher.rb +1 -3
  31. data/lib/splitclient-rb/engine/matchers/starts_with_matcher.rb +1 -3
  32. data/lib/splitclient-rb/engine/matchers/whitelist_matcher.rb +5 -8
  33. data/lib/splitclient-rb/engine/parser/evaluator.rb +9 -1
  34. data/lib/splitclient-rb/exceptions/impressions_shutdown_exception.rb +4 -0
  35. data/lib/splitclient-rb/split_config.rb +15 -1
  36. data/lib/splitclient-rb/version.rb +1 -1
  37. metadata +7 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 364fd2e02b4eb154e24b7495b828fe90de57281f
4
- data.tar.gz: 188dcf371f6b9c88916f56283b38cba859d8062c
3
+ metadata.gz: 7aba740fb6e199e7e82a1fc59c2af161010a3028
4
+ data.tar.gz: 85b1a7476a4ed1739e9b9de1c10ebb3f641eb6ad
5
5
  SHA512:
6
- metadata.gz: 75f8301c25574564e35414edd40c0e8ebe07d4a433c2e39d804d97aef27e1cd3b503badeb26718d2d31d5e8c342fbf96581721a356ce5c79c20bc1ef2c54ec1d
7
- data.tar.gz: d5ed059d54883256b1d2dc33257cd6a9fc88ce8f5ee5d3e1354b8c0d175b2b47f52e98dc2c24a1f85e1aa34c10ad5f41ccc0ba694ca5c61e849277bc62e5d71b
6
+ metadata.gz: 9367028653a703dc2ae91d8dd87f6eefd1ada85c523e2fc09c9d00091ba7412a6a6449b5a5d23bdb9ea1464f16ec1f9f5d3410686c4c7ff09eb177002e81f886
7
+ data.tar.gz: 1816f164bd047b30ec7825ef8bcc5619d8ce975ef79e03622fe611fb42c06810227e8fc98df3023c421c1cf666e029cc5a185a6722d187bab3e1d4c2553154a0
@@ -1,3 +1,12 @@
1
+ 4.3.0 (Sept 14th, 2017)
2
+ - Add impression listener
3
+ - Add support for client shutdown (destroy())
4
+ - Add "time" to the routed impressions
5
+ - Add support to apply attribute matcher to the traffic type
6
+
7
+ 4.2.3 (August 4, 2017)
8
+ - Use ENV vars in producer
9
+
1
10
  4.2.2 (July 28, 2017)
2
11
  - Fix treatments array in SplitManager
3
12
 
@@ -364,6 +364,27 @@ debug_enabled: true, # used for more verbose logging, including more debug infor
364
364
  transport_debug_enabled: true # used for log transport data (mostly http requests, false is the default)
365
365
  ```
366
366
 
367
+ ### Impression Listener
368
+
369
+ In order to capture every single impression in your app SDK provides option called Impression Listener. It works pretty straightforward: you define a class which must have instance method called `log`, which must receive 1 argument `impression`. Let's say you have the following impression listener class:
370
+
371
+ ```ruby
372
+ class MyImpressionListener
373
+ def log(impression)
374
+ Logger.new($stdout).info(impression)
375
+ end
376
+ end
377
+ ```
378
+
379
+ Nothing fancy here, it just takes impression and logs it to the stdout. Now, to actually use this class you'll need to specify it in your config (i.e. initializer) like this:
380
+
381
+ ```ruby
382
+ {
383
+ # other options
384
+ impression_listener: MyImpressionListener.new # do remember to initialize your class here
385
+ }
386
+ ```
387
+
367
388
  ### SDK Modes
368
389
 
369
390
  By default SDK would run alongside with your application and will be run in `standalone` mode, which includes two modes:
data/NEWS CHANGED
@@ -1,3 +1,8 @@
1
+ 4.3.0
2
+
3
+ Add support for impression listener, there is an ability to use user-defined class to capture all impressions
4
+ Now you can apply attribute matcher to the traffic type
5
+
1
6
  4.2.0
2
7
 
3
8
  Introduce new matchers: boolean and regexp (string).
@@ -7,7 +7,7 @@ require 'optparse'
7
7
  require 'yaml'
8
8
  require_relative '../lib/splitclient-rb'
9
9
 
10
- ARGV << '-h' if ARGV.empty?
10
+ # ARGV << '-h' if ARGV.empty?
11
11
 
12
12
  config_path = ''
13
13
  options = {}
@@ -84,9 +84,13 @@ begin
84
84
  exit(1)
85
85
  end
86
86
 
87
- config = YAML.load_file(config_path)
87
+ config = config_path != '' ? YAML.load_file(config_path) : {}
88
88
  config
89
89
  .merge!(mode: :producer, cache_adapter: :redis)
90
90
  .merge!(options)
91
+ .merge!(api_key: ENV['API_KEY'] || config[:api_key])
92
+ .merge!(base_uri: ENV['SDK_URI'] || config[:base_uri])
93
+ .merge!(events_uri: ENV['EVENTS_URI'] || config[:events_uri])
94
+ # IDENTIFY_BASE_URI
91
95
 
92
96
  SplitIoClient::SplitFactory.new(config[:api_key], config)
@@ -1,6 +1,8 @@
1
1
  require 'splitclient-rb/version'
2
2
 
3
+ require 'splitclient-rb/exceptions/impressions_shutdown_exception'
3
4
  require 'splitclient-rb/exceptions/sdk_blocker_timeout_expired_exception'
5
+ require 'splitclient-rb/cache/routers/impression_router'
4
6
  require 'splitclient-rb/cache/adapters/memory_adapters/map_adapter'
5
7
  require 'splitclient-rb/cache/adapters/memory_adapters/queue_adapter'
6
8
  require 'splitclient-rb/cache/adapters/memory_adapter'
@@ -41,6 +43,7 @@ require 'splitclient-rb/engine/parser/condition'
41
43
  require 'splitclient-rb/engine/parser/partition'
42
44
  require 'splitclient-rb/engine/parser/split_adapter'
43
45
  require 'splitclient-rb/engine/parser/evaluator'
46
+ require 'splitclient-rb/engine/matchers/attribute_matcher'
44
47
  require 'splitclient-rb/engine/matchers/combiners'
45
48
  require 'splitclient-rb/engine/matchers/combining_matcher'
46
49
  require 'splitclient-rb/engine/matchers/all_keys_matcher'
@@ -10,6 +10,10 @@ module SplitIoClient
10
10
  @map = Concurrent::Map.new
11
11
  end
12
12
 
13
+ def clear(_ = nil)
14
+ initialize
15
+ end
16
+
13
17
  # Map
14
18
  def initialize_map(key)
15
19
  @map[key] = Concurrent::Map.new
@@ -10,6 +10,11 @@ module SplitIoClient
10
10
  @current_size = Concurrent::AtomicFixnum.new(0)
11
11
  end
12
12
 
13
+ def clear(_ = nil)
14
+ @queue = Queue.new
15
+ @current_size.value = 0
16
+ end
17
+
13
18
  # Adds data to queue in non-blocking mode
14
19
  def add_to_queue(data)
15
20
  fail ThreadError if @current_size.value >= @max_size
@@ -138,6 +138,12 @@ module SplitIoClient
138
138
  block.call
139
139
  end
140
140
  end
141
+
142
+ def clear(prefix)
143
+ keys = @redis.keys("#{prefix}*")
144
+
145
+ keys.map { |key| @redis.del(key) }
146
+ end
141
147
  end
142
148
  end
143
149
  end
@@ -71,6 +71,12 @@ module SplitIoClient
71
71
  @gauges = []
72
72
  end
73
73
 
74
+ def clear
75
+ clear_counts
76
+ clear_latencies
77
+ clear_gauges
78
+ end
79
+
74
80
  #
75
81
  # small class to act as DTO for counts
76
82
  #
@@ -85,6 +85,12 @@ module SplitIoClient
85
85
  def clear_gauges
86
86
  # TODO
87
87
  end
88
+
89
+ def clear
90
+ clear_counts
91
+ clear_latencies
92
+ clear_gauges
93
+ end
88
94
  end
89
95
  end
90
96
  end
@@ -5,7 +5,7 @@ module SplitIoClient
5
5
  class MetricsRepository < Repository
6
6
  extend Forwardable
7
7
  def_delegators :@adapter, :add_count, :add_latency, :add_gauge, :counts, :latencies, :gauges,
8
- :clear_counts, :clear_latencies, :clear_gauges
8
+ :clear_counts, :clear_latencies, :clear_gauges, :clear
9
9
 
10
10
  def initialize(adapter, config)
11
11
  @config = config
@@ -11,7 +11,7 @@ module SplitIoClient
11
11
 
12
12
  protected
13
13
 
14
- def namespace_key(key)
14
+ def namespace_key(key = '')
15
15
  "#{@config.redis_namespace}#{key}"
16
16
  end
17
17
 
@@ -55,6 +55,10 @@ module SplitIoClient
55
55
  @adapter.set_string(namespace_key('.segments.ready'), Time.now.utc.to_i)
56
56
  end
57
57
 
58
+ def clear
59
+ @adapter.clear(namespace_key)
60
+ end
61
+
58
62
  private
59
63
 
60
64
  def segment_data(name)
@@ -96,6 +96,10 @@ module SplitIoClient
96
96
  def ready!
97
97
  @adapter.set_string(namespace_key('.splits.ready'), Time.now.utc.to_i)
98
98
  end
99
+
100
+ def clear
101
+ @adapter.clear(namespace_key)
102
+ end
99
103
  end
100
104
  end
101
105
  end
@@ -0,0 +1,52 @@
1
+ module SplitIoClient
2
+ class ImpressionRouter
3
+ attr_reader :router_thread
4
+
5
+ def initialize(config)
6
+ @config = config
7
+ @listener = config.impression_listener
8
+ @queue = Queue.new
9
+ router_thread
10
+
11
+ if defined?(PhusionPassenger)
12
+ PhusionPassenger.on_event(:starting_worker_process) do |forked|
13
+ router_thread if forked
14
+ end
15
+ end
16
+ end
17
+
18
+ def add(impression)
19
+ @queue.push(impression)
20
+ end
21
+
22
+ def add_bulk(impressions)
23
+ impressions[:split_names].each do |split_name|
24
+ @queue.push(
25
+ split_name: split_name.to_s,
26
+ matching_key: impressions[:matching_key],
27
+ bucketing_key: impressions[:bucketing_key],
28
+ treatment: {
29
+ label: impressions[:treatments_labels_change_numbers][split_name.to_sym][:label],
30
+ treatment: impressions[:treatments_labels_change_numbers][split_name.to_sym][:treatment],
31
+ change_number: impressions[:treatments_labels_change_numbers][split_name.to_sym][:change_number]
32
+ },
33
+ attributes: impressions[:attributes]
34
+ )
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def router_thread
41
+ @config.threads[:impression_router] = Thread.new do
42
+ loop do
43
+ begin
44
+ @listener.log(@queue.pop)
45
+ rescue StandardError => error
46
+ @config.log_found_exception(__method__.to_s, error)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -31,13 +31,19 @@ module SplitIoClient
31
31
  private
32
32
 
33
33
  def impressions_thread
34
- Thread.new do
35
- @config.logger.info('Starting impressions service')
34
+ @config.threads[:impressions_sender] = Thread.new do
35
+ begin
36
+ @config.logger.info('Starting impressions service')
36
37
 
37
- loop do
38
+ loop do
39
+ post_impressions
40
+
41
+ sleep(SplitIoClient::Utilities.randomize_interval(@config.impressions_refresh_rate))
42
+ end
43
+ rescue SplitIoClient::ImpressionShutdownException
38
44
  post_impressions
39
45
 
40
- sleep(SplitIoClient::Utilities.randomize_interval(@config.impressions_refresh_rate))
46
+ @impressions_repository.clear
41
47
  end
42
48
  end
43
49
  end
@@ -23,7 +23,7 @@ module SplitIoClient
23
23
  private
24
24
 
25
25
  def metrics_thread
26
- Thread.new do
26
+ @config.threads[:metrics_sender] = Thread.new do
27
27
  @config.logger.info('Starting metrics service')
28
28
 
29
29
  loop do
@@ -29,7 +29,7 @@ module SplitIoClient
29
29
  private
30
30
 
31
31
  def segments_thread
32
- @sdk_blocker.segments_thread = Thread.new do
32
+ @config.threads[:segment_store] = @sdk_blocker.segments_thread = Thread.new do
33
33
  @config.logger.info('Starting segments fetcher service')
34
34
  @config.block_until_ready > 0 ? blocked_store : unblocked_store
35
35
  end
@@ -29,7 +29,7 @@ module SplitIoClient
29
29
  private
30
30
 
31
31
  def splits_thread
32
- @sdk_blocker.splits_thread = Thread.new do
32
+ @config.threads[:split_store] = @sdk_blocker.splits_thread = Thread.new do
33
33
  @config.logger.info('Starting splits fetcher service')
34
34
  loop do
35
35
  store_splits
@@ -27,9 +27,12 @@ module SplitIoClient
27
27
  end
28
28
 
29
29
  if @config.impressions_queue_size > 0
30
+ time = (Time.now.to_f * 1000.0).to_i
30
31
  @impressions_repository.add_bulk(
31
- matching_key, bucketing_key, treatments_labels_change_numbers, (Time.now.to_f * 1000.0).to_i
32
+ matching_key, bucketing_key, treatments_labels_change_numbers, time
32
33
  )
34
+
35
+ route_impressions(split_names, matching_key, bucketing_key, time, treatments_labels_change_numbers, attributes)
33
36
  end
34
37
 
35
38
  split_names = treatments_labels_change_numbers.keys
@@ -87,8 +90,8 @@ module SplitIoClient
87
90
 
88
91
  store_impression(
89
92
  split_name, matching_key, bucketing_key,
90
- { treatment: SplitIoClient::Engine::Models::Treatment::CONTROL, label: Engine::Models::Label::EXCEPTION },
91
- store_impressions
93
+ { treatment: SplitIoClient::Engine::Models::Treatment::CONTROL, label: SplitIoClient::Engine::Models::Label::EXCEPTION },
94
+ store_impressions, attributes
92
95
  )
93
96
 
94
97
  return parsed_treatment(multiple, treatment_data)
@@ -97,7 +100,7 @@ module SplitIoClient
97
100
  begin
98
101
  latency = (Time.now - start) * 1000.0
99
102
  # Disable impressions if @config.impressions_queue_size == -1
100
- split && store_impression(split_name, matching_key, bucketing_key, treatment_data, store_impressions)
103
+ split && store_impression(split_name, matching_key, bucketing_key, treatment_data, store_impressions, attributes)
101
104
 
102
105
  # Measure
103
106
  @adapter.metrics.time('sdk.get_treatment', latency)
@@ -106,8 +109,8 @@ module SplitIoClient
106
109
 
107
110
  store_impression(
108
111
  split_name, matching_key, bucketing_key,
109
- { treatment: SplitIoClient::Engine::Models::Treatment::CONTROL, label: Engine::Models::Label::EXCEPTION },
110
- store_impressions
112
+ { treatment: SplitIoClient::Engine::Models::Treatment::CONTROL, label: SplitIoClient::Engine::Models::Label::EXCEPTION },
113
+ store_impressions, attributes
111
114
  )
112
115
 
113
116
  return parsed_treatment(multiple, treatment_data)
@@ -116,7 +119,25 @@ module SplitIoClient
116
119
  parsed_treatment(multiple, treatment_data)
117
120
  end
118
121
 
119
- def store_impression(split_name, matching_key, bucketing_key, treatment, store_impressions)
122
+ def destroy
123
+ @config.logger.info('Split client shutdown started...') if @config.debug_enabled
124
+
125
+ @config.threads[:impressions_sender].raise(SplitIoClient::ImpressionShutdownException)
126
+ @config.threads.reject { |k, _| k == :impressions_sender }.each do |name, thread|
127
+ Thread.kill(thread)
128
+ end
129
+
130
+ @metrics_repository.clear
131
+ @splits_repository.clear
132
+ @segments_repository.clear
133
+
134
+ @config.logger.info('Split client shutdown complete') if @config.debug_enabled
135
+ end
136
+
137
+ def store_impression(split_name, matching_key, bucketing_key, treatment, store_impressions, attributes)
138
+ time = (Time.now.to_f * 1000.0).to_i
139
+ route_impression(split_name, matching_key, bucketing_key, time, treatment, attributes) if @config.impression_listener && store_impressions
140
+
120
141
  return if @config.impressions_queue_size <= 0 || !store_impressions
121
142
 
122
143
  @impressions_repository.add(split_name,
@@ -124,11 +145,37 @@ module SplitIoClient
124
145
  'bucketingKey' => bucketing_key,
125
146
  'treatment' => treatment[:treatment],
126
147
  'label' => @config.labels_enabled ? treatment[:label] : nil,
127
- 'time' => (Time.now.to_f * 1000.0).to_i,
148
+ 'time' => time,
128
149
  'changeNumber' => treatment[:change_number]
129
150
  )
130
151
  end
131
152
 
153
+ def route_impression(split_name, matching_key, bucketing_key, time, treatment, attributes)
154
+ impression_router.add(
155
+ split_name: split_name,
156
+ matching_key: matching_key,
157
+ bucketing_key: bucketing_key,
158
+ time: time,
159
+ treatment: treatment,
160
+ attributes: attributes
161
+ )
162
+ end
163
+
164
+ def route_impressions(split_names, matching_key, bucketing_key, time, treatments_labels_change_numbers, attributes)
165
+ impression_router.add_bulk(
166
+ split_names: split_names,
167
+ matching_key: matching_key,
168
+ bucketing_key: bucketing_key,
169
+ time: time,
170
+ treatments_labels_change_numbers: treatments_labels_change_numbers,
171
+ attributes: attributes
172
+ )
173
+ end
174
+
175
+ def impression_router
176
+ @impression_router ||= SplitIoClient::ImpressionRouter.new(@config)
177
+ end
178
+
132
179
  def keys_from_key(key)
133
180
  case key.class.to_s
134
181
  when 'Hash'
@@ -0,0 +1,20 @@
1
+ module SplitIoClient
2
+ class AttributeMatcher
3
+ def initialize(attribute, matcher)
4
+ @attribute = attribute
5
+ @matcher = matcher
6
+ end
7
+
8
+ def match?(matching_key, bucketing_key = nil, evaluator = nil, attributes = {})
9
+ if @attribute != nil
10
+ return false unless attributes
11
+
12
+ value = attributes.fetch(@attribute) { |name| attributes[name.to_s] || attributes[name.to_sym] }
13
+
14
+ @matcher.match?(value, bucketing_key, nil, nil)
15
+ else
16
+ @matcher.match?(matching_key, bucketing_key, evaluator, attributes)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -12,10 +12,10 @@ module SplitIoClient
12
12
  @end_value = get_formatted_value attribute_hash[:end_value], true
13
13
  end
14
14
 
15
- def match?(_matching_key, _bucketing_key, _evaluator, attributes)
15
+ def match?(value, _matching_key, _bucketing_key, _evaluator)
16
16
  matches = false
17
- if (!attributes.nil? && attributes.key?(@attribute.to_sym))
18
- param_value = get_formatted_value(attributes[@attribute.to_sym])
17
+ if !value.nil?
18
+ param_value = get_formatted_value(value)
19
19
  matches = param_value.is_a?(Integer) ? ((param_value >= @start_value) && (param_value <= @end_value)) : false
20
20
  end
21
21
 
@@ -9,9 +9,7 @@ module SplitIoClient
9
9
  @substr_list = substr_list
10
10
  end
11
11
 
12
- def match?(_matching_key, _bucketing_key, _evaluator, data)
13
- value = data.fetch(@attribute) { |attr| data[attr.to_s] || data[attr.to_sym] }
14
-
12
+ def match?(value, _matching_key, _bucketing_key, _evaluator)
15
13
  return false if @substr_list.empty?
16
14
 
17
15
  @substr_list.any? { |substr| value.to_s.include? substr }
@@ -9,9 +9,7 @@ module SplitIoClient
9
9
  @suffix_list = suffix_list
10
10
  end
11
11
 
12
- def match?(_matching_key, _bucketing_key, _evaluator, data)
13
- value = data.fetch(@attribute) { |attr| data[attr.to_s] || data[attr.to_sym] }
14
-
12
+ def match?(value, _matching_key, _bucketing_key, _evaluator)
15
13
  return false if @suffix_list.empty?
16
14
 
17
15
  @suffix_list.any? { |suffix| value.to_s.end_with? suffix }
@@ -9,9 +9,7 @@ module SplitIoClient
9
9
  @boolean = boolean
10
10
  end
11
11
 
12
- def match?(_matching_key, _bucketing_key, _evaluator, data)
13
- value = data.fetch(@attribute) { |attr| data[attr.to_s] || data[attr.to_sym] }
14
-
12
+ def match?(value, _matching_key, _bucketing_key, _evaluator)
15
13
  value = false if value.to_s.downcase == 'false'
16
14
  value = true if value.to_s.downcase == 'true'
17
15
 
@@ -11,10 +11,10 @@ module SplitIoClient
11
11
  @value = get_formatted_value attribute_hash[:value], true
12
12
  end
13
13
 
14
- def match?(_matching_key, _bucketing_key, _evaluator, attributes)
14
+ def match?(value, _matching_key, _bucketing_key, _evaluator)
15
15
  matches = false
16
- if (!attributes.nil? && attributes.key?(@attribute.to_sym))
17
- param_value = get_formatted_value(attributes[@attribute.to_sym])
16
+ if !value.nil?
17
+ param_value = get_formatted_value(value)
18
18
  matches = param_value.is_a?(Integer) ? (param_value == @value) : false
19
19
  end
20
20
  end
@@ -11,10 +11,10 @@ module SplitIoClient
11
11
  @value = get_formatted_value attribute_hash[:value], true
12
12
  end
13
13
 
14
- def match?(_matching_key, _bucketing_key, _evaluator, attributes)
14
+ def match?(value, _matching_key, _bucketing_key, _evaluator)
15
15
  matches = false
16
- if (!attributes.nil? && attributes.key?(@attribute.to_sym))
17
- param_value = get_formatted_value(attributes[@attribute.to_sym])
16
+ if !value.nil?
17
+ param_value = get_formatted_value(value)
18
18
  matches = param_value.is_a?(Integer) ? (param_value >= @value) : false
19
19
  end
20
20
  end
@@ -11,10 +11,10 @@ module SplitIoClient
11
11
  @value = get_formatted_value attribute_hash[:value], true
12
12
  end
13
13
 
14
- def match?(_matching_key, _bucketing_key, _evaluator, attributes)
14
+ def match?(value, _matching_key, _bucketing_key, _evaluator)
15
15
  matches = false
16
- if (!attributes.nil? && attributes.key?(@attribute.to_sym))
17
- param_value = get_formatted_value(attributes[@attribute.to_sym])
16
+ if !value.nil?
17
+ param_value = get_formatted_value(value)
18
18
  matches = param_value.is_a?(Integer) ? (param_value <= @value) : false
19
19
  end
20
20
  end
@@ -9,9 +9,7 @@ module SplitIoClient
9
9
  @regexp_string = @regexp_string.is_a?(Regexp) ? regexp_string : Regexp.new(regexp_string)
10
10
  end
11
11
 
12
- def match?(_matching_key, _bucketing_key, _evaluator, data)
13
- value = data.fetch(@attribute) { |attr| data[attr.to_s] || data[attr.to_sym] }
14
-
12
+ def match?(value, _matching_key, _bucketing_key, _evaluator)
15
13
  (value =~ @regexp_string) != nil
16
14
  end
17
15
  end
@@ -9,9 +9,7 @@ module SplitIoClient
9
9
  @prefix_list = prefix_list
10
10
  end
11
11
 
12
- def match?(_matching_key, _bucketing_key, _evaluator, data)
13
- value = data.fetch(@attribute) { |attr| data[attr.to_s] || data[attr.to_sym] }
14
-
12
+ def match?(value, _matching_key, _bucketing_key, _evaluator)
15
13
  return false if @prefix_list.empty?
16
14
 
17
15
  @prefix_list.any? { |prefix| value.to_s.start_with? prefix }
@@ -20,16 +20,13 @@ module SplitIoClient
20
20
  end
21
21
  end
22
22
 
23
- def match?(matching_key, _bucketing_key, _evaluator, whitelist_data)
23
+ def match?(value, _matching_key, _bucketing_key, _evaluator)
24
24
  matches = false
25
- if !(@matcher_type == "ATTR_WHITELIST")
26
- matches = @whitelist.include?(matching_key)
27
- else
28
- if (!whitelist_data.nil? && whitelist_data.key?(@attribute.to_sym))
29
- value = whitelist_data[@attribute.to_sym]
30
- matches = @whitelist.include?(value)
31
- end
25
+
26
+ if !value.nil?
27
+ matches = @whitelist.include?(value)
32
28
  end
29
+
33
30
  matches
34
31
  end
35
32
 
@@ -58,7 +58,15 @@ module SplitIoClient
58
58
  in_rollout = true
59
59
  end
60
60
 
61
- if matcher_type(condition).match?(keys[:matching_key], keys[:bucketing_key], self, attributes)
61
+ begin
62
+ matcher = SplitIoClient::AttributeMatcher.new(
63
+ condition.data[:matcherGroup][:matchers][0][:keySelector][:attribute],
64
+ matcher_type(condition)
65
+ )
66
+ rescue NoMethodError
67
+ matcher = SplitIoClient::AttributeMatcher.new(nil, matcher_type(condition))
68
+ end
69
+ if matcher.match?(keys[:matching_key], keys[:bucketing_key], self, attributes)
62
70
  key = keys[:bucketing_key] ? keys[:bucketing_key] : keys[:matching_key]
63
71
  result = Splitter.get_treatment(key, split[:seed], condition.partitions, split[:algo])
64
72
 
@@ -0,0 +1,4 @@
1
+ module SplitIoClient
2
+ class ImpressionShutdownException < StandardError
3
+ end
4
+ end
@@ -61,6 +61,11 @@ module SplitIoClient
61
61
 
62
62
  @labels_enabled = opts[:labels_enabled].nil? ? SplitConfig.default_labels_logging : opts[:labels_enabled]
63
63
 
64
+ @impression_listener = opts[:impression_listener]
65
+ @impression_listener_refresh_rate = opts[:impression_listener_refresh_rate] || SplitConfig.default_impression_listener_refresh_rate
66
+
67
+ @threads = {}
68
+
64
69
  startup_log
65
70
  end
66
71
 
@@ -153,6 +158,9 @@ module SplitIoClient
153
158
  attr_reader :metrics_refresh_rate
154
159
  attr_reader :impressions_refresh_rate
155
160
 
161
+ attr_reader :impression_listener
162
+ attr_reader :impression_listener_refresh_rate
163
+
156
164
  #
157
165
  # Wow big the impressions queue is before dropping impressions. -1 to disable it.
158
166
  #
@@ -162,6 +170,8 @@ module SplitIoClient
162
170
  attr_reader :redis_url
163
171
  attr_reader :redis_namespace
164
172
 
173
+ attr_accessor :threads
174
+
165
175
  #
166
176
  # The default split client configuration
167
177
  #
@@ -251,6 +261,10 @@ module SplitIoClient
251
261
  60
252
262
  end
253
263
 
264
+ def self.default_impression_listener_refresh_rate
265
+ 0
266
+ end
267
+
254
268
  def self.default_impressions_queue_size
255
269
  5000
256
270
  end
@@ -302,7 +316,7 @@ module SplitIoClient
302
316
  def log_found_exception(caller, exn)
303
317
  error_traceback = "#{exn.inspect} #{exn}\n\t#{exn.backtrace.join("\n\t")}"
304
318
  error = "[splitclient-rb] Unexpected exception in #{caller}: #{error_traceback}"
305
- @logger.error(error)
319
+ @logger.warn(error)
306
320
  end
307
321
 
308
322
  #
@@ -1,3 +1,3 @@
1
1
  module SplitIoClient
2
- VERSION = '4.2.2'
2
+ VERSION = '4.3.0.canary.1'
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: splitclient-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.2
4
+ version: 4.3.0.canary.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Split Software
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-07-28 00:00:00.000000000 Z
11
+ date: 2017-09-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -266,6 +266,7 @@ files:
266
266
  - lib/splitclient-rb/cache/repositories/repository.rb
267
267
  - lib/splitclient-rb/cache/repositories/segments_repository.rb
268
268
  - lib/splitclient-rb/cache/repositories/splits_repository.rb
269
+ - lib/splitclient-rb/cache/routers/impression_router.rb
269
270
  - lib/splitclient-rb/cache/senders/impressions_formatter.rb
270
271
  - lib/splitclient-rb/cache/senders/impressions_sender.rb
271
272
  - lib/splitclient-rb/cache/senders/metrics_sender.rb
@@ -282,6 +283,7 @@ files:
282
283
  - lib/splitclient-rb/engine/api/splits.rb
283
284
  - lib/splitclient-rb/engine/evaluator/splitter.rb
284
285
  - lib/splitclient-rb/engine/matchers/all_keys_matcher.rb
286
+ - lib/splitclient-rb/engine/matchers/attribute_matcher.rb
285
287
  - lib/splitclient-rb/engine/matchers/between_matcher.rb
286
288
  - lib/splitclient-rb/engine/matchers/combiners.rb
287
289
  - lib/splitclient-rb/engine/matchers/combining_matcher.rb
@@ -311,6 +313,7 @@ files:
311
313
  - lib/splitclient-rb/engine/parser/evaluator.rb
312
314
  - lib/splitclient-rb/engine/parser/partition.rb
313
315
  - lib/splitclient-rb/engine/parser/split_adapter.rb
316
+ - lib/splitclient-rb/exceptions/impressions_shutdown_exception.rb
314
317
  - lib/splitclient-rb/exceptions/sdk_blocker_timeout_expired_exception.rb
315
318
  - lib/splitclient-rb/localhost_split_factory.rb
316
319
  - lib/splitclient-rb/localhost_utils.rb
@@ -343,9 +346,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
343
346
  version: '0'
344
347
  required_rubygems_version: !ruby/object:Gem::Requirement
345
348
  requirements:
346
- - - ">="
349
+ - - ">"
347
350
  - !ruby/object:Gem::Version
348
- version: '0'
351
+ version: 1.3.1
349
352
  requirements: []
350
353
  rubyforge_project:
351
354
  rubygems_version: 2.5.2