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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c798fcaaf0f9c858240f652507202cb26790aca64dc428b5ea4447b54b290534
4
- data.tar.gz: eb25193626f19f4bba50e6a1be9dea0f9a5e9b62b94097fb76a027f5c9986cd8
3
+ metadata.gz: 237f86d236d564cb69735a00ad88c5d833b152f9e6f9a64bd50529ba2c39dd0d
4
+ data.tar.gz: 11e612037550a9e83b763d0d2c80d2f5191b4a52c9288f2f61e9dbf6640d38f5
5
5
  SHA512:
6
- metadata.gz: 4aba0c24f6009023a38441e5e81c20ed20c71aeb0e0a4b2ff81dd1c30d744a0f3d811a58552da599b1ff9177cb8f3b093e85dd385f903fe6fa57e4a483a83a76
7
- data.tar.gz: f815ec3a46d4c13355d0dfcdc403e574361df07e1e753fae2c9285439356e6a2e0ff19a2b5516b1137d99d86efe423b64422b4e5aacaf37c99ecc7d4a1746449
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
- uri = URI.parse(url)
128
- uri.path = "#{uri.path}/events".squeeze("/")
129
- events_url = uri.to_s
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: events_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 : sync_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 sync_adapter
161
- Flipper::Adapters::Sync.new(local_adapter, http_adapter, {
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,
@@ -1,3 +1,3 @@
1
1
  module Flipper
2
- VERSION = '0.25.4'.freeze
2
+ VERSION = '0.26.0.rc2'.freeze
3
3
  end
@@ -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
- expect(instance.adapter.synchronizer.interval).to be(1)
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::Sync)
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.adapter.instance_variable_get("@local")
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")
@@ -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 instance to use http adapter' do
30
- expect(@http_adapter).to be_instance_of(Flipper::Adapters::Http)
31
- end
32
-
33
- it 'sets up correct url' do
34
- uri = @http_client.instance_variable_get('@uri')
35
- expect(uri.scheme).to eq('https')
36
- expect(uri.host).to eq('www.flippercloud.io')
37
- expect(uri.path).to eq('/adapter')
38
- end
39
-
40
- it 'sets correct token header' do
41
- headers = @http_client.instance_variable_get('@headers')
42
- expect(headers['Flipper-Cloud-Token']).to eq(token)
43
- end
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
- uri = @http_client.instance_variable_get('@uri')
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.25.4
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-07 00:00:00.000000000 Z
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.25.4
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.25.4
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: '0'
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