abstract_feature_branch 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +76 -38
- data/VERSION +1 -1
- data/abstract_feature_branch.gemspec +3 -2
- data/lib/abstract_feature_branch.rb +40 -1
- data/lib/ext/feature_branch.rb +3 -16
- data/lib/generators/abstract_feature_branch/install_generator.rb +7 -2
- data/lib/generators/templates/config/features.local.yml +15 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fb2107fd2312b62c1864e7b60ce08c24620229c0
|
4
|
+
data.tar.gz: e9a07cf69f21a778a4f9245e5c53e3b2e71f4d53
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bca12505dd8a199f432261d7498ed5378b2199a36236bca57cb13613c3f83b58acfefba5faa18cf9a223d5bdc29a451326429b9ae31bfa9f0923475d5d2e8c0a
|
7
|
+
data.tar.gz: f1f7d0a7db3534745891a034fd357557b93299b9ac86fb4879715a5bae84a53d825684dfe49d00557b3137f51b165d0cd3d6ca26cd9744178226c2f511fca650
|
data/README.md
CHANGED
@@ -24,13 +24,18 @@ Setup
|
|
24
24
|
-----
|
25
25
|
|
26
26
|
1. Configure Rubygem
|
27
|
-
- Rails (~> 4.0.0 or ~> 3.0): Add the following to Gemfile <pre>gem 'abstract_feature_branch', '0.
|
28
|
-
- Rails (~> 2.0): Add the following to config/environment.rb <pre>config.gem 'absract_feature_branch', :version => '0.
|
29
|
-
2. Generate config/features.yml in your Rails app directory by running <pre>rails g abstract_feature_branch:install</pre>
|
27
|
+
- Rails (~> 4.0.0 or ~> 3.0): Add the following to Gemfile <pre>gem 'abstract_feature_branch', '0.5.0'</pre>
|
28
|
+
- Rails (~> 2.0): Add the following to config/environment.rb <pre>config.gem 'absract_feature_branch', :version => '0.5.0'</pre>
|
29
|
+
2. Generate <code>config/features.yml</code> and <code>config/features.local.yml</code> in your Rails app directory by running <pre>rails g abstract_feature_branch:install</pre>
|
30
30
|
|
31
31
|
Instructions
|
32
32
|
------------
|
33
33
|
|
34
|
+
<code>config/features.yml</code> contains the main configuration for the application features.
|
35
|
+
|
36
|
+
<code>config/features.local.yml</code> contains local overrides for the configuration, ignored by git, thus useful for temporary
|
37
|
+
local feature switching for development/testing/troubleshooting purposes.
|
38
|
+
|
34
39
|
Here are the contents of the generated sample config/features.yml, which you can modify with your own features, each
|
35
40
|
enabled (true) or disabled (false) per environment (e.g. production).
|
36
41
|
|
@@ -67,7 +72,7 @@ multi-line logic:
|
|
67
72
|
single-line logic:
|
68
73
|
> feature_branch(:feature1) { # perform logic }
|
69
74
|
|
70
|
-
Note that feature_branch returns nil and does not execute the block if the feature is disabled or non-existent.
|
75
|
+
Note that <code>feature_branch</code> returns nil and does not execute the block if the feature is disabled or non-existent.
|
71
76
|
|
72
77
|
- Declaratively feature branch two paths of logic, one that runs when feature1 is enabled and one that runs when it is disabled:
|
73
78
|
|
@@ -79,7 +84,7 @@ Note that feature_branch returns nil and does not execute the block if the featu
|
|
79
84
|
> # perform alternate logic
|
80
85
|
> }
|
81
86
|
|
82
|
-
Note that feature_branch executes the false branch if the feature is non-existent.
|
87
|
+
Note that <code>feature_branch</code> executes the false branch if the feature is non-existent.
|
83
88
|
|
84
89
|
- Imperatively check if a feature is enabled or not:
|
85
90
|
|
@@ -89,39 +94,7 @@ Note that feature_branch executes the false branch if the feature is non-existen
|
|
89
94
|
> # perform alternate logic
|
90
95
|
> end
|
91
96
|
|
92
|
-
Note that feature_enabled
|
93
|
-
|
94
|
-
Environment Variable Overrides
|
95
|
-
------------------------------
|
96
|
-
|
97
|
-
You can override feature configuration with environment variables by setting an environment variable with
|
98
|
-
a name matching this convention (case-insensitive):
|
99
|
-
ABSTRACT_FEATURE_BRANCH_[feature_name] and giving it the case-insensitive value "TRUE" or "FALSE"
|
100
|
-
|
101
|
-
Examples:
|
102
|
-
|
103
|
-
- export ABSTRACT_FEATURE_BRANCH_FEATURE1=TRUE
|
104
|
-
- export abstract_feature_branch_feature2=false
|
105
|
-
|
106
|
-
Heroku
|
107
|
-
------
|
108
|
-
|
109
|
-
Environment variable overrides can be extremely helpful on Heroku as they allow developers to enable/disable features
|
110
|
-
at runtime without a redeploy.
|
111
|
-
|
112
|
-
Examples:
|
113
|
-
|
114
|
-
Enabling a new feature without a redeploy:
|
115
|
-
<pre>heroku config:add ABSTRACT_FEATURE_BRANCH_FEATURE3=true -a heroku_application_name</pre>
|
116
|
-
|
117
|
-
Disabling a buggy recently deployed feature without a redeploy:
|
118
|
-
<pre>heroku config:add ABSTRACT_FEATURE_BRANCH_FEATURE2=false -a heroku_application_name</pre>
|
119
|
-
|
120
|
-
Removing an environment variable override:
|
121
|
-
<pre>heroku config:remove ABSTRACT_FEATURE_BRANCH_FEATURE2 -a heroku_application_name</pre>
|
122
|
-
|
123
|
-
Note that it is recommended to use environment variables as an emergency or temporary measure, make the change
|
124
|
-
officially in config/features.yml afterward, deploy, and finally remove the environment variable override for the long term.
|
97
|
+
Note that <code>feature_enabled?</code> returns false if the feature is disabled and nil if the feature is non-existent (practically the same effect, but nil can sometimes be useful to detect if a feature is referenced).
|
125
98
|
|
126
99
|
Recommendations
|
127
100
|
---------------
|
@@ -183,9 +156,74 @@ simply switching off the URL route to them. Example:
|
|
183
156
|
it is recommended that its feature branching code is plucked out of the code base to simplify the code
|
184
157
|
for better maintenance as the need is not longer there for feature branching at that point.
|
185
158
|
|
159
|
+
- When working on a new feature locally that the developer does not want others on the team to see yet, the feature
|
160
|
+
can be enabled in <code>config/features.local.yml</code> only as it is git ignored, and disabled in <code>config/features.yml</code>
|
161
|
+
|
162
|
+
- When troubleshooting a deployed feature by simulating a non-development environment (e.g. staging or production) locally,
|
163
|
+
the developer can disable it temporarily in <code>config/features.local.yml</code> (git ignored) under the non-development environment,
|
164
|
+
perform tests on the feature, and then remove the local configuration once done.
|
165
|
+
|
166
|
+
Environment Variable Overrides
|
167
|
+
------------------------------
|
168
|
+
|
169
|
+
You can override feature configuration with environment variables by setting an environment variable with
|
170
|
+
a name matching this convention (case-insensitive):
|
171
|
+
ABSTRACT_FEATURE_BRANCH_[feature_name] and giving it the case-insensitive value "TRUE" or "FALSE"
|
172
|
+
|
173
|
+
Example:
|
174
|
+
|
175
|
+
> export ABSTRACT_FEATURE_BRANCH_FEATURE1=TRUE
|
176
|
+
> rails s
|
177
|
+
|
178
|
+
The first command adds an environment variable override for <code>feature1</code> that enables it regardless of any
|
179
|
+
feature configuration, and the second command starts the rails server with <code>feature1</code> enabled.
|
180
|
+
|
181
|
+
To remove environment variable override, you may run:
|
182
|
+
|
183
|
+
> unset ABSTRACT_FEATURE_BRANCH_FEATURE1
|
184
|
+
> rails s
|
185
|
+
|
186
|
+
This can be done more easily via <code>config/features.local.yml</code> mentioned above. However, environment variable overrides are
|
187
|
+
implemented to support overriding feature configuration for a Heroku deployed application more easily.
|
188
|
+
|
189
|
+
Heroku
|
190
|
+
------
|
191
|
+
|
192
|
+
Environment variable overrides can be extremely helpful on Heroku as they allow developers to enable/disable features
|
193
|
+
at runtime without a redeploy.
|
194
|
+
|
195
|
+
Examples:
|
196
|
+
|
197
|
+
Enabling a new feature without a redeploy:
|
198
|
+
<pre>heroku config:add ABSTRACT_FEATURE_BRANCH_FEATURE3=true -a heroku_application_name</pre>
|
199
|
+
|
200
|
+
Disabling a buggy recently deployed feature without a redeploy:
|
201
|
+
<pre>heroku config:add ABSTRACT_FEATURE_BRANCH_FEATURE2=false -a heroku_application_name</pre>
|
202
|
+
|
203
|
+
Removing an environment variable override:
|
204
|
+
<pre>heroku config:remove ABSTRACT_FEATURE_BRANCH_FEATURE2 -a heroku_application_name</pre>
|
205
|
+
|
206
|
+
Recommendation:
|
207
|
+
|
208
|
+
It is recommended that you use environment variable overrides on Heroku only as an emergency or temporary measure.
|
209
|
+
Afterward, make the change officially in config/features.yml, deploy, and remove the environment variable override for the long term.
|
210
|
+
|
211
|
+
Gotcha with abstract feature branching in CSS and JS files:
|
212
|
+
|
213
|
+
If you've used abstract feature branching in CSS or JS files via ERB, setting environment variable overrides won't
|
214
|
+
affect them as you need asset recompilation in addition to it, which can only be triggered by changing a CSS or JS
|
215
|
+
file and redeploying on Heroku (hint: even if it's just a minor change to force it). In any case, environment variable
|
216
|
+
overrides have been recommended above as an emergency or temporary measure. If there is a need to rely on environment
|
217
|
+
variable overrides to alter the style or JavaScript behavior of a page back and forth without a redeploy, one solution
|
218
|
+
is to do additional abstract feature branching in HTML templates (e.g. ERB or HAML templates) to link to different
|
219
|
+
CSS classes or invoke different JavaScript methods per branch of HTML for example.
|
220
|
+
|
186
221
|
Release Notes
|
187
222
|
-------------
|
188
223
|
|
224
|
+
Version 0.5.0:
|
225
|
+
- Added support for local configuration feature ignored by git + some performance optimizations via configuration caching and better algorithms.
|
226
|
+
|
189
227
|
Version 0.4.0:
|
190
228
|
- Added support for overwriting feature configuration with environment variable overrides. Very useful on Heroku to quickly enable/disable features without a redeploy.
|
191
229
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.5.0
|
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "abstract_feature_branch"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.5.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Annas \"Andy\" Maleh"]
|
12
|
-
s.date = "2013-11-
|
12
|
+
s.date = "2013-11-22"
|
13
13
|
s.description = "It gives ability to wrap blocks of code with an abstract feature branch name, and then\nspecify which features to be switched on or off in a configuration file.\n\nThe goal is to build out future features with full integration into the codebase, thus\nensuring no delay in integration in the future, while releasing currently done features\nat the same time. Developers then disable future features until they are ready to be\nswitched on in production, but do enable them in staging and locally.\n\nThis gives developers the added benefit of being able to switch a feature off after\nrelease should big problems arise for a high risk feature.\n"
|
14
14
|
s.extra_rdoc_files = [
|
15
15
|
"LICENSE.txt",
|
@@ -23,6 +23,7 @@ Gem::Specification.new do |s|
|
|
23
23
|
"lib/abstract_feature_branch.rb",
|
24
24
|
"lib/ext/feature_branch.rb",
|
25
25
|
"lib/generators/abstract_feature_branch/install_generator.rb",
|
26
|
+
"lib/generators/templates/config/features.local.yml",
|
26
27
|
"lib/generators/templates/config/features.yml"
|
27
28
|
]
|
28
29
|
s.homepage = "http://github.com/AndyObtiva/abstract_feature_branch"
|
@@ -10,8 +10,47 @@ rescue Bundler::BundlerError => e
|
|
10
10
|
end
|
11
11
|
|
12
12
|
module AbstractFeatureBranch
|
13
|
+
def self.environment_variable_overrides
|
14
|
+
@environment_variable_overrides ||= load_environment_variable_overrides
|
15
|
+
end
|
16
|
+
def self.load_environment_variable_overrides
|
17
|
+
@environment_variable_overrides = featureize_keys(select_feature_keys(booleanize_values(downcase_keys(ENV))))
|
18
|
+
end
|
19
|
+
def self.local_features
|
20
|
+
@local_features ||= Hash[YAML.load_file(File.join(Rails.root, 'config', 'features.local.yml')).map {|k, v| [k, v && downcase_keys(v)]}]
|
21
|
+
@local_features
|
22
|
+
end
|
13
23
|
def self.features
|
14
|
-
@features ||= YAML.load_file(File.join(Rails.root, 'config', 'features.yml'))
|
24
|
+
@features ||= Hash[YAML.load_file(File.join(Rails.root, 'config', 'features.yml')).map {|k, v| [k, v && downcase_keys(v)]}]
|
25
|
+
@features
|
26
|
+
end
|
27
|
+
# performance optimization via caching of feature values resolved through environment variable overrides and local features
|
28
|
+
def self.environment_features(environment)
|
29
|
+
@environment_features ||= {}
|
30
|
+
@environment_features[environment] ||= load_environment_features(environment)
|
31
|
+
end
|
32
|
+
def self.load_environment_features(environment)
|
33
|
+
@environment_features[environment] = features[environment].merge(local_features[environment]).merge(environment_variable_overrides)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
ENV_FEATURE_PREFIX = "abstract_feature_branch_"
|
39
|
+
|
40
|
+
def self.featureize_keys(hash)
|
41
|
+
Hash[hash.map {|k, v| [k.sub(ENV_FEATURE_PREFIX, ''), v]}]
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.select_feature_keys(hash)
|
45
|
+
hash.reject {|k, v| !k.start_with?(ENV_FEATURE_PREFIX)} # using reject for Ruby 1.8 compatibility as select returns an array in it
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.booleanize_values(hash)
|
49
|
+
Hash[hash.map {|k, v| [k, v.downcase == 'true']}]
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.downcase_keys(hash)
|
53
|
+
Hash[hash.map {|k, v| [k.downcase, v]}]
|
15
54
|
end
|
16
55
|
end
|
17
56
|
|
data/lib/ext/feature_branch.rb
CHANGED
@@ -3,15 +3,13 @@ class Object
|
|
3
3
|
def self.feature_branch(feature_name, branches = {}, &feature_work)
|
4
4
|
branches[:true] ||= feature_work
|
5
5
|
branches[:false] ||= lambda {}
|
6
|
-
|
7
|
-
|
8
|
-
feature_status = false if feature_status.nil?
|
9
|
-
branches[feature_status.to_s.to_sym].call
|
6
|
+
feature_branch_symbol_value = (!!feature_enabled?(feature_name)).to_s.to_sym
|
7
|
+
branches[feature_branch_symbol_value].call
|
10
8
|
end
|
11
9
|
|
12
10
|
raise 'Abstract feature branch conflicts with another Ruby library' if respond_to?(:feature_enabled?)
|
13
11
|
def self.feature_enabled?(feature_name)
|
14
|
-
AbstractFeatureBranch.
|
12
|
+
AbstractFeatureBranch.environment_features(Rails.env.to_s)[feature_name.to_s]
|
15
13
|
end
|
16
14
|
|
17
15
|
raise 'Abstract feature branch conflicts with another Ruby library' if Object.new.respond_to?(:feature_branch)
|
@@ -23,15 +21,4 @@ class Object
|
|
23
21
|
def feature_enabled?(feature_name)
|
24
22
|
Object.feature_enabled?(feature_name.to_s)
|
25
23
|
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
ABSTRACT_FEATURE_BRANCH_POSITIVE_VALUES = ['true', 'on', 'yes']
|
30
|
-
|
31
|
-
raise 'Abstract feature branch conflicts with another Ruby library' if respond_to?(:abstract_feature_branch_environment_value)
|
32
|
-
def self.abstract_feature_branch_environment_value(feature_name)
|
33
|
-
downcased_env = Hash[ENV.map {|k, v| [k.downcase, v]}]
|
34
|
-
value = downcased_env["abstract_feature_branch_#{feature_name.to_s.downcase}"]
|
35
|
-
value && ABSTRACT_FEATURE_BRANCH_POSITIVE_VALUES.include?(value.downcase)
|
36
|
-
end
|
37
24
|
end
|
@@ -3,10 +3,15 @@ module AbstractFeatureBranch
|
|
3
3
|
class InstallGenerator < Rails::Generators::Base
|
4
4
|
source_root File.expand_path("../../templates", __FILE__)
|
5
5
|
|
6
|
-
desc "Creates
|
7
|
-
|
6
|
+
desc "Creates Abstract Feature Branch configuration files in your application."
|
8
7
|
def copy_config
|
9
8
|
template "config/features.yml", "config/features.yml"
|
9
|
+
template "config/features.local.yml", "config/features.local.yml"
|
10
|
+
append_to_file '.gitignore', <<-GIT_IGNORE_CONTENT
|
11
|
+
|
12
|
+
#abstract_feature_branch local configuration file
|
13
|
+
config/features.local.yml
|
14
|
+
GIT_IGNORE_CONTENT
|
10
15
|
end
|
11
16
|
end
|
12
17
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# This file allows you to override any feature configuration locally without it being committed to git
|
2
|
+
# It is recommended to use this file only for temporary overrides. Once done, make final change in config/features.yml
|
3
|
+
defaults: &defaults
|
4
|
+
|
5
|
+
development:
|
6
|
+
<<: *defaults
|
7
|
+
|
8
|
+
test:
|
9
|
+
<<: *defaults
|
10
|
+
|
11
|
+
staging:
|
12
|
+
<<: *defaults
|
13
|
+
|
14
|
+
production:
|
15
|
+
<<: *defaults
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: abstract_feature_branch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Annas "Andy" Maleh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-11-
|
11
|
+
date: 2013-11-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -77,6 +77,7 @@ files:
|
|
77
77
|
- lib/abstract_feature_branch.rb
|
78
78
|
- lib/ext/feature_branch.rb
|
79
79
|
- lib/generators/abstract_feature_branch/install_generator.rb
|
80
|
+
- lib/generators/templates/config/features.local.yml
|
80
81
|
- lib/generators/templates/config/features.yml
|
81
82
|
homepage: http://github.com/AndyObtiva/abstract_feature_branch
|
82
83
|
licenses:
|