configcat 4.0.0 → 5.0.0
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/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.
|