TimePilot Testing Code Climate

TimePilot is a RubyGem that makes it possible to define features that can be enabled for a certain group of users. It requires very little configuration, and is designed to work stand-alone on any object. TimePilot uses Redis for storage.

Installation

Add this line to your application's Gemfile:

gem 'time_pilot'

And then execute:

$ bundle

Or install it yourself as:

$ gem install time_pilot

Configuration

The features that you want to specify need to be added in an initializer as such:

# config/initializers/time_pilot.rb
TimePilot.configure do |c|
  c.feature :planning
  c.feature :private_messaging
end

Using this configuration, the TimePilot::Features module is enriched with three methods per feature at boot time: {feature}_enabled?, enable_{feature} and disable_{feature}. In every model that you would want to invoke these methods, i.e. check if a feature is enabled or either enable or disable it for that group you need to include TimePilot::Features.

After including TimePilot::Features add: is_pilot_group. You can specify an overridden_by: [:foo, :bar, ...] to specify any relations that override the setting for the object itself. See the example below for Team and User.

# app/model/organization.rb
class Organization
  attr_accessor :id

  include TimePilot::Features
  is_pilot_group
end

# app/model/team.rb
class Team
  attr_accessor :organization_id, :id

  include TimePilot::Features
  is_pilot_group overridden_by: :organization
end

# app/model/user.rb
class User
  attr_accessor :organization_id, :team_id, :id

  include TimePilot::Features
  is_pilot_group overridden_by: [:organization, :team]
end

# app/model/person.rb
class Person
  attr_accessor :id

  include TimePilot::Features
  is_pilot_group
end

When you invoke user.planning_enabled?, TimePilot fill first check if the feature has been enabled for the organization with the ID obtained from user.organization_id. If that evaluates to false, TimePilot continues checking the feature, but now using user.team_id. Then, if that fails too, it checks whether the feature is enabled for the user itself.

Configuring Redis

TimePilot uses localhost:6379 by default for Redis. To connect to a Redis instance on a different port or host, provide a Redis client in TimePilot.configure block.

# config/initializers/time_pilot.rb
TimePilot.configure do |c|
  c.redis Redis.new(...)
end

TimePilot assumes you put in an object that responds to #sadd, #srem, #sismember and #scard.

Multiple configure blocks

TimePilot allows you to specify more than one configure block. This allows you to specify features at multiple levels. For example, you could specify features in a Rails Engine located in a separate gem, as well as the host application. It's easy as this:

# vendor/gems/.../config/initializers/time_pilot.rb
TimePilot.configure do |c|
  c.feature :this_could_be_my_rails_engine
end

# config/initializers/time_pilot.rb
TimePilot.configure do |c|
  c.feature :this_is_my_host_application
end

TimePilot Web

TimePilot provides a dashboard that is mountable inside a Rails app.

# in config/initializers/time_pilot.rb
require 'time_pilot/web'
TimePilot::Web.use(Rack::Auth::Basic) do |user, password|
  username == ENV["TIME_PILOT_USERNAME"] && password == ENV["TIME_PILOT_PASSWORD"]
end

# in routes.rb
Application.routes.draw do
  mount TimePilot::Web => '/time_pilot'
end

TimePilot::Web is just a Rack app. You can use it outside Rails.

Usage

The configuration above allows you to invoke the following methods on a Organization, Team, User and Person:

# Methods for feature 'planning'
user.planning_enabled?
user.enable_planning
user.disable_planning

# Methods for feature 'private_messaging'
user.private_messaging_enabled?
user.enable_private_messaging
user.disable_private_messaging

# The same methods can be invoked on an instance of Organization,
# Team and Person provided the example above, since they all include
# the `Features` module.