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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a581a01f7e6dade34fb261d0b6215f262125e4041a0095081dd54719db303656
4
- data.tar.gz: 1d3df8718d8a46f88cb33e97e4ef2d193e0eae2dd2cf4eecd23fc87dd977d11a
3
+ metadata.gz: 5373416d79c0f4e67654bf9945bb4b1702fae520946f127be970fa89a7f3151a
4
+ data.tar.gz: 194180daeb197f5f2323363915de63daea4458486585eb687c76d380cc48df93
5
5
  SHA512:
6
- metadata.gz: e56bd86ca42668104197ad8b64596f7639bbf25c04ac48e3da6cff8f3d71736ab62282d9e01a13f2eb85fa7c058a11e2360e1a260fe249972e16f911d83ca2f5
7
- data.tar.gz: a11b459234c76ac9ab928b26c9e12009736af655f602a04557d776dc72579877cf1b7ab38f4faecd54d49b765186ff0bf754eef174a96ee1f5a584daf2dacd16
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 = 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.1'.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.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-03-21 00:00:00.000000000 Z
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