Flip Fab

Feature flipping... Made FaBuLoUs!

A gem providing persistent (via cookie store) feature flipping to Rack applications.

This allows you to...

  • Define features that can be used to modify the behaviour of an application
  • Persist the features' states in a user's cookie so that they see a consistent feature set for all applications in a domain
  • Define your own custom persistence adapters to persist features in your own data store

Usage

Add the following to your Gemfile

gem 'flip_fab'

Rails

The helper will be included in ActionController::Base as a view helper method when the gem is loaded.

Sinatra

Add the following to Sinatra::Base

require 'flip_fab'
helpers FlipFab::Helper

Defining a feature

For Rails, this could go in config/initializers/flip_fab.rb

FlipFab.define_feature(:name_of_feature) # default state: :disabled
FlipFab.define_feature(:name_of_other_feature, { default: :enabled }) # default state: :enabled

Enabling/disabling a feature for a user

Within the context of a controller

features[:name_of_feature].enable # Enable the feature for the user
features[:name_of_feature].enabled? # => true
features[:name_of_feature].disable # Disable the feature for the user
features[:name_of_feature].disabled? # => true
  • If the feature is not defined, features[:name_of_feature] will raise an error
  • The enabled/disabled feature will be stored in the user's cookie

Outside of a controller

Outside of a controller, features cannot be enabled/disabled for individual users.

From URL params

You can explicitely request a feature to be enabled or disabled in your session by passing the desired state of a feature in the parameters of your request:

http://localhost:3000?name_of_feature=enabled&name_of_other_feature=disabled

  • The features' states specified in the URL will take precendence over those specified in the controller
  • The enabled/disabled features will be stored in the user's cookie
  • Smoke tests could enable or disable features using this mechanism

Note: This will enable users of a production system to show/hide features in their session. While this allows automated tests to be run against staging/production environment against a particular set of features, this will allow users to 'customize' their experience, so consider what types of features a user could be switching on and off i.e. authentication, security, etc.

Checking if a feature is enabled

From within a controller or view

# With the feature having been enabled for the user:
features[:name_of_feature].enable # Enable the feature for the user

features[:name_of_feature].enabled? # => true
features[:name_of_feature].disabled? # => false

Outside of a controller/view

# With the feature defined like this:
FlipFab.define_feature(:name_of_feature) # default state: :disabled

FlipFab.features[:name_of_feature].enabled? # => false
FlipFab.features[:name_of_feature].disabled? # => true

Precedence of feature lookup

  1. URL parameter
  2. Code (e.g. features[:name_of_feature].enable)
  3. Cookie
  4. Default

Out of the box, the features a user receives can be persisted in their cookie. The cookie will have the following parameters:

Parameter Value
Name flip_fab.[name of feature]
Value `enabled
Path /
Expires One year from now
Domain The top-level domain

To persist the features in a user's cookie, do any of the following operations

features[:name_of_feature].enable # Enable the feature for the user
features[:name_of_feature].disable # Disable the feature for the user
features[:name_of_feature].persist # Persist the feature for the user

Defining a custom persistence adapter

  1. Create a class that extends the FlipFab::Persistence class
  class ExamplePersistence < FlipFab::Persistence

    def initialize feature_name, context
      super
    end

    def read
      # lookup the state of the feature
    end

    def write state
      # write the state of the feature
    end
  end
  1. Implement the read and write operations. The following variables will be available from FlipFab::Persistence

    1. feature_name - the name of the feature to be read/written
    2. context - the controller context (whatever class has included FlipFab::Helper)
  2. Include your persistence class when defining the feature

  FlipFab.define_feature(:name_of_other_feature, { persistence_adapters: [ExamplePersistence] })

Note that you can define multiple custom adapters that will be read in precedence of the order specified

Example app

There is an example app that demonstrates the use of FlipFlab in example/rails_app. The app has a feature called :justin_beaver that allows you to flip the image on the page between a beaver and a 'justin beaver'.

Perform the following to try the example:

  1. Go into the app: cd example/rails_app
  2. Install the gems: bundle install
  3. Run the migrations: bundle exec rake db:migrate
  4. Start rails: bundle exec rails s
  5. View the page with the feature disabled: open 'http://localhost:3000/beavers?justin_beaver=disabled'
  6. View the page with the feature enabled: open 'http://localhost:3000/beavers?justin_beaver=enabled'

Contributing

  1. Make your changes in a branch
  2. Update the version using semantic versioning in lib/flip_fab/version.rb
  3. Once signed off, merge
  4. The gem will automatically be pushed to gemstash