flipper 0.28.0 → 0.28.1
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/Changelog.md +25 -0
- data/examples/mirroring.rb +59 -0
- data/lib/flipper/adapters/memory.rb +33 -13
- data/lib/flipper/instrumentation/log_subscriber.rb +17 -2
- data/lib/flipper/poller.rb +1 -1
- data/lib/flipper/version.rb +1 -1
- data/spec/flipper/adapters/memory_spec.rb +11 -2
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5373416d79c0f4e67654bf9945bb4b1702fae520946f127be970fa89a7f3151a
|
4
|
+
data.tar.gz: 194180daeb197f5f2323363915de63daea4458486585eb687c76d380cc48df93
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8e6652132da82dfd9ee46419d3f4fcc9836b6464fe2a89d670e17315f1664db6f88784ab3b3006b2aa43b150461a1645791d9334d96b93dd685b31cf98909a1d
|
7
|
+
data.tar.gz: 161e2acea4928753b8ebb8d83e9fa05e33dcee6bab15cc2323c3acc3beb8d5e95f96aace11ffceddcc2dc035ee3d9aa3918f61e956a9c4eeb243387c8eacf87a
|
data/Changelog.md
CHANGED
@@ -2,6 +2,13 @@
|
|
2
2
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
4
4
|
|
5
|
+
## 0.28.1
|
6
|
+
|
7
|
+
### Additions/Changes
|
8
|
+
|
9
|
+
* Use new method of making logs bold for rails (https://github.com/jnunemaker/flipper/pull/726)
|
10
|
+
* Bundle bootstrap, jquery and poppler with the library. (https://github.com/jnunemaker/flipper/pull/731)
|
11
|
+
|
5
12
|
## 0.28.0
|
6
13
|
|
7
14
|
### Additions/Changes
|
@@ -11,6 +18,24 @@ All notable changes to this project will be documented in this file.
|
|
11
18
|
- [user, user.team, user.org].any? { |actor| Flipper.enabled?(:my_feature, actor) }
|
12
19
|
+ Flipper.enabled?(:my_feature, user, user.team, user.org)
|
13
20
|
```
|
21
|
+
* If you currently use `actor.thing` in a group, you'll need to change it to `actor.actor`.
|
22
|
+
```diff
|
23
|
+
- Flipper.register(:our_group) do |actor|
|
24
|
+
- actor.thing.is_a?(OurClassName)
|
25
|
+
- end
|
26
|
+
+ Flipper.register(:our_group) do |actor|
|
27
|
+
+ actor.actor.is_a?(OurClassName)
|
28
|
+
+ end
|
29
|
+
```
|
30
|
+
* If you currently use `context.thing` in a group or elsewhere, you'll need to change it to `context.actors`.
|
31
|
+
```diff
|
32
|
+
- Flipper.register(:our_group) do |actor, context|
|
33
|
+
- context.thing.is_a?(OurClassName)
|
34
|
+
- end
|
35
|
+
+ Flipper.register(:our_group) do |actor, context|
|
36
|
+
+ context.actors.any? { |actor| actor.is_a?(OurClassName) }
|
37
|
+
+ end
|
38
|
+
```
|
14
39
|
|
15
40
|
### Deprecations
|
16
41
|
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require_relative 'active_record/ar_setup'
|
3
|
+
require 'flipper'
|
4
|
+
require 'flipper/adapters/redis'
|
5
|
+
require 'flipper/adapters/active_record'
|
6
|
+
|
7
|
+
# Say you have production...
|
8
|
+
production_adapter = Flipper::Adapters::Memory.new
|
9
|
+
production = Flipper.new(production_adapter)
|
10
|
+
|
11
|
+
# And production has some stuff enabled...
|
12
|
+
production.enable(:search)
|
13
|
+
production.enable_percentage_of_time(:verbose_logging, 5)
|
14
|
+
production.enable_percentage_of_actors(:new_feature, 5)
|
15
|
+
production.enable_actor(:issues, Flipper::Actor.new('1'))
|
16
|
+
production.enable_actor(:issues, Flipper::Actor.new('2'))
|
17
|
+
production.enable_group(:request_tracing, :staff)
|
18
|
+
|
19
|
+
# And you would like to mirror production to staging...
|
20
|
+
staging_adapter = Flipper::Adapters::Memory.new
|
21
|
+
staging = Flipper.new(staging_adapter)
|
22
|
+
staging_export = staging.export
|
23
|
+
|
24
|
+
puts "Here is the state of the world for staging and production..."
|
25
|
+
puts "Staging"
|
26
|
+
staging.features.each do |feature|
|
27
|
+
pp feature: feature.key, values: feature.gate_values
|
28
|
+
end
|
29
|
+
puts "Production"
|
30
|
+
production.features.each do |feature|
|
31
|
+
pp feature: feature.key, values: feature.gate_values
|
32
|
+
end
|
33
|
+
|
34
|
+
# NOTE: This wipes active record clean and copies features/gates from redis into active record.
|
35
|
+
puts "Mirroring production to staging..."
|
36
|
+
staging.import(production.export)
|
37
|
+
puts "Staging is now identical to Production."
|
38
|
+
|
39
|
+
puts "Staging"
|
40
|
+
staging.features.each do |feature|
|
41
|
+
pp feature: feature.key, values: feature.gate_values
|
42
|
+
end
|
43
|
+
puts "Production"
|
44
|
+
production.features.each do |feature|
|
45
|
+
pp feature: feature.key, values: feature.gate_values
|
46
|
+
end
|
47
|
+
|
48
|
+
puts "Restoring staging to original state..."
|
49
|
+
staging.import(staging_export)
|
50
|
+
puts "Staging restored."
|
51
|
+
|
52
|
+
puts "Staging"
|
53
|
+
staging.features.each do |feature|
|
54
|
+
pp feature: feature.key, values: feature.gate_values
|
55
|
+
end
|
56
|
+
puts "Production"
|
57
|
+
production.features.each do |feature|
|
58
|
+
pp feature: feature.key, values: feature.gate_values
|
59
|
+
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require "flipper/adapter"
|
2
2
|
require "flipper/typecast"
|
3
|
-
require 'concurrent/atomic/read_write_lock'
|
4
3
|
|
5
4
|
module Flipper
|
6
5
|
module Adapters
|
@@ -15,43 +14,44 @@ module Flipper
|
|
15
14
|
attr_reader :name
|
16
15
|
|
17
16
|
# Public
|
18
|
-
def initialize(source = nil)
|
17
|
+
def initialize(source = nil, threadsafe: true)
|
19
18
|
@source = Typecast.features_hash(source)
|
20
19
|
@name = :memory
|
21
|
-
@lock =
|
20
|
+
@lock = Mutex.new if threadsafe
|
21
|
+
reset
|
22
22
|
end
|
23
23
|
|
24
24
|
# Public: The set of known features.
|
25
25
|
def features
|
26
|
-
|
26
|
+
synchronize { @source.keys }.to_set
|
27
27
|
end
|
28
28
|
|
29
29
|
# Public: Adds a feature to the set of known features.
|
30
30
|
def add(feature)
|
31
|
-
|
31
|
+
synchronize { @source[feature.key] ||= default_config }
|
32
32
|
true
|
33
33
|
end
|
34
34
|
|
35
35
|
# Public: Removes a feature from the set of known features and clears
|
36
36
|
# all the values for the feature.
|
37
37
|
def remove(feature)
|
38
|
-
|
38
|
+
synchronize { @source.delete(feature.key) }
|
39
39
|
true
|
40
40
|
end
|
41
41
|
|
42
42
|
# Public: Clears all the gate values for a feature.
|
43
43
|
def clear(feature)
|
44
|
-
|
44
|
+
synchronize { @source[feature.key] = default_config }
|
45
45
|
true
|
46
46
|
end
|
47
47
|
|
48
48
|
# Public
|
49
49
|
def get(feature)
|
50
|
-
|
50
|
+
synchronize { @source[feature.key] } || default_config
|
51
51
|
end
|
52
52
|
|
53
53
|
def get_multi(features)
|
54
|
-
|
54
|
+
synchronize do
|
55
55
|
result = {}
|
56
56
|
features.each do |feature|
|
57
57
|
result[feature.key] = @source[feature.key] || default_config
|
@@ -61,12 +61,12 @@ module Flipper
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def get_all
|
64
|
-
|
64
|
+
synchronize { Typecast.features_hash(@source) }
|
65
65
|
end
|
66
66
|
|
67
67
|
# Public
|
68
68
|
def enable(feature, gate, thing)
|
69
|
-
|
69
|
+
synchronize do
|
70
70
|
@source[feature.key] ||= default_config
|
71
71
|
|
72
72
|
case gate.data_type
|
@@ -87,7 +87,7 @@ module Flipper
|
|
87
87
|
|
88
88
|
# Public
|
89
89
|
def disable(feature, gate, thing)
|
90
|
-
|
90
|
+
synchronize do
|
91
91
|
@source[feature.key] ||= default_config
|
92
92
|
|
93
93
|
case gate.data_type
|
@@ -118,9 +118,29 @@ module Flipper
|
|
118
118
|
def import(source)
|
119
119
|
adapter = self.class.from(source)
|
120
120
|
get_all = Typecast.features_hash(adapter.get_all)
|
121
|
-
|
121
|
+
synchronize { @source.replace(get_all) }
|
122
122
|
true
|
123
123
|
end
|
124
|
+
|
125
|
+
private
|
126
|
+
|
127
|
+
def reset
|
128
|
+
@pid = Process.pid
|
129
|
+
@lock&.unlock if @lock&.locked?
|
130
|
+
end
|
131
|
+
|
132
|
+
def forked?
|
133
|
+
@pid != Process.pid
|
134
|
+
end
|
135
|
+
|
136
|
+
def synchronize(&block)
|
137
|
+
if @lock
|
138
|
+
reset if forked?
|
139
|
+
@lock.synchronize(&block)
|
140
|
+
else
|
141
|
+
block.call
|
142
|
+
end
|
143
|
+
end
|
124
144
|
end
|
125
145
|
end
|
126
146
|
end
|
@@ -32,7 +32,7 @@ module Flipper
|
|
32
32
|
details += " gate_name=#{gate_name}" unless gate_name.nil?
|
33
33
|
|
34
34
|
name = '%s (%.1fms)' % [description, event.duration]
|
35
|
-
debug " #{
|
35
|
+
debug " #{color_name(name)} [ #{details} ]"
|
36
36
|
end
|
37
37
|
|
38
38
|
# Logs an adapter operation. If operation is for a feature, then that
|
@@ -64,12 +64,27 @@ module Flipper
|
|
64
64
|
details = "result=#{result.inspect}"
|
65
65
|
|
66
66
|
name = '%s (%.1fms)' % [description, event.duration]
|
67
|
-
debug " #{
|
67
|
+
debug " #{color_name(name)} [ #{details} ]"
|
68
68
|
end
|
69
69
|
|
70
70
|
def logger
|
71
71
|
self.class.logger
|
72
72
|
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
# Rails 7.1 changed the signature of this function.
|
77
|
+
# Checking if > 7.0.99 rather than >= 7.1 so that 7.1 pre-release versions are included.
|
78
|
+
COLOR_OPTIONS = if Rails.gem_version > Gem::Version.new('7.0.99')
|
79
|
+
{ bold: true }.freeze
|
80
|
+
else
|
81
|
+
true
|
82
|
+
end
|
83
|
+
private_constant :COLOR_OPTIONS
|
84
|
+
|
85
|
+
def color_name(name)
|
86
|
+
color(name, CYAN, COLOR_OPTIONS)
|
87
|
+
end
|
73
88
|
end
|
74
89
|
end
|
75
90
|
|
data/lib/flipper/poller.rb
CHANGED
@@ -28,7 +28,7 @@ module Flipper
|
|
28
28
|
@remote_adapter = options.fetch(:remote_adapter)
|
29
29
|
@interval = options.fetch(:interval, 10).to_f
|
30
30
|
@last_synced_at = Concurrent::AtomicFixnum.new(0)
|
31
|
-
@adapter = Adapters::Memory.new
|
31
|
+
@adapter = Adapters::Memory.new(nil, threadsafe: true)
|
32
32
|
|
33
33
|
if @interval < 1
|
34
34
|
warn "Flipper::Cloud poll interval must be greater than or equal to 1 but was #{@interval}. Setting @interval to 1."
|
data/lib/flipper/version.rb
CHANGED
@@ -1,8 +1,17 @@
|
|
1
1
|
RSpec.describe Flipper::Adapters::Memory do
|
2
2
|
let(:source) { {} }
|
3
|
-
subject { described_class.new(source) }
|
4
3
|
|
5
|
-
|
4
|
+
context 'threadsafe: true' do
|
5
|
+
subject { described_class.new(source, threadsafe: true) }
|
6
|
+
|
7
|
+
it_should_behave_like 'a flipper adapter'
|
8
|
+
end
|
9
|
+
|
10
|
+
context 'threadsafe: false' do
|
11
|
+
subject { described_class.new(source, threadsafe: false) }
|
12
|
+
|
13
|
+
it_should_behave_like 'a flipper adapter'
|
14
|
+
end
|
6
15
|
|
7
16
|
it "can initialize from big hash" do
|
8
17
|
flipper = Flipper.new(subject)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: flipper
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.28.
|
4
|
+
version: 0.28.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Nunemaker
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-07-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: concurrent-ruby
|
@@ -68,6 +68,7 @@ files:
|
|
68
68
|
- examples/instrumentation.rb
|
69
69
|
- examples/instrumentation_last_accessed_at.rb
|
70
70
|
- examples/memoizing.rb
|
71
|
+
- examples/mirroring.rb
|
71
72
|
- examples/percentage_of_actors.rb
|
72
73
|
- examples/percentage_of_actors_enabled_check.rb
|
73
74
|
- examples/percentage_of_actors_group.rb
|