togls 2.1.1 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|