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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 216277c9c25debbe5d07c33f6c73c850f774c5115b0dd324ee4973514f83d6b9
4
- data.tar.gz: 5e94aca770e2888ccc887befd5681950bf4dc6d6dc1819e57e731cf8e02e5793
3
+ metadata.gz: 118a3db79c8d7d9a0795897380a4dfaf14fecda8a636024abfd9d2433b3af924
4
+ data.tar.gz: 4c85c82b81fe08fcf9b6eed0a40bea06369939301aa09bc12e82b33a3b2176eb
5
5
  SHA512:
6
- metadata.gz: 308f9b0d6fcebebded88f6945aaf4ae025329e9ab21711f587f8fee366024788da514c2e30d1b875b025de12b692783a26ab163732a6c333e6513789a04ca496
7
- data.tar.gz: 8f73ab03c23e10d961564663078574839a91758108b02c8511c449e1d3fd12347186857871e3cd048d7d526fbffd204adbe0d950f55a589cd2d9f501854edd04
6
+ metadata.gz: afe03eda0affc92755ae0eb91196a15bfe61f68a12244270bb5d19b3870bc023fe9ccf3231d8f11f414c4439b49fd2f9481e9a64295c3ab87697511ebcf57b25
7
+ data.tar.gz: 5ac8e3beef89cbc84ef89f2eb5b071fcf5cb348db1c302755ba4fc619c759f9919f80785b2b08a4a583725d35ea6b9ef038d869706956485eec3b4a9d4cadbaa
@@ -10,29 +10,34 @@ module ConfigCat
10
10
  attr_accessor :logger
11
11
  end
12
12
 
13
- def ConfigCat.create_client(api_key)
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 api_key: ConfigCat ApiKey to access your configuration.
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(api_key)
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(api_key,
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 api_key: ConfigCat ApiKey to access your configuration.
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 api_key === nil
48
- raise ConfigCatClientException, "API Key is required."
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(api_key,
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(api_key,
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 api_key: ConfigCat ApiKey to access your configuration.
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 api_key === nil
91
- raise ConfigCatClientException, "API Key is required."
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(api_key,
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(api_key,
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 api_key: ConfigCat ApiKey to access your configuration.
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 api_key === nil
129
- raise ConfigCatClientException, "API Key is required."
149
+ if sdk_key === nil
150
+ raise ConfigCatClientException, "SDK Key is required."
130
151
  end
131
- return ConfigCatClient.new(api_key,
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
- configuration = @_config_fetcher.get_configuration_json()
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 configuration != old_configuration
61
- begin
62
- @_lock.acquire_write_lock()
63
- @_config_cache.set(configuration)
64
- @_initialized = true
65
- ensure
66
- @_lock.release_write_lock()
67
- end
68
- begin
69
- if !@_on_configuration_changed_callback.equal?(nil)
70
- @_on_configuration_changed_callback.()
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 API KEY at https://app.configcat.com/apikey.")
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 = nil
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(api_key,
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
- if api_key === nil
23
- raise ConfigCatClientException, "API Key is required."
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
- @_api_key = api_key
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(api_key, "p", base_url, proxy_address, proxy_port, proxy_user, proxy_pass)
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(api_key, "l", base_url, proxy_address, proxy_port, proxy_user, proxy_pass)
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(api_key, "m", base_url, proxy_address, proxy_port, proxy_user, proxy_pass)
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
- return RolloutEvaluator.evaluate(key, user, default_value, config)
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
- return config.keys
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
- BASE_URL = "https://cdn.configcat.com"
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 = "/config_v3.json"
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(api_key, mode, base_url=nil, proxy_address=nil, proxy_port=nil, proxy_user=nil, proxy_pass=nil)
14
- @_api_key = api_key
15
- @_etag = nil
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
- @_base_url = BASE_URL
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
- def get_configuration_json()
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) + @_api_key) + BASE_EXTENSION)
32
- @_headers["If-None-Match"] = @_etag unless @_etag.nil?
33
- request = Net::HTTP::Get.new(uri.request_uri, @_headers)
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
- json = JSON.parse(response.body)
36
- @_etag = response["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
- return json
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
@@ -23,13 +23,13 @@ module ConfigCat
23
23
  # Config cache interface
24
24
  #
25
25
 
26
- def get()
26
+ def get(key)
27
27
  #
28
28
  # :returns the config json object from the cache
29
29
  #
30
30
  end
31
31
 
32
- def set(value)
32
+ def set(key, value)
33
33
  #
34
34
  # Sets the config json cache.
35
35
  #
@@ -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
- configuration = @_config_fetcher.get_configuration_json()
44
- begin
45
- @_lock.acquire_write_lock()
46
- @_config_cache.set(configuration)
47
- @_last_updated = Time.now.utc
48
- ensure
49
- @_lock.release_write_lock()
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 API KEY at https://app.configcat.com/apikey.")
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
- configuration = @_config_fetcher.get_configuration_json()
25
- begin
26
- @_lock.acquire_write_lock()
27
- @_config_cache.set(configuration)
28
- ensure
29
- @_lock.release_write_lock()
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 API KEY at https://app.configcat.com/apikey.")
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
- 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
-
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
- setting_descriptor = config.fetch(key, nil)
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, config.keys.join(", ")])
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
- def_value = setting_descriptor.fetch(VALUE, default_value)
163
- ConfigCat.logger.info("Returning %s" % def_value)
164
- return def_value
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
@@ -5,11 +5,11 @@ module ConfigCat
5
5
  # The user object for variation evaluation
6
6
  #
7
7
 
8
- PREDEFINED = ["identifier", "email", "country"]
8
+ PREDEFINED = ["Identifier", "Email", "Country"]
9
9
 
10
10
  def initialize(identifier, email: nil, country: nil, custom: nil)
11
- @__identifier = identifier
12
- @__data = {"identifier" => identifier, "email" => email, "country" => country}
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.downcase()
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.downcase() == attribute
28
+ if customField.to_s == attribute
29
29
  return customValue
30
30
  end
31
31
  end
@@ -1,3 +1,3 @@
1
1
  module ConfigCat
2
- VERSION = "2.0.2"
2
+ VERSION = "4.0.0"
3
3
  end
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: 2.0.2
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-02-24 00:00:00.000000000 Z
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: '0'
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: '0'
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: '0'
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: '0'
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: '0'
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: '0'
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
- rubyforge_project:
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.