configcat 6.1.0 → 8.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/config.rb +317 -0
- data/lib/configcat/configcatclient.rb +98 -100
- data/lib/configcat/configcatlogger.rb +4 -0
- data/lib/configcat/configentry.rb +35 -21
- data/lib/configcat/configfetcher.rb +8 -5
- data/lib/configcat/configservice.rb +33 -34
- data/lib/configcat/evaluationcontext.rb +14 -0
- data/lib/configcat/evaluationdetails.rb +22 -4
- data/lib/configcat/evaluationlogbuilder.rb +81 -0
- data/lib/configcat/localdictionarydatasource.rb +20 -4
- data/lib/configcat/localfiledatasource.rb +23 -7
- data/lib/configcat/rolloutevaluator.rb +714 -151
- data/lib/configcat/user.rb +43 -4
- data/lib/configcat/utils.rb +21 -1
- data/lib/configcat/version.rb +1 -1
- data/lib/configcat.rb +0 -160
- metadata +5 -3
- data/lib/configcat/constants.rb +0 -17
@@ -2,37 +2,51 @@ require 'configcat/utils'
|
|
2
2
|
|
3
3
|
module ConfigCat
|
4
4
|
class ConfigEntry
|
5
|
-
|
6
|
-
ETAG = 'etag'
|
7
|
-
FETCH_TIME = 'fetch_time'
|
5
|
+
attr_accessor :config, :etag, :config_json_string, :fetch_time
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
def initialize(config = {}, etag = '', fetch_time = Utils::DISTANT_PAST)
|
7
|
+
def initialize(config = {}, etag = '', config_json_string = '{}', fetch_time = Utils::DISTANT_PAST)
|
12
8
|
@config = config
|
13
9
|
@etag = etag
|
10
|
+
@config_json_string = config_json_string
|
14
11
|
@fetch_time = fetch_time
|
15
12
|
end
|
16
13
|
|
17
|
-
def self.create_from_json(json)
|
18
|
-
return ConfigEntry::EMPTY if json.nil?
|
19
|
-
return ConfigEntry.new(
|
20
|
-
config = json.fetch(CONFIG, {}),
|
21
|
-
etag = json.fetch(ETAG, ''),
|
22
|
-
fetch_time = json.fetch(FETCH_TIME, Utils::DISTANT_PAST)
|
23
|
-
)
|
24
|
-
end
|
25
|
-
|
26
14
|
def empty?
|
27
15
|
self == ConfigEntry::EMPTY
|
28
16
|
end
|
29
17
|
|
30
|
-
def
|
31
|
-
{
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
18
|
+
def serialize
|
19
|
+
"#{(fetch_time * 1000).floor}\n#{etag}\n#{config_json_string}"
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.create_from_string(string)
|
23
|
+
return ConfigEntry.empty if string.nil? || string.empty?
|
24
|
+
|
25
|
+
fetch_time_index = string.index("\n")
|
26
|
+
etag_index = string.index("\n", fetch_time_index + 1)
|
27
|
+
if fetch_time_index.nil? || etag_index.nil?
|
28
|
+
raise 'Number of values is fewer than expected.'
|
29
|
+
end
|
30
|
+
|
31
|
+
begin
|
32
|
+
fetch_time = Float(string[0...fetch_time_index])
|
33
|
+
rescue ArgumentError
|
34
|
+
raise "Invalid fetch time: #{string[0...fetch_time_index]}"
|
35
|
+
end
|
36
|
+
|
37
|
+
etag = string[fetch_time_index + 1...etag_index]
|
38
|
+
if etag.nil? || etag.empty?
|
39
|
+
raise 'Empty eTag value'
|
40
|
+
end
|
41
|
+
begin
|
42
|
+
config_json = string[etag_index + 1..-1]
|
43
|
+
config = JSON.parse(config_json)
|
44
|
+
Config.fixup_config_salt_and_segments(config)
|
45
|
+
rescue => e
|
46
|
+
raise "Invalid config JSON: #{config_json}. #{e.message}"
|
47
|
+
end
|
48
|
+
|
49
|
+
ConfigEntry.new(config, etag, config_json, fetch_time / 1000.0)
|
36
50
|
end
|
37
51
|
|
38
52
|
EMPTY = ConfigEntry.new(etag: 'empty')
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'configcat/interfaces'
|
2
2
|
require 'configcat/version'
|
3
3
|
require 'configcat/datagovernance'
|
4
|
-
require 'configcat/
|
4
|
+
require 'configcat/config'
|
5
5
|
require 'configcat/configentry'
|
6
6
|
require 'net/http'
|
7
7
|
require 'uri'
|
@@ -20,8 +20,8 @@ module ConfigCat
|
|
20
20
|
end
|
21
21
|
|
22
22
|
class Status
|
23
|
-
FETCHED = 0
|
24
|
-
NOT_MODIFIED = 1
|
23
|
+
FETCHED = 0
|
24
|
+
NOT_MODIFIED = 1
|
25
25
|
FAILURE = 2
|
26
26
|
end
|
27
27
|
|
@@ -179,7 +179,8 @@ module ConfigCat
|
|
179
179
|
response_etag = ""
|
180
180
|
end
|
181
181
|
config = JSON.parse(response.body)
|
182
|
-
|
182
|
+
Config.fixup_config_salt_and_segments(config)
|
183
|
+
return FetchResponse.success(ConfigEntry.new(config, response_etag, response.body, Utils.get_utc_now_seconds_since_epoch))
|
183
184
|
when Net::HTTPNotModified
|
184
185
|
return FetchResponse.not_modified
|
185
186
|
when Net::HTTPNotFound, Net::HTTPForbidden
|
@@ -198,7 +199,9 @@ module ConfigCat
|
|
198
199
|
@log.error(1102, error)
|
199
200
|
return FetchResponse.failure(error, true)
|
200
201
|
rescue Exception => e
|
201
|
-
error = "Unexpected error occurred while trying to fetch config JSON
|
202
|
+
error = "Unexpected error occurred while trying to fetch config JSON. It is most likely due to a local network " \
|
203
|
+
"issue. Please make sure your application can reach the ConfigCat CDN servers (or your proxy server) " \
|
204
|
+
"over HTTP. #{e}"
|
202
205
|
@log.error(1103, error)
|
203
206
|
return FetchResponse.failure(error, true)
|
204
207
|
end
|
@@ -7,14 +7,13 @@ require 'configcat/refreshresult'
|
|
7
7
|
module ConfigCat
|
8
8
|
class ConfigService
|
9
9
|
def initialize(sdk_key, polling_mode, hooks, config_fetcher, log, config_cache, is_offline)
|
10
|
-
@sdk_key = sdk_key
|
11
10
|
@cached_entry = ConfigEntry::EMPTY
|
12
11
|
@cached_entry_string = ''
|
13
12
|
@polling_mode = polling_mode
|
14
13
|
@log = log
|
15
14
|
@config_cache = config_cache
|
16
15
|
@hooks = hooks
|
17
|
-
@cache_key =
|
16
|
+
@cache_key = ConfigService.get_cache_key(sdk_key)
|
18
17
|
@config_fetcher = config_fetcher
|
19
18
|
@is_offline = is_offline
|
20
19
|
@response_future = nil
|
@@ -31,13 +30,12 @@ module ConfigCat
|
|
31
30
|
end
|
32
31
|
end
|
33
32
|
|
34
|
-
def
|
33
|
+
def get_config
|
35
34
|
if @polling_mode.is_a?(LazyLoadingMode)
|
36
35
|
entry, _ = fetch_if_older(Utils.get_utc_now_seconds_since_epoch - @polling_mode.cache_refresh_interval_seconds)
|
37
36
|
return !entry.empty? ?
|
38
|
-
[entry.config
|
37
|
+
[entry.config, entry.fetch_time] :
|
39
38
|
[nil, Utils::DISTANT_PAST]
|
40
|
-
|
41
39
|
elsif @polling_mode.is_a?(AutoPollingMode) && !@initialized.set?
|
42
40
|
elapsed_time = Utils.get_utc_now_seconds_since_epoch - @start_time # Elapsed time in seconds
|
43
41
|
if elapsed_time < @polling_mode.max_init_wait_time_seconds
|
@@ -47,20 +45,27 @@ module ConfigCat
|
|
47
45
|
if !@initialized.set?
|
48
46
|
set_initialized
|
49
47
|
return !@cached_entry.empty? ?
|
50
|
-
[@cached_entry.config
|
48
|
+
[@cached_entry.config, @cached_entry.fetch_time] :
|
51
49
|
[nil, Utils::DISTANT_PAST]
|
52
50
|
end
|
53
51
|
end
|
54
52
|
end
|
55
53
|
|
56
|
-
|
54
|
+
# If we are initialized, we prefer the cached results
|
55
|
+
entry, _ = fetch_if_older(Utils::DISTANT_PAST, prefer_cache: @initialized.set?)
|
57
56
|
return !entry.empty? ?
|
58
|
-
[entry.config
|
57
|
+
[entry.config, entry.fetch_time] :
|
59
58
|
[nil, Utils::DISTANT_PAST]
|
60
59
|
end
|
61
60
|
|
62
61
|
# :return [RefreshResult]
|
63
62
|
def refresh
|
63
|
+
if offline?
|
64
|
+
offline_warning = "Client is in offline mode, it cannot initiate HTTP calls."
|
65
|
+
@log.warn(3200, offline_warning)
|
66
|
+
return RefreshResult.new(success = false, error = offline_warning)
|
67
|
+
end
|
68
|
+
|
64
69
|
_, error = fetch_if_older(Utils::DISTANT_FUTURE)
|
65
70
|
return RefreshResult.new(success = error.nil?, error = error)
|
66
71
|
end
|
@@ -75,7 +80,7 @@ module ConfigCat
|
|
75
80
|
if @polling_mode.is_a?(AutoPollingMode)
|
76
81
|
start_poll
|
77
82
|
end
|
78
|
-
@log.info(5200,
|
83
|
+
@log.info(5200, "Switched to ONLINE mode.")
|
79
84
|
end
|
80
85
|
end
|
81
86
|
|
@@ -91,7 +96,7 @@ module ConfigCat
|
|
91
96
|
@thread.join
|
92
97
|
end
|
93
98
|
|
94
|
-
@log.info(5200,
|
99
|
+
@log.info(5200, "Switched to OFFLINE mode.")
|
95
100
|
end
|
96
101
|
end
|
97
102
|
|
@@ -107,36 +112,30 @@ module ConfigCat
|
|
107
112
|
|
108
113
|
private
|
109
114
|
|
115
|
+
def self.get_cache_key(sdk_key)
|
116
|
+
Digest::SHA1.hexdigest("#{sdk_key}_#{CONFIG_FILE_NAME}.json_#{SERIALIZATION_FORMAT_VERSION}")
|
117
|
+
end
|
118
|
+
|
110
119
|
# :return [ConfigEntry, String] Returns the ConfigEntry object and error message in case of any error.
|
111
|
-
def fetch_if_older(
|
120
|
+
def fetch_if_older(threshold, prefer_cache: false)
|
112
121
|
# Sync up with the cache and use it when it's not expired.
|
113
122
|
@lock.synchronize do
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
end
|
120
|
-
|
121
|
-
# Cache isn't expired
|
122
|
-
if @cached_entry.fetch_time > time
|
123
|
-
set_initialized
|
124
|
-
return @cached_entry, nil
|
125
|
-
end
|
123
|
+
# Sync up with the cache and use it when it's not expired.
|
124
|
+
from_cache = read_cache
|
125
|
+
if !from_cache.empty? && from_cache.etag != @cached_entry.etag
|
126
|
+
@cached_entry = from_cache
|
127
|
+
@hooks.invoke_on_config_changed(from_cache.config[FEATURE_FLAGS])
|
126
128
|
end
|
127
129
|
|
128
|
-
#
|
129
|
-
|
130
|
-
|
131
|
-
if prefer_cache && @initialized.set?
|
130
|
+
# Cache isn't expired
|
131
|
+
if @cached_entry.fetch_time > threshold
|
132
|
+
set_initialized
|
132
133
|
return @cached_entry, nil
|
133
134
|
end
|
134
135
|
|
135
|
-
# If we are in offline mode
|
136
|
-
if @is_offline
|
137
|
-
|
138
|
-
@log.warn(3200, offline_warning)
|
139
|
-
return @cached_entry, offline_warning
|
136
|
+
# If we are in offline mode or the caller prefers cached values, do not initiate fetch.
|
137
|
+
if @is_offline || prefer_cache
|
138
|
+
return @cached_entry, nil
|
140
139
|
end
|
141
140
|
end
|
142
141
|
|
@@ -201,7 +200,7 @@ module ConfigCat
|
|
201
200
|
end
|
202
201
|
|
203
202
|
@cached_entry_string = json_string
|
204
|
-
return ConfigEntry.
|
203
|
+
return ConfigEntry.create_from_string(json_string)
|
205
204
|
rescue Exception => e
|
206
205
|
@log.error(2200, "Error occurred while reading the cache. #{e}")
|
207
206
|
return ConfigEntry::EMPTY
|
@@ -210,7 +209,7 @@ module ConfigCat
|
|
210
209
|
|
211
210
|
def write_cache(config_entry)
|
212
211
|
begin
|
213
|
-
@config_cache.set(@cache_key, config_entry.
|
212
|
+
@config_cache.set(@cache_key, config_entry.serialize)
|
214
213
|
rescue Exception => e
|
215
214
|
@log.error(2201, "Error occurred while writing the cache. #{e}")
|
216
215
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ConfigCat
|
2
|
+
class EvaluationContext
|
3
|
+
attr_accessor :key, :setting_type, :user, :visited_keys, :is_missing_user_object_logged, :is_missing_user_object_attribute_logged
|
4
|
+
|
5
|
+
def initialize(key, setting_type, user, visited_keys = nil, is_missing_user_object_logged = false, is_missing_user_object_attribute_logged = false)
|
6
|
+
@key = key
|
7
|
+
@setting_type = setting_type
|
8
|
+
@user = user
|
9
|
+
@visited_keys = visited_keys || []
|
10
|
+
@is_missing_user_object_logged = is_missing_user_object_logged
|
11
|
+
@is_missing_user_object_attribute_logged = is_missing_user_object_attribute_logged
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -1,19 +1,37 @@
|
|
1
1
|
module ConfigCat
|
2
2
|
class EvaluationDetails
|
3
3
|
attr_reader :key, :value, :variation_id, :fetch_time, :user, :is_default_value, :error,
|
4
|
-
:
|
4
|
+
:matched_targeting_rule, :matched_percentage_option
|
5
5
|
|
6
6
|
def initialize(key:, value:, variation_id: nil, fetch_time: nil, user: nil, is_default_value: false, error: nil,
|
7
|
-
|
7
|
+
matched_targeting_rule: nil, matched_percentage_option: nil)
|
8
|
+
# Key of the feature flag or setting.
|
8
9
|
@key = key
|
10
|
+
|
11
|
+
# Evaluated value of the feature flag or setting.
|
9
12
|
@value = value
|
13
|
+
|
14
|
+
# Variation ID of the feature flag or setting (if available).
|
10
15
|
@variation_id = variation_id
|
16
|
+
|
17
|
+
# Time of last successful config download.
|
11
18
|
@fetch_time = fetch_time
|
19
|
+
|
20
|
+
# The User Object used for the evaluation (if available).
|
12
21
|
@user = user
|
22
|
+
|
23
|
+
# Indicates whether the default value passed to the setting evaluation methods like ConfigCatClient.get_value,
|
24
|
+
# ConfigCatClient.get_value_details, etc. is used as the result of the evaluation.
|
13
25
|
@is_default_value = is_default_value
|
26
|
+
|
27
|
+
# Error message in case evaluation failed.
|
14
28
|
@error = error
|
15
|
-
|
16
|
-
|
29
|
+
|
30
|
+
# The targeting rule (if any) that matched during the evaluation and was used to return the evaluated value.
|
31
|
+
@matched_targeting_rule = matched_targeting_rule
|
32
|
+
|
33
|
+
# The percentage option (if any) that was used to select the evaluated value.
|
34
|
+
@matched_percentage_option = matched_percentage_option
|
17
35
|
end
|
18
36
|
|
19
37
|
def self.from_error(key, value, error:, variation_id: nil)
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module ConfigCat
|
2
|
+
class EvaluationLogBuilder
|
3
|
+
def initialize
|
4
|
+
@indent_level = 0
|
5
|
+
@text = ''
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.trunc_comparison_value_if_needed(comparator, comparison_value)
|
9
|
+
if [
|
10
|
+
Comparator::IS_ONE_OF_HASHED,
|
11
|
+
Comparator::IS_NOT_ONE_OF_HASHED,
|
12
|
+
Comparator::EQUALS_HASHED,
|
13
|
+
Comparator::NOT_EQUALS_HASHED,
|
14
|
+
Comparator::STARTS_WITH_ANY_OF_HASHED,
|
15
|
+
Comparator::NOT_STARTS_WITH_ANY_OF_HASHED,
|
16
|
+
Comparator::ENDS_WITH_ANY_OF_HASHED,
|
17
|
+
Comparator::NOT_ENDS_WITH_ANY_OF_HASHED,
|
18
|
+
Comparator::ARRAY_CONTAINS_ANY_OF_HASHED,
|
19
|
+
Comparator::ARRAY_NOT_CONTAINS_ANY_OF_HASHED
|
20
|
+
].include?(comparator)
|
21
|
+
if comparison_value.is_a?(Array)
|
22
|
+
length = comparison_value.length
|
23
|
+
if length > 1
|
24
|
+
return "[<#{length} hashed values>]"
|
25
|
+
end
|
26
|
+
return "[<#{length} hashed value>]"
|
27
|
+
end
|
28
|
+
|
29
|
+
return "'<hashed value>'"
|
30
|
+
end
|
31
|
+
|
32
|
+
if comparison_value.is_a?(Array)
|
33
|
+
length_limit = 10
|
34
|
+
length = comparison_value.length
|
35
|
+
if length > length_limit
|
36
|
+
remaining = length - length_limit
|
37
|
+
more_text = remaining == 1 ? "<1 more value>" : "<#{remaining} more values>"
|
38
|
+
|
39
|
+
formatted_strings = comparison_value.first(length_limit).map { |str| "'#{str}'" }.join(", ")
|
40
|
+
return "[#{formatted_strings}, ... #{more_text}]"
|
41
|
+
end
|
42
|
+
|
43
|
+
# replace '"' with "'" in the string representation of the array
|
44
|
+
formatted_strings = comparison_value.map { |str| "'#{str}'" }.join(", ")
|
45
|
+
return "[#{formatted_strings}]"
|
46
|
+
end
|
47
|
+
|
48
|
+
if [Comparator::BEFORE_DATETIME, Comparator::AFTER_DATETIME].include?(comparator)
|
49
|
+
time = Utils.get_date_time(comparison_value)
|
50
|
+
return "'#{comparison_value}' (#{time.strftime('%Y-%m-%dT%H:%M:%S.%L')}Z UTC)"
|
51
|
+
end
|
52
|
+
|
53
|
+
"'#{comparison_value.to_s}'"
|
54
|
+
end
|
55
|
+
|
56
|
+
def increase_indent
|
57
|
+
@indent_level += 1
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
def decrease_indent
|
62
|
+
@indent_level = [@indent_level - 1, 0].max
|
63
|
+
self
|
64
|
+
end
|
65
|
+
|
66
|
+
def append(text)
|
67
|
+
@text += text
|
68
|
+
self
|
69
|
+
end
|
70
|
+
|
71
|
+
def new_line(text = nil)
|
72
|
+
@text += "\n" + ' ' * @indent_level
|
73
|
+
@text += text if text
|
74
|
+
self
|
75
|
+
end
|
76
|
+
|
77
|
+
def to_s
|
78
|
+
@text
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'configcat/overridedatasource'
|
2
|
-
require 'configcat/
|
2
|
+
require 'configcat/config'
|
3
3
|
|
4
4
|
|
5
5
|
module ConfigCat
|
@@ -17,14 +17,30 @@ module ConfigCat
|
|
17
17
|
class LocalDictionaryDataSource < OverrideDataSource
|
18
18
|
def initialize(source, override_behaviour)
|
19
19
|
super(override_behaviour)
|
20
|
-
@
|
20
|
+
@_config = {}
|
21
21
|
source.each do |key, value|
|
22
|
-
|
22
|
+
value_type = case value
|
23
|
+
when TrueClass, FalseClass
|
24
|
+
BOOL_VALUE
|
25
|
+
when String
|
26
|
+
STRING_VALUE
|
27
|
+
when Integer
|
28
|
+
INT_VALUE
|
29
|
+
when Float
|
30
|
+
DOUBLE_VALUE
|
31
|
+
else
|
32
|
+
UNSUPPORTED_VALUE
|
33
|
+
end
|
34
|
+
|
35
|
+
@_config[FEATURE_FLAGS] ||= {}
|
36
|
+
@_config[FEATURE_FLAGS][key] = { VALUE => { value_type => value } }
|
37
|
+
setting_type = SettingType.from_type(value.class)
|
38
|
+
@_config[FEATURE_FLAGS][key][SETTING_TYPE] = setting_type.to_i unless setting_type.nil?
|
23
39
|
end
|
24
40
|
end
|
25
41
|
|
26
42
|
def get_overrides
|
27
|
-
return @
|
43
|
+
return @_config
|
28
44
|
end
|
29
45
|
end
|
30
46
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'configcat/overridedatasource'
|
2
|
-
require 'configcat/
|
2
|
+
require 'configcat/config'
|
3
3
|
|
4
4
|
|
5
5
|
module ConfigCat
|
@@ -18,17 +18,17 @@ module ConfigCat
|
|
18
18
|
def initialize(file_path, override_behaviour, log)
|
19
19
|
super(override_behaviour)
|
20
20
|
@log = log
|
21
|
-
if !File.
|
21
|
+
if !File.exist?(file_path)
|
22
22
|
@log.error(1300, "Cannot find the local config file '#{file_path}'. This is a path that your application provided to the ConfigCat SDK by passing it to the `LocalFileFlagOverrides.new()` method. Read more: https://configcat.com/docs/sdk-reference/ruby/#json-file")
|
23
23
|
end
|
24
24
|
@_file_path = file_path
|
25
|
-
@
|
25
|
+
@_config = nil
|
26
26
|
@_cached_file_stamp = 0
|
27
27
|
end
|
28
28
|
|
29
29
|
def get_overrides
|
30
30
|
reload_file_content()
|
31
|
-
return @
|
31
|
+
return @_config
|
32
32
|
end
|
33
33
|
|
34
34
|
private
|
@@ -41,13 +41,29 @@ module ConfigCat
|
|
41
41
|
file = File.read(@_file_path)
|
42
42
|
data = JSON.parse(file)
|
43
43
|
if data.key?("flags")
|
44
|
-
@
|
44
|
+
@_config = { FEATURE_FLAGS => {} }
|
45
45
|
source = data["flags"]
|
46
46
|
source.each do |key, value|
|
47
|
-
|
47
|
+
value_type = case value
|
48
|
+
when true, false
|
49
|
+
BOOL_VALUE
|
50
|
+
when String
|
51
|
+
STRING_VALUE
|
52
|
+
when Integer
|
53
|
+
INT_VALUE
|
54
|
+
when Float
|
55
|
+
DOUBLE_VALUE
|
56
|
+
else
|
57
|
+
UNSUPPORTED_VALUE
|
58
|
+
end
|
59
|
+
|
60
|
+
@_config[FEATURE_FLAGS][key] = { VALUE => { value_type => value } }
|
61
|
+
setting_type = SettingType.from_type(value.class)
|
62
|
+
@_config[FEATURE_FLAGS][key][SETTING_TYPE] = setting_type.to_i unless setting_type.nil?
|
48
63
|
end
|
49
64
|
else
|
50
|
-
|
65
|
+
Config.fixup_config_salt_and_segments(data)
|
66
|
+
@_config = data
|
51
67
|
end
|
52
68
|
end
|
53
69
|
rescue JSON::ParserError => e
|