togls 0.1.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 29cf1b0568aea63a9f302b01727e2b03e2f6b022
4
- data.tar.gz: 7b109336122ffd30f15621b8b769d3ad0556bd34
3
+ metadata.gz: f956666596995d557aa56048b785db8f490f73dc
4
+ data.tar.gz: ffa83b7c726ef586d20c169c6cf3d3189ee8207c
5
5
  SHA512:
6
- metadata.gz: 76ed9cf6f4d3b726902a8332fb16b57a1f4355cd3004cf0eca1f73228a9e4c304a89c967ebf157ebe5b3cc5003809223ed5dfd2577eb78dbc9197d4567bd7f05
7
- data.tar.gz: 466bb270590c5a5000340ce3585da8f51195ae9016f79b1200d09dcc2f2f620a3809ac4168b1f1b9b51f1dff6ca6f5911b15e4a3e4fd5303d0e35742db4357c9
6
+ metadata.gz: 50b448deb27e15b7815cafa0e02d366a01643038ca1a689695547e48234f0a2aafe5161cea5a9c9461d7b179022dd2cd22d8b8080756f3c62d760220a92e20f9
7
+ data.tar.gz: 00963574a1dbef269a279ae8b42294a11042a194f80b366f38b9136a5d23e4eba23a634d16705b1f8990ad9c4f56821db4bb754b8d3d05aba9ded8d8da6ba6a9
data/.gitignore CHANGED
@@ -7,3 +7,4 @@
7
7
  /pkg/
8
8
  /spec/reports/
9
9
  /tmp/
10
+ *.gem
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
+ [![Version](https://img.shields.io/gem/v/togls.svg)](https://rubygems.org/gems/togls)
1
2
  [![Build Status](https://travis-ci.org/codebreakdown/togls.svg?branch=master)](https://travis-ci.org/codebreakdown/togls)
2
3
  [![Code Climate](https://img.shields.io/codeclimate/github/codebreakdown/togls.svg)](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
- ### Setup
30
+ The basic usage of `Togls` is outlined below.
30
31
 
31
- The default behaviour for any feature that has not been defined that is accessed is to default to false.
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
- ### Evaluate
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
- # Do my awesome feature
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
- A simple rule can be defined by created a rule object and passing a block
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
- To implement a more complex rule a new rule object can be defined under Togls::Rules that implements the run method and returns a boolean. When a feature is defined, the rule will be added to the feature object and the run method called with whatever target is passed.
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.
@@ -0,0 +1,8 @@
1
+ namespace :togls do
2
+ desc "Output all features including status (on, off, ? - unknown due to complex rule), key, description"
3
+ task :features do
4
+ Togls.features.each do |key, feature|
5
+ puts feature.to_s
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ module Togls
2
+ class NoFeaturesError < StandardError; end
3
+ end
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 = Rule.new { true }
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 = Rule.new { false }
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
@@ -0,0 +1,13 @@
1
+ module Togls
2
+ module Rules
3
+ class Boolean < Rule
4
+ def initialize(bool)
5
+ @bool = bool
6
+ end
7
+
8
+ def run(target = nil)
9
+ return @bool
10
+ end
11
+ end
12
+ end
13
+ end
data/lib/togls/rules.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'togls/rules/boolean'
1
2
  require 'togls/rules/group'
2
3
 
3
4
  module Togls
data/lib/togls/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Togls
2
- VERSION = "0.1.0"
2
+ VERSION = "1.0.0"
3
3
  end
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
- @feature_registry = FeatureRegistry.create(&features)
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.8"
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: 0.1.0
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-03 00:00:00.000000000 Z
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.8'
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.8'
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