splitclient-rb 3.1.2 → 3.1.3.pre.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGES.txt +7 -0
- data/Detailed-README.md +20 -5
- data/exe/splitio +54 -3
- data/lib/cache/adapters/memory_adapters/map_adapter.rb +6 -0
- data/lib/cache/adapters/redis_adapter.rb +6 -1
- data/lib/cache/repositories/impressions/memory_repository.rb +8 -2
- data/lib/cache/repositories/impressions/redis_repository.rb +2 -2
- data/lib/cache/repositories/impressions_repository.rb +3 -0
- data/lib/cache/repositories/metrics_repository.rb +1 -0
- data/lib/cache/repositories/repository.rb +1 -1
- data/lib/cache/repositories/segments_repository.rb +16 -1
- data/lib/cache/repositories/splits_repository.rb +21 -7
- data/lib/cache/senders/impressions_sender.rb +16 -12
- data/lib/cache/senders/metrics_sender.rb +41 -0
- data/lib/cache/stores/sdk_blocker.rb +9 -8
- data/lib/cache/stores/segment_store.rb +1 -1
- data/lib/engine/api/client.rb +15 -2
- data/lib/engine/api/metrics.rb +57 -0
- data/lib/engine/parser/split_adapter.rb +9 -140
- data/lib/engine/parser/split_treatment.rb +7 -5
- data/lib/engine/partitions/treatments.rb +1 -5
- data/lib/splitclient-rb.rb +14 -4
- data/lib/splitclient-rb/clients/localhost_split_client.rb +89 -0
- data/lib/splitclient-rb/clients/split_client.rb +114 -0
- data/lib/splitclient-rb/localhost_split_factory.rb +6 -187
- data/lib/splitclient-rb/localhost_utils.rb +36 -0
- data/lib/splitclient-rb/managers/localhost_split_manager.rb +45 -0
- data/lib/splitclient-rb/managers/split_manager.rb +77 -0
- data/lib/splitclient-rb/split_config.rb +20 -0
- data/lib/splitclient-rb/split_factory.rb +16 -217
- data/lib/splitclient-rb/split_factory_builder.rb +3 -2
- data/lib/splitclient-rb/version.rb +1 -1
- data/lib/splitclient-rb_utilitites.rb +24 -19
- data/splitclient-rb.gemspec +1 -1
- data/{splitio.yml → splitio.yml.example} +0 -0
- metadata +26 -19
@@ -0,0 +1,114 @@
|
|
1
|
+
module SplitIoClient
|
2
|
+
class SplitClient
|
3
|
+
#
|
4
|
+
# Creates a new split client instance that connects to split.io API.
|
5
|
+
#
|
6
|
+
# @param api_key [String] the API key for your split account
|
7
|
+
#
|
8
|
+
# @return [SplitIoClient] split.io client instance
|
9
|
+
def initialize(api_key, config = {}, adapter = nil, splits_repository, segments_repository, impressions_repository, metrics_repository)
|
10
|
+
@config = config
|
11
|
+
|
12
|
+
@splits_repository = splits_repository
|
13
|
+
@segments_repository = segments_repository
|
14
|
+
@impressions_repository = impressions_repository
|
15
|
+
@metrics_repository = metrics_repository
|
16
|
+
|
17
|
+
@adapter = adapter
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_treatments(key, split_names, attributes = nil)
|
21
|
+
bucketing_key, matching_key = keys_from_key(key)
|
22
|
+
bucketing_key = matching_key if bucketing_key.nil?
|
23
|
+
|
24
|
+
treatments =
|
25
|
+
@splits_repository.get_splits(split_names).each_with_object({}) do |(name, data), memo|
|
26
|
+
memo.merge!(name => get_treatment(key, name, attributes, data, false))
|
27
|
+
end
|
28
|
+
|
29
|
+
if @config.impressions_queue_size > 0
|
30
|
+
@impressions_repository.add_bulk(matching_key, bucketing_key, treatments, (Time.now.to_f * 1000.0).to_i)
|
31
|
+
end
|
32
|
+
|
33
|
+
treatments
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# obtains the treatment for a given feature
|
38
|
+
#
|
39
|
+
# @param key [String/Hash] user id or hash with matching_key/bucketing_key
|
40
|
+
# @param split_name [String/Array] name of the feature that is being validated or array of them
|
41
|
+
#
|
42
|
+
# @return [String/Hash] Treatment as String or Hash of treatments in case of array of features
|
43
|
+
def get_treatment(key, split_name, attributes = nil, split_data = nil, store_impressions = true)
|
44
|
+
bucketing_key, matching_key = keys_from_key(key)
|
45
|
+
bucketing_key = matching_key if bucketing_key.nil?
|
46
|
+
|
47
|
+
if matching_key.nil?
|
48
|
+
@config.logger.warn('matching_key was null for split_name: ' + split_name.to_s)
|
49
|
+
return Treatments::CONTROL
|
50
|
+
end
|
51
|
+
|
52
|
+
if split_name.nil?
|
53
|
+
@config.logger.warn('split_name was null for key: ' + key)
|
54
|
+
return Treatments::CONTROL
|
55
|
+
end
|
56
|
+
|
57
|
+
start = Time.now
|
58
|
+
result = nil
|
59
|
+
|
60
|
+
begin
|
61
|
+
split = split_data ? split_data : @splits_repository.get_split(split_name)
|
62
|
+
|
63
|
+
result = if split.nil?
|
64
|
+
Treatments::CONTROL
|
65
|
+
else
|
66
|
+
SplitIoClient::Engine::Parser::SplitTreatment.new(@segments_repository).call(
|
67
|
+
{ bucketing_key: bucketing_key, matching_key: matching_key }, split, attributes
|
68
|
+
)
|
69
|
+
end
|
70
|
+
rescue StandardError => error
|
71
|
+
@config.log_found_exception(__method__.to_s, error)
|
72
|
+
end
|
73
|
+
|
74
|
+
result = result.nil? ? Treatments::CONTROL : result
|
75
|
+
|
76
|
+
begin
|
77
|
+
latency = (Time.now - start) * 1000.0
|
78
|
+
if @config.impressions_queue_size > 0 && store_impressions
|
79
|
+
# Disable impressions if @config.impressions_queue_size == -1
|
80
|
+
@impressions_repository.add(split_name,
|
81
|
+
'key_name' => matching_key,
|
82
|
+
'bucketing_key' => bucketing_key,
|
83
|
+
'treatment' => result,
|
84
|
+
'time' => (Time.now.to_f * 1000.0).to_i
|
85
|
+
)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Measure
|
89
|
+
@adapter.metrics.time("sdk.get_treatment", latency)
|
90
|
+
rescue StandardError => error
|
91
|
+
@config.log_found_exception(__method__.to_s, error)
|
92
|
+
end
|
93
|
+
|
94
|
+
result
|
95
|
+
end
|
96
|
+
|
97
|
+
def keys_from_key(key)
|
98
|
+
case key.class.to_s
|
99
|
+
when 'Hash'
|
100
|
+
key.values_at(:bucketing_key, :matching_key)
|
101
|
+
when 'String'
|
102
|
+
[key, key]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
# method that returns the sdk gem version
|
108
|
+
#
|
109
|
+
# @return [string] version value for this sdk
|
110
|
+
def self.sdk_version
|
111
|
+
'ruby-'+SplitIoClient::VERSION
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -1,194 +1,13 @@
|
|
1
|
-
require 'logger'
|
2
1
|
module SplitIoClient
|
2
|
+
class LocalhostSplitFactory
|
3
|
+
attr_reader :client, :manager
|
3
4
|
|
4
|
-
|
5
|
-
# main class for localhost split client sdk
|
6
|
-
#
|
7
|
-
class LocalhostSplitFactory < NoMethodError
|
8
|
-
class LocalhostSplitManager < NoMethodError
|
9
|
-
#
|
10
|
-
# constant that defines the localhost mode
|
11
|
-
LOCALHOST_MODE = 'localhost'
|
12
|
-
|
13
|
-
#
|
14
|
-
# object that acts as an api adapter connector. used to get and post to api endpoints
|
15
|
-
attr_reader :adapter
|
16
|
-
|
17
|
-
#
|
18
|
-
# Creates a new split manager instance that holds the splits from a given file
|
19
|
-
#
|
20
|
-
# @param splits_file [File] the .split file that contains the splits
|
21
|
-
#
|
22
|
-
# @return [LocalhostSplitIoManager] split.io localhost manager instance
|
23
|
-
def initialize(splits_file)
|
24
|
-
@localhost_mode = true
|
25
|
-
@localhost_mode_features = []
|
26
|
-
load_localhost_mode_features(splits_file)
|
27
|
-
end
|
28
|
-
|
29
|
-
#
|
30
|
-
# method to set localhost mode features by reading the given .splits
|
31
|
-
#
|
32
|
-
# @param splits_file [File] the .split file that contains the splits
|
33
|
-
# @returns [void]
|
34
|
-
def load_localhost_mode_features(splits_file)
|
35
|
-
if File.exists?(splits_file)
|
36
|
-
line_num=0
|
37
|
-
File.open(splits_file).each do |line|
|
38
|
-
line_data = line.strip.split(" ")
|
39
|
-
@localhost_mode_features << {feature: line_data[0], treatment: line_data[1]} unless line.start_with?('#') || line.strip.empty?
|
40
|
-
end
|
41
|
-
end
|
42
|
-
@localhost_mode_features
|
43
|
-
end
|
44
|
-
|
45
|
-
#
|
46
|
-
# method to get a split view
|
47
|
-
#
|
48
|
-
# @returns a split view
|
49
|
-
def split(split_name)
|
50
|
-
@localhost_mode_features.find {|x| x[:feature] == split_name}
|
51
|
-
end
|
52
|
-
|
53
|
-
#
|
54
|
-
# method to get the split list from the client
|
55
|
-
#
|
56
|
-
# @returns [object] array of splits
|
57
|
-
def splits
|
58
|
-
@localhost_mode_features
|
59
|
-
end
|
60
|
-
|
61
|
-
#
|
62
|
-
# method to get the list of just split names. Ideal for ietrating and calling client.get_treatment
|
63
|
-
#
|
64
|
-
# @returns [object] array of split names (String)
|
65
|
-
def split_names
|
66
|
-
@localhost_mode_features.each_with_object([]) do |split, memo|
|
67
|
-
memo << split[:feature]
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
class LocalhostSplitClient < NoMethodError
|
73
|
-
#
|
74
|
-
# constant that defines the localhost mode
|
75
|
-
LOCALHOST_MODE = 'localhost'
|
76
|
-
|
77
|
-
#
|
78
|
-
# variables to if the sdk is being used in localhost mode and store the list of features
|
79
|
-
attr_reader :localhost_mode
|
80
|
-
attr_reader :localhost_mode_features
|
81
|
-
|
82
|
-
#
|
83
|
-
# Creates a new split client instance that reads from the given splits file
|
84
|
-
#
|
85
|
-
# @param splits_file [File] file that contains some splits
|
86
|
-
#
|
87
|
-
# @return [LocalhostSplitIoClient] split.io localhost client instance
|
88
|
-
def initialize(splits_file)
|
89
|
-
@localhost_mode = true
|
90
|
-
@localhost_mode_features = []
|
91
|
-
load_localhost_mode_features(splits_file)
|
92
|
-
end
|
93
|
-
|
94
|
-
def get_treatments(key, split_names, attributes = nil)
|
95
|
-
split_names.each_with_object({}) do |name, memo|
|
96
|
-
puts "name #{name} memo #{memo}"
|
97
|
-
memo.merge!(name => get_treatment(key, name, attributes))
|
98
|
-
end
|
99
|
-
end
|
100
|
-
|
101
|
-
#
|
102
|
-
# obtains the treatment for a given feature
|
103
|
-
#
|
104
|
-
# @param id [string] user id
|
105
|
-
# @param feature [string] name of the feature that is being validated
|
106
|
-
#
|
107
|
-
# @return [Treatment] treatment constant value
|
108
|
-
def get_treatment(id, feature, attributes = nil)
|
109
|
-
unless id
|
110
|
-
@config.logger.warn('id was null for feature: ' + feature)
|
111
|
-
return Treatments::CONTROL
|
112
|
-
end
|
113
|
-
|
114
|
-
unless feature
|
115
|
-
@config.logger.warn('feature was null for id: ' + id)
|
116
|
-
return Treatments::CONTROL
|
117
|
-
end
|
118
|
-
result = get_localhost_treatment(feature)
|
119
|
-
end
|
120
|
-
|
121
|
-
|
122
|
-
#
|
123
|
-
# auxiliary method to get the treatments avoding exceptions
|
124
|
-
#
|
125
|
-
# @param id [string] user id
|
126
|
-
# @param feature [string] name of the feature that is being validated
|
127
|
-
#
|
128
|
-
# @return [Treatment] tretment constant value
|
129
|
-
def get_treatment_without_exception_handling(id, feature, attributes = nil)
|
130
|
-
get_treatment(id, feature, attributes)
|
131
|
-
end
|
132
|
-
|
133
|
-
#
|
134
|
-
# method that returns the sdk gem version
|
135
|
-
#
|
136
|
-
# @return [string] version value for this sdk
|
137
|
-
def self.sdk_version
|
138
|
-
'RubyClientSDK-'+SplitIoClient::VERSION
|
139
|
-
end
|
140
|
-
|
141
|
-
#
|
142
|
-
# method to check if the sdk is running in localhost mode based on api key
|
143
|
-
#
|
144
|
-
# @return [boolean] True if is in localhost mode, false otherwise
|
145
|
-
def is_localhost_mode?
|
146
|
-
true
|
147
|
-
end
|
148
|
-
|
149
|
-
#
|
150
|
-
# method to set localhost mode features by reading .splits file located at home directory
|
151
|
-
#
|
152
|
-
# @returns [void]
|
153
|
-
def load_localhost_mode_features(splits_file)
|
154
|
-
if File.exists?(splits_file)
|
155
|
-
line_num=0
|
156
|
-
File.open(splits_file).each do |line|
|
157
|
-
line_data = line.strip.split(" ")
|
158
|
-
@localhost_mode_features << {feature: line_data[0], treatment: line_data[1]} unless line.start_with?('#') || line.strip.empty?
|
159
|
-
end
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
#
|
164
|
-
# method to check the treatment for the given feature in localhost mode
|
165
|
-
#
|
166
|
-
# @return [boolean] true if the feature is available in localhost mode, false otherwise
|
167
|
-
def get_localhost_treatment(feature)
|
168
|
-
localhost_result = Treatments::CONTROL
|
169
|
-
treatment = @localhost_mode_features.select{|h| h[:feature] == feature}.last
|
170
|
-
localhost_result = treatment[:treatment] if !treatment.nil?
|
171
|
-
localhost_result
|
172
|
-
end
|
173
|
-
|
174
|
-
private :get_treatment_without_exception_handling, :is_localhost_mode?,
|
175
|
-
:load_localhost_mode_features, :get_localhost_treatment
|
176
|
-
|
177
|
-
end
|
178
|
-
|
179
|
-
private_constant :LocalhostSplitClient
|
180
|
-
private_constant :LocalhostSplitManager
|
181
|
-
|
182
|
-
def initialize(splits_file)
|
5
|
+
def initialize(splits_file, reload_rate = nil)
|
183
6
|
@splits_file = splits_file
|
184
|
-
|
185
|
-
|
186
|
-
def client
|
187
|
-
@client ||= LocalhostSplitClient.new(@splits_file)
|
188
|
-
end
|
7
|
+
@reload_rate = reload_rate
|
189
8
|
|
190
|
-
|
191
|
-
@manager
|
9
|
+
@client = LocalhostSplitClient.new(@splits_file, @reload_rate)
|
10
|
+
@manager = LocalhostSplitManager.new(@splits_file, @reload_rate)
|
192
11
|
end
|
193
12
|
end
|
194
13
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module SplitIoClient
|
2
|
+
module LocalhostUtils
|
3
|
+
#
|
4
|
+
# method to set localhost mode features by reading the given .splits
|
5
|
+
#
|
6
|
+
# @param splits_file [File] the .split file that contains the splits
|
7
|
+
# @param reload_rate [Integer] the number of seconds to reload splits_file
|
8
|
+
# @return nil
|
9
|
+
def load_localhost_mode_features(splits_file, reload_rate = nil)
|
10
|
+
return @localhost_mode_features unless File.exists?(splits_file)
|
11
|
+
|
12
|
+
store_features(splits_file)
|
13
|
+
|
14
|
+
return unless reload_rate
|
15
|
+
|
16
|
+
Thread.new do
|
17
|
+
loop do
|
18
|
+
@localhost_mode_features = []
|
19
|
+
store_features(splits_file)
|
20
|
+
|
21
|
+
sleep(::Utilities.randomize_interval(reload_rate))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def store_features(splits_file)
|
27
|
+
File.open(splits_file).each do |line|
|
28
|
+
feature, treatment = line.strip.split(' ')
|
29
|
+
|
30
|
+
next if line.start_with?('#') || line.strip.empty?
|
31
|
+
|
32
|
+
@localhost_mode_features << { feature: feature, treatment: treatment }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module SplitIoClient
|
2
|
+
class LocalhostSplitManager
|
3
|
+
include SplitIoClient::LocalhostUtils
|
4
|
+
|
5
|
+
#
|
6
|
+
# Creates a new split manager instance that holds the splits from a given file
|
7
|
+
#
|
8
|
+
# @param splits_file [File] the .split file that contains the splits
|
9
|
+
# @param reload_rate [Integer] the number of seconds to reload splits_file
|
10
|
+
#
|
11
|
+
# @return [LocalhostSplitIoManager] split.io localhost manager instance
|
12
|
+
def initialize(splits_file, reload_rate = nil)
|
13
|
+
@localhost_mode = true
|
14
|
+
@localhost_mode_features = []
|
15
|
+
|
16
|
+
load_localhost_mode_features(splits_file, reload_rate)
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# method to get a split view
|
21
|
+
#
|
22
|
+
# @returns a split view
|
23
|
+
def split(split_name)
|
24
|
+
@localhost_mode_features.find { |x| x[:feature] == split_name }
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# method to get the split list from the client
|
29
|
+
#
|
30
|
+
# @returns [object] array of splits
|
31
|
+
def splits
|
32
|
+
@localhost_mode_features
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# method to get the list of just split names. Ideal for ietrating and calling client.get_treatment
|
37
|
+
#
|
38
|
+
# @returns [object] array of split names (String)
|
39
|
+
def split_names
|
40
|
+
@localhost_mode_features.each_with_object([]) do |split, memo|
|
41
|
+
memo << split[:feature]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module SplitIoClient
|
2
|
+
class SplitManager
|
3
|
+
#
|
4
|
+
# Creates a new split manager instance that connects to split.io API.
|
5
|
+
#
|
6
|
+
# @param api_key [String] the API key for your split account
|
7
|
+
#
|
8
|
+
# @return [SplitIoManager] split.io client instance
|
9
|
+
def initialize(api_key, config = {}, adapter = nil, splits_repository = nil)
|
10
|
+
@localhost_mode_features = []
|
11
|
+
@config = config
|
12
|
+
@splits_repository = splits_repository
|
13
|
+
@adapter = adapter
|
14
|
+
end
|
15
|
+
|
16
|
+
#
|
17
|
+
# method to get the split list from the client
|
18
|
+
#
|
19
|
+
# @returns [object] array of splits
|
20
|
+
def splits
|
21
|
+
return if @splits_repository.nil?
|
22
|
+
|
23
|
+
@splits_repository.splits.each_with_object([]) do |(name, split), memo|
|
24
|
+
split_model = Engine::Models::Split.new(split)
|
25
|
+
|
26
|
+
memo << build_split_view(name, split) unless split_model.archived?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
# method to get the list of just split names. Ideal for ietrating and calling client.get_treatment
|
32
|
+
#
|
33
|
+
# @returns [object] array of split names (String)
|
34
|
+
def split_names
|
35
|
+
return if @splits_repository.nil?
|
36
|
+
|
37
|
+
@splits_repository.split_names
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# method to get a split view
|
42
|
+
#
|
43
|
+
# @returns a split view
|
44
|
+
def split(split_name)
|
45
|
+
if @splits_repository
|
46
|
+
split = @splits_repository.get_split(split_name)
|
47
|
+
|
48
|
+
build_split_view(split_name, split) unless split.nil? or split_model(split).archived?
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def build_split_view(name, split)
|
53
|
+
return {} unless split
|
54
|
+
|
55
|
+
treatments =
|
56
|
+
if split[:conditions] && split[:conditions][0][:partitions]
|
57
|
+
split[:conditions][0][:partitions].map { |partition| partition[:treatment] }
|
58
|
+
else
|
59
|
+
[]
|
60
|
+
end
|
61
|
+
|
62
|
+
{
|
63
|
+
name: name,
|
64
|
+
traffic_type_name: split[:trafficTypeName],
|
65
|
+
killed: split[:killed],
|
66
|
+
treatments: treatments,
|
67
|
+
change_number: split[:changeNumber]
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def split_model(split)
|
74
|
+
split_model = Engine::Models::Split.new(split)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|