Created
February 26, 2017 05:12
-
-
Save chunpan/2a5b18b7a729b2f96aa4b81cb5d4fa7a to your computer and use it in GitHub Desktop.
A Simple File-Based Feature Toggler
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# A service to properly route feature toggles, similar to Promote's Gate mechanism, | |
# but greatly simplified. | |
# @see https://martinfowler.com/articles/feature-toggles.html | |
class FeatureToggleService | |
attr_reader :config | |
DEFAULT_CONFIG_PATH = Rails.root.join('config', 'feature_toggles.yml') | |
def initialize(config: nil) | |
@config = | |
if config.blank? || !config.is_a?(Hash) | |
YAML.load_file(DEFAULT_CONFIG_PATH) | |
else | |
config | |
end | |
end | |
# Sets enabled/disabled state of a feature in this instance of service only, great for test. | |
# To permanently enable or disable a feature, update the `config/feature_toggles.yml` file. | |
# @param feature, required, a String with dot notation, | |
# e.g. <code>'scheduled_send.use_local_time'</code> | |
# @param enabled, Boolean value, default to <code>true</code> | |
def set(feature:, enabled: true) | |
if feature.present? | |
keys = feature.to_s.split('.').map(&:to_sym) | |
last_config_node = @config | |
keys.each_with_index do |key, index| | |
# last key gets assigned the toggle | |
if index == keys.size - 1 | |
last_config_node[key] = enabled | |
else | |
last_config_node[key] ||= {} | |
last_config_node = last_config_node[key] | |
end | |
end | |
return true | |
end | |
false | |
end | |
# Gets enabled/disabled state of a feature. | |
# @return <code>true</code> if a feature key is found with a <code>Boolean</code> true value. | |
def enabled?(feature:) | |
if feature.present? | |
keys = feature.to_s.split('.').map(&:to_sym) | |
last_config_node = config | |
keys.each_with_index do |key, index| | |
if index == keys.size - 1 | |
return last_config_node.try(:[], key) == true | |
else | |
return false unless last_config_node.try(:[], key) | |
last_config_node = last_config_node[key] | |
end | |
end | |
end | |
false | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'rails_helper' | |
describe FeatureToggleService do | |
let(:service) { described_class.new(config: config) } | |
let(:config) do | |
{ | |
scheduled_send: { | |
use_local_time: false, | |
finders: { | |
utc_offset_finder: true, | |
} | |
} | |
} | |
end | |
describe 'Constructor' do | |
subject { service } | |
it { is_expected.to be_a described_class } | |
it 'stores the provided config' do | |
expect(subject.config).to eq config | |
end | |
end | |
describe '#set' do | |
subject { service.set(feature: feature, enabled: enabled) } | |
let(:enabled) { true } | |
context 'with empty feature string' do | |
let(:feature) { nil } | |
it { is_expected.to be false } | |
end | |
context 'with existing feature string' do | |
let(:feature) { 'scheduled_send.use_local_time' } | |
it { is_expected.to be true } | |
it 'sets the feature state to `true`' do | |
subject | |
expect(service.config[:scheduled_send][:use_local_time]).to be true | |
end | |
end | |
context 'with new feature string' do | |
let(:feature) { 'triggered_email.new_feature' } | |
let(:enabled) { false } | |
it { is_expected.to be true } | |
it 'sets the new feature state to `false`' do | |
subject | |
expect(service.config[:triggered_email][:new_feature]).to be false | |
end | |
end | |
end | |
describe '#enabled?' do | |
subject { service.enabled?(feature: feature) } | |
context 'feature of using local time' do | |
let(:feature) { 'scheduled_send.use_local_time ' } | |
it { is_expected.to be false } | |
end | |
context 'feature of utc_offset_finder' do | |
let(:feature) { 'scheduled_send.finders.utc_offset_finder' } | |
it { is_expected.to be true } | |
end | |
end | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
--- | |
scheduled_send: | |
use_local_time: false |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment