togls 0.1.0 → 1.0.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/.gitignore +1 -0
- data/ChangeLog.md +6 -0
- data/README.md +178 -10
- data/lib/tasks/togls.rake +8 -0
- data/lib/togls/errors.rb +3 -0
- data/lib/togls/feature.rb +17 -4
- data/lib/togls/feature_registry.rb +7 -3
- data/lib/togls/rules/boolean.rb +13 -0
- data/lib/togls/rules.rb +1 -0
- data/lib/togls/version.rb +1 -1
- data/lib/togls.rb +10 -1
- data/togls.gemspec +1 -1
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f956666596995d557aa56048b785db8f490f73dc
|
4
|
+
data.tar.gz: ffa83b7c726ef586d20c169c6cf3d3189ee8207c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50b448deb27e15b7815cafa0e02d366a01643038ca1a689695547e48234f0a2aafe5161cea5a9c9461d7b179022dd2cd22d8b8080756f3c62d760220a92e20f9
|
7
|
+
data.tar.gz: 00963574a1dbef269a279ae8b42294a11042a194f80b366f38b9136a5d23e4eba23a634d16705b1f8990ad9c4f56821db4bb754b8d3d05aba9ded8d8da6ba6a9
|
data/.gitignore
CHANGED
data/ChangeLog.md
CHANGED
@@ -6,6 +6,12 @@ versions, as well as provide a rough history.
|
|
6
6
|
|
7
7
|
#### Next Release
|
8
8
|
|
9
|
+
#### v1.0.0
|
10
|
+
|
11
|
+
* Require human readable description to define a feature toggle
|
12
|
+
* Add rake task that outputs all the feature toggles states (on, off, ? -
|
13
|
+
unknown due to Compex Rule), keys, and human readable descritpions
|
14
|
+
|
9
15
|
#### v0.1.0
|
10
16
|
|
11
17
|
* Add concept of Groups as a provided rule
|
data/README.md
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
[](https://rubygems.org/gems/togls)
|
1
2
|
[](https://travis-ci.org/codebreakdown/togls)
|
2
3
|
[](https://codeclimate.com/github/codebreakdown/togls)
|
3
4
|
[![Code
|
@@ -24,35 +25,150 @@ Or install it yourself as:
|
|
24
25
|
|
25
26
|
$ gem install togls
|
26
27
|
|
27
|
-
## Usage
|
28
|
+
## Basic Usage
|
28
29
|
|
29
|
-
|
30
|
+
The basic usage of `Togls` is outlined below.
|
30
31
|
|
31
|
-
|
32
|
+
### Defining Feature Toggles
|
33
|
+
|
34
|
+
The first thing to do to use `Togls` is to define your feature toggles. The
|
35
|
+
following is an example of how you might define your feature toggles. It is
|
36
|
+
recommended this live in its own file. In Rails projects we recommend putting
|
37
|
+
it in `config/initializers/togls_features.rb`.
|
32
38
|
|
33
39
|
```ruby
|
34
40
|
Togls.features do
|
35
41
|
# Set this feature to always be on
|
36
|
-
feature(:pop_up_login_form).on
|
42
|
+
feature(:pop_up_login_form, "use the pop up login instead of normal login").on
|
37
43
|
# Set this feature to always be off
|
38
|
-
feature(:send_follup_email).off
|
44
|
+
feature(:send_follup_email, "send the follow up email").off
|
39
45
|
# Create a group rule
|
40
46
|
rule = Togls::Rules::Group.new(["user@email.com"])
|
41
|
-
feature(:new_contact_form).on(rule)
|
47
|
+
feature(:new_contact_form, "use new contact form instead of old one").on(rule)
|
42
48
|
end
|
43
49
|
```
|
44
50
|
|
45
|
-
###
|
51
|
+
### Evaluating Feature Toggles
|
52
|
+
|
53
|
+
Once you have defined your feature toggles. The next thing you would likely
|
54
|
+
want to do is conditionally control something based on them. The following are
|
55
|
+
a few examples of how you would do this given the above.
|
46
56
|
|
47
57
|
```ruby
|
58
|
+
if Togls.feature(:pop_up_login_form).on?
|
59
|
+
# Use pop up login form
|
60
|
+
else
|
61
|
+
# Use normal non-pop up login form
|
62
|
+
end
|
63
|
+
|
64
|
+
if Togls.feature(:send_follup_email).on?
|
65
|
+
# send the follow up email
|
66
|
+
end
|
67
|
+
|
48
68
|
if Togls.feature(:new_contact_form).on?("user@email.com")
|
49
|
-
#
|
69
|
+
# Use new contact form
|
70
|
+
else
|
71
|
+
# Use old contact form
|
72
|
+
end
|
73
|
+
```
|
74
|
+
|
75
|
+
**Note:** The default behaviour for any feature that has not been defined that
|
76
|
+
is accessed is to default to false.
|
77
|
+
|
78
|
+
### Output Feature Toggles
|
79
|
+
|
80
|
+
One other use case that we support as part of the *Basic Usage* is outputing all
|
81
|
+
of the features in your system and their respective, **states** (`on`, `off`, `?` -
|
82
|
+
unkown due to *Complex Rule*), **key**, and **human readable descrption**.
|
83
|
+
|
84
|
+
We provide this functionality via a [rake](https://github.com/ruby/rake) task.
|
85
|
+
|
86
|
+
#### Load rake task
|
87
|
+
|
88
|
+
To use it you must first load the provided
|
89
|
+
[rake](https://github.com/ruby/rake) file. This can be done in a number of
|
90
|
+
different ways.
|
91
|
+
|
92
|
+
##### Load rake task in another gem
|
93
|
+
|
94
|
+
To use it in another gem you can use the following in the second gem's
|
95
|
+
`Rakefile`.
|
96
|
+
|
97
|
+
```ruby
|
98
|
+
spec = Gem::Specification.find_by_name 'togls'
|
99
|
+
load "#{spec.gem_dir}/lib/tasks/togls.rake"
|
100
|
+
```
|
101
|
+
|
102
|
+
**Note:** The features must be defined and loaded before calling this task or
|
103
|
+
it will error out informing you that you need to define your features first.
|
104
|
+
|
105
|
+
##### Load rake task in a Rails app
|
106
|
+
|
107
|
+
To use the it in a Rails app you can do so by adding the following to the
|
108
|
+
`Rakefile`.
|
109
|
+
|
110
|
+
```ruby
|
111
|
+
namespace :togls do
|
112
|
+
task :features => [:environment] do
|
113
|
+
end
|
50
114
|
end
|
115
|
+
|
116
|
+
spec = Gem::Specification.find_by_name 'togls'
|
117
|
+
load "#{spec.gem_dir}/lib/tasks/togls.rake"
|
118
|
+
```
|
119
|
+
|
120
|
+
**Note:** The first hunk where it defines an empty `togls:features` tasks is
|
121
|
+
important in Rails because it takes advantage of
|
122
|
+
[rake](https://github.com/ruby/rake) task stacking and calls out the Rails
|
123
|
+
environment as a dependency. That way before the `togls:feature` task is
|
124
|
+
executed the `config/initializers/togls_features.rb` file is loaded which
|
125
|
+
defines the feature toggles.
|
126
|
+
|
127
|
+
#### Verify rake task loaded
|
128
|
+
|
129
|
+
To verify that the rake task is loaded you can run `rake -T` and you should
|
130
|
+
see something similar to the following in the output.
|
131
|
+
|
132
|
+
```text
|
133
|
+
rake togls:features # Output all features including status (on, off, ? - unknown due to complex rule), ke...
|
134
|
+
```
|
135
|
+
|
136
|
+
#### Run the rake task
|
137
|
+
|
138
|
+
Once you have verified the task is loaded and available you can run it as
|
139
|
+
follows.
|
140
|
+
|
141
|
+
```shell
|
142
|
+
rake togls:features
|
51
143
|
```
|
52
144
|
|
145
|
+
The following is an example of what the output might look like if you defined
|
146
|
+
a few test features.
|
147
|
+
|
148
|
+
```text
|
149
|
+
on - :test1 - test 1 feature
|
150
|
+
off - :test2 - test 2 feature
|
151
|
+
off - :test3 - test 3 feature
|
152
|
+
```
|
153
|
+
|
154
|
+
## Advanced Usage
|
155
|
+
|
156
|
+
Below is a breakdown of some of the more advanced features of `Togls` which
|
157
|
+
aren't necessary in order to use it for basic feature toggles.
|
158
|
+
|
53
159
|
### Custom Rules
|
54
160
|
|
55
|
-
|
161
|
+
`Togls` is specifically architected on top of a generic concept of a
|
162
|
+
`Togls::Rule`. This empowers the users to define any custom rules they would
|
163
|
+
like and use them to control their feature toggles. For example, you could
|
164
|
+
use them to do A/B testing, define alpha test group, give a percentage of a
|
165
|
+
user base a feature, etc.
|
166
|
+
|
167
|
+
### Simple Rules
|
168
|
+
|
169
|
+
A simple rule can be defined by creating a rule object and passing a block. In
|
170
|
+
the following example any feature using the `gmail_rule` would only be on if
|
171
|
+
the given `target` contained `gmail.com` at the end of the `target`.
|
56
172
|
|
57
173
|
```ruby
|
58
174
|
# Only allow users with email addresses at gmail.com
|
@@ -63,7 +179,40 @@ Togls.features do
|
|
63
179
|
end
|
64
180
|
```
|
65
181
|
|
66
|
-
|
182
|
+
### Complex Rules
|
183
|
+
|
184
|
+
To implement a more complex rule, a new rule object can be defined under
|
185
|
+
`Togls::Rules` that abides by the following.
|
186
|
+
|
187
|
+
- inherits from `Togls::Rule` or a decendent of it
|
188
|
+
- has an instance method named `run` that takes either a default value
|
189
|
+
parameter of `target` or a required paramter of `target`.
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
def run(target = 'foo')
|
193
|
+
...
|
194
|
+
end
|
195
|
+
|
196
|
+
# or
|
197
|
+
|
198
|
+
def run(target)
|
199
|
+
...
|
200
|
+
end
|
201
|
+
```
|
202
|
+
|
203
|
+
- has the instance method named `run` return a boolean value identifying if
|
204
|
+
that feature should be on(`true`)/off(`false`) given the `target`.
|
205
|
+
|
206
|
+
Thats it!
|
207
|
+
|
208
|
+
#### Example Complex Rule
|
209
|
+
|
210
|
+
Internally `Togls` uses these *Complex Rules* to provide functionality and
|
211
|
+
will evolve to contain more official rules over time. If you have a generic
|
212
|
+
*Complex Rule* you think should be part of `Togls` please make a pull request.
|
213
|
+
|
214
|
+
A prime example of a *Complex Rule* provided by `Togls` is the
|
215
|
+
`Togls::Rules::Group` rule. You can see it below.
|
67
216
|
|
68
217
|
```ruby
|
69
218
|
module Togls
|
@@ -81,6 +230,25 @@ module Togls
|
|
81
230
|
end
|
82
231
|
```
|
83
232
|
|
233
|
+
Lets take a closer look at exactly what is going on here.
|
234
|
+
|
235
|
+
- it is inheriting from the `Togls::Rule` class which meets one of the minimum
|
236
|
+
requirements for a rule.
|
237
|
+
- its defines constructor takes an array identifiers and stores them. These
|
238
|
+
identifiers could be id numbers, email address, etc. **Note:** This
|
239
|
+
constructor is completely different than that of `Togls::Rule`. This
|
240
|
+
is fine because it is **not** a requirements that the constructor match.
|
241
|
+
- its `run` method returns a boolean value identifying if the feature should
|
242
|
+
be on(`true`)/off(`false`) for the given `target`. It does so by identifying
|
243
|
+
if the array passed in at construction time `include?` the given `target`.
|
244
|
+
This meets one of the minimum requirement for a rule.
|
245
|
+
- its `run` method signature requires a `target`. This meets a minimum
|
246
|
+
requirement for a rule as well. It also makes sense in the case of group
|
247
|
+
based rule as it has to have something to compare against the group.
|
248
|
+
|
249
|
+
*Complex Rules* are a simple yet extremely power concept that you shouldn't
|
250
|
+
hesitate to use.
|
251
|
+
|
84
252
|
## Development
|
85
253
|
|
86
254
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bin/console` for an interactive prompt that will allow you to experiment.
|
data/lib/togls/errors.rb
ADDED
data/lib/togls/feature.rb
CHANGED
@@ -1,25 +1,38 @@
|
|
1
1
|
module Togls
|
2
2
|
class Feature
|
3
|
-
attr_reader :key
|
3
|
+
attr_reader :key, :description
|
4
4
|
|
5
|
-
def initialize(key)
|
5
|
+
def initialize(key, description)
|
6
6
|
@key = key
|
7
|
+
@description = description
|
7
8
|
off
|
8
9
|
end
|
9
10
|
|
10
11
|
def on(rule = nil)
|
11
12
|
if rule.nil?
|
12
|
-
rule =
|
13
|
+
rule = Togls::Rules::Boolean.new(true)
|
13
14
|
end
|
14
15
|
@rule = rule
|
16
|
+
self
|
15
17
|
end
|
16
18
|
|
17
19
|
def off
|
18
|
-
@rule =
|
20
|
+
@rule = Togls::Rules::Boolean.new(false)
|
21
|
+
self
|
19
22
|
end
|
20
23
|
|
21
24
|
def on?(target = nil)
|
22
25
|
@rule.run(target)
|
23
26
|
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
if @rule.is_a?(Togls::Rules::Boolean)
|
30
|
+
display_value = @rule.run ? ' on' : 'off'
|
31
|
+
else
|
32
|
+
display_value = ' ?'
|
33
|
+
end
|
34
|
+
|
35
|
+
"#{display_value} - #{@key.inspect} - #{@description}"
|
36
|
+
end
|
24
37
|
end
|
25
38
|
end
|
@@ -2,7 +2,7 @@ module Togls
|
|
2
2
|
class FeatureRegistry
|
3
3
|
def initialize
|
4
4
|
@registry = {}
|
5
|
-
@default_feature = Feature.new(:default).tap {|f| f.on(Rule.new { false }) }
|
5
|
+
@default_feature = Feature.new(:default, "the official default feature").tap {|f| f.on(Rule.new { false }) }
|
6
6
|
end
|
7
7
|
|
8
8
|
def self.create(&features)
|
@@ -11,8 +11,8 @@ module Togls
|
|
11
11
|
registry
|
12
12
|
end
|
13
13
|
|
14
|
-
def feature(tag)
|
15
|
-
@registry[tag.to_sym] = Feature.new(tag.to_sym)
|
14
|
+
def feature(tag, desc)
|
15
|
+
@registry[tag.to_sym] = Feature.new(tag.to_sym, desc)
|
16
16
|
end
|
17
17
|
|
18
18
|
def get(key)
|
@@ -21,5 +21,9 @@ module Togls
|
|
21
21
|
@default_feature
|
22
22
|
end
|
23
23
|
end
|
24
|
+
|
25
|
+
def registry
|
26
|
+
@registry
|
27
|
+
end
|
24
28
|
end
|
25
29
|
end
|
data/lib/togls/rules.rb
CHANGED
data/lib/togls/version.rb
CHANGED
data/lib/togls.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "togls/version"
|
2
|
+
require "togls/errors"
|
2
3
|
require "togls/feature_registry"
|
3
4
|
require "togls/feature"
|
4
5
|
require "togls/rule"
|
@@ -7,7 +8,15 @@ require "logger"
|
|
7
8
|
|
8
9
|
module Togls
|
9
10
|
def self.features(&features)
|
10
|
-
|
11
|
+
if !features.nil?
|
12
|
+
@feature_registry = FeatureRegistry.create(&features)
|
13
|
+
else
|
14
|
+
if @feature_registry.nil?
|
15
|
+
raise Togls::NoFeaturesError, "Need to define features before you can get them"
|
16
|
+
else
|
17
|
+
@feature_registry.registry
|
18
|
+
end
|
19
|
+
end
|
11
20
|
end
|
12
21
|
|
13
22
|
def self.feature(key)
|
data/togls.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
-
spec.add_development_dependency "bundler", "~> 1.
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.9"
|
23
23
|
spec.add_development_dependency "rake", "~> 10.0"
|
24
24
|
spec.add_development_dependency "rspec", "~> 3.2"
|
25
25
|
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:
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Brian Miller
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date: 2015-05-
|
12
|
+
date: 2015-05-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bundler
|
@@ -17,14 +17,14 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '1.
|
20
|
+
version: '1.9'
|
21
21
|
type: :development
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: '1.
|
27
|
+
version: '1.9'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: rake
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -75,11 +75,14 @@ files:
|
|
75
75
|
- Rakefile
|
76
76
|
- bin/console
|
77
77
|
- bin/setup
|
78
|
+
- lib/tasks/togls.rake
|
78
79
|
- lib/togls.rb
|
80
|
+
- lib/togls/errors.rb
|
79
81
|
- lib/togls/feature.rb
|
80
82
|
- lib/togls/feature_registry.rb
|
81
83
|
- lib/togls/rule.rb
|
82
84
|
- lib/togls/rules.rb
|
85
|
+
- lib/togls/rules/boolean.rb
|
83
86
|
- lib/togls/rules/group.rb
|
84
87
|
- lib/togls/version.rb
|
85
88
|
- togls.gemspec
|