configcat 2.0.2 → 4.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.rb +52 -30
- data/lib/configcat/autopollingcachepolicy.rb +23 -18
- data/lib/configcat/configcache.rb +5 -5
- data/lib/configcat/configcatclient.rb +85 -13
- data/lib/configcat/configfetcher.rb +120 -18
- data/lib/configcat/constants.rb +17 -0
- data/lib/configcat/datagovernance.rb +10 -0
- data/lib/configcat/interfaces.rb +2 -2
- data/lib/configcat/lazyloadingcachepolicy.rb +17 -11
- data/lib/configcat/manualpollingcachepolicy.rb +14 -9
- data/lib/configcat/rolloutevaluator.rb +29 -26
- data/lib/configcat/user.rb +5 -5
- data/lib/configcat/version.rb +1 -1
- metadata +36 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 118a3db79c8d7d9a0795897380a4dfaf14fecda8a636024abfd9d2433b3af924
|
4
|
+
data.tar.gz: 4c85c82b81fe08fcf9b6eed0a40bea06369939301aa09bc12e82b33a3b2176eb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: afe03eda0affc92755ae0eb91196a15bfe61f68a12244270bb5d19b3870bc023fe9ccf3231d8f11f414c4439b49fd2f9481e9a64295c3ab87697511ebcf57b25
|
7
|
+
data.tar.gz: 5ac8e3beef89cbc84ef89f2eb5b071fcf5cb348db1c302755ba4fc619c759f9919f80785b2b08a4a583725d35ea6b9ef038d869706956485eec3b4a9d4cadbaa
|
data/lib/configcat.rb
CHANGED
@@ -10,29 +10,34 @@ module ConfigCat
|
|
10
10
|
attr_accessor :logger
|
11
11
|
end
|
12
12
|
|
13
|
-
def ConfigCat.create_client(
|
13
|
+
def ConfigCat.create_client(sdk_key, data_governance: DataGovernance::GLOBAL)
|
14
14
|
#
|
15
15
|
# Create an instance of ConfigCatClient and setup Auto Poll mode with default options
|
16
16
|
#
|
17
|
-
# :param
|
17
|
+
# :param sdk_key: ConfigCat SDK Key to access your configuration.
|
18
|
+
# :param data_governance:
|
19
|
+
# Default: Global. Set this parameter to be in sync with the Data Governance preference on the Dashboard:
|
20
|
+
# https://app.configcat.com/organization/data-governance
|
21
|
+
# (Only Organization Admins have access)
|
18
22
|
#
|
19
|
-
return create_client_with_auto_poll(
|
23
|
+
return create_client_with_auto_poll(sdk_key, data_governance: data_governance)
|
20
24
|
end
|
21
25
|
|
22
|
-
def ConfigCat.create_client_with_auto_poll(
|
26
|
+
def ConfigCat.create_client_with_auto_poll(sdk_key,
|
23
27
|
poll_interval_seconds: 60,
|
24
28
|
max_init_wait_time_seconds: 5,
|
25
29
|
on_configuration_changed_callback: nil,
|
26
30
|
config_cache_class: nil,
|
27
31
|
base_url: nil,
|
28
|
-
proxy_address:nil,
|
29
|
-
proxy_port:nil,
|
30
|
-
proxy_user:nil,
|
31
|
-
proxy_pass:nil
|
32
|
+
proxy_address: nil,
|
33
|
+
proxy_port: nil,
|
34
|
+
proxy_user: nil,
|
35
|
+
proxy_pass: nil,
|
36
|
+
data_governance: DataGovernance::GLOBAL)
|
32
37
|
#
|
33
38
|
# Create an instance of ConfigCatClient and setup Auto Poll mode with custom options
|
34
39
|
#
|
35
|
-
# :param
|
40
|
+
# :param sdk_key: ConfigCat SDK Key to access your configuration.
|
36
41
|
# :param poll_interval_seconds: The client's poll interval in seconds. Default: 60 seconds.
|
37
42
|
# :param on_configuration_changed_callback: You can subscribe to configuration changes with this callback
|
38
43
|
# :param max_init_wait_time_seconds: maximum waiting time for first configuration fetch in polling mode.
|
@@ -43,9 +48,13 @@ module ConfigCat
|
|
43
48
|
# :param proxy_port: Proxy port
|
44
49
|
# :param proxy_user: username for proxy authentication
|
45
50
|
# :param proxy_pass: password for proxy authentication
|
51
|
+
# :param data_governance:
|
52
|
+
# Default: Global. Set this parameter to be in sync with the Data Governance preference on the Dashboard:
|
53
|
+
# https://app.configcat.com/organization/data-governance
|
54
|
+
# (Only Organization Admins have access)
|
46
55
|
#
|
47
|
-
if
|
48
|
-
raise ConfigCatClientException, "
|
56
|
+
if sdk_key === nil
|
57
|
+
raise ConfigCatClientException, "SDK Key is required."
|
49
58
|
end
|
50
59
|
if poll_interval_seconds < 1
|
51
60
|
poll_interval_seconds = 1
|
@@ -53,7 +62,7 @@ module ConfigCat
|
|
53
62
|
if max_init_wait_time_seconds < 0
|
54
63
|
max_init_wait_time_seconds = 0
|
55
64
|
end
|
56
|
-
return ConfigCatClient.new(
|
65
|
+
return ConfigCatClient.new(sdk_key,
|
57
66
|
poll_interval_seconds: poll_interval_seconds,
|
58
67
|
max_init_wait_time_seconds: max_init_wait_time_seconds,
|
59
68
|
on_configuration_changed_callback: on_configuration_changed_callback,
|
@@ -63,21 +72,23 @@ module ConfigCat
|
|
63
72
|
proxy_address: proxy_address,
|
64
73
|
proxy_port: proxy_port,
|
65
74
|
proxy_user: proxy_user,
|
66
|
-
proxy_pass: proxy_pass
|
75
|
+
proxy_pass: proxy_pass,
|
76
|
+
data_governance: data_governance)
|
67
77
|
end
|
68
78
|
|
69
|
-
def ConfigCat.create_client_with_lazy_load(
|
79
|
+
def ConfigCat.create_client_with_lazy_load(sdk_key,
|
70
80
|
cache_time_to_live_seconds: 60,
|
71
81
|
config_cache_class: nil,
|
72
82
|
base_url: nil,
|
73
|
-
proxy_address:nil,
|
74
|
-
proxy_port:nil,
|
75
|
-
proxy_user:nil,
|
76
|
-
proxy_pass:nil
|
83
|
+
proxy_address: nil,
|
84
|
+
proxy_port: nil,
|
85
|
+
proxy_user: nil,
|
86
|
+
proxy_pass: nil,
|
87
|
+
data_governance: DataGovernance::GLOBAL)
|
77
88
|
#
|
78
89
|
# Create an instance of ConfigCatClient and setup Lazy Load mode with custom options
|
79
90
|
#
|
80
|
-
# :param
|
91
|
+
# :param sdk_key: ConfigCat SDK Key to access your configuration.
|
81
92
|
# :param cache_time_to_live_seconds: The cache TTL.
|
82
93
|
# :param config_cache_class: If you want to use custom caching instead of the client's default InMemoryConfigCache,
|
83
94
|
# You can provide an implementation of ConfigCache.
|
@@ -86,14 +97,18 @@ module ConfigCat
|
|
86
97
|
# :param proxy_port: Proxy port
|
87
98
|
# :param proxy_user: username for proxy authentication
|
88
99
|
# :param proxy_pass: password for proxy authentication
|
100
|
+
# :param data_governance:
|
101
|
+
# Default: Global. Set this parameter to be in sync with the Data Governance preference on the Dashboard:
|
102
|
+
# https://app.configcat.com/organization/data-governance
|
103
|
+
# (Only Organization Admins have access)
|
89
104
|
#
|
90
|
-
if
|
91
|
-
raise ConfigCatClientException, "
|
105
|
+
if sdk_key === nil
|
106
|
+
raise ConfigCatClientException, "SDK Key is required."
|
92
107
|
end
|
93
108
|
if cache_time_to_live_seconds < 1
|
94
109
|
cache_time_to_live_seconds = 1
|
95
110
|
end
|
96
|
-
return ConfigCatClient.new(
|
111
|
+
return ConfigCatClient.new(sdk_key,
|
97
112
|
poll_interval_seconds: 0,
|
98
113
|
max_init_wait_time_seconds: 0,
|
99
114
|
on_configuration_changed_callback: nil,
|
@@ -103,20 +118,22 @@ module ConfigCat
|
|
103
118
|
proxy_address: proxy_address,
|
104
119
|
proxy_port: proxy_port,
|
105
120
|
proxy_user: proxy_user,
|
106
|
-
proxy_pass: proxy_pass
|
121
|
+
proxy_pass: proxy_pass,
|
122
|
+
data_governance: data_governance)
|
107
123
|
end
|
108
124
|
|
109
|
-
def ConfigCat.create_client_with_manual_poll(
|
125
|
+
def ConfigCat.create_client_with_manual_poll(sdk_key,
|
110
126
|
config_cache_class: nil,
|
111
127
|
base_url: nil,
|
112
128
|
proxy_address:nil,
|
113
129
|
proxy_port:nil,
|
114
130
|
proxy_user:nil,
|
115
|
-
proxy_pass:nil
|
131
|
+
proxy_pass:nil,
|
132
|
+
data_governance: DataGovernance::GLOBAL)
|
116
133
|
#
|
117
134
|
# Create an instance of ConfigCatClient and setup Manual Poll mode with custom options
|
118
135
|
#
|
119
|
-
# :param
|
136
|
+
# :param sdk_key: ConfigCat SDK Key to access your configuration.
|
120
137
|
# :param config_cache_class: If you want to use custom caching instead of the client's default InMemoryConfigCache,
|
121
138
|
# You can provide an implementation of ConfigCache.
|
122
139
|
# :param base_url: You can set a base_url if you want to use a proxy server between your application and ConfigCat
|
@@ -124,11 +141,15 @@ module ConfigCat
|
|
124
141
|
# :param proxy_port: Proxy port
|
125
142
|
# :param proxy_user: username for proxy authentication
|
126
143
|
# :param proxy_pass: password for proxy authentication
|
144
|
+
# :param data_governance:
|
145
|
+
# Default: Global. Set this parameter to be in sync with the Data Governance preference on the Dashboard:
|
146
|
+
# https://app.configcat.com/organization/data-governance
|
147
|
+
# (Only Organization Admins have access)
|
127
148
|
#
|
128
|
-
if
|
129
|
-
raise ConfigCatClientException, "
|
149
|
+
if sdk_key === nil
|
150
|
+
raise ConfigCatClientException, "SDK Key is required."
|
130
151
|
end
|
131
|
-
return ConfigCatClient.new(
|
152
|
+
return ConfigCatClient.new(sdk_key,
|
132
153
|
poll_interval_seconds: 0,
|
133
154
|
max_init_wait_time_seconds: 0,
|
134
155
|
on_configuration_changed_callback: nil,
|
@@ -138,7 +159,8 @@ module ConfigCat
|
|
138
159
|
proxy_address: proxy_address,
|
139
160
|
proxy_port: proxy_port,
|
140
161
|
proxy_user: proxy_user,
|
141
|
-
proxy_pass: proxy_pass
|
162
|
+
proxy_pass: proxy_pass,
|
163
|
+
data_governance: data_governance)
|
142
164
|
end
|
143
165
|
|
144
166
|
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
require 'configcat/interfaces'
|
2
|
+
require 'configcat/constants'
|
2
3
|
require 'concurrent'
|
3
4
|
|
4
5
|
module ConfigCat
|
5
6
|
class AutoPollingCachePolicy < CachePolicy
|
6
|
-
def initialize(config_fetcher, config_cache, poll_interval_seconds=60, max_init_wait_time_seconds=5, on_configuration_changed_callback=nil)
|
7
|
+
def initialize(config_fetcher, config_cache, cache_key, poll_interval_seconds=60, max_init_wait_time_seconds=5, on_configuration_changed_callback=nil)
|
7
8
|
if poll_interval_seconds < 1
|
8
9
|
poll_interval_seconds = 1
|
9
10
|
end
|
@@ -12,6 +13,7 @@ module ConfigCat
|
|
12
13
|
end
|
13
14
|
@_config_fetcher = config_fetcher
|
14
15
|
@_config_cache = config_cache
|
16
|
+
@_cache_key = cache_key
|
15
17
|
@_poll_interval_seconds = poll_interval_seconds
|
16
18
|
@_max_init_wait_time_seconds = max_init_wait_time_seconds
|
17
19
|
@_on_configuration_changed_callback = on_configuration_changed_callback
|
@@ -40,7 +42,7 @@ module ConfigCat
|
|
40
42
|
end
|
41
43
|
begin
|
42
44
|
@_lock.acquire_read_lock()
|
43
|
-
return @_config_cache.get()
|
45
|
+
return @_config_cache.get(@_cache_key)
|
44
46
|
ensure
|
45
47
|
@_lock.release_read_lock()
|
46
48
|
end
|
@@ -48,29 +50,32 @@ module ConfigCat
|
|
48
50
|
|
49
51
|
def force_refresh()
|
50
52
|
begin
|
51
|
-
|
53
|
+
configuration_response = @_config_fetcher.get_configuration_json()
|
52
54
|
|
53
55
|
begin
|
54
56
|
@_lock.acquire_read_lock()
|
55
|
-
old_configuration = @_config_cache.get()
|
57
|
+
old_configuration = @_config_cache.get(@_cache_key)
|
56
58
|
ensure
|
57
59
|
@_lock.release_read_lock()
|
58
60
|
end
|
59
61
|
|
60
|
-
if
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
62
|
+
if configuration_response.is_fetched()
|
63
|
+
configuration = configuration_response.json()
|
64
|
+
if configuration != old_configuration
|
65
|
+
begin
|
66
|
+
@_lock.acquire_write_lock()
|
67
|
+
@_config_cache.set(@_cache_key, configuration)
|
68
|
+
@_initialized = true
|
69
|
+
ensure
|
70
|
+
@_lock.release_write_lock()
|
71
|
+
end
|
72
|
+
begin
|
73
|
+
if !@_on_configuration_changed_callback.equal?(nil)
|
74
|
+
@_on_configuration_changed_callback.()
|
75
|
+
end
|
76
|
+
rescue Exception => e
|
77
|
+
ConfigCat.logger.error("Exception in on_configuration_changed_callback: #{e.class}:'#{e}'")
|
71
78
|
end
|
72
|
-
rescue Exception => e
|
73
|
-
ConfigCat.logger.error("Exception in on_configuration_changed_callback: #{e.class}:'#{e}'")
|
74
79
|
end
|
75
80
|
end
|
76
81
|
|
@@ -78,7 +83,7 @@ module ConfigCat
|
|
78
83
|
@_initialized = true
|
79
84
|
end
|
80
85
|
rescue Exception => e
|
81
|
-
ConfigCat.logger.error("Double-check your
|
86
|
+
ConfigCat.logger.error("Double-check your SDK Key at https://app.configcat.com/sdkkey.")
|
82
87
|
ConfigCat.logger.error "threw exception #{e.class}:'#{e}'"
|
83
88
|
ConfigCat.logger.error "stacktrace: #{e.backtrace}"
|
84
89
|
end
|
@@ -3,15 +3,15 @@ require 'configcat/interfaces'
|
|
3
3
|
module ConfigCat
|
4
4
|
class InMemoryConfigCache < ConfigCache
|
5
5
|
def initialize()
|
6
|
-
@_value =
|
6
|
+
@_value = {}
|
7
7
|
end
|
8
8
|
|
9
|
-
def get()
|
10
|
-
return @_value
|
9
|
+
def get(key)
|
10
|
+
return @_value.fetch(key, nil)
|
11
11
|
end
|
12
12
|
|
13
|
-
def set(value)
|
14
|
-
@_value = value
|
13
|
+
def set(key, value)
|
14
|
+
@_value[key] = value
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
@@ -5,10 +5,12 @@ require 'configcat/autopollingcachepolicy'
|
|
5
5
|
require 'configcat/manualpollingcachepolicy'
|
6
6
|
require 'configcat/lazyloadingcachepolicy'
|
7
7
|
require 'configcat/rolloutevaluator'
|
8
|
+
require 'configcat/datagovernance'
|
8
9
|
|
9
10
|
module ConfigCat
|
11
|
+
KeyValue = Struct.new(:key, :value)
|
10
12
|
class ConfigCatClient
|
11
|
-
def initialize(
|
13
|
+
def initialize(sdk_key,
|
12
14
|
poll_interval_seconds:60,
|
13
15
|
max_init_wait_time_seconds:5,
|
14
16
|
on_configuration_changed_callback:nil,
|
@@ -18,11 +20,12 @@ module ConfigCat
|
|
18
20
|
proxy_address:nil,
|
19
21
|
proxy_port:nil,
|
20
22
|
proxy_user:nil,
|
21
|
-
proxy_pass:nil
|
22
|
-
|
23
|
-
|
23
|
+
proxy_pass:nil,
|
24
|
+
data_governance: DataGovernance::GLOBAL)
|
25
|
+
if sdk_key === nil
|
26
|
+
raise ConfigCatClientException, "SDK Key is required."
|
24
27
|
end
|
25
|
-
@
|
28
|
+
@_sdk_key = sdk_key
|
26
29
|
|
27
30
|
if config_cache_class
|
28
31
|
@_config_cache = config_cache_class.new()
|
@@ -31,15 +34,15 @@ module ConfigCat
|
|
31
34
|
end
|
32
35
|
|
33
36
|
if poll_interval_seconds > 0
|
34
|
-
@_config_fetcher = CacheControlConfigFetcher.new(
|
35
|
-
@_cache_policy = AutoPollingCachePolicy.new(@_config_fetcher, @_config_cache, poll_interval_seconds, max_init_wait_time_seconds, on_configuration_changed_callback)
|
37
|
+
@_config_fetcher = CacheControlConfigFetcher.new(sdk_key, "p", base_url, proxy_address, proxy_port, proxy_user, proxy_pass, data_governance)
|
38
|
+
@_cache_policy = AutoPollingCachePolicy.new(@_config_fetcher, @_config_cache, _get_cache_key(), poll_interval_seconds, max_init_wait_time_seconds, on_configuration_changed_callback)
|
36
39
|
else
|
37
40
|
if cache_time_to_live_seconds > 0
|
38
|
-
@_config_fetcher = CacheControlConfigFetcher.new(
|
39
|
-
@_cache_policy = LazyLoadingCachePolicy.new(@_config_fetcher, @_config_cache, cache_time_to_live_seconds)
|
41
|
+
@_config_fetcher = CacheControlConfigFetcher.new(sdk_key, "l", base_url, proxy_address, proxy_port, proxy_user, proxy_pass, data_governance)
|
42
|
+
@_cache_policy = LazyLoadingCachePolicy.new(@_config_fetcher, @_config_cache, _get_cache_key(), cache_time_to_live_seconds)
|
40
43
|
else
|
41
|
-
@_config_fetcher = CacheControlConfigFetcher.new(
|
42
|
-
@_cache_policy = ManualPollingCachePolicy.new(@_config_fetcher, @_config_cache)
|
44
|
+
@_config_fetcher = CacheControlConfigFetcher.new(sdk_key, "m", base_url, proxy_address, proxy_port, proxy_user, proxy_pass, data_governance)
|
45
|
+
@_cache_policy = ManualPollingCachePolicy.new(@_config_fetcher, @_config_cache, _get_cache_key())
|
43
46
|
end
|
44
47
|
end
|
45
48
|
end
|
@@ -49,7 +52,8 @@ module ConfigCat
|
|
49
52
|
if config === nil
|
50
53
|
return default_value
|
51
54
|
end
|
52
|
-
|
55
|
+
value, variation_id = RolloutEvaluator.evaluate(key, user, default_value, nil, config)
|
56
|
+
return value
|
53
57
|
end
|
54
58
|
|
55
59
|
def get_all_keys()
|
@@ -57,7 +61,69 @@ module ConfigCat
|
|
57
61
|
if config === nil
|
58
62
|
return []
|
59
63
|
end
|
60
|
-
|
64
|
+
feature_flags = config.fetch(FEATURE_FLAGS, nil)
|
65
|
+
if feature_flags === nil
|
66
|
+
return []
|
67
|
+
end
|
68
|
+
return feature_flags.keys
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_variation_id(key, default_variation_id, user=nil)
|
72
|
+
config = @_cache_policy.get()
|
73
|
+
if config === nil
|
74
|
+
ConfigCat.logger.warn("Evaluating get_variation_id('%s') failed. Cache is empty. "\
|
75
|
+
"Returning default_variation_id in your get_variation_id call: [%s]." %
|
76
|
+
[key, default_variation_id.to_s])
|
77
|
+
return default_variation_id
|
78
|
+
end
|
79
|
+
value, variation_id = RolloutEvaluator.evaluate(key, user, nil, default_variation_id, config)
|
80
|
+
return variation_id
|
81
|
+
end
|
82
|
+
|
83
|
+
def get_all_variation_ids(user: nil)
|
84
|
+
keys = get_all_keys()
|
85
|
+
variation_ids = []
|
86
|
+
for key in keys
|
87
|
+
variation_id = get_variation_id(key, nil, user)
|
88
|
+
if !variation_id.equal?(nil)
|
89
|
+
variation_ids.push(variation_id)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
return variation_ids
|
93
|
+
end
|
94
|
+
|
95
|
+
def get_key_and_value(variation_id)
|
96
|
+
config = @_cache_policy.get()
|
97
|
+
if config === nil
|
98
|
+
ConfigCat.logger.warn("Evaluating get_variation_id('%s') failed. Cache is empty. Returning nil." % variation_id)
|
99
|
+
return nil
|
100
|
+
end
|
101
|
+
|
102
|
+
feature_flags = config.fetch(FEATURE_FLAGS, nil)
|
103
|
+
if feature_flags === nil
|
104
|
+
ConfigCat.logger.warn("Evaluating get_key_and_value('%s') failed. Cache is empty. Returning None." % variation_id)
|
105
|
+
return nil
|
106
|
+
end
|
107
|
+
|
108
|
+
for key, value in feature_flags
|
109
|
+
if variation_id == value.fetch(VARIATION_ID, nil)
|
110
|
+
return KeyValue.new(key, value[VALUE])
|
111
|
+
end
|
112
|
+
|
113
|
+
rollout_rules = value.fetch(ROLLOUT_RULES, [])
|
114
|
+
for rollout_rule in rollout_rules
|
115
|
+
if variation_id == rollout_rule.fetch(VARIATION_ID, nil)
|
116
|
+
return KeyValue.new(key, rollout_rule[VALUE])
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
rollout_percentage_items = value.fetch(ROLLOUT_PERCENTAGE_ITEMS, [])
|
121
|
+
for rollout_percentage_item in rollout_percentage_items
|
122
|
+
if variation_id == rollout_percentage_item.fetch(VARIATION_ID, nil)
|
123
|
+
return KeyValue.new(key, rollout_percentage_item[VALUE])
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
61
127
|
end
|
62
128
|
|
63
129
|
def force_refresh()
|
@@ -69,5 +135,11 @@ module ConfigCat
|
|
69
135
|
@_config_fetcher.close()
|
70
136
|
end
|
71
137
|
|
138
|
+
private
|
139
|
+
|
140
|
+
def _get_cache_key()
|
141
|
+
return Digest::SHA1.hexdigest("ruby_" + CONFIG_FILE_NAME + "_" + @_sdk_key)
|
142
|
+
end
|
143
|
+
|
72
144
|
end
|
73
145
|
end
|
@@ -1,41 +1,129 @@
|
|
1
1
|
require 'configcat/interfaces'
|
2
2
|
require 'configcat/version'
|
3
|
+
require 'configcat/datagovernance'
|
4
|
+
require 'configcat/constants'
|
3
5
|
require 'net/http'
|
4
6
|
require 'uri'
|
5
7
|
require 'json'
|
6
8
|
|
7
9
|
module ConfigCat
|
8
|
-
|
10
|
+
BASE_URL_GLOBAL = "https://cdn-global.configcat.com"
|
11
|
+
BASE_URL_EU_ONLY = "https://cdn-eu.configcat.com"
|
9
12
|
BASE_PATH = "configuration-files/"
|
10
|
-
BASE_EXTENSION = "/
|
13
|
+
BASE_EXTENSION = "/" + CONFIG_FILE_NAME + ".json"
|
14
|
+
|
15
|
+
class RedirectMode
|
16
|
+
NO_REDIRECT = 0
|
17
|
+
SHOULD_REDIRECT = 1
|
18
|
+
FORCE_REDIRECT = 2
|
19
|
+
end
|
20
|
+
|
21
|
+
class FetchResponse
|
22
|
+
def initialize(response)
|
23
|
+
@_response = response
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns the json-encoded content of a response, if any
|
27
|
+
def json()
|
28
|
+
return JSON.parse(@_response.body)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Gets whether a new configuration value was fetched or not
|
32
|
+
def is_fetched()
|
33
|
+
code = @_response.code.to_i
|
34
|
+
return 200 <= code && code < 300
|
35
|
+
end
|
36
|
+
|
37
|
+
# Gets whether the fetch resulted a '304 Not Modified' or not
|
38
|
+
def is_not_modified()
|
39
|
+
return @_response.code == "304"
|
40
|
+
end
|
41
|
+
end
|
11
42
|
|
12
43
|
class CacheControlConfigFetcher < ConfigFetcher
|
13
|
-
def initialize(
|
14
|
-
|
15
|
-
@
|
44
|
+
def initialize(sdk_key, mode, base_url=nil, proxy_address=nil, proxy_port=nil, proxy_user=nil, proxy_pass=nil,
|
45
|
+
data_governance=DataGovernance::GLOBAL)
|
46
|
+
@_sdk_key = sdk_key
|
47
|
+
@_proxy_address = proxy_address
|
48
|
+
@_proxy_port = proxy_port
|
49
|
+
@_proxy_user = proxy_user
|
50
|
+
@_proxy_pass = proxy_pass
|
51
|
+
@_etag = ""
|
16
52
|
@_headers = {"User-Agent" => ((("ConfigCat-Ruby/") + mode) + ("-")) + VERSION, "X-ConfigCat-UserAgent" => ((("ConfigCat-Ruby/") + mode) + ("-")) + VERSION, "Content-Type" => "application/json"}
|
17
53
|
if !base_url.equal?(nil)
|
54
|
+
@_base_url_overridden = true
|
18
55
|
@_base_url = base_url.chomp("/")
|
19
56
|
else
|
20
|
-
@
|
57
|
+
@_base_url_overridden = false
|
58
|
+
if data_governance == DataGovernance::EU_ONLY
|
59
|
+
@_base_url = BASE_URL_EU_ONLY
|
60
|
+
else
|
61
|
+
@_base_url = BASE_URL_GLOBAL
|
62
|
+
end
|
21
63
|
end
|
22
|
-
uri = URI.parse(@_base_url)
|
23
|
-
@_http = Net::HTTP.new(uri.host, uri.port, proxy_address, proxy_port, proxy_user, proxy_pass)
|
24
|
-
@_http.use_ssl = true if uri.scheme == 'https'
|
25
|
-
@_http.open_timeout = 10 # in seconds
|
26
|
-
@_http.read_timeout = 30 # in seconds
|
27
64
|
end
|
28
65
|
|
29
|
-
|
66
|
+
# Returns the FetchResponse object contains configuration json Dictionary
|
67
|
+
def get_configuration_json(retries=0)
|
30
68
|
ConfigCat.logger.debug "Fetching configuration from ConfigCat"
|
31
|
-
uri = URI.parse((((@_base_url + ("/")) + BASE_PATH) + @
|
32
|
-
|
33
|
-
|
69
|
+
uri = URI.parse((((@_base_url + ("/")) + BASE_PATH) + @_sdk_key) + BASE_EXTENSION)
|
70
|
+
headers = @_headers
|
71
|
+
headers["If-None-Match"] = @_etag unless @_etag.empty?
|
72
|
+
_create_http()
|
73
|
+
request = Net::HTTP::Get.new(uri.request_uri, headers)
|
34
74
|
response = @_http.request(request)
|
35
|
-
|
36
|
-
@_etag =
|
75
|
+
etag = response["ETag"]
|
76
|
+
@_etag = etag unless etag.nil? || etag.empty?
|
37
77
|
ConfigCat.logger.debug "ConfigCat configuration json fetch response code:#{response.code} Cached:#{response['ETag']}"
|
38
|
-
|
78
|
+
fetch_response = FetchResponse.new(response)
|
79
|
+
|
80
|
+
# If there wasn't a config change, we return the response.
|
81
|
+
if !fetch_response.is_fetched()
|
82
|
+
return fetch_response
|
83
|
+
end
|
84
|
+
|
85
|
+
preferences = fetch_response.json().fetch(PREFERENCES, nil)
|
86
|
+
if preferences === nil
|
87
|
+
return fetch_response
|
88
|
+
end
|
89
|
+
|
90
|
+
base_url = preferences.fetch(BASE_URL, nil)
|
91
|
+
|
92
|
+
# If the base_url is the same as the last called one, just return the response.
|
93
|
+
if base_url.equal?(nil) || @_base_url == base_url
|
94
|
+
return fetch_response
|
95
|
+
end
|
96
|
+
|
97
|
+
redirect = preferences.fetch(REDIRECT, nil)
|
98
|
+
# If the base_url is overridden, and the redirect parameter is not 2 (force),
|
99
|
+
# the SDK should not redirect the calls and it just have to return the response.
|
100
|
+
if @_base_url_overridden && redirect != RedirectMode::FORCE_REDIRECT
|
101
|
+
return fetch_response
|
102
|
+
end
|
103
|
+
|
104
|
+
# The next call should use the base_url provided in the config json
|
105
|
+
@_base_url = base_url
|
106
|
+
|
107
|
+
# If the redirect property == 0 (redirect not needed), return the response
|
108
|
+
if redirect == RedirectMode::NO_REDIRECT
|
109
|
+
# Return the response
|
110
|
+
return fetch_response
|
111
|
+
end
|
112
|
+
|
113
|
+
# Try to download again with the new url
|
114
|
+
|
115
|
+
if redirect == RedirectMode::SHOULD_REDIRECT
|
116
|
+
ConfigCat.logger.warn("Your data_governance parameter at ConfigCatClient initialization is not in sync with your preferences on the ConfigCat Dashboard: https://app.configcat.com/organization/data-governance. Only Organization Admins can set this preference.")
|
117
|
+
end
|
118
|
+
|
119
|
+
# To prevent loops we check if we retried at least 3 times with the new base_url
|
120
|
+
if retries >= 2
|
121
|
+
ConfigCat.logger.error("Redirect loop during config.json fetch. Please contact support@configcat.com.")
|
122
|
+
return fetch_response
|
123
|
+
end
|
124
|
+
|
125
|
+
# Retry the config download with the new base_url
|
126
|
+
return get_configuration_json(retries + 1)
|
39
127
|
end
|
40
128
|
|
41
129
|
def close()
|
@@ -43,5 +131,19 @@ module ConfigCat
|
|
43
131
|
@_http = nil
|
44
132
|
end
|
45
133
|
end
|
134
|
+
|
135
|
+
private
|
136
|
+
|
137
|
+
def _create_http()
|
138
|
+
uri = URI.parse(@_base_url)
|
139
|
+
use_ssl = true if uri.scheme == 'https'
|
140
|
+
if @_http.equal?(nil) || @_http.address != uri.host || @_http.port != uri.port || @_http.use_ssl? != use_ssl
|
141
|
+
close()
|
142
|
+
@_http = Net::HTTP.new(uri.host, uri.port, @_proxy_address, @_proxy_port, @_proxy_user, @_proxy_pass)
|
143
|
+
@_http.use_ssl = use_ssl
|
144
|
+
@_http.open_timeout = 10 # in seconds
|
145
|
+
@_http.read_timeout = 30 # in seconds
|
146
|
+
end
|
147
|
+
end
|
46
148
|
end
|
47
149
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ConfigCat
|
2
|
+
CONFIG_FILE_NAME = "config_v5"
|
3
|
+
|
4
|
+
PREFERENCES = "p"
|
5
|
+
BASE_URL = "u"
|
6
|
+
REDIRECT = "r"
|
7
|
+
|
8
|
+
FEATURE_FLAGS = "f"
|
9
|
+
VALUE = "v"
|
10
|
+
COMPARATOR = "t"
|
11
|
+
COMPARISON_ATTRIBUTE = "a"
|
12
|
+
COMPARISON_VALUE = "c"
|
13
|
+
ROLLOUT_PERCENTAGE_ITEMS = "p"
|
14
|
+
PERCENTAGE = "p"
|
15
|
+
ROLLOUT_RULES = "r"
|
16
|
+
VARIATION_ID = "i"
|
17
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module ConfigCat
|
2
|
+
class DataGovernance
|
3
|
+
# Control the location of the config.json files containing your feature flags
|
4
|
+
# and settings within the ConfigCat CDN.
|
5
|
+
# Global: Select this if your feature flags are published to all global CDN nodes.
|
6
|
+
# EuOnly: Select this if your feature flags are published to CDN nodes only in the EU.
|
7
|
+
GLOBAL = 0
|
8
|
+
EU_ONLY = 1
|
9
|
+
end
|
10
|
+
end
|
data/lib/configcat/interfaces.rb
CHANGED
@@ -1,15 +1,18 @@
|
|
1
1
|
require 'configcat/interfaces'
|
2
|
+
require 'configcat/constants'
|
2
3
|
require 'concurrent'
|
3
4
|
|
5
|
+
|
4
6
|
module ConfigCat
|
5
7
|
class LazyLoadingCachePolicy < CachePolicy
|
6
8
|
|
7
|
-
def initialize(config_fetcher, config_cache, cache_time_to_live_seconds=60)
|
9
|
+
def initialize(config_fetcher, config_cache, cache_key, cache_time_to_live_seconds=60)
|
8
10
|
if cache_time_to_live_seconds < 1
|
9
11
|
cache_time_to_live_seconds = 1
|
10
12
|
end
|
11
13
|
@_config_fetcher = config_fetcher
|
12
14
|
@_config_cache = config_cache
|
15
|
+
@_cache_key = cache_key
|
13
16
|
@_cache_time_to_live = cache_time_to_live_seconds
|
14
17
|
@_lock = Concurrent::ReadWriteLock.new()
|
15
18
|
@_last_updated = nil
|
@@ -20,7 +23,7 @@ module ConfigCat
|
|
20
23
|
@_lock.acquire_read_lock()
|
21
24
|
utc_now = Time.now.utc
|
22
25
|
if !@_last_updated.equal?(nil) && (@_last_updated + @_cache_time_to_live > utc_now)
|
23
|
-
config = @_config_cache.get()
|
26
|
+
config = @_config_cache.get(@_cache_key)
|
24
27
|
if !config.equal?(nil)
|
25
28
|
return config
|
26
29
|
end
|
@@ -31,7 +34,7 @@ module ConfigCat
|
|
31
34
|
force_refresh()
|
32
35
|
begin
|
33
36
|
@_lock.acquire_read_lock()
|
34
|
-
config = @_config_cache.get()
|
37
|
+
config = @_config_cache.get(@_cache_key)
|
35
38
|
return config
|
36
39
|
ensure
|
37
40
|
@_lock.release_read_lock()
|
@@ -40,16 +43,19 @@ module ConfigCat
|
|
40
43
|
|
41
44
|
def force_refresh()
|
42
45
|
begin
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
46
|
+
configuration_response = @_config_fetcher.get_configuration_json()
|
47
|
+
if configuration_response.is_fetched()
|
48
|
+
configuration = configuration_response.json()
|
49
|
+
begin
|
50
|
+
@_lock.acquire_write_lock()
|
51
|
+
@_config_cache.set(@_cache_key, configuration)
|
52
|
+
@_last_updated = Time.now.utc
|
53
|
+
ensure
|
54
|
+
@_lock.release_write_lock()
|
55
|
+
end
|
50
56
|
end
|
51
57
|
rescue StandardError => e
|
52
|
-
ConfigCat.logger.error("Double-check your
|
58
|
+
ConfigCat.logger.error("Double-check your SDK Key at https://app.configcat.com/sdkkey.")
|
53
59
|
ConfigCat.logger.error "threw exception #{e.class}:'#{e}'"
|
54
60
|
ConfigCat.logger.error "stacktrace: #{e.backtrace}"
|
55
61
|
end
|
@@ -1,18 +1,20 @@
|
|
1
1
|
require 'configcat/interfaces'
|
2
|
+
require 'configcat/constants'
|
2
3
|
require 'concurrent'
|
3
4
|
|
4
5
|
module ConfigCat
|
5
6
|
class ManualPollingCachePolicy < CachePolicy
|
6
|
-
def initialize(config_fetcher, config_cache)
|
7
|
+
def initialize(config_fetcher, config_cache, cache_key)
|
7
8
|
@_config_fetcher = config_fetcher
|
8
9
|
@_config_cache = config_cache
|
10
|
+
@_cache_key = cache_key
|
9
11
|
@_lock = Concurrent::ReadWriteLock.new()
|
10
12
|
end
|
11
13
|
|
12
14
|
def get()
|
13
15
|
begin
|
14
16
|
@_lock.acquire_read_lock()
|
15
|
-
config = @_config_cache.get()
|
17
|
+
config = @_config_cache.get(@_cache_key)
|
16
18
|
return config
|
17
19
|
ensure
|
18
20
|
@_lock.release_read_lock()
|
@@ -21,15 +23,18 @@ module ConfigCat
|
|
21
23
|
|
22
24
|
def force_refresh()
|
23
25
|
begin
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
26
|
+
configuration_response = @_config_fetcher.get_configuration_json()
|
27
|
+
if configuration_response.is_fetched()
|
28
|
+
configuration = configuration_response.json()
|
29
|
+
begin
|
30
|
+
@_lock.acquire_write_lock()
|
31
|
+
@_config_cache.set(@_cache_key, configuration)
|
32
|
+
ensure
|
33
|
+
@_lock.release_write_lock()
|
34
|
+
end
|
30
35
|
end
|
31
36
|
rescue StandardError => e
|
32
|
-
ConfigCat.logger.error("Double-check your
|
37
|
+
ConfigCat.logger.error("Double-check your SDK Key at https://app.configcat.com/sdkkey.")
|
33
38
|
ConfigCat.logger.error "threw exception #{e.class}:'#{e}'"
|
34
39
|
ConfigCat.logger.error "stacktrace: #{e.backtrace}"
|
35
40
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'configcat/user'
|
2
|
+
require 'configcat/constants'
|
2
3
|
require 'digest'
|
3
4
|
require 'semantic'
|
4
5
|
|
@@ -6,21 +7,19 @@ module ConfigCat
|
|
6
7
|
class RolloutEvaluator
|
7
8
|
COMPARATOR_TEXTS = ["IS ONE OF", "IS NOT ONE OF", "CONTAINS", "DOES NOT CONTAIN", "IS ONE OF (SemVer)", "IS NOT ONE OF (SemVer)", "< (SemVer)", "<= (SemVer)", "> (SemVer)", ">= (SemVer)", "= (Number)", "<> (Number)", "< (Number)", "<= (Number)", "> (Number)", ">= (Number)"]
|
8
9
|
|
9
|
-
|
10
|
-
COMPARATOR = "t"
|
11
|
-
COMPARISON_ATTRIBUTE = "a"
|
12
|
-
COMPARISON_VALUE = "c"
|
13
|
-
ROLLOUT_PERCENTAGE_ITEMS = "p"
|
14
|
-
PERCENTAGE = "p"
|
15
|
-
ROLLOUT_RULES = "r"
|
16
|
-
|
17
|
-
def self.evaluate(key, user, default_value, config)
|
10
|
+
def self.evaluate(key, user, default_value, default_variation_id, config)
|
18
11
|
ConfigCat.logger.info("Evaluating get_value('%s')." % key)
|
19
12
|
|
20
|
-
|
13
|
+
feature_flags = config.fetch(FEATURE_FLAGS, nil)
|
14
|
+
if feature_flags === nil
|
15
|
+
ConfigCat.logger.error("Evaluating get_value('%s') failed. Value not found for key '%s' Returning default_value: [%s]." % [key, key, default_value.to_s])
|
16
|
+
return default_value, default_variation_id
|
17
|
+
end
|
18
|
+
|
19
|
+
setting_descriptor = feature_flags.fetch(key, nil)
|
21
20
|
if setting_descriptor === nil
|
22
|
-
ConfigCat.logger.error("Evaluating get_value('%s') failed. Value not found for key '%s'. Returning default_value: [%s]. Here are the available keys: %s" % [key, key, default_value.to_s,
|
23
|
-
return default_value
|
21
|
+
ConfigCat.logger.error("Evaluating get_value('%s') failed. Value not found for key '%s'. Returning default_value: [%s]. Here are the available keys: %s" % [key, key, default_value.to_s, feature_flags.keys.join(", ")])
|
22
|
+
return default_value, default_variation_id
|
24
23
|
end
|
25
24
|
|
26
25
|
rollout_rules = setting_descriptor.fetch(ROLLOUT_RULES, [])
|
@@ -35,8 +34,9 @@ module ConfigCat
|
|
35
34
|
ConfigCat.logger.warn("Evaluating get_value('%s'). UserObject missing! You should pass a UserObject to get_value(), in order to make targeting work properly. Read more: https://configcat.com/docs/advanced/user-object/" % key)
|
36
35
|
end
|
37
36
|
return_value = setting_descriptor.fetch(VALUE, default_value)
|
37
|
+
return_variation_id = setting_descriptor.fetch(VARIATION_ID, default_variation_id)
|
38
38
|
ConfigCat.logger.info("Returning [%s]" % return_value.to_s)
|
39
|
-
return return_value
|
39
|
+
return return_value, return_variation_id
|
40
40
|
end
|
41
41
|
|
42
42
|
ConfigCat.logger.info("User object:\n%s" % user.to_s)
|
@@ -54,30 +54,31 @@ module ConfigCat
|
|
54
54
|
end
|
55
55
|
|
56
56
|
value = rollout_rule.fetch(VALUE, nil)
|
57
|
+
variation_id = rollout_rule.fetch(VARIATION_ID, default_variation_id)
|
57
58
|
|
58
59
|
# IS ONE OF
|
59
60
|
if comparator == 0
|
60
61
|
if comparison_value.to_s.split(",").map { |x| x.strip() }.include?(user_value.to_s)
|
61
62
|
ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
62
|
-
return value
|
63
|
+
return value, variation_id
|
63
64
|
end
|
64
65
|
# IS NOT ONE OF
|
65
66
|
elsif comparator == 1
|
66
67
|
if !comparison_value.to_s.split(",").map { |x| x.strip() }.include?(user_value.to_s)
|
67
68
|
ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
68
|
-
return value
|
69
|
+
return value, variation_id
|
69
70
|
end
|
70
71
|
# CONTAINS
|
71
72
|
elsif comparator == 2
|
72
73
|
if user_value.to_s.include?(comparison_value.to_s)
|
73
74
|
ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
74
|
-
return value
|
75
|
+
return value, variation_id
|
75
76
|
end
|
76
77
|
# DOES NOT CONTAIN
|
77
78
|
elsif comparator == 3
|
78
79
|
if !user_value.to_s.include?(comparison_value.to_s)
|
79
80
|
ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
80
|
-
return value
|
81
|
+
return value, variation_id
|
81
82
|
end
|
82
83
|
# IS ONE OF, IS NOT ONE OF (Semantic version)
|
83
84
|
elsif (4 <= comparator) && (comparator <= 5)
|
@@ -90,7 +91,7 @@ module ConfigCat
|
|
90
91
|
}
|
91
92
|
if match && comparator == 4 || !match && comparator == 5
|
92
93
|
ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
93
|
-
return value
|
94
|
+
return value, variation_id
|
94
95
|
end
|
95
96
|
rescue ArgumentError => e
|
96
97
|
ConfigCat.logger.warn(format_validation_error_rule(comparison_attribute, user_value, comparator, comparison_value, e.to_s))
|
@@ -106,7 +107,7 @@ module ConfigCat
|
|
106
107
|
(comparator == 8 && user_value_version > comparison_value_version) ||
|
107
108
|
(comparator == 9 && user_value_version >= comparison_value_version)
|
108
109
|
ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
109
|
-
return value
|
110
|
+
return value, variation_id
|
110
111
|
end
|
111
112
|
rescue ArgumentError => e
|
112
113
|
ConfigCat.logger.warn(format_validation_error_rule(comparison_attribute, user_value, comparator, comparison_value, e.to_s))
|
@@ -123,7 +124,7 @@ module ConfigCat
|
|
123
124
|
(comparator == 14 && user_value_float > comparison_value_float) ||
|
124
125
|
(comparator == 15 && user_value_float >= comparison_value_float)
|
125
126
|
ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
126
|
-
return value
|
127
|
+
return value, variation_id
|
127
128
|
end
|
128
129
|
rescue Exception => e
|
129
130
|
ConfigCat.logger.warn(format_validation_error_rule(comparison_attribute, user_value, comparator, comparison_value, e.to_s))
|
@@ -133,13 +134,13 @@ module ConfigCat
|
|
133
134
|
elsif comparator == 16
|
134
135
|
if comparison_value.to_s.split(",").map { |x| x.strip() }.include?(Digest::SHA1.hexdigest(user_value).to_s)
|
135
136
|
ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
136
|
-
return value
|
137
|
+
return value, variation_id
|
137
138
|
end
|
138
139
|
# IS NOT ONE OF (Sensitive)
|
139
140
|
elsif comparator == 17
|
140
141
|
if !comparison_value.to_s.split(",").map { |x| x.strip() }.include?(Digest::SHA1.hexdigest(user_value).to_s)
|
141
142
|
ConfigCat.logger.info(format_match_rule(comparison_attribute, user_value, comparator, comparison_value, value))
|
142
|
-
return value
|
143
|
+
return value, variation_id
|
143
144
|
end
|
144
145
|
end
|
145
146
|
ConfigCat.logger.info(format_no_match_rule(comparison_attribute, user_value, comparator, comparison_value))
|
@@ -154,14 +155,16 @@ module ConfigCat
|
|
154
155
|
bucket += rollout_percentage_item.fetch(PERCENTAGE, 0)
|
155
156
|
if hash_val < bucket
|
156
157
|
percentage_value = rollout_percentage_item.fetch(VALUE, nil)
|
158
|
+
variation_id = rollout_percentage_item.fetch(VARIATION_ID, default_variation_id)
|
157
159
|
ConfigCat.logger.info("Evaluating %% options. Returning %s" % percentage_value)
|
158
|
-
return percentage_value
|
160
|
+
return percentage_value, variation_id
|
159
161
|
end
|
160
162
|
end
|
161
163
|
end
|
162
|
-
|
163
|
-
|
164
|
-
|
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
|
165
168
|
end
|
166
169
|
|
167
170
|
private
|
data/lib/configcat/user.rb
CHANGED
@@ -5,11 +5,11 @@ module ConfigCat
|
|
5
5
|
# The user object for variation evaluation
|
6
6
|
#
|
7
7
|
|
8
|
-
PREDEFINED = ["
|
8
|
+
PREDEFINED = ["Identifier", "Email", "Country"]
|
9
9
|
|
10
10
|
def initialize(identifier, email: nil, country: nil, custom: nil)
|
11
|
-
@__identifier = identifier
|
12
|
-
@__data = {"
|
11
|
+
@__identifier = (!identifier.equal?(nil)) ? identifier : ""
|
12
|
+
@__data = {"Identifier" => identifier, "Email" => email, "Country" => country}
|
13
13
|
@__custom = custom
|
14
14
|
end
|
15
15
|
|
@@ -18,14 +18,14 @@ module ConfigCat
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def get_attribute(attribute)
|
21
|
-
attribute = attribute.to_s
|
21
|
+
attribute = attribute.to_s
|
22
22
|
if PREDEFINED.include?(attribute)
|
23
23
|
return @__data[attribute]
|
24
24
|
end
|
25
25
|
|
26
26
|
if !@__custom.equal?(nil)
|
27
27
|
@__custom.each do |customField, customValue|
|
28
|
-
if customField.to_s
|
28
|
+
if customField.to_s == attribute
|
29
29
|
return customValue
|
30
30
|
end
|
31
31
|
end
|
data/lib/configcat/version.rb
CHANGED
metadata
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: configcat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.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: 2020-
|
11
|
+
date: 2020-10-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '1.1'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '1.1'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: semantic
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '1.6'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '1.6'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rspec
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -56,30 +56,44 @@ dependencies:
|
|
56
56
|
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '12.3'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '12.3'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: coveralls
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - "
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0.8'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0.8'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: webmock
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
74
88
|
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
89
|
+
version: '3.0'
|
76
90
|
type: :development
|
77
91
|
prerelease: false
|
78
92
|
version_requirements: !ruby/object:Gem::Requirement
|
79
93
|
requirements:
|
80
|
-
- - "
|
94
|
+
- - "~>"
|
81
95
|
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
96
|
+
version: '3.0'
|
83
97
|
description: Feature Flags created by developers for developers with ❤️. ConfigCat
|
84
98
|
lets you manage feature flags across frontend, backend, mobile, and desktop apps
|
85
99
|
without (re)deploying code. % rollouts, user targeting, segmentation. Feature toggle
|
@@ -96,6 +110,8 @@ files:
|
|
96
110
|
- lib/configcat/configcache.rb
|
97
111
|
- lib/configcat/configcatclient.rb
|
98
112
|
- lib/configcat/configfetcher.rb
|
113
|
+
- lib/configcat/constants.rb
|
114
|
+
- lib/configcat/datagovernance.rb
|
99
115
|
- lib/configcat/interfaces.rb
|
100
116
|
- lib/configcat/lazyloadingcachepolicy.rb
|
101
117
|
- lib/configcat/manualpollingcachepolicy.rb
|
@@ -112,7 +128,7 @@ require_paths:
|
|
112
128
|
- lib
|
113
129
|
required_ruby_version: !ruby/object:Gem::Requirement
|
114
130
|
requirements:
|
115
|
-
- - "
|
131
|
+
- - ">="
|
116
132
|
- !ruby/object:Gem::Version
|
117
133
|
version: '2.2'
|
118
134
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
@@ -121,8 +137,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
121
137
|
- !ruby/object:Gem::Version
|
122
138
|
version: '0'
|
123
139
|
requirements: []
|
124
|
-
|
125
|
-
rubygems_version: 2.7.7
|
140
|
+
rubygems_version: 3.0.8
|
126
141
|
signing_key:
|
127
142
|
specification_version: 4
|
128
143
|
summary: ConfigCat SDK for Ruby.
|