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.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.txt +7 -0
  3. data/Detailed-README.md +20 -5
  4. data/exe/splitio +54 -3
  5. data/lib/cache/adapters/memory_adapters/map_adapter.rb +6 -0
  6. data/lib/cache/adapters/redis_adapter.rb +6 -1
  7. data/lib/cache/repositories/impressions/memory_repository.rb +8 -2
  8. data/lib/cache/repositories/impressions/redis_repository.rb +2 -2
  9. data/lib/cache/repositories/impressions_repository.rb +3 -0
  10. data/lib/cache/repositories/metrics_repository.rb +1 -0
  11. data/lib/cache/repositories/repository.rb +1 -1
  12. data/lib/cache/repositories/segments_repository.rb +16 -1
  13. data/lib/cache/repositories/splits_repository.rb +21 -7
  14. data/lib/cache/senders/impressions_sender.rb +16 -12
  15. data/lib/cache/senders/metrics_sender.rb +41 -0
  16. data/lib/cache/stores/sdk_blocker.rb +9 -8
  17. data/lib/cache/stores/segment_store.rb +1 -1
  18. data/lib/engine/api/client.rb +15 -2
  19. data/lib/engine/api/metrics.rb +57 -0
  20. data/lib/engine/parser/split_adapter.rb +9 -140
  21. data/lib/engine/parser/split_treatment.rb +7 -5
  22. data/lib/engine/partitions/treatments.rb +1 -5
  23. data/lib/splitclient-rb.rb +14 -4
  24. data/lib/splitclient-rb/clients/localhost_split_client.rb +89 -0
  25. data/lib/splitclient-rb/clients/split_client.rb +114 -0
  26. data/lib/splitclient-rb/localhost_split_factory.rb +6 -187
  27. data/lib/splitclient-rb/localhost_utils.rb +36 -0
  28. data/lib/splitclient-rb/managers/localhost_split_manager.rb +45 -0
  29. data/lib/splitclient-rb/managers/split_manager.rb +77 -0
  30. data/lib/splitclient-rb/split_config.rb +20 -0
  31. data/lib/splitclient-rb/split_factory.rb +16 -217
  32. data/lib/splitclient-rb/split_factory_builder.rb +3 -2
  33. data/lib/splitclient-rb/version.rb +1 -1
  34. data/lib/splitclient-rb_utilitites.rb +24 -19
  35. data/splitclient-rb.gemspec +1 -1
  36. data/{splitio.yml → splitio.yml.example} +0 -0
  37. 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
- end
185
-
186
- def client
187
- @client ||= LocalhostSplitClient.new(@splits_file)
188
- end
7
+ @reload_rate = reload_rate
189
8
 
190
- def manager
191
- @manager ||= LocalhostSplitManager.new(@splits_file)
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