togls 2.1.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.codeclimate.yml +18 -2
- data/.ruby-version +1 -1
- data/.travis.yml +1 -0
- data/{ChangeLog.md → CHANGELOG.md} +6 -0
- data/DEVELOPMENT.md +53 -0
- data/Gemfile +1 -1
- data/LICENSE.txt +1 -1
- data/Rakefile +2 -2
- data/bin/console +3 -3
- data/lib/tasks/togls.rake +2 -1
- data/lib/togls/feature.rb +4 -0
- data/lib/togls/feature_repository.rb +10 -6
- data/lib/togls/feature_repository_drivers/in_memory_driver.rb +13 -2
- data/lib/togls/feature_repository_drivers.rb +4 -0
- data/lib/togls/feature_toggle_registry.rb +18 -12
- data/lib/togls/helpers.rb +4 -1
- data/lib/togls/null_toggle.rb +5 -1
- data/lib/togls/release_toggle_registry_manager.rb +43 -0
- data/lib/togls/rule.rb +7 -5
- data/lib/togls/rule_repository.rb +11 -6
- data/lib/togls/rule_repository_drivers/in_memory_driver.rb +14 -3
- data/lib/togls/rule_repository_drivers.rb +4 -1
- data/lib/togls/rules/boolean.rb +16 -2
- data/lib/togls/rules/group.rb +17 -1
- data/lib/togls/rules.rb +4 -0
- data/lib/togls/test_toggle_registry.rb +26 -0
- data/lib/togls/toggle.rb +11 -6
- data/lib/togls/toggle_repository.rb +12 -12
- data/lib/togls/toggle_repository_drivers/env_override_driver.rb +24 -12
- data/lib/togls/toggle_repository_drivers/in_memory_driver.rb +16 -3
- data/lib/togls/toggle_repository_drivers.rb +4 -1
- data/lib/togls/toggler.rb +5 -3
- data/lib/togls/version.rb +1 -1
- data/lib/togls.rb +28 -48
- data/togls.gemspec +20 -14
- metadata +9 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9abbf07a34b200f7000e94608724c8499cd0277a
|
4
|
+
data.tar.gz: 5724c3c1c652bf825a1de22b539b1f848f1d1a11
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a74f6677b8c623e757a32c3bddcc026571485263bbf75ad6ac4c5ab53c22856e26299835d1bf83e7ac6b62bfddea4698754a04ab2738249e477ab7719b16aeab
|
7
|
+
data.tar.gz: c3cd113e76fc0035bd3994684cc84208a69fb9e496a4402dfa23c9c0dc47e510277fa8cb73344c541fd8465c75ac3db03b844ecece48d1f23e40a83d88c72691
|
data/.codeclimate.yml
CHANGED
@@ -1,2 +1,18 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
---
|
2
|
+
engines:
|
3
|
+
duplication:
|
4
|
+
enabled: true
|
5
|
+
config:
|
6
|
+
languages:
|
7
|
+
- ruby
|
8
|
+
rubocop:
|
9
|
+
enabled: true
|
10
|
+
fixme:
|
11
|
+
enabled: true
|
12
|
+
ratings:
|
13
|
+
paths:
|
14
|
+
- lib/**
|
15
|
+
- exe/**
|
16
|
+
exclude_paths:
|
17
|
+
- spec/**/*
|
18
|
+
- docs/**/*
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.3.0
|
data/.travis.yml
CHANGED
@@ -6,6 +6,12 @@ versions, as well as provide a rough history.
|
|
6
6
|
|
7
7
|
#### Next Release
|
8
8
|
|
9
|
+
### v2.2.0
|
10
|
+
|
11
|
+
* Changed Togls to be thread-safe
|
12
|
+
* Added Togls::TestToggleRegistry class for initializing test state
|
13
|
+
* Added ability to create additional toggle registries
|
14
|
+
|
9
15
|
### v2.1.1
|
10
16
|
|
11
17
|
* Fixed issue #21, env variable override wasn't falling through to in
|
data/DEVELOPMENT.md
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
Welcome to the development guide for
|
2
|
+
[Togls](https://github.com/codebreakdown/togls). The hope is that the
|
3
|
+
following will aid in your development contributions to
|
4
|
+
[Togls](https://github.com/codebreakdown/togls).
|
5
|
+
|
6
|
+
# Where should I start?
|
7
|
+
|
8
|
+
We recommend generally that you start with first understanding the
|
9
|
+
existing features and documentation. These can be obtained by
|
10
|
+
referencing the [README](https://github.com/codebreakdown/togls) and the
|
11
|
+
[Wiki](https://github.com/codebreakdown/togls/wiki).
|
12
|
+
|
13
|
+
Once you are well versed in concepts, features, and documentation from
|
14
|
+
an end user standpoint. The next thing we feel is worth exploring is the
|
15
|
+
architecture. We recommend doing this after understanding it from the
|
16
|
+
end user standpoint as it should give you context when learning about
|
17
|
+
the internal concepts.
|
18
|
+
|
19
|
+
## The Architecture
|
20
|
+
|
21
|
+
### Models
|
22
|
+
|
23
|
+
At the core exists three main concepts, the Feature, the Rule, and the
|
24
|
+
Toggle.
|
25
|
+
|
26
|
+
#### Feature
|
27
|
+
|
28
|
+
A Feature is the code that provides some functionality coupled with a
|
29
|
+
unique identifier.
|
30
|
+
|
31
|
+
#### Rule
|
32
|
+
|
33
|
+
A Rule is conceptually the logic used to determine whether or not to
|
34
|
+
toggle a feature on or off. This is generally a Rule class provided by
|
35
|
+
[Togls](https://github.com/codebreakdown/togls/wiki) or a custom Rule
|
36
|
+
class provided by the end user conforming to the Rule interface.
|
37
|
+
|
38
|
+
#### Toggle
|
39
|
+
|
40
|
+
Conceptually, a Toggle is a switch that is used to turn something on or
|
41
|
+
off. In our case it is used to turn a Feature on or off. It does so by
|
42
|
+
consulting an associated Rule to determine if the Toggle should be on or
|
43
|
+
off.
|
44
|
+
|
45
|
+
The Toggle is really the linch pin of these three concepts as it ties
|
46
|
+
all three of them together. Forming, a functional feature toggle.
|
47
|
+
|
48
|
+
### Repositories
|
49
|
+
|
50
|
+
In addition to the three model concepts from above. We also have
|
51
|
+
Repositories for each of the concepts respectively. They are responsible
|
52
|
+
for managing and separating the business representations from the
|
53
|
+
persistance layers.
|
data/Gemfile
CHANGED
data/LICENSE.txt
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
|
-
Copyright (c) 2015 Brian Miller, Andrew De Ponte, Ryan Hedges
|
3
|
+
Copyright (c) 2015-2016 Brian Miller, Andrew De Ponte, Ryan Hedges
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
data/Rakefile
CHANGED
data/bin/console
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'togls'
|
5
5
|
|
6
6
|
# You can add fixtures and/or initialization code here to make experimenting
|
7
7
|
# with your gem easier. You can also use a different console, if you like.
|
@@ -10,5 +10,5 @@ require "togls"
|
|
10
10
|
# require "pry"
|
11
11
|
# Pry.start
|
12
12
|
|
13
|
-
require
|
13
|
+
require 'irb'
|
14
14
|
IRB.start
|
data/lib/tasks/togls.rake
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
namespace :togls do
|
2
|
-
desc
|
2
|
+
desc 'Output all features including status (on, off, ? - unknown due to' \
|
3
|
+
' complex rule), key, description'
|
3
4
|
task :features do
|
4
5
|
Togls.features.all.each do |toggle|
|
5
6
|
puts toggle.to_s
|
data/lib/togls/feature.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
module Togls
|
2
|
+
# Feature Repository
|
3
|
+
#
|
4
|
+
# The Feature Repository is the primary interface for storing and retrieving
|
5
|
+
# feature entities using the initialized prioritized drivers Array.
|
2
6
|
class FeatureRepository
|
3
7
|
def initialize(drivers)
|
4
|
-
|
5
|
-
raise Togls::InvalidDriver
|
8
|
+
unless drivers.is_a?(Array)
|
9
|
+
raise Togls::InvalidDriver, 'FeatureRepository requires a valid driver'
|
6
10
|
end
|
7
11
|
if drivers.empty?
|
8
|
-
raise Togls::MissingDriver
|
12
|
+
raise Togls::MissingDriver, 'FeatureRepository requires a driver'
|
9
13
|
end
|
10
14
|
@drivers = drivers
|
11
15
|
end
|
@@ -18,7 +22,7 @@ module Togls
|
|
18
22
|
end
|
19
23
|
|
20
24
|
def extract_feature_data(feature)
|
21
|
-
{
|
25
|
+
{ 'key' => feature.key, 'description' => feature.description }
|
22
26
|
end
|
23
27
|
|
24
28
|
def fetch_feature_data(id)
|
@@ -36,8 +40,8 @@ module Togls
|
|
36
40
|
end
|
37
41
|
|
38
42
|
def reconstitute_feature(feature_data)
|
39
|
-
Togls::Feature.new(feature_data[
|
40
|
-
feature_data[
|
43
|
+
Togls::Feature.new(feature_data['key'],
|
44
|
+
feature_data['description'])
|
41
45
|
end
|
42
46
|
end
|
43
47
|
end
|
@@ -1,16 +1,27 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
1
3
|
module Togls
|
2
4
|
module FeatureRepositoryDrivers
|
5
|
+
# Feature Repository In-Memory Driver
|
6
|
+
#
|
7
|
+
# The Feature Repository In-Memory Driver provides the facility to store and
|
8
|
+
# retrieve features to and from the in-memory store.
|
3
9
|
class InMemoryDriver
|
4
10
|
def initialize
|
5
11
|
@features = {}
|
12
|
+
@features_lock = Mutex.new
|
6
13
|
end
|
7
14
|
|
8
15
|
def store(feature_id, feature_data)
|
9
|
-
@
|
16
|
+
@features_lock.synchronize do
|
17
|
+
@features[feature_id] = feature_data
|
18
|
+
end
|
10
19
|
end
|
11
20
|
|
12
21
|
def get(feature_id)
|
13
|
-
@
|
22
|
+
@features_lock.synchronize do
|
23
|
+
@features[feature_id]
|
24
|
+
end
|
14
25
|
end
|
15
26
|
end
|
16
27
|
end
|
@@ -1,4 +1,11 @@
|
|
1
1
|
module Togls
|
2
|
+
# Feature Toggle Registry
|
3
|
+
#
|
4
|
+
# The Feature Toggle Registry conceptually houses a registry of toggles. It
|
5
|
+
# accomplishes this by technically housing a toggle repository, rule
|
6
|
+
# repository, and feature repository which is uses to store and retrieve the
|
7
|
+
# respective entities. This plays a significant portion in the primary DSL as
|
8
|
+
# well.
|
2
9
|
class FeatureToggleRegistry
|
3
10
|
def initialize
|
4
11
|
@toggle_repository_drivers = [
|
@@ -8,25 +15,24 @@ module Togls
|
|
8
15
|
[Togls::FeatureRepositoryDrivers::InMemoryDriver.new]
|
9
16
|
@rule_repository_drivers =
|
10
17
|
[Togls::RuleRepositoryDrivers::InMemoryDriver.new]
|
11
|
-
@feature_repository = Togls::FeatureRepository.new(
|
18
|
+
@feature_repository = Togls::FeatureRepository.new(
|
19
|
+
@feature_repository_drivers)
|
12
20
|
@rule_repository = Togls::RuleRepository.new(@rule_repository_drivers)
|
13
|
-
@toggle_repository = Togls::ToggleRepository.new(
|
14
|
-
|
15
|
-
@
|
16
|
-
@
|
17
|
-
@rule_repository.store(@boolean_false_rule)
|
18
|
-
@rule_repository.store(@boolean_true_rule)
|
21
|
+
@toggle_repository = Togls::ToggleRepository.new(
|
22
|
+
@toggle_repository_drivers, @feature_repository, @rule_repository)
|
23
|
+
@rule_repository.store(Togls::Rules::Boolean.new(true))
|
24
|
+
@rule_repository.store(Togls::Rules::Boolean.new(false))
|
19
25
|
end
|
20
26
|
|
21
27
|
def self.create(&block)
|
22
|
-
feature_toggle_registry =
|
28
|
+
feature_toggle_registry = new
|
23
29
|
feature_toggle_registry.instance_eval(&block)
|
24
|
-
|
30
|
+
feature_toggle_registry
|
25
31
|
end
|
26
32
|
|
27
33
|
def expand(&block)
|
28
|
-
|
29
|
-
|
34
|
+
instance_eval(&block)
|
35
|
+
self
|
30
36
|
end
|
31
37
|
|
32
38
|
def feature(key, desc)
|
@@ -41,7 +47,7 @@ module Togls
|
|
41
47
|
if toggle.is_a?(Togls::NullToggle)
|
42
48
|
Togls.logger.warn("Feature identified by '#{key}' has not been defined")
|
43
49
|
end
|
44
|
-
|
50
|
+
toggle
|
45
51
|
end
|
46
52
|
|
47
53
|
def all
|
data/lib/togls/helpers.rb
CHANGED
data/lib/togls/null_toggle.rb
CHANGED
@@ -1,7 +1,11 @@
|
|
1
1
|
module Togls
|
2
|
+
# Null Toggle
|
3
|
+
#
|
4
|
+
# The Null Toggle model is designed to be used as a response when there is no
|
5
|
+
# toggle found when requested to be retreived through a Toggle Repository.
|
2
6
|
class NullToggle < Togls::Toggle
|
3
7
|
def initialize
|
4
|
-
feature = Togls::Feature.new(
|
8
|
+
feature = Togls::Feature.new('null', 'the official null feature')
|
5
9
|
super(feature)
|
6
10
|
end
|
7
11
|
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Togls
|
2
|
+
# Release Toggle Registry Manager
|
3
|
+
#
|
4
|
+
# This is the primary DSL interface. It provides a DSL to facilitate housing
|
5
|
+
# and managing a toggle registry.
|
6
|
+
module ReleaseToggleRegistryManager
|
7
|
+
def self.included(mod)
|
8
|
+
mod.extend(ClassMethods)
|
9
|
+
end
|
10
|
+
|
11
|
+
# Release Toggle Registry Manager Class Methods
|
12
|
+
#
|
13
|
+
# The class methods that should be extended onto the module/class when
|
14
|
+
# ReleaseToggleRegistryManager is included.
|
15
|
+
module ClassMethods
|
16
|
+
def features(&block)
|
17
|
+
if @feature_toggle_registry.nil?
|
18
|
+
@feature_toggle_registry = FeatureToggleRegistry.new
|
19
|
+
end
|
20
|
+
|
21
|
+
@feature_toggle_registry.expand(&block) if block
|
22
|
+
|
23
|
+
@feature_toggle_registry
|
24
|
+
end
|
25
|
+
|
26
|
+
def features=(feature_toggle_registry)
|
27
|
+
@feature_toggle_registry = feature_toggle_registry
|
28
|
+
end
|
29
|
+
|
30
|
+
def feature(key)
|
31
|
+
if @feature_toggle_registry.nil?
|
32
|
+
@feature_toggle_registry = FeatureToggleRegistry.new
|
33
|
+
end
|
34
|
+
|
35
|
+
@feature_toggle_registry.get(key)
|
36
|
+
end
|
37
|
+
|
38
|
+
def logger
|
39
|
+
@logger ||= Logger.new(STDOUT)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/togls/rule.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
module Togls
|
2
|
+
# Rule
|
3
|
+
#
|
4
|
+
# The Rule is an abstract base class that is intended to act as an interface
|
5
|
+
# for other rules to be implemented against.
|
2
6
|
class Rule
|
3
7
|
attr_reader :data
|
4
8
|
|
5
|
-
def initialize(data=nil)
|
6
|
-
@data = data
|
9
|
+
def initialize(data = nil)
|
10
|
+
@data = data
|
7
11
|
end
|
8
12
|
|
9
13
|
def run(key, target = nil)
|
10
|
-
raise Togls::NotImplemented
|
11
|
-
"Rule's #run method must be implemented"
|
12
|
-
)
|
14
|
+
raise Togls::NotImplemented, "Rule's #run method must be implemented"
|
13
15
|
end
|
14
16
|
|
15
17
|
def id
|
@@ -1,11 +1,16 @@
|
|
1
1
|
module Togls
|
2
|
+
# Rule Repository
|
3
|
+
#
|
4
|
+
# The Rule Repository is the intended interface to store and retrieve rules.
|
5
|
+
# It does these by interfacing with Rule Repository Drivers which are passed
|
6
|
+
# in during construction as an Array.
|
2
7
|
class RuleRepository
|
3
8
|
def initialize(drivers)
|
4
|
-
|
5
|
-
raise Togls::InvalidDriver
|
9
|
+
unless drivers.is_a?(Array)
|
10
|
+
raise Togls::InvalidDriver, 'RuleRepository requires a valid driver'
|
6
11
|
end
|
7
12
|
if drivers.empty?
|
8
|
-
raise Togls::MissingDriver
|
13
|
+
raise Togls::MissingDriver, 'RuleRepository requires a driver'
|
9
14
|
end
|
10
15
|
@drivers = drivers
|
11
16
|
end
|
@@ -16,9 +21,9 @@ module Togls
|
|
16
21
|
driver.store(rule.id, rule_data)
|
17
22
|
end
|
18
23
|
end
|
19
|
-
|
24
|
+
|
20
25
|
def extract_storage_payload(rule)
|
21
|
-
|
26
|
+
{ 'klass' => rule.class, 'data' => rule.data }
|
22
27
|
end
|
23
28
|
|
24
29
|
def fetch_rule_data(id)
|
@@ -36,7 +41,7 @@ module Togls
|
|
36
41
|
end
|
37
42
|
|
38
43
|
def reconstitute_rule(rule_data)
|
39
|
-
rule_data[
|
44
|
+
rule_data['klass'].new(rule_data['data'])
|
40
45
|
end
|
41
46
|
end
|
42
47
|
end
|
@@ -1,16 +1,27 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
1
3
|
module Togls
|
2
4
|
module RuleRepositoryDrivers
|
5
|
+
# Rule Repository In-Memory Driver
|
6
|
+
#
|
7
|
+
# The Rule Repository In-Memory Driver provides the interface to store and
|
8
|
+
# retrieve rules. This is intended to be used by a Rule Repository instance.
|
3
9
|
class InMemoryDriver
|
4
10
|
def initialize
|
5
11
|
@rules = {}
|
12
|
+
@rules_lock = Mutex.new
|
6
13
|
end
|
7
14
|
|
8
15
|
def store(rule_id, rule_data)
|
9
|
-
@
|
16
|
+
@rules_lock.synchronize do
|
17
|
+
@rules[rule_id] = rule_data
|
18
|
+
end
|
10
19
|
end
|
11
|
-
|
20
|
+
|
12
21
|
def get(rule_id)
|
13
|
-
@
|
22
|
+
@rules_lock.synchronize do
|
23
|
+
@rules[rule_id]
|
24
|
+
end
|
14
25
|
end
|
15
26
|
end
|
16
27
|
end
|
data/lib/togls/rules/boolean.rb
CHANGED
@@ -1,8 +1,22 @@
|
|
1
1
|
module Togls
|
2
2
|
module Rules
|
3
|
+
# Boolean Rule
|
4
|
+
#
|
5
|
+
# The Boolean Rule is a provided Rule that expects to be given a boolean as
|
6
|
+
# it's initialization data and when evaluated determines the toggle state
|
7
|
+
# based on the initialization value. Example:
|
8
|
+
#
|
9
|
+
# always_on = Togls::Rules::Boolean.new(true)
|
10
|
+
# Togls.features do
|
11
|
+
# feature(:foo).on(always_on)
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# if Togls.feature(:foo).on?
|
15
|
+
# ...
|
16
|
+
# end
|
3
17
|
class Boolean < Rule
|
4
|
-
def run(
|
5
|
-
|
18
|
+
def run(_key, _target = nil)
|
19
|
+
@data
|
6
20
|
end
|
7
21
|
end
|
8
22
|
end
|
data/lib/togls/rules/group.rb
CHANGED
@@ -1,7 +1,23 @@
|
|
1
1
|
module Togls
|
2
2
|
module Rules
|
3
|
+
# Group Rule
|
4
|
+
#
|
5
|
+
# The Group Rule is a provided Rule that expects to be given an Array as
|
6
|
+
# it's initialization data and when evaluated determines the toggle state
|
7
|
+
# based on the given target being included in the Array that was passed in
|
8
|
+
# during initialization. This allows you to define various groups. For
|
9
|
+
# example:
|
10
|
+
#
|
11
|
+
# alpha_testers = Togls::Rules::Group.new(['bob@ex.com', 'cindy@ex.com'])
|
12
|
+
# Togls.features do
|
13
|
+
# feature(:foo).on(alpha_testers)
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# if Togls.feature(:foo).on?(current_user.email)
|
17
|
+
# ...
|
18
|
+
# end
|
3
19
|
class Group < Rule
|
4
|
-
def run(
|
20
|
+
def run(_key, target)
|
5
21
|
@data.include?(target)
|
6
22
|
end
|
7
23
|
end
|
data/lib/togls/rules.rb
CHANGED
@@ -0,0 +1,26 @@
|
|
1
|
+
module Togls
|
2
|
+
# Test Toggle Registry
|
3
|
+
#
|
4
|
+
# The Test Toggle Registry is a toggle feature toggle registry specifically
|
5
|
+
# intended to be used in the use case of automated test suites. The difference
|
6
|
+
# between this toggle registry and the normal FeatureToggleRegistry is that
|
7
|
+
# this registry only uses in-memory drivers and the FeatureToggleRegistry uses
|
8
|
+
# in-memory drivers as well as the environment override drivers.
|
9
|
+
class TestToggleRegistry < FeatureToggleRegistry
|
10
|
+
def initialize
|
11
|
+
@toggle_repository_drivers = [
|
12
|
+
Togls::ToggleRepositoryDrivers::InMemoryDriver.new]
|
13
|
+
@feature_repository_drivers =
|
14
|
+
[Togls::FeatureRepositoryDrivers::InMemoryDriver.new]
|
15
|
+
@rule_repository_drivers =
|
16
|
+
[Togls::RuleRepositoryDrivers::InMemoryDriver.new]
|
17
|
+
@feature_repository = Togls::FeatureRepository.new(
|
18
|
+
@feature_repository_drivers)
|
19
|
+
@rule_repository = Togls::RuleRepository.new(@rule_repository_drivers)
|
20
|
+
@toggle_repository = Togls::ToggleRepository.new(
|
21
|
+
@toggle_repository_drivers, @feature_repository, @rule_repository)
|
22
|
+
@rule_repository.store(Togls::Rules::Boolean.new(true))
|
23
|
+
@rule_repository.store(Togls::Rules::Boolean.new(false))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/togls/toggle.rb
CHANGED
@@ -1,4 +1,9 @@
|
|
1
1
|
module Togls
|
2
|
+
# Toggle
|
3
|
+
#
|
4
|
+
# The model representing a Toggle within the world of Togls. A Toggle's
|
5
|
+
# responsibility is binding a specific rule to a specific feature. Toggle's by
|
6
|
+
# default are associated with a boolean rule initialized to false.
|
2
7
|
class Toggle
|
3
8
|
attr_reader :feature
|
4
9
|
attr_accessor :rule
|
@@ -17,15 +22,15 @@ module Togls
|
|
17
22
|
end
|
18
23
|
|
19
24
|
def off?(target = nil)
|
20
|
-
|
25
|
+
!@rule.run(@feature.key, target)
|
21
26
|
end
|
22
27
|
|
23
28
|
def to_s
|
24
|
-
if @rule.is_a?(Togls::Rules::Boolean)
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
+
display_value = if @rule.is_a?(Togls::Rules::Boolean)
|
30
|
+
@rule.run(@feature.key) ? ' on' : 'off'
|
31
|
+
else
|
32
|
+
' ?'
|
33
|
+
end
|
29
34
|
|
30
35
|
"#{display_value} - #{@feature.key} - #{@feature.description}"
|
31
36
|
end
|
@@ -1,11 +1,14 @@
|
|
1
1
|
module Togls
|
2
|
+
# Toggle Repository
|
3
|
+
#
|
4
|
+
# Repository interface for storing and retrieving toggles.
|
2
5
|
class ToggleRepository
|
3
6
|
def initialize(drivers, feature_repository, rule_repository)
|
4
|
-
|
5
|
-
raise Togls::InvalidDriver
|
7
|
+
unless drivers.is_a?(Array)
|
8
|
+
raise Togls::InvalidDriver, 'ToggleRepository requires a valid driver'
|
6
9
|
end
|
7
10
|
if drivers.empty?
|
8
|
-
raise Togls::MissingDriver
|
11
|
+
raise Togls::MissingDriver, 'ToggleRepository requires a driver'
|
9
12
|
end
|
10
13
|
@drivers = drivers
|
11
14
|
@feature_repository = feature_repository
|
@@ -23,24 +26,21 @@ module Togls
|
|
23
26
|
end
|
24
27
|
|
25
28
|
def extract_storage_payload(toggle)
|
26
|
-
{
|
29
|
+
{ 'feature_id' => toggle.feature.id, 'rule_id' => toggle.rule.id }
|
27
30
|
end
|
28
31
|
|
29
32
|
def get(id)
|
30
33
|
toggle_data = fetch_toggle_data(id)
|
31
|
-
if toggle_data
|
32
|
-
|
33
|
-
else
|
34
|
-
return Togls::NullToggle.new
|
35
|
-
end
|
34
|
+
return reconstitute_toggle(toggle_data) if toggle_data
|
35
|
+
Togls::NullToggle.new
|
36
36
|
end
|
37
37
|
|
38
38
|
def reconstitute_toggle(toggle_data)
|
39
|
-
feature = @feature_repository.get(toggle_data[
|
40
|
-
rule = @rule_repository.get(toggle_data[
|
39
|
+
feature = @feature_repository.get(toggle_data['feature_id'])
|
40
|
+
rule = @rule_repository.get(toggle_data['rule_id'])
|
41
41
|
toggle = Togls::Toggle.new(feature)
|
42
42
|
toggle.rule = rule
|
43
|
-
|
43
|
+
toggle
|
44
44
|
end
|
45
45
|
|
46
46
|
def fetch_toggle_data(id)
|
@@ -1,33 +1,45 @@
|
|
1
1
|
module Togls
|
2
2
|
module ToggleRepositoryDrivers
|
3
|
+
# Toggle Repository Environment Override Driver
|
4
|
+
#
|
5
|
+
# The Toggle Repository Environment Override Driver provides a Toggle
|
6
|
+
# Repository driver that passively ignores requests to store toggles but
|
7
|
+
# still responds to retrieval requests. This conceptually makes it what I am
|
8
|
+
# referring to as an "Override Driver" because it only allows retrieving
|
9
|
+
# overrides from the store.
|
10
|
+
#
|
11
|
+
# In this particular case it is an Environment Override Driver. Therefore,
|
12
|
+
# the store that backs this driver is environment variables. Specifically,
|
13
|
+
# this driver would retrieve a boolean rule initialized true if the
|
14
|
+
# associated environment variable was equal to the string, 'true'. If the
|
15
|
+
# associated environment variable was equal to the string, 'false', it would
|
16
|
+
# return a boolean rule initialized to false. Any other value and the driver
|
17
|
+
# returns nil indicating that it couldn't find the toggle in the store.
|
3
18
|
class EnvOverrideDriver
|
4
19
|
def store(toggle_id, toggle_data)
|
5
20
|
end
|
6
21
|
|
7
22
|
def get(toggle_id)
|
8
|
-
if ENV[toggle_env_key(toggle_id)]
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
else
|
16
|
-
return nil
|
17
|
-
end
|
23
|
+
return nil if ENV[toggle_env_key(toggle_id)].nil?
|
24
|
+
if ENV[toggle_env_key(toggle_id)] == 'true'
|
25
|
+
return { 'feature_id' => toggle_id, 'rule_id' => Togls::Helpers.sha1(
|
26
|
+
Togls::Rules::Boolean, true) }
|
27
|
+
elsif ENV[toggle_env_key(toggle_id)] == 'false'
|
28
|
+
return { 'feature_id' => toggle_id, 'rule_id' => Togls::Helpers.sha1(
|
29
|
+
Togls::Rules::Boolean, false) }
|
18
30
|
else
|
19
31
|
return nil
|
20
32
|
end
|
21
33
|
end
|
22
34
|
|
23
35
|
def all
|
24
|
-
|
36
|
+
{}
|
25
37
|
end
|
26
38
|
|
27
39
|
private
|
28
40
|
|
29
41
|
def toggle_env_key(toggle_id)
|
30
|
-
|
42
|
+
"TOGLS_#{toggle_id.to_s.upcase}"
|
31
43
|
end
|
32
44
|
end
|
33
45
|
end
|
@@ -1,20 +1,33 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
1
3
|
module Togls
|
2
4
|
module ToggleRepositoryDrivers
|
5
|
+
# Toggle Repositoy In-Memory Driver
|
6
|
+
#
|
7
|
+
# The Toggle Repository In-Memory Driver provides the interface to store and
|
8
|
+
# retrieve toggles from the in-memory store.
|
3
9
|
class InMemoryDriver
|
4
10
|
def initialize
|
5
11
|
@toggles = {}
|
12
|
+
@toggles_lock = Mutex.new
|
6
13
|
end
|
7
14
|
|
8
15
|
def store(toggle_id, toggle_data)
|
9
|
-
@
|
16
|
+
@toggles_lock.synchronize do
|
17
|
+
@toggles[toggle_id] = toggle_data
|
18
|
+
end
|
10
19
|
end
|
11
20
|
|
12
21
|
def get(toggle_id)
|
13
|
-
@
|
22
|
+
@toggles_lock.synchronize do
|
23
|
+
@toggles[toggle_id]
|
24
|
+
end
|
14
25
|
end
|
15
26
|
|
16
27
|
def all
|
17
|
-
@
|
28
|
+
@toggles_lock.synchronize do
|
29
|
+
@toggles
|
30
|
+
end
|
18
31
|
end
|
19
32
|
end
|
20
33
|
end
|
data/lib/togls/toggler.rb
CHANGED
@@ -1,4 +1,8 @@
|
|
1
1
|
module Togls
|
2
|
+
# Toggle Toggler
|
3
|
+
#
|
4
|
+
# The Toggle Toggler provides the convenience interface of being able to
|
5
|
+
# toggle a feature on/off via the `on` or `off` methods respectively.
|
2
6
|
class Toggler
|
3
7
|
def initialize(toggle_repository, toggle)
|
4
8
|
@toggle_repository = toggle_repository
|
@@ -6,9 +10,7 @@ module Togls
|
|
6
10
|
end
|
7
11
|
|
8
12
|
def on(rule = nil)
|
9
|
-
if rule.nil?
|
10
|
-
rule = Togls::Rules::Boolean.new(true)
|
11
|
-
end
|
13
|
+
rule = Togls::Rules::Boolean.new(true) if rule.nil?
|
12
14
|
@toggle.rule = rule
|
13
15
|
@toggle_repository.store(@toggle)
|
14
16
|
@toggle
|
data/lib/togls/version.rb
CHANGED
data/lib/togls.rb
CHANGED
@@ -1,51 +1,31 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
21
|
-
require
|
1
|
+
require 'togls/version'
|
2
|
+
require 'togls/errors'
|
3
|
+
require 'togls/helpers'
|
4
|
+
require 'togls/toggle_repository_drivers'
|
5
|
+
require 'togls/toggle_repository_drivers/in_memory_driver'
|
6
|
+
require 'togls/toggle_repository_drivers/env_override_driver'
|
7
|
+
require 'togls/feature_repository_drivers'
|
8
|
+
require 'togls/feature_repository_drivers/in_memory_driver'
|
9
|
+
require 'togls/rule_repository_drivers'
|
10
|
+
require 'togls/rule_repository_drivers/in_memory_driver'
|
11
|
+
require 'togls/toggler'
|
12
|
+
require 'togls/feature_toggle_registry'
|
13
|
+
require 'togls/test_toggle_registry'
|
14
|
+
require 'togls/feature_repository'
|
15
|
+
require 'togls/rule_repository'
|
16
|
+
require 'togls/toggle_repository'
|
17
|
+
require 'togls/feature'
|
18
|
+
require 'togls/toggle'
|
19
|
+
require 'togls/null_toggle'
|
20
|
+
require 'togls/rule'
|
21
|
+
require 'togls/rules'
|
22
|
+
require 'logger'
|
23
|
+
require 'togls/release_toggle_registry_manager'
|
22
24
|
|
25
|
+
# Togls
|
26
|
+
#
|
27
|
+
# Togls is the primary interface to the out of the box toggle registry. It is
|
28
|
+
# the namespace the DSL is exposed under.
|
23
29
|
module Togls
|
24
|
-
|
25
|
-
if @feature_toggle_registry.nil?
|
26
|
-
@feature_toggle_registry = FeatureToggleRegistry.new
|
27
|
-
end
|
28
|
-
|
29
|
-
if block
|
30
|
-
@feature_toggle_registry.expand(&block)
|
31
|
-
end
|
32
|
-
|
33
|
-
return @feature_toggle_registry
|
34
|
-
end
|
35
|
-
|
36
|
-
def self.features=(feature_toggle_registry)
|
37
|
-
@feature_toggle_registry = feature_toggle_registry
|
38
|
-
end
|
39
|
-
|
40
|
-
def self.feature(key)
|
41
|
-
if @feature_toggle_registry.nil?
|
42
|
-
@feature_toggle_registry = FeatureToggleRegistry.new
|
43
|
-
end
|
44
|
-
|
45
|
-
return @feature_toggle_registry.get(key)
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.logger
|
49
|
-
@logger ||= Logger.new(STDOUT)
|
50
|
-
end
|
30
|
+
include ReleaseToggleRegistryManager
|
51
31
|
end
|
data/togls.gemspec
CHANGED
@@ -4,23 +4,29 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
require 'togls/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
7
|
+
spec.name = 'togls'
|
8
8
|
spec.version = Togls::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
9
|
+
spec.authors = ['Brian Miller', 'Andrew DePonte', 'Ryan Hedges']
|
10
|
+
spec.email = ['brimil01@gmail.com', 'cyphactor@gmail.com',
|
11
|
+
'ryanhedges@gmail.com']
|
11
12
|
|
12
|
-
spec.summary =
|
13
|
-
|
14
|
-
spec.
|
15
|
-
|
13
|
+
spec.summary = 'An ultra light weight yet ridiculously powerful' \
|
14
|
+
'ruby feature toggle gem.'
|
15
|
+
spec.description = 'An ultra light weight yet ridiculously powerful' \
|
16
|
+
' ruby feature toggle gem that supports the concept' \
|
17
|
+
' of release toggles and business toggles.'
|
18
|
+
spec.homepage = 'http://github.com/codebreakdown/togls'
|
19
|
+
spec.license = 'MIT'
|
16
20
|
|
17
|
-
spec.files
|
18
|
-
|
21
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
22
|
+
f.match(%r{^(test|spec|features)/})
|
23
|
+
end
|
24
|
+
spec.bindir = 'exe'
|
19
25
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
|
-
spec.require_paths = [
|
26
|
+
spec.require_paths = ['lib']
|
21
27
|
|
22
|
-
spec.add_development_dependency
|
23
|
-
spec.add_development_dependency
|
24
|
-
spec.add_development_dependency
|
25
|
-
spec.add_development_dependency
|
28
|
+
spec.add_development_dependency 'bundler', '~> 1.9'
|
29
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
30
|
+
spec.add_development_dependency 'rspec', '~> 3.2'
|
31
|
+
spec.add_development_dependency 'pry', '~> 0.10'
|
26
32
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: togls
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Miller
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2016-03-04 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: bundler
|
@@ -83,8 +83,9 @@ files:
|
|
83
83
|
- ".rspec"
|
84
84
|
- ".ruby-version"
|
85
85
|
- ".travis.yml"
|
86
|
+
- CHANGELOG.md
|
86
87
|
- CODE_OF_CONDUCT.md
|
87
|
-
-
|
88
|
+
- DEVELOPMENT.md
|
88
89
|
- Gemfile
|
89
90
|
- LICENSE.txt
|
90
91
|
- README.md
|
@@ -102,6 +103,7 @@ files:
|
|
102
103
|
- lib/togls/feature_toggle_registry.rb
|
103
104
|
- lib/togls/helpers.rb
|
104
105
|
- lib/togls/null_toggle.rb
|
106
|
+
- lib/togls/release_toggle_registry_manager.rb
|
105
107
|
- lib/togls/rule.rb
|
106
108
|
- lib/togls/rule_repository.rb
|
107
109
|
- lib/togls/rule_repository_drivers.rb
|
@@ -109,6 +111,7 @@ files:
|
|
109
111
|
- lib/togls/rules.rb
|
110
112
|
- lib/togls/rules/boolean.rb
|
111
113
|
- lib/togls/rules/group.rb
|
114
|
+
- lib/togls/test_toggle_registry.rb
|
112
115
|
- lib/togls/toggle.rb
|
113
116
|
- lib/togls/toggle_repository.rb
|
114
117
|
- lib/togls/toggle_repository_drivers.rb
|
@@ -137,8 +140,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
137
140
|
version: '0'
|
138
141
|
requirements: []
|
139
142
|
rubyforge_project:
|
140
|
-
rubygems_version: 2.
|
143
|
+
rubygems_version: 2.5.1
|
141
144
|
signing_key:
|
142
145
|
specification_version: 4
|
143
|
-
summary: An ultra light weight yet ridiculously
|
146
|
+
summary: An ultra light weight yet ridiculously powerfulruby feature toggle gem.
|
144
147
|
test_files: []
|
148
|
+
has_rdoc:
|