configcat 4.0.0 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/configcat/autopollingcachepolicy.rb +3 -0
- data/lib/configcat/configcatclient.rb +92 -25
- data/lib/configcat/configfetcher.rb +15 -4
- data/lib/configcat/localdictionarydatasource.rb +20 -0
- data/lib/configcat/localfiledatasource.rb +49 -0
- data/lib/configcat/overridedatasource.rb +32 -0
- data/lib/configcat/rolloutevaluator.rb +122 -112
- data/lib/configcat/user.rb +19 -0
- data/lib/configcat/version.rb +1 -1
- data/lib/configcat.rb +31 -4
- metadata +9 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 84a383f0a76b49e3daf0072676592b23836937e54430b16fc3183c8f517c0dac
|
4
|
+
data.tar.gz: 8d56fcce1d537ce84360a4f9cdc36ac795102ed37015c6efc9e872b8d44cb765
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4bf91c58c110054015a41062f23dfd8d48b4998b892a255bc8872eab5c7a87209f244a99a1f0dd4face989d1da8cd73c64e3de0efa6154f01bf48f8a1e635f81
|
7
|
+
data.tar.gz: 52669ab34441b4abae836c0b474358a78ebbb1c0c1d56447d823eb51a1cfa2c32e999c27f2d5e904aba53a6dc925bf77e227f5e1897d087b585a7655cb07dc6c
|
@@ -82,6 +82,9 @@ module ConfigCat
|
|
82
82
|
if !@_initialized && !old_configuration.equal?(nil)
|
83
83
|
@_initialized = true
|
84
84
|
end
|
85
|
+
rescue Timeout::Error => e
|
86
|
+
ConfigCat.logger.error("Request timed out. Timeout values: [open: %ss, read: %ss]" %
|
87
|
+
[@_config_fetcher.get_open_timeout(), @_config_fetcher.get_read_timeout()])
|
85
88
|
rescue Exception => e
|
86
89
|
ConfigCat.logger.error("Double-check your SDK Key at https://app.configcat.com/sdkkey.")
|
87
90
|
ConfigCat.logger.error "threw exception #{e.class}:'#{e}'"
|
@@ -7,25 +7,41 @@ require 'configcat/lazyloadingcachepolicy'
|
|
7
7
|
require 'configcat/rolloutevaluator'
|
8
8
|
require 'configcat/datagovernance'
|
9
9
|
|
10
|
+
|
10
11
|
module ConfigCat
|
11
12
|
KeyValue = Struct.new(:key, :value)
|
12
13
|
class ConfigCatClient
|
14
|
+
@@sdk_keys = []
|
15
|
+
|
13
16
|
def initialize(sdk_key,
|
14
|
-
poll_interval_seconds:60,
|
15
|
-
max_init_wait_time_seconds:5,
|
16
|
-
on_configuration_changed_callback:nil,
|
17
|
-
cache_time_to_live_seconds:60,
|
18
|
-
config_cache_class:nil,
|
19
|
-
base_url:nil,
|
20
|
-
proxy_address:nil,
|
21
|
-
proxy_port:nil,
|
22
|
-
proxy_user:nil,
|
23
|
-
proxy_pass:nil,
|
17
|
+
poll_interval_seconds: 60,
|
18
|
+
max_init_wait_time_seconds: 5,
|
19
|
+
on_configuration_changed_callback: nil,
|
20
|
+
cache_time_to_live_seconds: 60,
|
21
|
+
config_cache_class: nil,
|
22
|
+
base_url: nil,
|
23
|
+
proxy_address: nil,
|
24
|
+
proxy_port: nil,
|
25
|
+
proxy_user: nil,
|
26
|
+
proxy_pass: nil,
|
27
|
+
open_timeout: 10,
|
28
|
+
read_timeout: 30,
|
29
|
+
flag_overrides: nil,
|
24
30
|
data_governance: DataGovernance::GLOBAL)
|
25
31
|
if sdk_key === nil
|
26
32
|
raise ConfigCatClientException, "SDK Key is required."
|
27
33
|
end
|
34
|
+
|
35
|
+
if @@sdk_keys.include?(sdk_key)
|
36
|
+
ConfigCat.logger.warn("A ConfigCat Client is already initialized with sdk_key %s. "\
|
37
|
+
"We strongly recommend you to use the ConfigCat Client as "\
|
38
|
+
"a Singleton object in your application." % sdk_key)
|
39
|
+
else
|
40
|
+
@@sdk_keys.push(sdk_key)
|
41
|
+
end
|
42
|
+
|
28
43
|
@_sdk_key = sdk_key
|
44
|
+
@_override_data_source = flag_overrides
|
29
45
|
|
30
46
|
if config_cache_class
|
31
47
|
@_config_cache = config_cache_class.new()
|
@@ -33,23 +49,35 @@ module ConfigCat
|
|
33
49
|
@_config_cache = InMemoryConfigCache.new()
|
34
50
|
end
|
35
51
|
|
36
|
-
if
|
37
|
-
@_config_fetcher =
|
52
|
+
if !@_override_data_source.equal?(nil) && @_override_data_source.get_behaviour() == OverrideBehaviour::LOCAL_ONLY
|
53
|
+
@_config_fetcher = nil
|
54
|
+
@_cache_policy = nil
|
55
|
+
elsif poll_interval_seconds > 0
|
56
|
+
@_config_fetcher = CacheControlConfigFetcher.new(sdk_key, "p", base_url: base_url,
|
57
|
+
proxy_address: proxy_address, proxy_port: proxy_port, proxy_user: proxy_user, proxy_pass: proxy_pass,
|
58
|
+
open_timeout: open_timeout, read_timeout: read_timeout,
|
59
|
+
data_governance: data_governance)
|
38
60
|
@_cache_policy = AutoPollingCachePolicy.new(@_config_fetcher, @_config_cache, _get_cache_key(), poll_interval_seconds, max_init_wait_time_seconds, on_configuration_changed_callback)
|
61
|
+
elsif cache_time_to_live_seconds > 0
|
62
|
+
@_config_fetcher = CacheControlConfigFetcher.new(sdk_key, "l", base_url: base_url,
|
63
|
+
proxy_address: proxy_address, proxy_port: proxy_port, proxy_user: proxy_user, proxy_pass: proxy_pass,
|
64
|
+
open_timeout: open_timeout, read_timeout: read_timeout,
|
65
|
+
data_governance: data_governance)
|
66
|
+
@_cache_policy = LazyLoadingCachePolicy.new(@_config_fetcher, @_config_cache, _get_cache_key(), cache_time_to_live_seconds)
|
39
67
|
else
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
@_cache_policy = ManualPollingCachePolicy.new(@_config_fetcher, @_config_cache, _get_cache_key())
|
46
|
-
end
|
68
|
+
@_config_fetcher = CacheControlConfigFetcher.new(sdk_key, "m", base_url: base_url,
|
69
|
+
proxy_address: proxy_address, proxy_port: proxy_port, proxy_user: proxy_user, proxy_pass: proxy_pass,
|
70
|
+
open_timeout: open_timeout, read_timeout: read_timeout,
|
71
|
+
data_governance: data_governance)
|
72
|
+
@_cache_policy = ManualPollingCachePolicy.new(@_config_fetcher, @_config_cache, _get_cache_key())
|
47
73
|
end
|
48
74
|
end
|
49
75
|
|
50
76
|
def get_value(key, default_value, user=nil)
|
51
|
-
config =
|
77
|
+
config = _get_settings()
|
52
78
|
if config === nil
|
79
|
+
ConfigCat.logger.warn("Evaluating get_value('%s') failed. Cache is empty. "\
|
80
|
+
"Returning default_value in your get_value call: [%s]." % [key, default_value.to_s])
|
53
81
|
return default_value
|
54
82
|
end
|
55
83
|
value, variation_id = RolloutEvaluator.evaluate(key, user, default_value, nil, config)
|
@@ -57,7 +85,7 @@ module ConfigCat
|
|
57
85
|
end
|
58
86
|
|
59
87
|
def get_all_keys()
|
60
|
-
config =
|
88
|
+
config = _get_settings()
|
61
89
|
if config === nil
|
62
90
|
return []
|
63
91
|
end
|
@@ -69,7 +97,7 @@ module ConfigCat
|
|
69
97
|
end
|
70
98
|
|
71
99
|
def get_variation_id(key, default_variation_id, user=nil)
|
72
|
-
config =
|
100
|
+
config = _get_settings()
|
73
101
|
if config === nil
|
74
102
|
ConfigCat.logger.warn("Evaluating get_variation_id('%s') failed. Cache is empty. "\
|
75
103
|
"Returning default_variation_id in your get_variation_id call: [%s]." %
|
@@ -93,7 +121,7 @@ module ConfigCat
|
|
93
121
|
end
|
94
122
|
|
95
123
|
def get_key_and_value(variation_id)
|
96
|
-
config =
|
124
|
+
config = _get_settings()
|
97
125
|
if config === nil
|
98
126
|
ConfigCat.logger.warn("Evaluating get_variation_id('%s') failed. Cache is empty. Returning nil." % variation_id)
|
99
127
|
return nil
|
@@ -126,17 +154,56 @@ module ConfigCat
|
|
126
154
|
end
|
127
155
|
end
|
128
156
|
|
157
|
+
def get_all_values(user: nil)
|
158
|
+
keys = get_all_keys()
|
159
|
+
all_values = {}
|
160
|
+
for key in keys
|
161
|
+
value = get_value(key, nil, user)
|
162
|
+
if !value.equal?(nil)
|
163
|
+
all_values[key] = value
|
164
|
+
end
|
165
|
+
end
|
166
|
+
return all_values
|
167
|
+
end
|
168
|
+
|
129
169
|
def force_refresh()
|
130
170
|
@_cache_policy.force_refresh()
|
131
171
|
end
|
132
172
|
|
133
173
|
def stop()
|
134
|
-
@_cache_policy.stop()
|
135
|
-
@_config_fetcher.close()
|
174
|
+
@_cache_policy.stop() if @_cache_policy
|
175
|
+
@_config_fetcher.close() if @_config_fetcher
|
176
|
+
@@sdk_keys.delete(@_sdk_key)
|
136
177
|
end
|
137
178
|
|
138
179
|
private
|
139
180
|
|
181
|
+
def _get_settings()
|
182
|
+
if !@_override_data_source.nil?
|
183
|
+
behaviour = @_override_data_source.get_behaviour()
|
184
|
+
if behaviour == OverrideBehaviour::LOCAL_ONLY
|
185
|
+
return @_override_data_source.get_overrides()
|
186
|
+
elsif behaviour == OverrideBehaviour::REMOTE_OVER_LOCAL
|
187
|
+
remote_settings = @_cache_policy.get()
|
188
|
+
local_settings = @_override_data_source.get_overrides()
|
189
|
+
result = local_settings.clone()
|
190
|
+
if remote_settings.key?(FEATURE_FLAGS) && local_settings.key?(FEATURE_FLAGS)
|
191
|
+
result[FEATURE_FLAGS] = result[FEATURE_FLAGS].merge(remote_settings[FEATURE_FLAGS])
|
192
|
+
end
|
193
|
+
return result
|
194
|
+
elsif behaviour == OverrideBehaviour::LOCAL_OVER_REMOTE
|
195
|
+
remote_settings = @_cache_policy.get()
|
196
|
+
local_settings = @_override_data_source.get_overrides()
|
197
|
+
result = remote_settings.clone()
|
198
|
+
if remote_settings.key?(FEATURE_FLAGS) && local_settings.key?(FEATURE_FLAGS)
|
199
|
+
result[FEATURE_FLAGS] = result[FEATURE_FLAGS].merge(local_settings[FEATURE_FLAGS])
|
200
|
+
end
|
201
|
+
return result
|
202
|
+
end
|
203
|
+
end
|
204
|
+
return @_cache_policy.get()
|
205
|
+
end
|
206
|
+
|
140
207
|
def _get_cache_key()
|
141
208
|
return Digest::SHA1.hexdigest("ruby_" + CONFIG_FILE_NAME + "_" + @_sdk_key)
|
142
209
|
end
|
@@ -41,13 +41,16 @@ module ConfigCat
|
|
41
41
|
end
|
42
42
|
|
43
43
|
class CacheControlConfigFetcher < ConfigFetcher
|
44
|
-
def initialize(sdk_key, mode, base_url
|
45
|
-
|
44
|
+
def initialize(sdk_key, mode, base_url:nil, proxy_address:nil, proxy_port:nil, proxy_user:nil, proxy_pass:nil,
|
45
|
+
open_timeout:10, read_timeout:30,
|
46
|
+
data_governance:DataGovernance::GLOBAL)
|
46
47
|
@_sdk_key = sdk_key
|
47
48
|
@_proxy_address = proxy_address
|
48
49
|
@_proxy_port = proxy_port
|
49
50
|
@_proxy_user = proxy_user
|
50
51
|
@_proxy_pass = proxy_pass
|
52
|
+
@_open_timeout = open_timeout
|
53
|
+
@_read_timeout = read_timeout
|
51
54
|
@_etag = ""
|
52
55
|
@_headers = {"User-Agent" => ((("ConfigCat-Ruby/") + mode) + ("-")) + VERSION, "X-ConfigCat-UserAgent" => ((("ConfigCat-Ruby/") + mode) + ("-")) + VERSION, "Content-Type" => "application/json"}
|
53
56
|
if !base_url.equal?(nil)
|
@@ -63,6 +66,14 @@ module ConfigCat
|
|
63
66
|
end
|
64
67
|
end
|
65
68
|
|
69
|
+
def get_open_timeout()
|
70
|
+
return @_open_timeout
|
71
|
+
end
|
72
|
+
|
73
|
+
def get_read_timeout()
|
74
|
+
return @_read_timeout
|
75
|
+
end
|
76
|
+
|
66
77
|
# Returns the FetchResponse object contains configuration json Dictionary
|
67
78
|
def get_configuration_json(retries=0)
|
68
79
|
ConfigCat.logger.debug "Fetching configuration from ConfigCat"
|
@@ -141,8 +152,8 @@ module ConfigCat
|
|
141
152
|
close()
|
142
153
|
@_http = Net::HTTP.new(uri.host, uri.port, @_proxy_address, @_proxy_port, @_proxy_user, @_proxy_pass)
|
143
154
|
@_http.use_ssl = use_ssl
|
144
|
-
@_http.open_timeout =
|
145
|
-
@_http.read_timeout =
|
155
|
+
@_http.open_timeout = @_open_timeout
|
156
|
+
@_http.read_timeout = @_read_timeout
|
146
157
|
end
|
147
158
|
end
|
148
159
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'configcat/overridedatasource'
|
2
|
+
require 'configcat/constants'
|
3
|
+
|
4
|
+
|
5
|
+
module ConfigCat
|
6
|
+
class LocalDictionaryDataSource < OverrideDataSource
|
7
|
+
def initialize(source, override_behaviour)
|
8
|
+
super(override_behaviour)
|
9
|
+
dictionary = {}
|
10
|
+
source.each do |key, value|
|
11
|
+
dictionary[key] = {VALUE => value}
|
12
|
+
end
|
13
|
+
@_settings = {FEATURE_FLAGS => dictionary}
|
14
|
+
end
|
15
|
+
|
16
|
+
def get_overrides()
|
17
|
+
return @_settings
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'configcat/overridedatasource'
|
2
|
+
require 'configcat/constants'
|
3
|
+
|
4
|
+
|
5
|
+
module ConfigCat
|
6
|
+
class LocalFileDataSource < OverrideDataSource
|
7
|
+
def initialize(file_path, override_behaviour)
|
8
|
+
super(override_behaviour)
|
9
|
+
if File.exists?(file_path)
|
10
|
+
ConfigCat.logger.error("The file '%s' does not exists." % file_path)
|
11
|
+
end
|
12
|
+
@_file_path = file_path
|
13
|
+
@_settings = nil
|
14
|
+
@_cached_file_stamp = 0
|
15
|
+
end
|
16
|
+
|
17
|
+
def get_overrides()
|
18
|
+
reload_file_content()
|
19
|
+
return @_settings
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def reload_file_content()
|
25
|
+
begin
|
26
|
+
stamp = File.mtime(@_file_path)
|
27
|
+
if stamp != @_cached_file_stamp
|
28
|
+
@_cached_file_stamp = stamp
|
29
|
+
file = File.read(@_file_path)
|
30
|
+
data = JSON.parse(file)
|
31
|
+
if data.key?("flags")
|
32
|
+
dictionary = {}
|
33
|
+
source = data["flags"]
|
34
|
+
source.each do |key, value|
|
35
|
+
dictionary[key] = {VALUE => value}
|
36
|
+
end
|
37
|
+
@_settings = {FEATURE_FLAGS => dictionary}
|
38
|
+
else
|
39
|
+
@_settings = data
|
40
|
+
end
|
41
|
+
end
|
42
|
+
rescue JSON::ParserError => e
|
43
|
+
ConfigCat.logger.error("Could not decode json from file %s. %s" % [@_file_path, e.to_s])
|
44
|
+
rescue Exception => e
|
45
|
+
ConfigCat.logger.error("Could not read the content of the file %s. %s" % [@_file_path, e.to_s])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ConfigCat
|
2
|
+
class OverrideBehaviour
|
3
|
+
# When evaluating values, the SDK will not use feature flags & settings from the ConfigCat CDN, but it will use
|
4
|
+
# all feature flags & settings that are loaded from local-override sources.
|
5
|
+
LOCAL_ONLY = 0
|
6
|
+
|
7
|
+
# When evaluating values, the SDK will use all feature flags & settings that are downloaded from the ConfigCat CDN,
|
8
|
+
# plus all feature flags & settings that are loaded from local-override sources. If a feature flag or a setting is
|
9
|
+
# defined both in the fetched and the local-override source then the local-override version will take precedence.
|
10
|
+
LOCAL_OVER_REMOTE = 1
|
11
|
+
|
12
|
+
# When evaluating values, the SDK will use all feature flags & settings that are downloaded from the ConfigCat CDN,
|
13
|
+
# plus all feature flags & settings that are loaded from local-override sources. If a feature flag or a setting is
|
14
|
+
# defined both in the fetched and the local-override source then the fetched version will take precedence.
|
15
|
+
REMOTE_OVER_LOCAL = 2
|
16
|
+
end
|
17
|
+
|
18
|
+
class OverrideDataSource
|
19
|
+
def initialize(override_behaviour)
|
20
|
+
@_override_behaviour = override_behaviour
|
21
|
+
end
|
22
|
+
|
23
|
+
def get_behaviour()
|
24
|
+
return @_override_behaviour
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_overrides()
|
28
|
+
# :returns the override dictionary
|
29
|
+
return {}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -39,132 +39,142 @@ module ConfigCat
|
|
39
39
|
return return_value, return_variation_id
|
40
40
|
end
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
42
|
+
log_entries = ["Evaluating get_value('%s')." % key, "User object:\n%s" % user.to_s]
|
43
|
+
|
44
|
+
begin
|
45
|
+
# Evaluate targeting rules
|
46
|
+
for rollout_rule in rollout_rules
|
47
|
+
comparison_attribute = rollout_rule.fetch(COMPARISON_ATTRIBUTE)
|
48
|
+
comparison_value = rollout_rule.fetch(COMPARISON_VALUE, nil)
|
49
|
+
comparator = rollout_rule.fetch(COMPARATOR, nil)
|
50
|
+
|
51
|
+
user_value = user.get_attribute(comparison_attribute)
|
52
|
+
if user_value === nil || !user_value
|
53
|
+
log_entries.push(format_no_match_rule(comparison_attribute, user_value, comparator, comparison_value))
|
54
|
+
next
|
55
|
+
end
|
55
56
|
|
56
|
-
|
57
|
-
|
57
|
+
value = rollout_rule.fetch(VALUE, nil)
|
58
|
+
variation_id = rollout_rule.fetch(VARIATION_ID, default_variation_id)
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
return value, variation_id
|
64
|
-
end
|
65
|
-
# IS NOT ONE OF
|
66
|
-
elsif comparator == 1
|
67
|
-
if !comparison_value.to_s.split(",").map { |x| x.strip() }.include?(user_value.to_s)
|
68
|
-
ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
69
|
-
return value, variation_id
|
70
|
-
end
|
71
|
-
# CONTAINS
|
72
|
-
elsif comparator == 2
|
73
|
-
if user_value.to_s.include?(comparison_value.to_s)
|
74
|
-
ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
75
|
-
return value, variation_id
|
76
|
-
end
|
77
|
-
# DOES NOT CONTAIN
|
78
|
-
elsif comparator == 3
|
79
|
-
if !user_value.to_s.include?(comparison_value.to_s)
|
80
|
-
ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
81
|
-
return value, variation_id
|
82
|
-
end
|
83
|
-
# IS ONE OF, IS NOT ONE OF (Semantic version)
|
84
|
-
elsif (4 <= comparator) && (comparator <= 5)
|
85
|
-
begin
|
86
|
-
match = false
|
87
|
-
user_value_version = Semantic::Version.new(user_value.to_s.strip())
|
88
|
-
((comparison_value.to_s.split(",").map { |x| x.strip() }).reject { |c| c.empty? }).each { |x|
|
89
|
-
version = Semantic::Version.new(x)
|
90
|
-
match = (user_value_version == version) || match
|
91
|
-
}
|
92
|
-
if match && comparator == 4 || !match && comparator == 5
|
93
|
-
ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
60
|
+
# IS ONE OF
|
61
|
+
if comparator == 0
|
62
|
+
if comparison_value.to_s.split(",").map { |x| x.strip() }.include?(user_value.to_s)
|
63
|
+
log_entries.push(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
94
64
|
return value, variation_id
|
95
65
|
end
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
# LESS THAN, LESS THAN OR EQUALS TO, GREATER THAN, GREATER THAN OR EQUALS TO (Semantic version)
|
101
|
-
elsif (6 <= comparator) && (comparator <= 9)
|
102
|
-
begin
|
103
|
-
user_value_version = Semantic::Version.new(user_value.to_s.strip())
|
104
|
-
comparison_value_version = Semantic::Version.new(comparison_value.to_s.strip())
|
105
|
-
if (comparator == 6 && user_value_version < comparison_value_version) ||
|
106
|
-
(comparator == 7 && user_value_version <= comparison_value_version) ||
|
107
|
-
(comparator == 8 && user_value_version > comparison_value_version) ||
|
108
|
-
(comparator == 9 && user_value_version >= comparison_value_version)
|
109
|
-
ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
66
|
+
# IS NOT ONE OF
|
67
|
+
elsif comparator == 1
|
68
|
+
if !comparison_value.to_s.split(",").map { |x| x.strip() }.include?(user_value.to_s)
|
69
|
+
log_entries.push(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
110
70
|
return value, variation_id
|
111
71
|
end
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
if (
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
72
|
+
# CONTAINS
|
73
|
+
elsif comparator == 2
|
74
|
+
if user_value.to_s.include?(comparison_value.to_s)
|
75
|
+
log_entries.push(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
76
|
+
return value, variation_id
|
77
|
+
end
|
78
|
+
# DOES NOT CONTAIN
|
79
|
+
elsif comparator == 3
|
80
|
+
if !user_value.to_s.include?(comparison_value.to_s)
|
81
|
+
log_entries.push(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
82
|
+
return value, variation_id
|
83
|
+
end
|
84
|
+
# IS ONE OF, IS NOT ONE OF (Semantic version)
|
85
|
+
elsif (4 <= comparator) && (comparator <= 5)
|
86
|
+
begin
|
87
|
+
match = false
|
88
|
+
user_value_version = Semantic::Version.new(user_value.to_s.strip())
|
89
|
+
((comparison_value.to_s.split(",").map { |x| x.strip() }).reject { |c| c.empty? }).each { |x|
|
90
|
+
version = Semantic::Version.new(x)
|
91
|
+
match = (user_value_version == version) || match
|
92
|
+
}
|
93
|
+
if match && comparator == 4 || !match && comparator == 5
|
94
|
+
log_entries.push(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
95
|
+
return value, variation_id
|
96
|
+
end
|
97
|
+
rescue ArgumentError => e
|
98
|
+
message = format_validation_error_rule(comparison_attribute, user_value, comparator, comparison_value, e.to_s)
|
99
|
+
ConfigCat.logger.warn(message)
|
100
|
+
log_entries.push(message)
|
101
|
+
next
|
102
|
+
end
|
103
|
+
# LESS THAN, LESS THAN OR EQUALS TO, GREATER THAN, GREATER THAN OR EQUALS TO (Semantic version)
|
104
|
+
elsif (6 <= comparator) && (comparator <= 9)
|
105
|
+
begin
|
106
|
+
user_value_version = Semantic::Version.new(user_value.to_s.strip())
|
107
|
+
comparison_value_version = Semantic::Version.new(comparison_value.to_s.strip())
|
108
|
+
if (comparator == 6 && user_value_version < comparison_value_version) ||
|
109
|
+
(comparator == 7 && user_value_version <= comparison_value_version) ||
|
110
|
+
(comparator == 8 && user_value_version > comparison_value_version) ||
|
111
|
+
(comparator == 9 && user_value_version >= comparison_value_version)
|
112
|
+
log_entries.push(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
113
|
+
return value, variation_id
|
114
|
+
end
|
115
|
+
rescue ArgumentError => e
|
116
|
+
message = format_validation_error_rule(comparison_attribute, user_value, comparator, comparison_value, e.to_s)
|
117
|
+
ConfigCat.logger.warn(message)
|
118
|
+
log_entries.push(message)
|
119
|
+
next
|
120
|
+
end
|
121
|
+
elsif (10 <= comparator) && (comparator <= 15)
|
122
|
+
begin
|
123
|
+
user_value_float = Float(user_value.to_s.gsub(",", "."))
|
124
|
+
comparison_value_float = Float(comparison_value.to_s.gsub(",", "."))
|
125
|
+
if (comparator == 10 && user_value_float == comparison_value_float) ||
|
126
|
+
(comparator == 11 && user_value_float != comparison_value_float) ||
|
127
|
+
(comparator == 12 && user_value_float < comparison_value_float) ||
|
128
|
+
(comparator == 13 && user_value_float <= comparison_value_float) ||
|
129
|
+
(comparator == 14 && user_value_float > comparison_value_float) ||
|
130
|
+
(comparator == 15 && user_value_float >= comparison_value_float)
|
131
|
+
log_entries.push(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
132
|
+
return value, variation_id
|
133
|
+
end
|
134
|
+
rescue Exception => e
|
135
|
+
message = format_validation_error_rule(comparison_attribute, user_value, comparator, comparison_value, e.to_s)
|
136
|
+
ConfigCat.logger.warn(message)
|
137
|
+
log_entries.push(message)
|
138
|
+
next
|
139
|
+
end
|
140
|
+
# IS ONE OF (Sensitive)
|
141
|
+
elsif comparator == 16
|
142
|
+
if comparison_value.to_s.split(",").map { |x| x.strip() }.include?(Digest::SHA1.hexdigest(user_value).to_s)
|
143
|
+
log_entries.push(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
144
|
+
return value, variation_id
|
145
|
+
end
|
146
|
+
# IS NOT ONE OF (Sensitive)
|
147
|
+
elsif comparator == 17
|
148
|
+
if !comparison_value.to_s.split(",").map { |x| x.strip() }.include?(Digest::SHA1.hexdigest(user_value).to_s)
|
149
|
+
log_entries.push(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
127
150
|
return value, variation_id
|
128
151
|
end
|
129
|
-
rescue Exception => e
|
130
|
-
ConfigCat.logger.warn(format_validation_error_rule(comparison_attribute, user_value, comparator, comparison_value, e.to_s))
|
131
|
-
next
|
132
|
-
end
|
133
|
-
# IS ONE OF (Sensitive)
|
134
|
-
elsif comparator == 16
|
135
|
-
if comparison_value.to_s.split(",").map { |x| x.strip() }.include?(Digest::SHA1.hexdigest(user_value).to_s)
|
136
|
-
ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
137
|
-
return value, variation_id
|
138
|
-
end
|
139
|
-
# IS NOT ONE OF (Sensitive)
|
140
|
-
elsif comparator == 17
|
141
|
-
if !comparison_value.to_s.split(",").map { |x| x.strip() }.include?(Digest::SHA1.hexdigest(user_value).to_s)
|
142
|
-
ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
143
|
-
return value, variation_id
|
144
152
|
end
|
153
|
+
log_entries.push(format_no_match_rule(comparison_attribute, user_value, comparator, comparison_value))
|
145
154
|
end
|
146
|
-
ConfigCat.logger.info(format_no_match_rule(comparison_attribute, user_value, comparator, comparison_value))
|
147
|
-
end
|
148
155
|
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
156
|
+
if rollout_percentage_items.size > 0
|
157
|
+
user_key = user.get_identifier()
|
158
|
+
hash_candidate = ("%s%s" % [key, user_key]).encode("utf-8")
|
159
|
+
hash_val = Digest::SHA1.hexdigest(hash_candidate)[0...7].to_i(base=16) % 100
|
160
|
+
bucket = 0
|
161
|
+
for rollout_percentage_item in rollout_percentage_items || []
|
162
|
+
bucket += rollout_percentage_item.fetch(PERCENTAGE, 0)
|
163
|
+
if hash_val < bucket
|
164
|
+
percentage_value = rollout_percentage_item.fetch(VALUE, nil)
|
165
|
+
variation_id = rollout_percentage_item.fetch(VARIATION_ID, default_variation_id)
|
166
|
+
log_entries.push("Evaluating %% options. Returning %s" % percentage_value)
|
167
|
+
return percentage_value, variation_id
|
168
|
+
end
|
161
169
|
end
|
162
170
|
end
|
171
|
+
return_value = setting_descriptor.fetch(VALUE, default_value)
|
172
|
+
return_variation_id = setting_descriptor.fetch(VARIATION_ID, default_variation_id)
|
173
|
+
log_entries.push("Returning %s" % return_value)
|
174
|
+
return return_value, return_variation_id
|
175
|
+
ensure
|
176
|
+
ConfigCat.logger.info(log_entries.join("\n"))
|
163
177
|
end
|
164
|
-
return_value = setting_descriptor.fetch(VALUE, default_value)
|
165
|
-
return_variation_id = setting_descriptor.fetch(VARIATION_ID, default_variation_id)
|
166
|
-
ConfigCat.logger.info("Returning %s" % return_value)
|
167
|
-
return return_value, return_variation_id
|
168
178
|
end
|
169
179
|
|
170
180
|
private
|
data/lib/configcat/user.rb
CHANGED
@@ -32,6 +32,25 @@ module ConfigCat
|
|
32
32
|
end
|
33
33
|
return nil
|
34
34
|
end
|
35
|
+
|
36
|
+
def to_s()
|
37
|
+
r = %Q({\n "Identifier": "#{@__identifier}")
|
38
|
+
if !@__data["Email"].equal?(nil)
|
39
|
+
r += %Q(,\n "Email": "#{@__data["Email"]}")
|
40
|
+
end
|
41
|
+
if !@__data["Country"].equal?(nil)
|
42
|
+
r += %Q(,\n "Country": "#{@__data["Country"]}")
|
43
|
+
end
|
44
|
+
if !@__custom.equal?(nil)
|
45
|
+
r += %Q(,\n "Custom": {)
|
46
|
+
for customField in @__custom
|
47
|
+
r += %Q(\n "#{customField}": "#{@__custom[customField]}",)
|
48
|
+
end
|
49
|
+
r += "\n }"
|
50
|
+
end
|
51
|
+
r += "\n}"
|
52
|
+
return r
|
53
|
+
end
|
35
54
|
end
|
36
55
|
|
37
56
|
end
|
data/lib/configcat/version.rb
CHANGED
data/lib/configcat.rb
CHANGED
@@ -33,6 +33,9 @@ module ConfigCat
|
|
33
33
|
proxy_port: nil,
|
34
34
|
proxy_user: nil,
|
35
35
|
proxy_pass: nil,
|
36
|
+
open_timeout: 10,
|
37
|
+
read_timeout: 30,
|
38
|
+
flag_overrides: nil,
|
36
39
|
data_governance: DataGovernance::GLOBAL)
|
37
40
|
#
|
38
41
|
# Create an instance of ConfigCatClient and setup Auto Poll mode with custom options
|
@@ -48,6 +51,9 @@ module ConfigCat
|
|
48
51
|
# :param proxy_port: Proxy port
|
49
52
|
# :param proxy_user: username for proxy authentication
|
50
53
|
# :param proxy_pass: password for proxy authentication
|
54
|
+
# :param open_timeout: The number of seconds to wait for the server to make the initial connection. Default: 10 seconds.
|
55
|
+
# :param read_timeout: The number of seconds to wait for the server to respond before giving up. Default: 30 seconds.
|
56
|
+
# :param flag_overrides: An OverrideDataSource implementation used to override feature flags & settings.
|
51
57
|
# :param data_governance:
|
52
58
|
# Default: Global. Set this parameter to be in sync with the Data Governance preference on the Dashboard:
|
53
59
|
# https://app.configcat.com/organization/data-governance
|
@@ -73,6 +79,9 @@ module ConfigCat
|
|
73
79
|
proxy_port: proxy_port,
|
74
80
|
proxy_user: proxy_user,
|
75
81
|
proxy_pass: proxy_pass,
|
82
|
+
open_timeout: open_timeout,
|
83
|
+
read_timeout: read_timeout,
|
84
|
+
flag_overrides: flag_overrides,
|
76
85
|
data_governance: data_governance)
|
77
86
|
end
|
78
87
|
|
@@ -84,6 +93,9 @@ module ConfigCat
|
|
84
93
|
proxy_port: nil,
|
85
94
|
proxy_user: nil,
|
86
95
|
proxy_pass: nil,
|
96
|
+
open_timeout: 10,
|
97
|
+
read_timeout: 30,
|
98
|
+
flag_overrides: nil,
|
87
99
|
data_governance: DataGovernance::GLOBAL)
|
88
100
|
#
|
89
101
|
# Create an instance of ConfigCatClient and setup Lazy Load mode with custom options
|
@@ -97,6 +109,9 @@ module ConfigCat
|
|
97
109
|
# :param proxy_port: Proxy port
|
98
110
|
# :param proxy_user: username for proxy authentication
|
99
111
|
# :param proxy_pass: password for proxy authentication
|
112
|
+
# :param open_timeout: The number of seconds to wait for the server to make the initial connection. Default: 10 seconds.
|
113
|
+
# :param read_timeout: The number of seconds to wait for the server to respond before giving up. Default: 30 seconds.
|
114
|
+
# :param flag_overrides: An OverrideDataSource implementation used to override feature flags & settings.
|
100
115
|
# :param data_governance:
|
101
116
|
# Default: Global. Set this parameter to be in sync with the Data Governance preference on the Dashboard:
|
102
117
|
# https://app.configcat.com/organization/data-governance
|
@@ -119,16 +134,22 @@ module ConfigCat
|
|
119
134
|
proxy_port: proxy_port,
|
120
135
|
proxy_user: proxy_user,
|
121
136
|
proxy_pass: proxy_pass,
|
137
|
+
open_timeout: open_timeout,
|
138
|
+
read_timeout: read_timeout,
|
139
|
+
flag_overrides: flag_overrides,
|
122
140
|
data_governance: data_governance)
|
123
141
|
end
|
124
142
|
|
125
143
|
def ConfigCat.create_client_with_manual_poll(sdk_key,
|
126
144
|
config_cache_class: nil,
|
127
145
|
base_url: nil,
|
128
|
-
proxy_address:nil,
|
129
|
-
proxy_port:nil,
|
130
|
-
proxy_user:nil,
|
131
|
-
proxy_pass:nil,
|
146
|
+
proxy_address: nil,
|
147
|
+
proxy_port: nil,
|
148
|
+
proxy_user: nil,
|
149
|
+
proxy_pass: nil,
|
150
|
+
open_timeout: 10,
|
151
|
+
read_timeout: 30,
|
152
|
+
flag_overrides: nil,
|
132
153
|
data_governance: DataGovernance::GLOBAL)
|
133
154
|
#
|
134
155
|
# Create an instance of ConfigCatClient and setup Manual Poll mode with custom options
|
@@ -141,6 +162,9 @@ module ConfigCat
|
|
141
162
|
# :param proxy_port: Proxy port
|
142
163
|
# :param proxy_user: username for proxy authentication
|
143
164
|
# :param proxy_pass: password for proxy authentication
|
165
|
+
# :param open_timeout: The number of seconds to wait for the server to make the initial connection. Default: 10 seconds.
|
166
|
+
# :param read_timeout: The number of seconds to wait for the server to respond before giving up. Default: 30 seconds.
|
167
|
+
# :param flag_overrides: An OverrideDataSource implementation used to override feature flags & settings.
|
144
168
|
# :param data_governance:
|
145
169
|
# Default: Global. Set this parameter to be in sync with the Data Governance preference on the Dashboard:
|
146
170
|
# https://app.configcat.com/organization/data-governance
|
@@ -160,6 +184,9 @@ module ConfigCat
|
|
160
184
|
proxy_port: proxy_port,
|
161
185
|
proxy_user: proxy_user,
|
162
186
|
proxy_pass: proxy_pass,
|
187
|
+
open_timeout: open_timeout,
|
188
|
+
read_timeout: read_timeout,
|
189
|
+
flag_overrides: flag_overrides,
|
163
190
|
data_governance: data_governance)
|
164
191
|
end
|
165
192
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: configcat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 5.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ConfigCat
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-04-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -67,19 +67,19 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '12.3'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: codecov
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '0.
|
75
|
+
version: '0.5'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '0.
|
82
|
+
version: '0.5'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: webmock
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,7 +114,10 @@ files:
|
|
114
114
|
- lib/configcat/datagovernance.rb
|
115
115
|
- lib/configcat/interfaces.rb
|
116
116
|
- lib/configcat/lazyloadingcachepolicy.rb
|
117
|
+
- lib/configcat/localdictionarydatasource.rb
|
118
|
+
- lib/configcat/localfiledatasource.rb
|
117
119
|
- lib/configcat/manualpollingcachepolicy.rb
|
120
|
+
- lib/configcat/overridedatasource.rb
|
118
121
|
- lib/configcat/rolloutevaluator.rb
|
119
122
|
- lib/configcat/user.rb
|
120
123
|
- lib/configcat/version.rb
|
@@ -137,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
137
140
|
- !ruby/object:Gem::Version
|
138
141
|
version: '0'
|
139
142
|
requirements: []
|
140
|
-
rubygems_version: 3.0.
|
143
|
+
rubygems_version: 3.0.3.1
|
141
144
|
signing_key:
|
142
145
|
specification_version: 4
|
143
146
|
summary: ConfigCat SDK for Ruby.
|