flipper-cloud 0.25.4 → 0.26.0.rc2
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/examples/cloud/forked.rb +31 -0
- data/examples/cloud/threaded.rb +36 -0
- data/lib/flipper/cloud/configuration.rb +26 -13
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/cloud/configuration_spec.rb +4 -3
- data/spec/flipper/cloud_spec.rb +21 -36
- metadata +8 -7
- data/lib/flipper/cloud/registry.rb +0 -46
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 237f86d236d564cb69735a00ad88c5d833b152f9e6f9a64bd50529ba2c39dd0d
|
|
4
|
+
data.tar.gz: 11e612037550a9e83b763d0d2c80d2f5191b4a52c9288f2f61e9dbf6640d38f5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: caa1819e0815fe2ba4a3fad0be799664dce53d86abd66e729f9c14cc870061478718396b610ef0f3221f25334fa83a7d970d3059c09289fa3767b87efeef49be
|
|
7
|
+
data.tar.gz: ac5113e292581c9842154cdfed2ddbc3d23aa66934845a530aa59b8d2e08a765c057fd8b757cd1bd526a5f11946debb0ea6a69732939f4f79183e998f047e9de
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Usage (from the repo root):
|
|
2
|
+
# env FLIPPER_CLOUD_TOKEN=<token> bundle exec ruby examples/cloud/threaded.rb
|
|
3
|
+
|
|
4
|
+
require_relative "./cloud_setup"
|
|
5
|
+
require 'bundler/setup'
|
|
6
|
+
require 'flipper/cloud'
|
|
7
|
+
|
|
8
|
+
pids = 5.times.map do |n|
|
|
9
|
+
fork {
|
|
10
|
+
# Check every second to see if the feature is enabled
|
|
11
|
+
threads = []
|
|
12
|
+
5.times do
|
|
13
|
+
threads << Thread.new do
|
|
14
|
+
loop do
|
|
15
|
+
sleep rand
|
|
16
|
+
|
|
17
|
+
if Flipper[:stats].enabled?
|
|
18
|
+
puts "#{Process.pid} #{Time.now.to_i} Enabled!"
|
|
19
|
+
else
|
|
20
|
+
puts "#{Process.pid} #{Time.now.to_i} Disabled!"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
threads.map(&:join)
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
pids.each do |pid|
|
|
30
|
+
Process.waitpid pid, 0
|
|
31
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Usage (from the repo root):
|
|
2
|
+
# env FLIPPER_CLOUD_TOKEN=<token> bundle exec ruby examples/cloud/threaded.rb
|
|
3
|
+
|
|
4
|
+
require_relative "./cloud_setup"
|
|
5
|
+
require 'bundler/setup'
|
|
6
|
+
require 'flipper/cloud'
|
|
7
|
+
require "active_support/notifications"
|
|
8
|
+
require "active_support/isolated_execution_state"
|
|
9
|
+
|
|
10
|
+
ActiveSupport::Notifications.subscribe(/poller\.flipper/) do |*args|
|
|
11
|
+
p args: args
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
Flipper.configure do |config|
|
|
15
|
+
config.default {
|
|
16
|
+
Flipper::Cloud.new(local_adapter: config.adapter, instrumenter: ActiveSupport::Notifications)
|
|
17
|
+
}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Check every second to see if the feature is enabled
|
|
21
|
+
threads = []
|
|
22
|
+
10.times do
|
|
23
|
+
threads << Thread.new do
|
|
24
|
+
loop do
|
|
25
|
+
sleep rand
|
|
26
|
+
|
|
27
|
+
if Flipper[:stats].enabled?
|
|
28
|
+
puts "#{Time.now.to_i} Enabled!"
|
|
29
|
+
else
|
|
30
|
+
puts "#{Time.now.to_i} Disabled!"
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
threads.map(&:join)
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
require "socket"
|
|
2
2
|
require "flipper/adapters/http"
|
|
3
|
+
require "flipper/adapters/poll"
|
|
4
|
+
require "flipper/adapters/poll/poller"
|
|
3
5
|
require "flipper/adapters/memory"
|
|
4
6
|
require "flipper/adapters/dual_write"
|
|
5
|
-
require "flipper/adapters/sync"
|
|
7
|
+
require "flipper/adapters/sync/synchronizer"
|
|
6
8
|
require "flipper/cloud/instrumenter"
|
|
7
|
-
require "flipper/cloud/registry"
|
|
8
9
|
require "brow"
|
|
9
10
|
|
|
10
11
|
module Flipper
|
|
@@ -18,6 +19,12 @@ module Flipper
|
|
|
18
19
|
|
|
19
20
|
DEFAULT_URL = "https://www.flippercloud.io/adapter".freeze
|
|
20
21
|
|
|
22
|
+
# Private: Keeps track of brow instances so they can be shared across
|
|
23
|
+
# threads.
|
|
24
|
+
def self.brow_instances
|
|
25
|
+
@brow_instances ||= Concurrent::Map.new
|
|
26
|
+
end
|
|
27
|
+
|
|
21
28
|
# Public: The token corresponding to an environment on flippercloud.io.
|
|
22
29
|
attr_accessor :token
|
|
23
30
|
|
|
@@ -124,13 +131,12 @@ module Flipper
|
|
|
124
131
|
end
|
|
125
132
|
|
|
126
133
|
def brow
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
134
|
+
self.class.brow_instances.compute_if_absent(url + token) do
|
|
135
|
+
uri = URI.parse(url)
|
|
136
|
+
uri.path = "#{uri.path}/events".squeeze("/")
|
|
130
137
|
|
|
131
|
-
Registry.default.fetch(events_url) {
|
|
132
138
|
Brow::Client.new({
|
|
133
|
-
url:
|
|
139
|
+
url: uri.to_s,
|
|
134
140
|
headers: {
|
|
135
141
|
"Accept" => "application/json",
|
|
136
142
|
"Content-Type" => "application/json",
|
|
@@ -138,7 +144,7 @@ module Flipper
|
|
|
138
144
|
"Flipper-Cloud-Token" => @token,
|
|
139
145
|
}
|
|
140
146
|
})
|
|
141
|
-
|
|
147
|
+
end
|
|
142
148
|
end
|
|
143
149
|
|
|
144
150
|
# Public: The method that will be used to synchronize local adapter with
|
|
@@ -150,18 +156,23 @@ module Flipper
|
|
|
150
156
|
private
|
|
151
157
|
|
|
152
158
|
def app_adapter
|
|
153
|
-
sync_method == :webhook ? dual_write_adapter :
|
|
159
|
+
sync_method == :webhook ? dual_write_adapter : poll_adapter
|
|
154
160
|
end
|
|
155
161
|
|
|
156
162
|
def dual_write_adapter
|
|
157
163
|
Flipper::Adapters::DualWrite.new(local_adapter, http_adapter)
|
|
158
164
|
end
|
|
159
165
|
|
|
160
|
-
def
|
|
161
|
-
Flipper::Adapters::
|
|
162
|
-
instrumenter: instrumenter,
|
|
166
|
+
def poller
|
|
167
|
+
Flipper::Adapters::Poll::Poller.get(@url + @token, {
|
|
163
168
|
interval: sync_interval,
|
|
164
|
-
|
|
169
|
+
remote_adapter: http_adapter,
|
|
170
|
+
instrumenter: instrumenter,
|
|
171
|
+
}).tap(&:start)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def poll_adapter
|
|
175
|
+
Flipper::Adapters::Poll.new(poller, dual_write_adapter)
|
|
165
176
|
end
|
|
166
177
|
|
|
167
178
|
def http_adapter
|
|
@@ -169,6 +180,8 @@ module Flipper
|
|
|
169
180
|
url: @url,
|
|
170
181
|
read_timeout: @read_timeout,
|
|
171
182
|
open_timeout: @open_timeout,
|
|
183
|
+
write_timeout: @write_timeout,
|
|
184
|
+
max_retries: 0, # we'll handle retries ourselves
|
|
172
185
|
debug_output: @debug_output,
|
|
173
186
|
headers: {
|
|
174
187
|
"Flipper-Cloud-Token" => @token,
|
data/lib/flipper/version.rb
CHANGED
|
@@ -72,7 +72,8 @@ RSpec.describe Flipper::Cloud::Configuration do
|
|
|
72
72
|
stub_request(:get, /flippercloud\.io/).to_return(status: 200, body: "{}")
|
|
73
73
|
|
|
74
74
|
instance = described_class.new(required_options.merge(sync_interval: 1))
|
|
75
|
-
|
|
75
|
+
poller = instance.send(:poller)
|
|
76
|
+
expect(poller.interval).to eq(1)
|
|
76
77
|
end
|
|
77
78
|
|
|
78
79
|
it "can set debug_output" do
|
|
@@ -85,7 +86,7 @@ RSpec.describe Flipper::Cloud::Configuration do
|
|
|
85
86
|
stub_request(:get, /flippercloud\.io/).to_return(status: 200, body: "{}")
|
|
86
87
|
|
|
87
88
|
instance = described_class.new(required_options)
|
|
88
|
-
expect(instance.adapter).to be_instance_of(Flipper::Adapters::
|
|
89
|
+
expect(instance.adapter).to be_instance_of(Flipper::Adapters::Poll)
|
|
89
90
|
end
|
|
90
91
|
|
|
91
92
|
it "can override adapter block" do
|
|
@@ -234,7 +235,7 @@ RSpec.describe Flipper::Cloud::Configuration do
|
|
|
234
235
|
expect(stub).to have_been_requested
|
|
235
236
|
|
|
236
237
|
# Check that local adapter really did sync.
|
|
237
|
-
local_adapter = instance.
|
|
238
|
+
local_adapter = instance.local_adapter
|
|
238
239
|
all = local_adapter.get_all
|
|
239
240
|
expect(all.keys).to eq(["search", "history"])
|
|
240
241
|
expect(all["search"][:boolean]).to eq("true")
|
data/spec/flipper/cloud_spec.rb
CHANGED
|
@@ -12,10 +12,6 @@ RSpec.describe Flipper::Cloud do
|
|
|
12
12
|
|
|
13
13
|
before do
|
|
14
14
|
@instance = described_class.new(token: token)
|
|
15
|
-
memoized_adapter = @instance.adapter
|
|
16
|
-
sync_adapter = memoized_adapter.adapter
|
|
17
|
-
@http_adapter = sync_adapter.instance_variable_get('@remote')
|
|
18
|
-
@http_client = @http_adapter.instance_variable_get('@client')
|
|
19
15
|
end
|
|
20
16
|
|
|
21
17
|
it 'returns Flipper::DSL instance' do
|
|
@@ -26,40 +22,29 @@ RSpec.describe Flipper::Cloud do
|
|
|
26
22
|
expect(@instance.cloud_configuration).to be_instance_of(Flipper::Cloud::Configuration)
|
|
27
23
|
end
|
|
28
24
|
|
|
29
|
-
it 'configures
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
expect(
|
|
36
|
-
expect(
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
expect(
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
it 'uses noop instrumenter' do
|
|
25
|
+
it 'configures the correct adapter' do
|
|
26
|
+
# pardon the nesting...
|
|
27
|
+
memoized_adapter = @instance.adapter
|
|
28
|
+
poll_adapter = memoized_adapter.adapter
|
|
29
|
+
dual_write_adapter = poll_adapter.adapter
|
|
30
|
+
|
|
31
|
+
expect(poll_adapter).to be_instance_of(Flipper::Adapters::Poll)
|
|
32
|
+
expect(dual_write_adapter).to be_instance_of(Flipper::Adapters::DualWrite)
|
|
33
|
+
|
|
34
|
+
http_adapter = dual_write_adapter.remote
|
|
35
|
+
client = http_adapter.client
|
|
36
|
+
expect(client.uri.scheme).to eq('https')
|
|
37
|
+
expect(client.uri.host).to eq('www.flippercloud.io')
|
|
38
|
+
expect(client.uri.path).to eq('/adapter')
|
|
39
|
+
expect(client.headers['Flipper-Cloud-Token']).to eq(token)
|
|
46
40
|
expect(@instance.instrumenter).to be(Flipper::Instrumenters::Noop)
|
|
47
41
|
end
|
|
48
42
|
end
|
|
49
43
|
|
|
50
44
|
context 'initialize with token and options' do
|
|
51
|
-
before do
|
|
52
|
-
stub_request(:get, /fakeflipper\.com/).to_return(status: 200, body: "{}")
|
|
53
|
-
|
|
54
|
-
@instance = described_class.new(token: 'asdf', url: 'https://www.fakeflipper.com/sadpanda')
|
|
55
|
-
memoized_adapter = @instance.adapter
|
|
56
|
-
sync_adapter = memoized_adapter.adapter
|
|
57
|
-
@http_adapter = sync_adapter.instance_variable_get('@remote')
|
|
58
|
-
@http_client = @http_adapter.instance_variable_get('@client')
|
|
59
|
-
end
|
|
60
|
-
|
|
61
45
|
it 'sets correct url' do
|
|
62
|
-
|
|
46
|
+
@instance = described_class.new(token: 'asdf', url: 'https://www.fakeflipper.com/sadpanda')
|
|
47
|
+
uri = @instance.adapter.adapter.adapter.remote.client.uri
|
|
63
48
|
expect(uri.scheme).to eq('https')
|
|
64
49
|
expect(uri.host).to eq('www.fakeflipper.com')
|
|
65
50
|
expect(uri.path).to eq('/sadpanda')
|
|
@@ -89,26 +74,26 @@ RSpec.describe Flipper::Cloud do
|
|
|
89
74
|
|
|
90
75
|
it 'can set debug_output' do
|
|
91
76
|
expect(Flipper::Adapters::Http::Client).to receive(:new)
|
|
92
|
-
.with(hash_including(debug_output: STDOUT))
|
|
77
|
+
.with(hash_including(debug_output: STDOUT)).at_least(:once)
|
|
93
78
|
described_class.new(token: 'asdf', debug_output: STDOUT)
|
|
94
79
|
end
|
|
95
80
|
|
|
96
81
|
it 'can set read_timeout' do
|
|
97
82
|
expect(Flipper::Adapters::Http::Client).to receive(:new)
|
|
98
|
-
.with(hash_including(read_timeout: 1))
|
|
83
|
+
.with(hash_including(read_timeout: 1)).at_least(:once)
|
|
99
84
|
described_class.new(token: 'asdf', read_timeout: 1)
|
|
100
85
|
end
|
|
101
86
|
|
|
102
87
|
it 'can set open_timeout' do
|
|
103
88
|
expect(Flipper::Adapters::Http::Client).to receive(:new)
|
|
104
|
-
.with(hash_including(open_timeout: 1))
|
|
89
|
+
.with(hash_including(open_timeout: 1)).at_least(:once)
|
|
105
90
|
described_class.new(token: 'asdf', open_timeout: 1)
|
|
106
91
|
end
|
|
107
92
|
|
|
108
93
|
if RUBY_VERSION >= '2.6.0'
|
|
109
94
|
it 'can set write_timeout' do
|
|
110
95
|
expect(Flipper::Adapters::Http::Client).to receive(:new)
|
|
111
|
-
.with(hash_including(open_timeout: 1))
|
|
96
|
+
.with(hash_including(open_timeout: 1)).at_least(:once)
|
|
112
97
|
described_class.new(token: 'asdf', open_timeout: 1)
|
|
113
98
|
end
|
|
114
99
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: flipper-cloud
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.26.0.rc2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- John Nunemaker
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2022-11-
|
|
11
|
+
date: 2022-11-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: flipper
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.
|
|
19
|
+
version: 0.26.0.rc2
|
|
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: 0.26.0.rc2
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: brow
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -49,7 +49,9 @@ files:
|
|
|
49
49
|
- examples/cloud/app.ru
|
|
50
50
|
- examples/cloud/basic.rb
|
|
51
51
|
- examples/cloud/cloud_setup.rb
|
|
52
|
+
- examples/cloud/forked.rb
|
|
52
53
|
- examples/cloud/import.rb
|
|
54
|
+
- examples/cloud/threaded.rb
|
|
53
55
|
- flipper-cloud.gemspec
|
|
54
56
|
- lib/flipper-cloud.rb
|
|
55
57
|
- lib/flipper/cloud.rb
|
|
@@ -59,7 +61,6 @@ files:
|
|
|
59
61
|
- lib/flipper/cloud/instrumenter.rb
|
|
60
62
|
- lib/flipper/cloud/message_verifier.rb
|
|
61
63
|
- lib/flipper/cloud/middleware.rb
|
|
62
|
-
- lib/flipper/cloud/registry.rb
|
|
63
64
|
- lib/flipper/cloud/routes.rb
|
|
64
65
|
- lib/flipper/version.rb
|
|
65
66
|
- spec/flipper/cloud/configuration_spec.rb
|
|
@@ -84,9 +85,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
84
85
|
version: '0'
|
|
85
86
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
86
87
|
requirements:
|
|
87
|
-
- - "
|
|
88
|
+
- - ">"
|
|
88
89
|
- !ruby/object:Gem::Version
|
|
89
|
-
version:
|
|
90
|
+
version: 1.3.1
|
|
90
91
|
requirements: []
|
|
91
92
|
rubygems_version: 3.3.7
|
|
92
93
|
signing_key:
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
require "thread"
|
|
2
|
-
|
|
3
|
-
module Flipper
|
|
4
|
-
module Cloud
|
|
5
|
-
class Registry
|
|
6
|
-
def self.default
|
|
7
|
-
@default ||= new
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def initialize
|
|
11
|
-
@mutex = Mutex.new
|
|
12
|
-
@data = {}
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def fetch(key, &block)
|
|
16
|
-
@mutex.synchronize do
|
|
17
|
-
if data = @data[key]
|
|
18
|
-
data
|
|
19
|
-
else
|
|
20
|
-
@data[key] = yield
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def keys
|
|
26
|
-
@mutex.synchronize do
|
|
27
|
-
@data.keys
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def each(&block)
|
|
32
|
-
data = @mutex.synchronize do
|
|
33
|
-
@data.dup
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
data.each(&block)
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def clear
|
|
40
|
-
@mutex.synchronize do
|
|
41
|
-
@data = {}
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|