prefab-cloud-ruby 0.5.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.tool-versions +1 -0
- data/CODEOWNERS +1 -0
- data/Gemfile +6 -3
- data/Gemfile.lock +63 -47
- data/README.md +1 -1
- data/VERSION +1 -1
- data/compile_protos.sh +3 -0
- data/lib/prefab/cancellable_interceptor.rb +18 -0
- data/lib/prefab/client.rb +7 -4
- data/lib/prefab/config_client.rb +27 -15
- data/lib/prefab/config_helper.rb +20 -0
- data/lib/prefab/config_loader.rb +3 -3
- data/lib/prefab/config_resolver.rb +38 -34
- data/lib/prefab/feature_flag_client.rb +99 -11
- data/lib/prefab-cloud-ruby.rb +1 -0
- data/lib/prefab_pb.rb +206 -132
- data/lib/prefab_services_pb.rb +6 -6
- data/prefab-cloud-ruby.gemspec +30 -30
- data/run_test_harness_server.sh +2 -0
- data/test/harness_server.rb +46 -0
- data/test/test_config_loader.rb +12 -12
- data/test/test_config_resolver.rb +107 -37
- data/test/test_feature_flag_client.rb +191 -35
- data/test/test_helper.rb +13 -3
- metadata +70 -25
- data/.ruby-version +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a36d24a94355b3b7a2b3b889734e1f6f6ccafc7dcebfb11ff11d77ba92ea7828
|
4
|
+
data.tar.gz: 326b3166d4fc2589ebb2550114b2cc198617620daa70ecfb6b9fe394c9dd79a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fb73839190f23e05540688572b8f47c1300ae9181bdecef0f24fdecb9a9e73ace1c88937e7c228601c20ff68405876c21b977e45bd691ba33f59d71dd4811718
|
7
|
+
data.tar.gz: 792f04aa032c2f9975444a191ddbb8f727e256d12ec2fc4b3c26819bff4a425deabffb3ff262221e7fe90ed4c1d1422d7a2cb447530264cde494b4e149de7e78
|
data/.tool-versions
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby 3.0.3
|
data/CODEOWNERS
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
* @prefab-cloud/prefabdevs @prefab-cloud/prefabmaintainers @prefab-cloud/prefabadmins
|
data/Gemfile
CHANGED
@@ -2,13 +2,16 @@ source "https://rubygems.org"
|
|
2
2
|
|
3
3
|
gem 'concurrent-ruby', '~> 1.0', '>= 1.0.5'
|
4
4
|
gem 'faraday'
|
5
|
-
gem 'grpc',
|
5
|
+
gem 'grpc', :platforms => :ruby
|
6
|
+
gem 'google-protobuf', :platforms => :ruby
|
7
|
+
gem 'googleapis-common-protos-types', :platforms => :ruby
|
6
8
|
|
7
9
|
group :development do
|
8
|
-
gem 'grpc-tools',
|
10
|
+
gem 'grpc-tools', :platforms => :ruby
|
9
11
|
gem "shoulda", ">= 0"
|
10
12
|
gem "rdoc", "~> 3.12"
|
11
|
-
gem "bundler"
|
13
|
+
gem "bundler"
|
12
14
|
gem "juwelier", "~> 2.4.9"
|
13
15
|
gem "simplecov", ">= 0"
|
16
|
+
gem 'thin'
|
14
17
|
end
|
data/Gemfile.lock
CHANGED
@@ -1,37 +1,43 @@
|
|
1
1
|
GEM
|
2
2
|
remote: https://rubygems.org/
|
3
3
|
specs:
|
4
|
-
activesupport (5.2.
|
4
|
+
activesupport (5.2.4.5)
|
5
5
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
6
6
|
i18n (>= 0.7, < 2)
|
7
7
|
minitest (~> 5.1)
|
8
8
|
tzinfo (~> 1.1)
|
9
|
-
addressable (2.
|
10
|
-
public_suffix (>= 2.0.2, <
|
11
|
-
builder (3.2.
|
12
|
-
concurrent-ruby (1.
|
9
|
+
addressable (2.7.0)
|
10
|
+
public_suffix (>= 2.0.2, < 5.0)
|
11
|
+
builder (3.2.4)
|
12
|
+
concurrent-ruby (1.1.8)
|
13
|
+
daemons (1.4.1)
|
13
14
|
descendants_tracker (0.0.4)
|
14
15
|
thread_safe (~> 0.3, >= 0.3.1)
|
15
|
-
docile (1.3.
|
16
|
-
|
16
|
+
docile (1.3.5)
|
17
|
+
eventmachine (1.2.7)
|
18
|
+
faraday (1.3.0)
|
19
|
+
faraday-net_http (~> 1.0)
|
17
20
|
multipart-post (>= 1.2, < 3)
|
18
|
-
|
19
|
-
|
21
|
+
ruby2_keywords
|
22
|
+
faraday-net_http (1.0.1)
|
23
|
+
git (1.8.1)
|
24
|
+
rchardet (~> 1.8)
|
25
|
+
github_api (0.19.0)
|
20
26
|
addressable (~> 2.4)
|
21
27
|
descendants_tracker (~> 0.0.4)
|
22
|
-
faraday (
|
28
|
+
faraday (>= 0.8, < 2)
|
23
29
|
hashie (~> 3.5, >= 3.5.2)
|
24
30
|
oauth2 (~> 1.0)
|
25
|
-
google-protobuf (3.
|
26
|
-
googleapis-common-protos-types (1.0
|
27
|
-
google-protobuf (~> 3.
|
28
|
-
grpc (1.
|
29
|
-
google-protobuf (~> 3.
|
30
|
-
googleapis-common-protos-types (~> 1.0
|
31
|
-
grpc-tools (1.
|
31
|
+
google-protobuf (3.19.3)
|
32
|
+
googleapis-common-protos-types (1.3.0)
|
33
|
+
google-protobuf (~> 3.14)
|
34
|
+
grpc (1.43.1)
|
35
|
+
google-protobuf (~> 3.18)
|
36
|
+
googleapis-common-protos-types (~> 1.0)
|
37
|
+
grpc-tools (1.43.1)
|
32
38
|
hashie (3.6.0)
|
33
|
-
highline (2.0.
|
34
|
-
i18n (1.
|
39
|
+
highline (2.0.3)
|
40
|
+
i18n (1.8.9)
|
35
41
|
concurrent-ruby (~> 1.0)
|
36
42
|
json (1.8.6)
|
37
43
|
juwelier (2.4.9)
|
@@ -46,57 +52,67 @@ GEM
|
|
46
52
|
rake
|
47
53
|
rdoc
|
48
54
|
semver2
|
49
|
-
jwt (2.
|
55
|
+
jwt (2.2.2)
|
50
56
|
kamelcase (0.0.2)
|
51
57
|
semver2 (~> 3)
|
52
|
-
mini_portile2 (2.
|
53
|
-
minitest (5.
|
54
|
-
multi_json (1.
|
58
|
+
mini_portile2 (2.7.1)
|
59
|
+
minitest (5.14.4)
|
60
|
+
multi_json (1.15.0)
|
55
61
|
multi_xml (0.6.0)
|
56
|
-
multipart-post (2.
|
57
|
-
nokogiri (1.
|
58
|
-
mini_portile2 (~> 2.
|
59
|
-
|
60
|
-
|
62
|
+
multipart-post (2.1.1)
|
63
|
+
nokogiri (1.13.1)
|
64
|
+
mini_portile2 (~> 2.7.0)
|
65
|
+
racc (~> 1.4)
|
66
|
+
oauth2 (1.4.7)
|
67
|
+
faraday (>= 0.8, < 2.0)
|
61
68
|
jwt (>= 1.0, < 3.0)
|
62
69
|
multi_json (~> 1.3)
|
63
70
|
multi_xml (~> 0.5)
|
64
71
|
rack (>= 1.2, < 3)
|
65
|
-
psych (3.1
|
66
|
-
public_suffix (
|
67
|
-
|
68
|
-
|
72
|
+
psych (3.3.1)
|
73
|
+
public_suffix (4.0.6)
|
74
|
+
racc (1.6.0)
|
75
|
+
rack (2.2.3)
|
76
|
+
rake (13.0.3)
|
77
|
+
rchardet (1.8.0)
|
69
78
|
rdoc (3.12.2)
|
70
79
|
json (~> 1.4)
|
80
|
+
ruby2_keywords (0.0.4)
|
71
81
|
semver2 (3.4.2)
|
72
|
-
shoulda (
|
73
|
-
shoulda-context (~>
|
74
|
-
shoulda-matchers (
|
75
|
-
shoulda-context (
|
76
|
-
shoulda-matchers (
|
77
|
-
activesupport (>=
|
78
|
-
simplecov (0.
|
82
|
+
shoulda (4.0.0)
|
83
|
+
shoulda-context (~> 2.0)
|
84
|
+
shoulda-matchers (~> 4.0)
|
85
|
+
shoulda-context (2.0.0)
|
86
|
+
shoulda-matchers (4.5.1)
|
87
|
+
activesupport (>= 4.2.0)
|
88
|
+
simplecov (0.18.5)
|
79
89
|
docile (~> 1.1)
|
80
|
-
|
81
|
-
|
82
|
-
|
90
|
+
simplecov-html (~> 0.11)
|
91
|
+
simplecov-html (0.12.3)
|
92
|
+
thin (1.8.1)
|
93
|
+
daemons (~> 1.0, >= 1.0.9)
|
94
|
+
eventmachine (~> 1.0, >= 1.0.4)
|
95
|
+
rack (>= 1, < 3)
|
83
96
|
thread_safe (0.3.6)
|
84
|
-
tzinfo (1.2.
|
97
|
+
tzinfo (1.2.9)
|
85
98
|
thread_safe (~> 0.1)
|
86
99
|
|
87
100
|
PLATFORMS
|
88
101
|
ruby
|
89
102
|
|
90
103
|
DEPENDENCIES
|
91
|
-
bundler
|
104
|
+
bundler
|
92
105
|
concurrent-ruby (~> 1.0, >= 1.0.5)
|
93
106
|
faraday
|
94
|
-
|
95
|
-
|
107
|
+
google-protobuf
|
108
|
+
googleapis-common-protos-types
|
109
|
+
grpc
|
110
|
+
grpc-tools
|
96
111
|
juwelier (~> 2.4.9)
|
97
112
|
rdoc (~> 3.12)
|
98
113
|
shoulda
|
99
114
|
simplecov
|
115
|
+
thin
|
100
116
|
|
101
117
|
BUNDLED WITH
|
102
|
-
|
118
|
+
2.3.5
|
data/README.md
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.7.0
|
data/compile_protos.sh
CHANGED
@@ -1,8 +1,26 @@
|
|
1
1
|
module Prefab
|
2
2
|
class CancellableInterceptor < GRPC::ClientInterceptor
|
3
|
+
WAIT_SEC = 3
|
4
|
+
|
5
|
+
def initialize(base_client)
|
6
|
+
@base_client = base_client
|
7
|
+
end
|
3
8
|
|
4
9
|
def cancel
|
5
10
|
@call.instance_variable_get("@wrapped").instance_variable_get("@call").cancel
|
11
|
+
i = 0
|
12
|
+
while (i < WAIT_SEC) do
|
13
|
+
if @call.instance_variable_get("@wrapped").cancelled?
|
14
|
+
@base_client.log_internal Logger::DEBUG, "Cancelled streaming."
|
15
|
+
return
|
16
|
+
else
|
17
|
+
@base_client.log_internal Logger::DEBUG, "Unable to cancel streaming. Trying again"
|
18
|
+
@call.instance_variable_get("@wrapped").instance_variable_get("@call").cancel
|
19
|
+
i += 1
|
20
|
+
sleep(1)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
@base_client.log_internal Logger::INFO, "Unable to cancel streaming."
|
6
24
|
end
|
7
25
|
|
8
26
|
def request_response(request:, call:, method:, metadata:, &block)
|
data/lib/prefab/client.rb
CHANGED
@@ -8,7 +8,7 @@ module Prefab
|
|
8
8
|
}
|
9
9
|
|
10
10
|
|
11
|
-
attr_reader :
|
11
|
+
attr_reader :project_id, :shared_cache, :stats, :namespace, :interceptor, :api_key, :environment
|
12
12
|
|
13
13
|
def initialize(api_key: ENV['PREFAB_API_KEY'],
|
14
14
|
logdev: nil,
|
@@ -19,13 +19,15 @@ module Prefab
|
|
19
19
|
log_formatter: DEFAULT_LOG_FORMATTER
|
20
20
|
)
|
21
21
|
raise "No API key. Set PREFAB_API_KEY env var" if api_key.nil? || api_key.empty?
|
22
|
+
raise "PREFAB_API_KEY format invalid. Expecting 123-development-yourapikey" unless api_key.count("-") == 2
|
22
23
|
@logdev = (logdev || $stdout)
|
23
24
|
@log_formatter = log_formatter
|
24
25
|
@local = local
|
25
26
|
@stats = (stats || NoopStats.new)
|
26
27
|
@shared_cache = (shared_cache || NoopCache.new)
|
27
28
|
@api_key = api_key
|
28
|
-
@
|
29
|
+
@project_id = api_key.split("-")[0].to_i
|
30
|
+
@environment = api_key.split("-")[1]
|
29
31
|
@namespace = namespace
|
30
32
|
@interceptor = Prefab::AuthInterceptor.new(api_key)
|
31
33
|
@stubs = {}
|
@@ -37,7 +39,8 @@ module Prefab
|
|
37
39
|
|
38
40
|
def channel
|
39
41
|
credentials = ENV["PREFAB_CLOUD_HTTP"] == "true" ? :this_channel_is_insecure : creds
|
40
|
-
url = ENV["PREFAB_API_URL"] || '
|
42
|
+
url = ENV["PREFAB_API_URL"] || 'grpc.prefab.cloud:443'
|
43
|
+
log_internal Logger::DEBUG, "GRPC Channel #{url} #{credentials}"
|
41
44
|
@_channel ||= GRPC::Core::Channel.new(url, nil, credentials)
|
42
45
|
end
|
43
46
|
|
@@ -88,7 +91,7 @@ module Prefab
|
|
88
91
|
end
|
89
92
|
|
90
93
|
def cache_key(post_fix)
|
91
|
-
"prefab:#{
|
94
|
+
"prefab:#{project_id}:#{post_fix}"
|
92
95
|
end
|
93
96
|
|
94
97
|
def reset!
|
data/lib/prefab/config_client.rb
CHANGED
@@ -6,6 +6,7 @@ module Prefab
|
|
6
6
|
|
7
7
|
def initialize(base_client, timeout)
|
8
8
|
@base_client = base_client
|
9
|
+
@base_client.log_internal Logger::DEBUG, "Initialize ConfigClient"
|
9
10
|
@timeout = timeout
|
10
11
|
@initialization_lock = Concurrent::ReadWriteLock.new
|
11
12
|
|
@@ -14,26 +15,26 @@ module Prefab
|
|
14
15
|
@config_loader = Prefab::ConfigLoader.new(@base_client)
|
15
16
|
@config_resolver = Prefab::ConfigResolver.new(@base_client, @config_loader)
|
16
17
|
|
18
|
+
@base_client.log_internal Logger::DEBUG, "Initialize ConfigClient: AcquireWriteLock"
|
17
19
|
@initialization_lock.acquire_write_lock
|
20
|
+
@base_client.log_internal Logger::DEBUG, "Initialize ConfigClient: AcquiredWriteLock"
|
18
21
|
|
19
|
-
@cancellable_interceptor = Prefab::CancellableInterceptor.new
|
22
|
+
@cancellable_interceptor = Prefab::CancellableInterceptor.new(@base_client)
|
20
23
|
|
21
24
|
@s3_cloud_front = ENV["PREFAB_S3CF_BUCKET"] || DEFAULT_S3CF_BUCKET
|
25
|
+
|
22
26
|
load_checkpoint
|
23
27
|
start_checkpointing_thread
|
24
28
|
end
|
25
29
|
|
26
30
|
def start_streaming
|
27
|
-
|
28
|
-
@cancellable_interceptor.cancel
|
29
|
-
end
|
30
|
-
|
31
|
+
@streaming = true
|
31
32
|
start_api_connection_thread(@config_loader.highwater_mark)
|
32
33
|
end
|
33
34
|
|
34
|
-
def get(
|
35
|
+
def get(key)
|
35
36
|
@initialization_lock.with_read_lock do
|
36
|
-
@config_resolver.get(
|
37
|
+
@config_resolver.get(key)
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
@@ -89,9 +90,12 @@ module Prefab
|
|
89
90
|
end
|
90
91
|
|
91
92
|
def load_checkpoint_from_config
|
92
|
-
|
93
|
+
@base_client.log_internal Logger::DEBUG, "Load Checkpoint From Config"
|
94
|
+
|
95
|
+
config_req = Prefab::ConfigServicePointer.new(project_id: @base_client.project_id,
|
93
96
|
start_at_id: @config_loader.highwater_mark)
|
94
97
|
resp = stub.get_all_config(config_req)
|
98
|
+
@base_client.log_internal Logger::DEBUG, "Got Response #{resp}"
|
95
99
|
load_deltas(resp, :api)
|
96
100
|
resp.deltas.each do |delta|
|
97
101
|
@config_loader.set(delta)
|
@@ -115,7 +119,6 @@ module Prefab
|
|
115
119
|
end
|
116
120
|
end
|
117
121
|
|
118
|
-
|
119
122
|
def load_deltas(deltas, source)
|
120
123
|
deltas.deltas.each do |delta|
|
121
124
|
@config_loader.set(delta)
|
@@ -156,12 +159,18 @@ module Prefab
|
|
156
159
|
# Setup a streaming connection to the API
|
157
160
|
# Save new config values into the loader
|
158
161
|
def start_api_connection_thread(start_at_id)
|
159
|
-
config_req = Prefab::ConfigServicePointer.new(
|
162
|
+
config_req = Prefab::ConfigServicePointer.new(project_id: @base_client.project_id,
|
160
163
|
start_at_id: start_at_id)
|
161
164
|
@base_client.log_internal Logger::DEBUG, "start api connection thread #{start_at_id}"
|
162
165
|
@base_client.stats.increment("prefab.config.api.start")
|
166
|
+
|
163
167
|
@api_connection_thread = Thread.new do
|
164
|
-
|
168
|
+
at_exit do
|
169
|
+
@streaming = false
|
170
|
+
@cancellable_interceptor.cancel
|
171
|
+
end
|
172
|
+
|
173
|
+
while @streaming do
|
165
174
|
begin
|
166
175
|
resp = stub.get_config(config_req)
|
167
176
|
resp.each do |r|
|
@@ -172,13 +181,16 @@ module Prefab
|
|
172
181
|
finish_init!(:streaming)
|
173
182
|
end
|
174
183
|
rescue => e
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
184
|
+
if @streaming
|
185
|
+
level = e.code == 1 ? Logger::DEBUG : Logger::INFO
|
186
|
+
@base_client.log_internal level, ("config client encountered #{e.message} pausing #{RECONNECT_WAIT}")
|
187
|
+
reset
|
188
|
+
sleep(RECONNECT_WAIT)
|
189
|
+
end
|
179
190
|
end
|
180
191
|
end
|
181
192
|
end
|
193
|
+
|
182
194
|
end
|
183
195
|
end
|
184
196
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Prefab
|
2
|
+
module ConfigHelper
|
3
|
+
def value_of(config_value)
|
4
|
+
case config_value.type
|
5
|
+
when :string
|
6
|
+
config_value.string
|
7
|
+
when :int
|
8
|
+
config_value.int
|
9
|
+
when :double
|
10
|
+
config_value.double
|
11
|
+
when :bool
|
12
|
+
config_value.bool
|
13
|
+
when :feature_flag
|
14
|
+
config_value.feature_flag
|
15
|
+
when :segment
|
16
|
+
config_value.segment
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/prefab/config_loader.rb
CHANGED
@@ -14,7 +14,7 @@ module Prefab
|
|
14
14
|
def calc_config
|
15
15
|
rtn = @classpath_config.clone
|
16
16
|
@api_config.each_key do |k|
|
17
|
-
rtn[k] = @api_config[k]
|
17
|
+
rtn[k] = @api_config[k]
|
18
18
|
end
|
19
19
|
rtn = rtn.merge(@local_overrides)
|
20
20
|
rtn
|
@@ -26,7 +26,7 @@ module Prefab
|
|
26
26
|
return
|
27
27
|
end
|
28
28
|
|
29
|
-
if delta.
|
29
|
+
if delta.default.nil?
|
30
30
|
@api_config.delete(delta.key)
|
31
31
|
else
|
32
32
|
@api_config[delta.key] = delta
|
@@ -63,7 +63,7 @@ module Prefab
|
|
63
63
|
Dir.glob(glob).each do |file|
|
64
64
|
yaml = load(file)
|
65
65
|
yaml.each do |k, v|
|
66
|
-
rtn[k] = Prefab::ConfigValue.new(value_from(v))
|
66
|
+
rtn[k] = Prefab::ConfigDelta.new(key: k, default: Prefab::ConfigValue.new(value_from(v)))
|
67
67
|
end
|
68
68
|
end
|
69
69
|
rtn
|
@@ -1,11 +1,12 @@
|
|
1
1
|
module Prefab
|
2
2
|
class ConfigResolver
|
3
|
+
include Prefab::ConfigHelper
|
3
4
|
NAMESPACE_DELIMITER = ".".freeze
|
4
|
-
NAME_KEY_DELIMITER = ":".freeze
|
5
5
|
|
6
6
|
def initialize(base_client, config_loader)
|
7
7
|
@lock = Concurrent::ReadWriteLock.new
|
8
8
|
@local_store = {}
|
9
|
+
@environment = base_client.environment
|
9
10
|
@namespace = base_client.namespace
|
10
11
|
@config_loader = config_loader
|
11
12
|
make_local
|
@@ -16,7 +17,7 @@ module Prefab
|
|
16
17
|
@lock.with_read_lock do
|
17
18
|
@local_store.each do |k, v|
|
18
19
|
value = v[:value]
|
19
|
-
str << "|#{k}|
|
20
|
+
str << "|#{k}| from #{v[:match]} |#{value_of(value)}|#{value_of(value).class}\n"
|
20
21
|
end
|
21
22
|
end
|
22
23
|
str
|
@@ -39,21 +40,6 @@ module Prefab
|
|
39
40
|
|
40
41
|
private
|
41
42
|
|
42
|
-
def value_of(config_value)
|
43
|
-
case config_value.type
|
44
|
-
when :string
|
45
|
-
config_value.string
|
46
|
-
when :int
|
47
|
-
config_value.int
|
48
|
-
when :double
|
49
|
-
config_value.double
|
50
|
-
when :bool
|
51
|
-
config_value.bool
|
52
|
-
when :feature_flag
|
53
|
-
config_value.feature_flag
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
43
|
# Should client a.b.c see key in namespace a.b? yes
|
58
44
|
# Should client a.b.c see key in namespace a.b.c? yes
|
59
45
|
# Should client a.b.c see key in namespace a.b.d? no
|
@@ -61,32 +47,50 @@ module Prefab
|
|
61
47
|
#
|
62
48
|
def starts_with_ns?(key_namespace, client_namespace)
|
63
49
|
zipped = key_namespace.split(NAMESPACE_DELIMITER).zip(client_namespace.split(NAMESPACE_DELIMITER))
|
64
|
-
zipped.map do |k, c|
|
65
|
-
(k.nil? || k.empty?) ||
|
66
|
-
end
|
50
|
+
mapped = zipped.map do |k, c|
|
51
|
+
(k.nil? || k.empty?) || k == c
|
52
|
+
end
|
53
|
+
[mapped.all?, mapped.size]
|
67
54
|
end
|
68
55
|
|
69
56
|
def make_local
|
70
57
|
store = {}
|
71
|
-
@config_loader.calc_config.each do |
|
72
|
-
|
73
|
-
|
58
|
+
@config_loader.calc_config.each do |key, delta|
|
59
|
+
# start with the top level default
|
60
|
+
to_store = { match: "default", value: delta.default }
|
61
|
+
if delta.envs.any?
|
62
|
+
env_values = delta.envs.select { |e| e.environment == @environment }
|
74
63
|
|
75
|
-
|
64
|
+
# do we have and env_values that match our env?
|
65
|
+
if env_values.any?
|
66
|
+
env_value = env_values.first
|
76
67
|
|
77
|
-
|
78
|
-
|
79
|
-
key_namespace = split[0]
|
80
|
-
end
|
68
|
+
# override the top level default with env default
|
69
|
+
to_store = { match: "env_default", env: env_value.environment, value: env_value.default }
|
81
70
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
71
|
+
if env_value.namespace_values.any?
|
72
|
+
# check all namespace_values for match
|
73
|
+
env_value.namespace_values.each do |namespace_value|
|
74
|
+
(starts_with, count) = starts_with_ns?(namespace_value.namespace, @namespace)
|
75
|
+
if starts_with
|
76
|
+
# is this match the best match?
|
77
|
+
if count > (to_store[:match_depth_count] || 0)
|
78
|
+
to_store = { match: namespace_value.namespace, count: count, value: namespace_value.config_value }
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
88
83
|
end
|
89
84
|
end
|
85
|
+
|
86
|
+
# feature flags are a funny case
|
87
|
+
# we only define the variants in the default in order to be DRY
|
88
|
+
# but we want to access them in environments, clone them over
|
89
|
+
if to_store[:value].type == :feature_flag
|
90
|
+
to_store[:value].feature_flag.variants = delta.default.feature_flag.variants
|
91
|
+
end
|
92
|
+
|
93
|
+
store[key] = to_store
|
90
94
|
end
|
91
95
|
@lock.with_write_lock do
|
92
96
|
@local_store = store
|