Skip to content

Commit

Permalink
Merge pull request #6 from jcagarcia/rakes
Browse files Browse the repository at this point in the history
feat(#4): Adding rake tasks for activating, deactivating and listing feature flags
  • Loading branch information
jcagarcia committed Oct 23, 2023
2 parents 527cc13 + 976c749 commit bd7aa45
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 1 deletion.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).


## [0.3.0] - 2023-10-24

### Added
- Providing some rake tasks to the consumers of the gem for allowing them to easily manage their feature flags in their applications:
- `bundle exec rake rollout:on` rake task for activating feature flags
- `bundle exec rake rollout:off` rake task for deactivating feature flags
- `bundle exec rake rollout:list` rake task for listing stored feature flags

## [0.2.0] - 2023-10-23

### Added
Expand Down
2 changes: 2 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ GEM
connection_pool (2.4.1)
diff-lcs (1.5.0)
mock_redis (0.37.0)
rake (13.0.6)
redis (5.0.0)
redis-client (~> 0.7)
redis-client (0.17.1)
Expand All @@ -34,6 +35,7 @@ PLATFORMS
DEPENDENCIES
bundler (>= 2.4)
mock_redis (~> 0.37)
rake (~> 13)
rollout-redis!
rspec (~> 3.12)

Expand Down
34 changes: 34 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Topics covered in this README:
- [Gradual activation based on percentages](#gradual-activation-based-on-percentages)
- [Caching Feature Flags](#caching-feature-flags)
- [Auto-deactivating flags](#auto-deactivating-flags)
- [Rake tasks](#rake-tasks)
- [Migrating from rollout gem](#migrating-from-rollout-gem-🚨)
- [Changelog](#changelog)
- [Contributing](#contributing)
Expand Down Expand Up @@ -159,6 +160,39 @@ end

When any unexpected error appears during the wrapped code execution, the Rollout gem will take it into account for automatically deactivating the feature flag if the threshold of errors is reached. All the managed or captured errors inside the wrapped code will not be taken into consideration.

## Rake tasks

In order to have access to the rollout rakes, you have to load manually the task definitions. For doing so load the rollout rake task:

```ruby
require 'rollout'

load 'rollout/tasks/rollout.rake'
```

### Usage

To activate/deactivate features, execute the following rake tasks:

```shell
bundle exec rake rollout:on[feature_name]
bundle exec rake rollout:off[feature_name]
```

To a gradual activation based on percentages, pass the percentage as the second parameter when executing the `on` task.

```shell
bundle exec rake rollout:on[feature_name,50]
```

_NOTE_: In both cases, `feature_name` **must not** include quotes e.g. `bundle exec rake rollout:on['feature_name']`, as the gem will be unable to fetch its activation status if so.

For listing all the stored feature flags, do:

```shell
bundle exec rake rollout:list
```

## Migrating from rollout gem 🚨

If you are currently using the unmaintained [rollout](https://github.com/fetlife/rollout) gem, you should consider checking this [migration guide](https://github.com/jcagarcia/rollout-redis/blob/main/MIGRATING_FROM_ROLLOUT_GEM.md) for start using the new `rollout-redis` gem.
Expand Down
63 changes: 63 additions & 0 deletions lib/rollout/tasks/rollout.rake
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@

namespace :rollout do
desc "Activate a feature"
task :on, [:feature, :percentage] => :environment do |task, args|
if args.feature
puts "Activating feature #{args.feature}..."
if args.percentage
activated = rollout.activate(args.feature, args.percentage.to_i)
else
activated = rollout.activate(args.feature)
end

if activated
puts "Feature flag #{args.feature} has been activated! :)"
else
puts "Feature flag #{args.feature} has NOT been activated! :("
end
end
end

desc "Deactivate a feature"
task :off, [:feature] => :environment do |task, args|
if args.feature
puts "Deactivating feature #{args.feature}..."
deactivated = rollout.deactivate(args.feature)
if deactivated
puts "Feature flag #{args.feature} has been deactivated! :)"
else
puts "Feature flag #{args.feature} has NOT been deactivated! :("
end
end
end

desc "List features"
task list: :environment do
features = rollout.features
puts "This is the list of all the available features:"
puts ""
if !features.empty?
puts features
else
puts "- No feature flags stored"
end
end

private

def rollout
@rollout ||= Rollout.new(storage)
end

def storage
begin
@storage ||= Redis.new(
host: ENV.fetch('ROLLOUT_REDIS_HOST'),
port: ENV.fetch('ROLLOUT_REDIS_PORT')
)
rescue KeyError => e
puts "ROLLOUT_REDIS_HOST and ROLLOUT_REDIS_PORT are mandatory env variables to define in order to run rollout rake tasks"
raise e
end
end
end
2 changes: 1 addition & 1 deletion lib/rollout/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

class Rollout
VERSION = '0.2.0'
VERSION = '0.3.0'
end
1 change: 1 addition & 0 deletions rollout-redis.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ Gem::Specification.new do |spec|
spec.add_development_dependency 'bundler', '>= 2.4'
spec.add_development_dependency 'rspec', '~> 3.12'
spec.add_development_dependency 'mock_redis', '~> 0.37'
spec.add_development_dependency 'rake', '~> 13'
end
1 change: 1 addition & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'mock_redis'
require 'rake'
require 'rollout'

RSpec.configure do |config|
Expand Down
74 changes: 74 additions & 0 deletions spec/tasks/tasks_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
require 'spec_helper'

RSpec.describe "Rake tasks" do
let(:storage) { MockRedis.new }
let(:instance) { Rollout.new(storage) }
let(:redis_host) { 'localhost' }
let(:redis_port) { '6379' }
let!(:original_stdout) { $stdout }
let(:rake_output) { StringIO.new }

before(:each) do
$stdout = rake_output
allow(Redis).to receive(:new).with(host: redis_host, port: redis_port).and_return(storage)
ENV["ROLLOUT_REDIS_HOST"] = redis_host
ENV["ROLLOUT_REDIS_PORT"] = redis_port
end

after(:each) do
$stdout = original_stdout
end

it "performs all the operations" do
Rake.application.rake_require "rollout/tasks/rollout"
Rake::Task.define_task(:environment)

Rake::Task["rollout:on"].reenable
Rake.application.invoke_task "rollout:on[a-feature-flag]"
Rake::Task["rollout:on"].reenable
Rake.application.invoke_task "rollout:on[another-feature-flag,20]"
Rake::Task["rollout:on"].reenable
Rake.application.invoke_task "rollout:on[super-feature-flag,50]"

expect(instance.active?('a-feature-flag')).to be true
expect(instance.active?('another-feature-flag')).to be false
expect(instance.active?('super-feature-flag')).to be false

Rake::Task["rollout:list"].reenable
Rake.application.invoke_task "rollout:list"

Rake::Task["rollout:off"].reenable
Rake.application.invoke_task "rollout:off[a-feature-flag]"
Rake::Task["rollout:off"].reenable
Rake.application.invoke_task "rollout:off[another-feature-flag]"
Rake::Task["rollout:off"].reenable
Rake.application.invoke_task "rollout:off[super-feature-flag]"

expect(instance.active?('a-feature-flag')).to be false
expect(instance.active?('another-feature-flag')).to be false
expect(instance.active?('super-feature-flag')).to be false

Rake::Task["rollout:list"].reenable
Rake.application.invoke_task "rollout:list"

output = rake_output.string
expect(output).to include('Activating feature a-feature-flag...')
expect(output).to include('Feature flag a-feature-flag has been activated! :)')
expect(output).to include('Activating feature another-feature-flag...')
expect(output).to include('Feature flag another-feature-flag has been activated! :)')
expect(output).to include('Activating feature super-feature-flag...')
expect(output).to include('Feature flag super-feature-flag has been activated! :)')
expect(output).to include('This is the list of all the available features:')
expect(output).to include('{:name=>"a-feature-flag", :percentage=>100, :data=>{:requests=>0, :errors=>0}}')
expect(output).to include('{:name=>"another-feature-flag", :percentage=>20, :data=>{:requests=>0, :errors=>0}}')
expect(output).to include('{:name=>"super-feature-flag", :percentage=>50, :data=>{:requests=>0, :errors=>0}}')
expect(output).to include('Deactivating feature a-feature-flag...')
expect(output).to include('Feature flag a-feature-flag has been deactivated! :)')
expect(output).to include('Deactivating feature another-feature-flag...')
expect(output).to include('Feature flag another-feature-flag has been deactivated! :)')
expect(output).to include('Deactivating feature super-feature-flag...')
expect(output).to include('Feature flag super-feature-flag has been deactivated! :)')
expect(output).to include('This is the list of all the available features:')
expect(output).to include('- No feature flags stored')
end
end

0 comments on commit bd7aa45

Please sign in to comment.