flipper 0.26.0.rc2 → 0.26.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: 60ce7896afa06dd25ad8e6f8b4f419bbb992e16ce7147339af30ac15f338871d
4
- data.tar.gz: 3a6635db6af529d29a2f1bd075fe45a82c77211bdd012d3613a89f2b98566975
3
+ metadata.gz: cbdfdece0141d3724dbd78a9df4062193e6860a95baff3b617b07bb2e81e50a5
4
+ data.tar.gz: bead9a1b7f0842bc3b3d1560ea51132d60a55f1399b80390c3d7ccfcec56abe5
5
5
  SHA512:
6
- metadata.gz: 6ce977a03bcafdfd4cb0427541eb0277b4dfbbadd723c86e12a7fcbec65d398663ed74fbdb9c46aa6b460e00e0025bd9853ed52c7a3cf3f44016e005d79e6eff
7
- data.tar.gz: c422114b0e7076ebd7c0c63bef943a54aede502f253c5310a4e59f17daaadeafd435594aac7936c441adfc3a39fa3d5ba2ef17c589d828cefb5a977d750f0215
6
+ metadata.gz: 5528f98059ae40042c4f894062652c9438e798d09ae89dbb65a70af1681031c7d2b57facb36791b653899524a2df16d0a58d6be599b766b3baf576f40eff4609
7
+ data.tar.gz: c4c32a2b1d8015645660eef4ce9c4639e0a271ed35144fc3b00a6dfe59f101583e84cf17e6fd7f0f0b23bce224195f2e06ae232e6b4a767c2415b12e7f809a83
@@ -15,7 +15,7 @@ jobs:
15
15
  --health-retries 5
16
16
  strategy:
17
17
  matrix:
18
- ruby: ['2.6', '2.7', '3.0', '3.1']
18
+ ruby: ['2.6', '2.7', '3.0', '3.1', '3.2']
19
19
  rails: ['5.2', '6.0.0', '6.1.0', '7.0.0']
20
20
  exclude:
21
21
  - ruby: "2.6"
@@ -26,15 +26,22 @@ jobs:
26
26
  rails: "5.2"
27
27
  - ruby: "3.1"
28
28
  rails: "6.0.0"
29
+ - ruby: "3.2"
30
+ rails: "5.2"
31
+ - ruby: "3.2"
32
+ rails: "6.0.0"
33
+ - ruby: "3.2"
34
+ rails: "6.1.0"
29
35
  env:
30
36
  SQLITE3_VERSION: 1.4.1
31
37
  REDIS_URL: redis://localhost:6379/0
32
38
  CI: true
39
+ RAILS_VERSION: ${{ matrix.rails }}
33
40
  steps:
34
41
  - name: Setup memcached
35
42
  uses: KeisukeYamashita/memcached-actions@v1
36
43
  - name: Start MongoDB
37
- uses: supercharge/mongodb-github-action@1.8.0
44
+ uses: supercharge/mongodb-github-action@1.9.0
38
45
  with:
39
46
  mongodb-version: 4.0
40
47
  - name: Check out repository code
@@ -46,17 +53,12 @@ jobs:
46
53
  key: ${{ runner.os }}-gems-${{ matrix.ruby }}-${{ matrix.rails }}-${{ hashFiles('**/Gemfile.lock') }}
47
54
  restore-keys: |
48
55
  ${{ runner.os }}-gems-${{ matrix.ruby }}-${{ matrix.rails }}-
56
+ - name: Install libpq-dev
57
+ run: sudo apt-get -yqq install libpq-dev
49
58
  - name: Set up Ruby ${{ matrix.ruby }}
50
59
  uses: ruby/setup-ruby@v1
51
60
  with:
52
61
  ruby-version: ${{ matrix.ruby }}
53
- - name: Install libpq-dev
54
- run: sudo apt-get -yqq install libpq-dev
55
- - name: Install bundler
56
- run: gem install bundler
62
+ bundler-cache: true # 'bundle install' and cache gems
57
63
  - name: Run Rake with Rails ${{ matrix.rails }}
58
- env:
59
- RAILS_VERSION: ${{ matrix.rails }}
60
- run: |
61
- bundle install --jobs 4 --retry 3
62
- bundle exec rake
64
+ run: bundle exec rake
@@ -32,11 +32,12 @@ jobs:
32
32
  SQLITE3_VERSION: 1.4.1
33
33
  REDIS_URL: redis://localhost:6379/0
34
34
  CI: true
35
+ RAILS_VERSION: ${{ matrix.rails }}
35
36
  steps:
36
37
  - name: Setup memcached
37
38
  uses: KeisukeYamashita/memcached-actions@v1
38
39
  - name: Start MongoDB
39
- uses: supercharge/mongodb-github-action@1.8.0
40
+ uses: supercharge/mongodb-github-action@1.9.0
40
41
  with:
41
42
  mongodb-version: 4.0
42
43
  - name: Check out repository code
@@ -48,20 +49,14 @@ jobs:
48
49
  key: ${{ runner.os }}-gems-${{ matrix.ruby }}-${{ matrix.rails }}-${{ hashFiles('**/Gemfile.lock') }}
49
50
  restore-keys: |
50
51
  ${{ runner.os }}-gems-${{ matrix.ruby }}-${{ matrix.rails }}-
52
+ - name: Install libpq-dev
53
+ run: sudo apt-get -yqq install libpq-dev
51
54
  - name: Set up Ruby ${{ matrix.ruby }}
52
55
  uses: ruby/setup-ruby@v1
53
56
  with:
54
57
  ruby-version: ${{ matrix.ruby }}
55
- - name: Install libpq-dev
56
- run: sudo apt-get -yqq install libpq-dev
57
- - name: Install bundler
58
- run: gem install bundler
59
- - name: Bundle install with Rails ${{ matrix.rails }}
60
- env:
61
- RAILS_VERSION: ${{ matrix.rails }}
62
- run: bundle install --jobs 4 --retry 3
58
+ bundler-cache: true # 'bundle install' and cache gems
63
59
  - name: Run Examples with Rails ${{ matrix.rails }}
64
60
  env:
65
61
  FLIPPER_CLOUD_TOKEN: ${{ secrets.FLIPPER_CLOUD_TOKEN }}
66
- RAILS_VERSION: ${{ matrix.rails }}
67
62
  run: script/examples
data/Changelog.md CHANGED
@@ -2,9 +2,20 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
- ## 0.26.0.rc1
5
+ ## 0.26.1
6
+
7
+ * Improve `Flipper#enabled?` performance by ~37%-55% (https://github.com/jnunemaker/flipper/pull/706)
8
+ * Make Memory adapter threadsafe (https://github.com/jnunemaker/flipper/pull/702 and https://github.com/jnunemaker/flipper/pull/703)
9
+ * ActiveRecord adapter: wrap all reads/writes in `with_connection` (https://github.com/jnunemaker/flipper/pull/705)
10
+ * Improve performance of background polling (https://github.com/jnunemaker/flipper/pull/699)
11
+ * Remove executables directive from gem (https://github.com/jnunemaker/flipper/pull/693)
12
+
13
+ ## 0.26.0
6
14
 
7
15
  * Cloud Background Polling (https://github.com/jnunemaker/flipper/pull/682)
16
+ * Changed default branch from master to main
17
+ * Allow configuring railtie via ENV vars (https://github.com/jnunemaker/flipper/pull/681)
18
+ * flipper-ui: Fix issue preventing feature flags being enabled when confirm_fully_enable is on and feature_removal_enabled is off (https://github.com/jnunemaker/flipper/pull/680)
8
19
 
9
20
  ## 0.25.4
10
21
 
data/Gemfile CHANGED
@@ -20,6 +20,10 @@ gem 'webmock', '~> 3.0'
20
20
  gem 'ice_age'
21
21
  gem 'redis-namespace'
22
22
  gem 'webrick'
23
+ gem 'stackprof'
24
+ gem 'benchmark-ips'
25
+ gem 'stackprof-webnav'
26
+ gem 'flamegraph'
23
27
 
24
28
  group(:guard) do
25
29
  gem 'guard', '~> 2.15'
data/Rakefile CHANGED
@@ -18,7 +18,7 @@ end
18
18
  desc 'Tags version, pushes to remote, and pushes gem'
19
19
  task release: :build do
20
20
  sh 'git', 'tag', "v#{Flipper::VERSION}"
21
- sh 'git push origin master'
21
+ sh 'git push origin main'
22
22
  sh "git push origin v#{Flipper::VERSION}"
23
23
  puts "\nWhat OTP code should be used?"
24
24
  otp_code = STDIN.gets.chomp
@@ -0,0 +1,10 @@
1
+ require 'bundler/setup'
2
+ require 'flipper'
3
+ require 'benchmark/ips'
4
+
5
+ actor = Flipper::Actor.new("User;1")
6
+
7
+ Benchmark.ips do |x|
8
+ x.report("with actor") { Flipper.enabled?(:foo, actor) }
9
+ x.report("without actor") { Flipper.enabled?(:foo) }
10
+ end
@@ -0,0 +1,20 @@
1
+ require 'bundler/setup'
2
+ require 'flipper'
3
+ require 'stackprof'
4
+ require 'benchmark/ips'
5
+
6
+ flipper = Flipper.new(Flipper::Adapters::Memory.new)
7
+ feature = flipper.feature(:foo)
8
+ actor = Flipper::Actor.new("User;1")
9
+
10
+ profile = StackProf.run(mode: :wall, interval: 1_000) do
11
+ 2_000_000.times do
12
+ feature.enabled?(actor)
13
+ end
14
+ end
15
+
16
+ result = StackProf::Report.new(profile)
17
+ puts
18
+ result.print_text
19
+ puts "\n\n\n"
20
+ result.print_method(/Flipper::Feature#enabled?/)
@@ -0,0 +1,21 @@
1
+ require 'bundler/setup'
2
+ require 'flipper'
3
+ require 'active_support/notifications'
4
+ require 'active_support/isolated_execution_state'
5
+ require 'benchmark/ips'
6
+
7
+ class FlipperSubscriber
8
+ def call(name, start, finish, id, payload)
9
+ end
10
+
11
+ ActiveSupport::Notifications.subscribe(/flipper/, new)
12
+ end
13
+
14
+ actor = Flipper::Actor.new("User;1")
15
+ bare = Flipper.new(Flipper::Adapters::Memory.new)
16
+ instrumented = Flipper.new(Flipper::Adapters::Memory.new, instrumenter: ActiveSupport::Notifications)
17
+
18
+ Benchmark.ips do |x|
19
+ x.report("with instrumentation") { instrumented.enabled?(:foo, actor) }
20
+ x.report("without instrumentation") { bare.enabled?(:foo, actor) }
21
+ end
@@ -0,0 +1,19 @@
1
+ require 'bundler/setup'
2
+ require 'flipper'
3
+ require 'benchmark/ips'
4
+
5
+ Benchmark.ips do |x|
6
+ x.report("Typecast.to_boolean true") { Flipper::Typecast.to_boolean(true) }
7
+ x.report("Typecast.to_boolean 1") { Flipper::Typecast.to_boolean(1) }
8
+ x.report("Typecast.to_boolean 'true'") { Flipper::Typecast.to_boolean('true'.freeze) }
9
+ x.report("Typecast.to_boolean '1'") { Flipper::Typecast.to_boolean('1'.freeze) }
10
+ x.report("Typecast.to_boolean false") { Flipper::Typecast.to_boolean(false) }
11
+
12
+ x.report("Typecast.to_integer 1") { Flipper::Typecast.to_integer(1) }
13
+ x.report("Typecast.to_integer '1'") { Flipper::Typecast.to_integer('1') }
14
+
15
+ x.report("Typecast.to_float 1") { Flipper::Typecast.to_float(1) }
16
+ x.report("Typecast.to_float '1'") { Flipper::Typecast.to_float('1'.freeze) }
17
+ x.report("Typecast.to_float 1.01") { Flipper::Typecast.to_float(1) }
18
+ x.report("Typecast.to_float '1.01'") { Flipper::Typecast.to_float('1'.freeze) }
19
+ end
data/flipper.gemspec CHANGED
@@ -13,7 +13,6 @@ end
13
13
 
14
14
  ignored_files = plugin_files
15
15
  ignored_files << Dir['script/*']
16
- ignored_files << '.travis.yml'
17
16
  ignored_files << '.gitignore'
18
17
  ignored_files << 'Guardfile'
19
18
  ignored_files.flatten!.uniq!
@@ -28,7 +27,6 @@ Gem::Specification.new do |gem|
28
27
  gem.homepage = 'https://github.com/jnunemaker/flipper'
29
28
  gem.license = 'MIT'
30
29
 
31
- gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
32
30
  gem.files = `git ls-files`.split("\n") - ignored_files + ['lib/flipper/version.rb']
33
31
  gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") - ignored_test_files
34
32
  gem.name = 'flipper'
@@ -1,4 +1,4 @@
1
- require 'set'
1
+ require 'concurrent/atomic/read_write_lock'
2
2
 
3
3
  module Flipper
4
4
  module Adapters
@@ -14,86 +14,93 @@ module Flipper
14
14
 
15
15
  # Public
16
16
  def initialize(source = nil)
17
- @source = source || {}
17
+ @source = Hash.new.update(source || {})
18
18
  @name = :memory
19
+ @lock = Concurrent::ReadWriteLock.new
19
20
  end
20
21
 
21
22
  # Public: The set of known features.
22
23
  def features
23
- @source.keys.to_set
24
+ @lock.with_read_lock { @source.keys }.to_set
24
25
  end
25
26
 
26
27
  # Public: Adds a feature to the set of known features.
27
28
  def add(feature)
28
- @source[feature.key] ||= default_config
29
+ @lock.with_write_lock { @source[feature.key] ||= default_config }
29
30
  true
30
31
  end
31
32
 
32
33
  # Public: Removes a feature from the set of known features and clears
33
34
  # all the values for the feature.
34
35
  def remove(feature)
35
- @source.delete(feature.key)
36
+ @lock.with_write_lock { @source.delete(feature.key) }
36
37
  true
37
38
  end
38
39
 
39
40
  # Public: Clears all the gate values for a feature.
40
41
  def clear(feature)
41
- @source[feature.key] = default_config
42
+ @lock.with_write_lock { @source[feature.key] = default_config }
42
43
  true
43
44
  end
44
45
 
45
46
  # Public
46
47
  def get(feature)
47
- @source[feature.key] || default_config
48
+ @lock.with_read_lock { @source[feature.key] } || default_config
48
49
  end
49
50
 
50
51
  def get_multi(features)
51
- result = {}
52
- features.each do |feature|
53
- result[feature.key] = @source[feature.key] || default_config
52
+ @lock.with_read_lock do
53
+ result = {}
54
+ features.each do |feature|
55
+ result[feature.key] = @source[feature.key] || default_config
56
+ end
57
+ result
54
58
  end
55
- result
56
59
  end
57
60
 
58
61
  def get_all
59
- @source
62
+ @lock.with_read_lock { @source.to_h }
60
63
  end
61
64
 
62
65
  # Public
63
66
  def enable(feature, gate, thing)
64
- @source[feature.key] ||= default_config
65
-
66
- case gate.data_type
67
- when :boolean
68
- clear(feature)
69
- @source[feature.key][gate.key] = thing.value.to_s
70
- when :integer
71
- @source[feature.key][gate.key] = thing.value.to_s
72
- when :set
73
- @source[feature.key][gate.key] << thing.value.to_s
74
- else
75
- raise "#{gate} is not supported by this adapter yet"
67
+ @lock.with_write_lock do
68
+ @source[feature.key] ||= default_config
69
+
70
+ case gate.data_type
71
+ when :boolean
72
+ @source[feature.key] = default_config
73
+ @source[feature.key][gate.key] = thing.value.to_s
74
+ when :integer
75
+ @source[feature.key][gate.key] = thing.value.to_s
76
+ when :set
77
+ @source[feature.key][gate.key] << thing.value.to_s
78
+ else
79
+ raise "#{gate} is not supported by this adapter yet"
80
+ end
81
+
82
+ true
76
83
  end
77
-
78
- true
79
84
  end
80
85
 
81
86
  # Public
82
87
  def disable(feature, gate, thing)
83
- @source[feature.key] ||= default_config
84
-
85
- case gate.data_type
86
- when :boolean
87
- clear(feature)
88
- when :integer
89
- @source[feature.key][gate.key] = thing.value.to_s
90
- when :set
91
- @source[feature.key][gate.key].delete thing.value.to_s
92
- else
93
- raise "#{gate} is not supported by this adapter yet"
88
+ @lock.with_write_lock do
89
+ @source[feature.key] ||= default_config
90
+
91
+ case gate.data_type
92
+ when :boolean
93
+ @source[feature.key] = default_config
94
+ when :integer
95
+ @source[feature.key][gate.key] = thing.value.to_s
96
+ when :set
97
+ @source[feature.key][gate.key].delete thing.value.to_s
98
+ else
99
+ raise "#{gate} is not supported by this adapter yet"
100
+ end
101
+
102
+ true
94
103
  end
95
-
96
- true
97
104
  end
98
105
 
99
106
  # Public
@@ -104,6 +111,12 @@ module Flipper
104
111
  ]
105
112
  "#<#{self.class.name}:#{object_id} #{attributes.join(', ')}>"
106
113
  end
114
+
115
+ # Public: a more efficient implementation of import for this adapter
116
+ def import(source_adapter)
117
+ get_all = source_adapter.get_all
118
+ @lock.with_write_lock { @source.replace(get_all) }
119
+ end
107
120
  end
108
121
  end
109
122
  end
@@ -1,125 +1,2 @@
1
- require 'logger'
2
- require 'concurrent/atomic/read_write_lock'
3
- require 'concurrent/utility/monotonic_time'
4
- require 'concurrent/map'
5
-
6
- module Flipper
7
- module Adapters
8
- class Poll
9
- class Poller
10
- attr_reader :thread, :pid, :mutex, :interval, :last_synced_at
11
-
12
- def self.instances
13
- @instances ||= Concurrent::Map.new
14
- end
15
- private_class_method :instances
16
-
17
- def self.get(key, options = {})
18
- instances.compute_if_absent(key) { new(options) }
19
- end
20
-
21
- def self.reset
22
- instances.clear
23
- end
24
-
25
- def initialize(options = {})
26
- @thread = nil
27
- @pid = Process.pid
28
- @mutex = Mutex.new
29
- @adapter = Memory.new
30
- @instrumenter = options.fetch(:instrumenter, Instrumenters::Noop)
31
- @remote_adapter = options.fetch(:remote_adapter)
32
- @interval = options.fetch(:interval, 10).to_f
33
- @lock = Concurrent::ReadWriteLock.new
34
- @last_synced_at = Concurrent::AtomicFixnum.new(0)
35
-
36
- if @interval < 1
37
- warn "Flipper::Cloud poll interval must be greater than or equal to 1 but was #{@interval}. Setting @interval to 1."
38
- @interval = 1
39
- end
40
-
41
- @start_automatically = options.fetch(:start_automatically, true)
42
-
43
- if options.fetch(:shutdown_automatically, true)
44
- at_exit { stop }
45
- end
46
- end
47
-
48
- def adapter
49
- @lock.with_read_lock { Memory.new(@adapter.get_all.dup) }
50
- end
51
-
52
- def start
53
- reset if forked?
54
- ensure_worker_running
55
- end
56
-
57
- def stop
58
- @instrumenter.instrument("poller.#{InstrumentationNamespace}", {
59
- operation: :stop,
60
- })
61
- @thread&.kill
62
- end
63
-
64
- def run
65
- loop do
66
- sleep jitter
67
- start = Concurrent.monotonic_time
68
- begin
69
- @instrumenter.instrument("poller.#{InstrumentationNamespace}", operation: :poll) do
70
- adapter = Memory.new
71
- adapter.import(@remote_adapter)
72
-
73
- @lock.with_write_lock { @adapter.import(adapter) }
74
- @last_synced_at.update { |time| Concurrent.monotonic_time }
75
- end
76
- rescue => exception
77
- # you can instrument these using poller.flipper
78
- end
79
-
80
- sleep_interval = interval - (Concurrent.monotonic_time - start)
81
- sleep sleep_interval if sleep_interval.positive?
82
- end
83
- end
84
-
85
- private
86
-
87
- def jitter
88
- rand
89
- end
90
-
91
- def forked?
92
- pid != Process.pid
93
- end
94
-
95
- def ensure_worker_running
96
- # Return early if thread is alive and avoid the mutex lock and unlock.
97
- return if thread_alive?
98
-
99
- # If another thread is starting worker thread, then return early so this
100
- # thread can enqueue and move on with life.
101
- return unless mutex.try_lock
102
-
103
- begin
104
- return if thread_alive?
105
- @thread = Thread.new { run }
106
- @instrumenter.instrument("poller.#{InstrumentationNamespace}", {
107
- operation: :thread_start,
108
- })
109
- ensure
110
- mutex.unlock
111
- end
112
- end
113
-
114
- def thread_alive?
115
- @thread && @thread.alive?
116
- end
117
-
118
- def reset
119
- @pid = Process.pid
120
- mutex.unlock if mutex.locked?
121
- end
122
- end
123
- end
124
- end
125
- end
1
+ warn "DEPRECATION WARNING: Flipper::Adapters::Poll::Poller is deprecated. Use Flipper::Poller instead."
2
+ require 'flipper/adapters/poll'
@@ -1,4 +1,5 @@
1
1
  require 'flipper/adapters/sync/synchronizer'
2
+ require 'flipper/poller'
2
3
 
3
4
  module Flipper
4
5
  module Adapters
@@ -6,6 +7,9 @@ module Flipper
6
7
  extend Forwardable
7
8
  include ::Flipper::Adapter
8
9
 
10
+ # Deprecated
11
+ Poller = ::Flipper::Poller
12
+
9
13
  # Public: The name of the adapter.
10
14
  attr_reader :name, :adapter, :poller
11
15
 
@@ -100,13 +100,12 @@ module Flipper
100
100
  #
101
101
  # Returns true if enabled, false if not.
102
102
  def enabled?(thing = nil)
103
- instrument(:enabled?) do |payload|
104
- values = gate_values
105
- thing = gate(:actor).wrap(thing) unless thing.nil?
106
- payload[:thing] = thing
103
+ thing = Types::Actor.wrap(thing) unless thing.nil?
104
+
105
+ instrument(:enabled?, thing: thing) do |payload|
107
106
  context = FeatureCheckContext.new(
108
107
  feature_name: @name,
109
- values: values,
108
+ values: gate_values,
110
109
  thing: thing
111
110
  )
112
111
 
@@ -207,7 +206,7 @@ module Flipper
207
206
 
208
207
  if values.boolean || values.percentage_of_time == 100
209
208
  :on
210
- elsif non_boolean_gates.detect { |gate| gate.enabled?(values[gate.key]) }
209
+ elsif non_boolean_gates.detect { |gate| gate.enabled?(values.send(gate.key)) }
211
210
  :conditional
212
211
  else
213
212
  :off
@@ -232,7 +231,8 @@ module Flipper
232
231
 
233
232
  # Public: Returns the raw gate values stored by the adapter.
234
233
  def gate_values
235
- GateValues.new(adapter.get(self))
234
+ adapter_values = adapter.get(self)
235
+ GateValues.new(adapter_values)
236
236
  end
237
237
 
238
238
  # Public: Get groups enabled for this feature.
@@ -290,7 +290,7 @@ module Flipper
290
290
  # Returns an Array of Flipper::Gate instances.
291
291
  def enabled_gates
292
292
  values = gate_values
293
- gates.select { |gate| gate.enabled?(values[gate.key]) }
293
+ gates.select { |gate| gate.enabled?(values.send(gate.key)) }
294
294
  end
295
295
 
296
296
  # Public: Get the names of the enabled gates.
@@ -339,20 +339,24 @@ module Flipper
339
339
  #
340
340
  # Returns an array of gates
341
341
  def gates
342
- @gates ||= [
343
- Gates::Boolean.new,
344
- Gates::Actor.new,
345
- Gates::PercentageOfActors.new,
346
- Gates::PercentageOfTime.new,
347
- Gates::Group.new,
348
- ]
342
+ @gates ||= gates_hash.values.freeze
343
+ end
344
+
345
+ def gates_hash
346
+ @gates_hash ||= {
347
+ boolean: Gates::Boolean.new,
348
+ actor: Gates::Actor.new,
349
+ percentage_of_actors: Gates::PercentageOfActors.new,
350
+ percentage_of_time: Gates::PercentageOfTime.new,
351
+ group: Gates::Group.new,
352
+ }.freeze
349
353
  end
350
354
 
351
355
  # Public: Find a gate by name.
352
356
  #
353
357
  # Returns a Flipper::Gate if found, nil if not.
354
358
  def gate(name)
355
- gates.detect { |gate| gate.name == name.to_sym }
359
+ gates_hash[name.to_sym]
356
360
  end
357
361
 
358
362
  # Public: Find the gate that protects a thing.
@@ -368,8 +372,8 @@ module Flipper
368
372
  private
369
373
 
370
374
  # Private: Instrument a feature operation.
371
- def instrument(operation)
372
- @instrumenter.instrument(InstrumentationName) do |payload|
375
+ def instrument(operation, initial_payload = {})
376
+ @instrumenter.instrument(InstrumentationName, initial_payload) do |payload|
373
377
  payload[:feature_name] = name
374
378
  payload[:operation] = operation
375
379
  payload[:result] = yield(payload) if block_given?
@@ -10,10 +10,10 @@ module Flipper
10
10
  # Public: The thing we want to know if a feature is enabled for.
11
11
  attr_reader :thing
12
12
 
13
- def initialize(options = {})
14
- @feature_name = options.fetch(:feature_name)
15
- @values = options.fetch(:values)
16
- @thing = options.fetch(:thing)
13
+ def initialize(feature_name:, values:, thing:)
14
+ @feature_name = feature_name
15
+ @values = values
16
+ @thing = thing
17
17
  end
18
18
 
19
19
  # Public: Convenience method for groups value like Feature has.
@@ -3,16 +3,6 @@ require 'flipper/typecast'
3
3
 
4
4
  module Flipper
5
5
  class GateValues
6
- # Private: Array of instance variables that are readable through the []
7
- # instance method.
8
- LegitIvars = {
9
- 'boolean' => '@boolean',
10
- 'actors' => '@actors',
11
- 'groups' => '@groups',
12
- 'percentage_of_time' => '@percentage_of_time',
13
- 'percentage_of_actors' => '@percentage_of_actors',
14
- }.freeze
15
-
16
6
  attr_reader :boolean
17
7
  attr_reader :actors
18
8
  attr_reader :groups
@@ -27,12 +17,6 @@ module Flipper
27
17
  @percentage_of_time = Typecast.to_percentage(adapter_values[:percentage_of_time])
28
18
  end
29
19
 
30
- def [](key)
31
- if ivar = LegitIvars[key.to_s]
32
- instance_variable_get(ivar)
33
- end
34
- end
35
-
36
20
  def eql?(other)
37
21
  self.class.eql?(other.class) &&
38
22
  boolean == other.boolean &&