flipper 0.11.0.beta6 → 0.11.0.beta7

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
  SHA1:
3
- metadata.gz: fc657f1ced7ca41760051223843e487c3c820277
4
- data.tar.gz: '0520502010855501979601516821cac277ce3891'
3
+ metadata.gz: 258c0cfa4877a2bea9e7927f5d39ed1339c3f698
4
+ data.tar.gz: 4402f1aa545f09f2bf0dd97937d6e7564ec6d008
5
5
  SHA512:
6
- metadata.gz: b9afe59c11ad41ec976ed8cb804929985afdb9e737146d9639510c75298ee88f1fb1573d92580f3504840a453e3cf84ed1a4b9d9c61ad5975fc1471d8096d34f
7
- data.tar.gz: a1c682c40e47feb582e74069aad67f261b9a2cb14b3cd0978a175a305e6fd31a062804514608c60dffe743a635d4037b7aba55a6410b12a68eaef8a9343506a1
6
+ metadata.gz: 7596ed84b459acc7b052a11e7a86e5be07d4bc52b083e31dcd14749b56e026f7a6ee0a6b3aac50fdc61950afcc764bdf64834bcecd4668baadc62e06a60d248b
7
+ data.tar.gz: 18a608871470cd7ae57db9b4dccf661e9ea8fe68e55efced9fea8cec26b9374a9c528b324516da9f263cad8cd4da28d697501f321d8f9a2b0280a351e1ed057c
data/Changelog.md CHANGED
@@ -23,6 +23,8 @@
23
23
  * Allow setting debug output of http adapter (https://github.com/jnunemaker/flipper/pull/256 and https://github.com/jnunemaker/flipper/pull/258).
24
24
  * Allow setting env key for middleware (https://github.com/jnunemaker/flipper/pull/259).
25
25
  * Added ActiveSupport cache store adapter for use with Rails.cache (https://github.com/jnunemaker/flipper/pull/265).
26
+ * Added support for up to 3 decimal places in percentage based rollouts (https://github.com/jnunemaker/flipper/pull/274).
27
+ * Removed Flipper::GroupNotRegistered error as it is now unused (https://github.com/jnunemaker/flipper/pull/270).
26
28
 
27
29
  ## 0.10.2
28
30
 
data/README.md CHANGED
@@ -41,26 +41,34 @@ The goal of the API for flipper was to have everything revolve around features a
41
41
 
42
42
  ```ruby
43
43
  require 'flipper'
44
-
45
- # pick an adapter
46
44
  require 'flipper/adapters/memory'
47
- adapter = Flipper::Adapters::Memory.new
48
45
 
49
- # get a handy dsl instance
50
- flipper = Flipper.new(adapter)
46
+ Flipper.configure do |config|
47
+ config.default do
48
+ # pick an adapter, this uses memory, any will do
49
+ adapter = Flipper::Adapters::Memory.new
51
50
 
52
- # grab a feature
53
- search = flipper[:search]
51
+ # pass adapter to handy DSL instance
52
+ Flipper.new(adapter)
53
+ end
54
+ end
54
55
 
55
- # check if that feature is enabled
56
- if search.enabled?
56
+ # check if search is enabled
57
+ if Flipper.enabled?(:search)
57
58
  puts 'Search away!'
58
59
  else
59
60
  puts 'No search for you!'
60
61
  end
61
62
 
62
63
  puts 'Enabling Search...'
63
- search.enable
64
+ Flipper.enable(:search)
65
+
66
+ # check if search is enabled
67
+ if Flipper.enabled?(:search)
68
+ puts 'Search away!'
69
+ else
70
+ puts 'No search for you!'
71
+ end
64
72
  ```
65
73
 
66
74
  Of course there are more [examples for you to peruse](examples/). You could also check out the [DSL](lib/flipper/dsl.rb) and [Feature](lib/flipper/feature.rb) classes for code/docs.
data/docs/Adapters.md CHANGED
@@ -5,6 +5,7 @@ I plan on supporting the adapters in the flipper repo. Other adapters are welcom
5
5
  ## Officially Supported
6
6
 
7
7
  * [ActiveRecord adapter](https://github.com/jnunemaker/flipper/blob/master/docs/active_record) - Rails 3, 4, and 5.
8
+ * [CacheStore adapter](https://github.com/jnunemaker/flipper/blob/master/docs/cache_store) - ActiveSupport::Cache::Store
8
9
  * [Cassanity adapter](https://github.com/jnunemaker/flipper-cassanity)
9
10
  * [Http adapter](https://github.com/jnunemaker/flipper/blob/master/docs/http)
10
11
  * [memory adapter](https://github.com/jnunemaker/flipper/blob/master/lib/flipper/adapters/memory.rb) – great for tests
data/docs/Optimization.md CHANGED
@@ -95,3 +95,34 @@ Example using the RedisCache adapter with the Memory adapter and a TTL of 4800 s
95
95
  adapter = Flipper::Adapters::RedisCache.new(memory_adapter, redis, 4800)
96
96
  flipper = Flipper.new(adapter)
97
97
  ```
98
+
99
+ ### CacheStore
100
+
101
+ Rails applications can cache Flipper calls in any [ActiveSupport::Cache::Store](http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html) implementation.
102
+
103
+ Add this line to your application's Gemfile:
104
+
105
+ gem 'flipper-cache-store'
106
+
107
+ And then execute:
108
+
109
+ $ bundle
110
+
111
+ Or install it yourself with:
112
+
113
+ $ gem install flipper-cache-store
114
+
115
+ Example using the CacheStore adapter with ActiveSupport's [MemoryStore](http://api.rubyonrails.org/classes/ActiveSupport/Cache/MemoryStore.html), Flipper's [Memory adapter](https://github.com/jnunemaker/flipper/blob/master/lib/flipper/adapters/memory.rb), and a TTL of 5 minutes.
116
+
117
+ ```ruby
118
+ require 'active_support/cache'
119
+ require 'flipper/adapters/memory'
120
+ require 'flipper/adapters/cache_store'
121
+
122
+ memory_adapter = Flipper::Adapters::Memory.new
123
+ cache = ActiveSupport::Cache::MemoryStore.new
124
+ adapter = Flipper::Adapters::CacheStore.new(memory_adapter, cache, expires_in: 5.minutes)
125
+ flipper = Flipper.new(adapter)
126
+ ```
127
+
128
+ Setting `expires_in` is optional and will set an expiration time on Flipper cache keys. If specified, all flipper keys will use this `expires_in` over the `expires_in` passed to your ActiveSupport cache constructor.
data/examples/basic.rb CHANGED
@@ -3,25 +3,29 @@ require File.expand_path('../example_setup', __FILE__)
3
3
  require 'flipper'
4
4
  require 'flipper/adapters/memory'
5
5
 
6
- # pick an adapter
7
- adapter = Flipper::Adapters::Memory.new
6
+ Flipper.configure do |config|
7
+ config.default do
8
+ # pick an adapter, this uses memory, any will do
9
+ adapter = Flipper::Adapters::Memory.new
8
10
 
9
- # get a handy dsl instance
10
- flipper = Flipper.new(adapter)
11
-
12
- # grab a feature
13
- search = flipper[:search]
14
-
15
- perform = lambda do
16
- # check if that feature is enabled
17
- if search.enabled?
18
- puts 'Search away!'
19
- else
20
- puts 'No search for you!'
11
+ # pass adapter to handy DSL instance
12
+ Flipper.new(adapter)
21
13
  end
22
14
  end
23
15
 
24
- perform.call
16
+ # check if search is enabled
17
+ if Flipper.enabled?(:search)
18
+ puts 'Search away!'
19
+ else
20
+ puts 'No search for you!'
21
+ end
22
+
25
23
  puts 'Enabling Search...'
26
- search.enable
27
- perform.call
24
+ Flipper.enable(:search)
25
+
26
+ # check if search is enabled
27
+ if Flipper.enabled?(:search)
28
+ puts 'Search away!'
29
+ else
30
+ puts 'No search for you!'
31
+ end
@@ -0,0 +1,23 @@
1
+ require File.expand_path('../example_setup', __FILE__)
2
+
3
+ require 'flipper'
4
+ require 'flipper/adapters/memory'
5
+
6
+ # sets up default adapter so Flipper works like Flipper::DSL
7
+ Flipper.configure do |config|
8
+ config.default do
9
+ Flipper.new Flipper::Adapters::Memory.new
10
+ end
11
+ end
12
+
13
+ puts Flipper.enabled?(:search) # => false
14
+ Flipper.enable(:search)
15
+ puts Flipper.enabled?(:search) # => true
16
+ Flipper.disable(:search)
17
+
18
+ enabled_actor = Flipper::Actor.new("1")
19
+ disabled_actor = Flipper::Actor.new("2")
20
+ Flipper.enable_actor(:search, enabled_actor)
21
+
22
+ puts Flipper.enabled?(:search, enabled_actor)
23
+ puts Flipper.enabled?(:search, disabled_actor)
@@ -19,7 +19,7 @@ class User
19
19
  alias_method :flipper_id, :id
20
20
  end
21
21
 
22
- total = 10_000
22
+ total = 100_000
23
23
 
24
24
  # create array of fake users
25
25
  users = (1..total).map { |n| User.new(n) }
@@ -31,13 +31,12 @@ perform_test = lambda { |number|
31
31
  flipper[:stats].enabled?(user) ? true : nil
32
32
  }.compact
33
33
 
34
- actual = (enabled.size / total.to_f * 100).round(2)
34
+ actual = (enabled.size / total.to_f * 100).round(3)
35
35
 
36
36
  puts "percentage: #{actual.to_s.rjust(6, ' ')} vs #{number.to_s.rjust(3, ' ')}"
37
37
  }
38
38
 
39
39
  puts "percentage: Actual vs Hoped For"
40
-
41
- [1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99, 100].each do |number|
40
+ [0.001, 0.01, 0.1, 1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99, 100].each do |number|
42
41
  perform_test.call number
43
42
  end
@@ -10,7 +10,7 @@ logging = flipper[:logging]
10
10
  perform_test = lambda do |number|
11
11
  logging.enable flipper.time(number)
12
12
 
13
- total = 1_000
13
+ total = 100_000
14
14
  enabled = []
15
15
  disabled = []
16
16
 
@@ -18,7 +18,7 @@ perform_test = lambda do |number|
18
18
  logging.enabled? ? true : nil
19
19
  }.compact
20
20
 
21
- actual = (enabled.size / total.to_f * 100).round(2)
21
+ actual = (enabled.size / total.to_f * 100).round(3)
22
22
 
23
23
  # puts "#{enabled.size} / #{total}"
24
24
  puts "percentage: #{actual.to_s.rjust(6, ' ')} vs #{number.to_s.rjust(3, ' ')}"
@@ -26,6 +26,6 @@ end
26
26
 
27
27
  puts "percentage: Actual vs Hoped For"
28
28
 
29
- [1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99, 100].each do |number|
29
+ [0.001, 0.01, 0.1, 1, 5, 10, 20, 30, 40, 50, 60, 70, 80, 90, 95, 99, 100].each do |number|
30
30
  perform_test.call number
31
31
  end
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/flipper/version', __FILE__)
3
+
4
+ flipper_cache_store_files = lambda do |file|
5
+ file =~ /cache_store/
6
+ end
7
+
8
+ Gem::Specification.new do |gem|
9
+ gem.authors = ['John Nunemaker']
10
+ gem.email = ['nunemaker@gmail.com']
11
+ gem.summary = 'ActiveSupport::Cache::Store adapter for Flipper'
12
+ gem.description = 'ActiveSupport::Cache::Store adapter for Flipper'
13
+ gem.license = 'MIT'
14
+ gem.homepage = 'https://github.com/jnunemaker/flipper'
15
+
16
+ gem.files = `git ls-files`.split("\n").select(&flipper_cache_store_files) + ['lib/flipper/version.rb']
17
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n").select(&flipper_cache_store_files)
18
+ gem.name = 'flipper-cache-store'
19
+ gem.require_paths = ['lib']
20
+ gem.version = Flipper::VERSION
21
+
22
+ gem.add_dependency 'flipper', "~> #{Flipper::VERSION}"
23
+ gem.add_dependency 'activesupport', '>= 3.2', '< 6'
24
+ end
data/lib/flipper.rb CHANGED
@@ -1,13 +1,62 @@
1
+ require "forwardable"
2
+
1
3
  module Flipper
4
+ extend self # rubocop:disable Style/ModuleFunction
5
+ extend Forwardable
6
+
2
7
  # Private: The namespace for all instrumented events.
3
8
  InstrumentationNamespace = :flipper
4
9
 
5
10
  # Public: Start here. Given an adapter returns a handy DSL to all the flipper
6
11
  # goodness. To see supported options, check out dsl.rb.
7
- def self.new(adapter, options = {})
12
+ def new(adapter, options = {})
8
13
  DSL.new(adapter, options)
9
14
  end
10
15
 
16
+ # Public: Configure flipper.
17
+ #
18
+ # Flipper.configure do |config|
19
+ # config.default { ... }
20
+ # end
21
+ #
22
+ # Yields Flipper::Configuration instance.
23
+ def configure
24
+ yield configuration if block_given?
25
+ end
26
+
27
+ # Public: Returns Flipper::Configuration instance.
28
+ def configuration
29
+ @configuration ||= Configuration.new
30
+ end
31
+
32
+ # Public: Sets Flipper::Configuration instance.
33
+ def configuration=(configuration)
34
+ @configuration = configuration
35
+ end
36
+
37
+ # Public: Default per thread flipper instance if configured. You should not
38
+ # need to use this directly as most of the Flipper::DSL methods are delegated
39
+ # from Flipper module itself. Instead of doing Flipper.instance.enabled?(:search),
40
+ # you can use Flipper.enabled?(:search) for the same result.
41
+ #
42
+ # Returns Flipper::DSL instance.
43
+ def instance
44
+ Thread.current[:flipper_instance] ||= configuration.default
45
+ end
46
+
47
+ # Public: All the methods delegated to instance. These should match the
48
+ # interface of Flipper::DSL.
49
+ def_delegators :instance,
50
+ :enabled?, :enable, :disable, :bool, :boolean,
51
+ :enable_actor, :disable_actor, :actor,
52
+ :enable_group, :disable_group,
53
+ :enable_percentage_of_actors, :disable_percentage_of_actors,
54
+ :actors, :percentage_of_actors,
55
+ :enable_percentage_of_time, :disable_percentage_of_time,
56
+ :time, :percentage_of_time,
57
+ :features, :feature, :[], :preload, :preload_all,
58
+ :add, :remove, :import
59
+
11
60
  # Public: Use this to register a group by name.
12
61
  #
13
62
  # name - The Symbol name of the group.
@@ -22,7 +71,7 @@ module Flipper
22
71
  #
23
72
  # Returns a Flipper::Group.
24
73
  # Raises Flipper::DuplicateGroup if the group is already registered.
25
- def self.register(name, &block)
74
+ def register(name, &block)
26
75
  group = Types::Group.new(name, &block)
27
76
  groups_registry.add(group.name, group)
28
77
  group
@@ -31,28 +80,28 @@ module Flipper
31
80
  end
32
81
 
33
82
  # Public: Returns a Set of registered Types::Group instances.
34
- def self.groups
83
+ def groups
35
84
  groups_registry.values.to_set
36
85
  end
37
86
 
38
87
  # Public: Returns a Set of symbols where each symbol is a registered
39
88
  # group name. If you just want the names, this is more efficient than doing
40
89
  # `Flipper.groups.map(&:name)`.
41
- def self.group_names
90
+ def group_names
42
91
  groups_registry.keys.to_set
43
92
  end
44
93
 
45
94
  # Public: Clears the group registry.
46
95
  #
47
96
  # Returns nothing.
48
- def self.unregister_groups
97
+ def unregister_groups
49
98
  groups_registry.clear
50
99
  end
51
100
 
52
101
  # Public: Check if a group exists
53
102
  #
54
103
  # Returns boolean
55
- def self.group_exists?(name)
104
+ def group_exists?(name)
56
105
  groups_registry.key?(name)
57
106
  end
58
107
 
@@ -65,22 +114,23 @@ module Flipper
65
114
  # Flipper.group(:admins)
66
115
  #
67
116
  # Returns Flipper::Group.
68
- def self.group(name)
117
+ def group(name)
69
118
  groups_registry.get(name) || Types::Group.new(name)
70
119
  end
71
120
 
72
121
  # Internal: Registry of all groups_registry.
73
- def self.groups_registry
122
+ def groups_registry
74
123
  @groups_registry ||= Registry.new
75
124
  end
76
125
 
77
126
  # Internal: Change the groups_registry registry.
78
- def self.groups_registry=(registry)
127
+ def groups_registry=(registry)
79
128
  @groups_registry = registry
80
129
  end
81
130
  end
82
131
 
83
132
  require 'flipper/actor'
133
+ require 'flipper/configuration'
84
134
  require 'flipper/adapter'
85
135
  require 'flipper/dsl'
86
136
  require 'flipper/errors'
@@ -0,0 +1,32 @@
1
+ module Flipper
2
+ class Configuration
3
+ def initialize
4
+ @default = -> { raise DefaultNotSet }
5
+ end
6
+
7
+ # Controls the default instance for flipper. When used with a block it
8
+ # assigns a new default block to use to generate an instance. When used
9
+ # without a block, it performs a block invocation and returns the result.
10
+ #
11
+ # configuration = Flipper::Configuration.new
12
+ # configuration.default # => raises DefaultNotSet error.
13
+ #
14
+ # # sets the default block to generate a new instance using Memory adapter
15
+ # configuration.default do
16
+ # require "flipper/adapters/memory"
17
+ # Flipper.new(Flipper::Adapters::Memory.new)
18
+ # end
19
+ #
20
+ # configuration.default # => Flipper::DSL instance using Memory adapter
21
+ #
22
+ # Returns result of default block invocation if called without block. If
23
+ # called with block, assigns the default block.
24
+ def default(&block)
25
+ if block_given?
26
+ @default = block
27
+ else
28
+ @default.call
29
+ end
30
+ end
31
+ end
32
+ end
data/lib/flipper/dsl.rb CHANGED
@@ -217,7 +217,6 @@ module Flipper
217
217
  # name - The String or Symbol name of the feature.
218
218
  #
219
219
  # Returns an instance of Flipper::Group.
220
- # Raises Flipper::GroupNotRegistered if group has not been registered.
221
220
  def group(name)
222
221
  Flipper.group(name)
223
222
  end
@@ -12,6 +12,13 @@ module Flipper
12
12
  # Raised when attempting to declare a group name that has already been used.
13
13
  class DuplicateGroup < Error; end
14
14
 
15
- # Raised when attempting to access a group that is not registered.
16
- class GroupNotRegistered < Error; end
15
+ # Raised when default instance not configured but there is an attempt to
16
+ # use it.
17
+ class DefaultNotSet < Flipper::Error
18
+ def initialize(message = nil)
19
+ default = "Default flipper instance not configured. See " \
20
+ "Flipper.configure for how to configure the default instance."
21
+ super(message || default)
22
+ end
23
+ end
17
24
  end