flipper 0.28.0 → 0.28.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a581a01f7e6dade34fb261d0b6215f262125e4041a0095081dd54719db303656
4
- data.tar.gz: 1d3df8718d8a46f88cb33e97e4ef2d193e0eae2dd2cf4eecd23fc87dd977d11a
3
+ metadata.gz: e98ea278996cb134ed25a1647bbafa6307a412ec39a68d207a90df4119b39cc3
4
+ data.tar.gz: 2f6b50f372c51c3b140d55a9ea12397f60d63b05ca64db5fc1c84d7f676e6297
5
5
  SHA512:
6
- metadata.gz: e56bd86ca42668104197ad8b64596f7639bbf25c04ac48e3da6cff8f3d71736ab62282d9e01a13f2eb85fa7c058a11e2360e1a260fe249972e16f911d83ca2f5
7
- data.tar.gz: a11b459234c76ac9ab928b26c9e12009736af655f602a04557d776dc72579877cf1b7ab38f4faecd54d49b765186ff0bf754eef174a96ee1f5a584daf2dacd16
6
+ metadata.gz: 1834f7728947e8bb8ecff891dcf6f419aecead9afca29941b33dec839ea49fd55c2821729112e59c99519906ee32cbfa74c3be52f346df29dbe60298114270e9
7
+ data.tar.gz: 043ae6d39063b93a68bc0b88f65becf9818b6f6ba32bc523b7a6a45f87b45a5b740f0f64f27f18c1660db766953a7bfba598d9e9db81aab284204444e9814083
data/Changelog.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## 0.28.2
6
+
7
+ * UI: fix path to bundled assets when mounted in another Rack app (https://github.com/jnunemaker/flipper/pull/742)
8
+
9
+ ## 0.28.1
10
+
11
+ ### Additions/Changes
12
+
13
+ * Use new method of making logs bold for rails (https://github.com/jnunemaker/flipper/pull/726)
14
+ * Bundle bootstrap, jquery and poppler with the library. (https://github.com/jnunemaker/flipper/pull/731)
15
+
5
16
  ## 0.28.0
6
17
 
7
18
  ### Additions/Changes
@@ -11,6 +22,24 @@ All notable changes to this project will be documented in this file.
11
22
  - [user, user.team, user.org].any? { |actor| Flipper.enabled?(:my_feature, actor) }
12
23
  + Flipper.enabled?(:my_feature, user, user.team, user.org)
13
24
  ```
25
+ * If you currently use `actor.thing` in a group, you'll need to change it to `actor.actor`.
26
+ ```diff
27
+ - Flipper.register(:our_group) do |actor|
28
+ - actor.thing.is_a?(OurClassName)
29
+ - end
30
+ + Flipper.register(:our_group) do |actor|
31
+ + actor.actor.is_a?(OurClassName)
32
+ + end
33
+ ```
34
+ * If you currently use `context.thing` in a group or elsewhere, you'll need to change it to `context.actors`.
35
+ ```diff
36
+ - Flipper.register(:our_group) do |actor, context|
37
+ - context.thing.is_a?(OurClassName)
38
+ - end
39
+ + Flipper.register(:our_group) do |actor, context|
40
+ + context.actors.any? { |actor| actor.is_a?(OurClassName) }
41
+ + end
42
+ ```
14
43
 
15
44
  ### Deprecations
16
45
 
@@ -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 = Concurrent::ReadWriteLock.new
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
- @lock.with_read_lock { @source.keys }.to_set
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
- @lock.with_write_lock { @source[feature.key] ||= default_config }
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
- @lock.with_write_lock { @source.delete(feature.key) }
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
- @lock.with_write_lock { @source[feature.key] = default_config }
44
+ synchronize { @source[feature.key] = default_config }
45
45
  true
46
46
  end
47
47
 
48
48
  # Public
49
49
  def get(feature)
50
- @lock.with_read_lock { @source[feature.key] } || default_config
50
+ synchronize { @source[feature.key] } || default_config
51
51
  end
52
52
 
53
53
  def get_multi(features)
54
- @lock.with_read_lock do
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
- @lock.with_read_lock { Typecast.features_hash(@source) }
64
+ synchronize { Typecast.features_hash(@source) }
65
65
  end
66
66
 
67
67
  # Public
68
68
  def enable(feature, gate, thing)
69
- @lock.with_write_lock do
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
- @lock.with_write_lock do
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
- @lock.with_write_lock { @source.replace(get_all) }
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 " #{color(name, CYAN, true)} [ #{details} ]"
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 " #{color(name, CYAN, true)} [ #{details} ]"
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
 
@@ -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."
@@ -1,3 +1,3 @@
1
1
  module Flipper
2
- VERSION = '0.28.0'.freeze
2
+ VERSION = '0.28.2'.freeze
3
3
  end
@@ -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
- it_should_behave_like 'a flipper adapter'
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.0
4
+ version: 0.28.2
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-03-21 00:00:00.000000000 Z
11
+ date: 2023-07-18 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