:flags: Flagship :ship:
Ship/unship features using flags defined with declarative DSL.
Installation
Add this line to your application's Gemfile:
gem 'flagship'
And then execute:
$ bundle
Or install it yourself as:
$ gem install flagship
Usage
Define and use a flagset
Flagship.define :app do
enable :stable_feature
enable :experimental_feature, if: ->(context) { context.current_user.staff? }
disable :deprecate_feature
end
Flagship.select_flagset(:app)
Branch with a flag
if Flagship.enabled?(:some_feature)
# Implement the feature here
end
if Flagship.disabled?(:some_feature)
# Run when :some_feature is not enabled
end
Set context variables
Both of below can be called as context.foo from :if block.
# Set a value
Flagship.set_context :foo, 'FOO'
# Set a lambda
Flagship.set_context :foo, ->(context) { 'FOO' }
Or you can set a method too.
Flagship.set_context :current_user, method(:current_user)
Apply temporal changes to context variables
Using Flagship.with_context method, you can override context variables temporarily.
class User
def enabled_features
Flagship.with_context current_user: self do
Flagship.features.enabled.map(&:key)
end
end
end
It's useful when you implement a method using Flagship into some domain objects.
By using this, values specified in the argument is overridden and other values are inherited.
Extend flagset
Flagship.define :common do
enable :stable_feature
end
Flagship.define :development, extend: :common do
enable :experimental_feature
end
Flagship.define :production, extend: :common do
disable :experimental_feature
end
if Rails.env.production?
Flagship.select_flagset(:production)
else
Flagship.select_flagset(:development)
end
Override flag with ENV
You can override flags with ENV named FLAGSHIP_***.
Assuming that there is a flag :foo, you can override it with ENV FLAGSHIP_FOO=1.
Fetch all features
Flagship.features
# => Array of Flagship::Feature
Flagship.features.map(&:key)
# => Array key of all features
Flagship.features.enabled.map(&:key)
# => Array key of all enabled features
Categorize features with tags
Flagship.define :blog do
enable :post
enable :comment, communication: true
enable :trackback, communication: true, tracking: true
end
Flagship.select_flagset(:blog)
Flagship.features.enabled.tagged(communication: true).map(&:key)
# => [:comment, :trackback]
Flagship.features.enabled.tagged(communication: true, tracking: true).map(&:key)
# => [:trackback]
with_tags
Using with_tags, you can set same tags to multiple features at once.
Flagship.define :blog do
enable :post
with_tags(communication: true) do
enable :comment
enable :trackback
end
end
Feature flag composition
You can call #enabled? method inside of DSL.
Flagship.define :blog do
enable :comment, if: ->(context) { context.current_user.activated? }
enable :comment_deletion, if: ->(context) { enabled?(:comment) && context.current_user.moderator? }
end
Helper methods
You can define helpers as normal methods with def. Methods can be used within blocks, procs, or as symbolic names for if statements to tidy up your code.
Flagship.define :blog do
def is_author(comment, user)
comment.author == user
end
def can_view_comment(context)
context.current_user.moderator?
end
enable :comment, if: :can_view_comment
enable :comment_deletion, if: ->(context) { is_author(context.comment, context.current_user) }
end
To share helpers, you can simply include them as modules.
module FlagHelpers
def is_author(context)
context.comment.author == context.current_user
end
end
Flagship.define :development do
include FlagHelpers
enable :delete, if: :is_author
end
Flagship.define :production do
include FlagHelpers
enable :delete, if: :is_author
end
And you can also extend helper methods from base flagset.
Flagship.define :base do
def is_author(context)
context.comment.author == context.current_user
end
end
Flagship.define :production do
enable :delete, if: :is_author
end
Testing
RSpec
It's recommended to clear state of Flagship before the suite and after the each tests.
You can do it by configuring like below:
RSpec.configure do |config|
config.before(:suite) do
Flagship.clear_state
end
config.after(:each) do
Flagship.clear_state
end
end
Development
After checking out the repo, run bin/setup to install dependencies. You can also run bin/console for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/yuya-takeyama/flagship.
License
The gem is available as open source under the terms of the MIT License.