Features are the heart of Flipper. They allow you to control code execution based on a few different types of enablement.
All on or all off. Think top level things like :stats, :search, :logging, etc. Also, an easy way to release a new feature, as once a feature is boolean enabled it is on for every situation.
user = User.find(...) Flipper.enabled?(:search) # false Flipper.enabled?(:search, user) # false Flipper.enable(:search) Flipper.enabled?(:search) # true Flipper.enabled?(:search, user) # true
Turn on feature based on the return value of block. Super flexible way to turn on a feature for multiple things (users, people, accounts, etc.) as long as the thing returns true when passed to the block.
user = User.find(...) # user who returns false for admin? method admin = User.find(...) # user who returns true for admin? method # Define the block that will return true or false based on actor. Registers a # group named admins which saves the block to be called later. Flipper.register(:admins) do |actor, context| actor.respond_to?(:admin?) && actor.admin? end Flipper.enabled?(:documentation, user) # false Flipper.enabled?(:documentation, admin) # false # Enable the block named admins which means future enabled? checks will see if # the passing the actor to the block returns true. Flipper.enable_group(:documentation, :admins) # When user is passed to enabled?, Flipper knows that the admins group is # enabled. It executes the registered block above, yielding user. user responds # to admin?, but admin? returns false, which means the feature will not be # enabled for the user (true && false == false). Flipper.enabled?(:documentation, user) # false # Same as the previous check, but this time admin? returns true for the user # which means the feature will be enabled (true && true == true). Flipper.enabled?(:documentation, admin) # true
There is no requirement that the thing yielded to the block be a user model or whatever. It can be anything you want, therefore it is a good idea to check that the thing passed into the group block actually responds to what you are trying to do in the
register proc. This is why the
:admin group above uses
Turn feature on for individual thing. Think enable feature for someone to test or for a buddy.
user = User.find(...) other_user = User.find(...) Flipper.enabled?(:verbose_logging) # false Flipper.enabled?(:verbose_logging, user) # false Flipper.enable_actor(:verbose_logging, user) Flipper.enabled?(:verbose_logging) # false Flipper.enabled?(:verbose_logging, user) # true Flipper.enabled?(:verbose_logging, other_user) # false
The only requirement for an individual actor is that it must have a unique
flipper_id. Include the
Flipper::Identifier module for a default implementation which combines the class name and
class User < Struct.new(:id) include Flipper::Identifier end User.new(5).flipper_id # => "User;5"
You can also define your own implementation:
class Organization < Struct.new(:uuid) def flipper_id uuid end end Organization.new("DEB3D850-39FB-444B-A1E9-404A990FDBE0").flipper_id # => "DEB3D850-39FB-444B-A1E9-404A990FDBE0"
Just make sure each type of object has a unique
enable_actor is not designed for hundreds or thousands of actors to be enabled. This is an explicit choice to make it easier to batch load data from the adapters instead of performing individual checks for actors over and over. If you need to enable something for more than 100 individual actors, use a group or % of actors.
Turn this on for a percentage of actors (think user, member, account, group, whatever). Consistently on or off for this user as long as percentage increases. Think slow rollout of a new feature to a percentage of things.
user = User.find(...) # responds to flipper_id method call Flipper.enable_percentage_of_actors(:new_design, 25) # returns true or false consistently for the same enabled percentage and actor Flipper.enabled?(:new_design, user)
Percentage of actors also takes into account the feature name to ensure that the same actors are not granted access first to every feature.
Note: 100% of actors will not return true if no actor is provided.
user = User.find(...) # responds to flipper_id method call # turn on for all actors Flipper.enable_percentage_of_actors(:new_design, 100) # true because 100% and actor is passed Flipper.enabled?(:new_design, user) # true # false because no actor provided Flipper.enabled?(:new_design) # => false Flipper.enabled?(:new_design, nil) # => false
Turn this on for a percentage of time. Think load testing new features behind the scenes and such.
user = User.find(...) Flipper.enable_percentage_of_time(:dark_ship_new_feature, 25) # returns true for ~25% of the enabled? calls irregardless of the actor provided Flipper.enabled?(:dark_ship_new_feature) Flipper.enabled?(:dark_ship_new_feature, user)
Percentage of time is not a good idea for enabling new features in a UI. Most often you'll use percentage of actors, but there are definitely times when I have found percentage of time to be very useful.
disablemethod exists only to clear something that is enabled. If the thing you are disabling is not enabled, the disable is pointless. This means that if you enable one group an actor is in and disable another group, the feature will be enabled for the actor. (related issue)