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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c6a6aaa5dee1e8e0fa7d7dba8051fe8a160e1cb1
4
- data.tar.gz: 7f089bf19a1c027ba5e4e27cea6cfccf92d5dd40
3
+ metadata.gz: fb2107fd2312b62c1864e7b60ce08c24620229c0
4
+ data.tar.gz: e9a07cf69f21a778a4f9245e5c53e3b2e71f4d53
5
5
  SHA512:
6
- metadata.gz: 7b8e441a5218c69ba59fee50418b379d99ffd00298f370b3e6939a043f97f2f85d1e72a056722e005f0d2694672023fa6efe6b6a72455767e009c996f9c4fdb2
7
- data.tar.gz: a07a4fbcc2ad0e16d9f75be7563a91970eda21e67d1c085804f2648f215f7fdb65f9076949ce9c843b3fc64d8f5740790bc557d0c95ae7990c7f10ff223bcfb3
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.4.0'</pre>
28
- - Rails (~> 2.0): Add the following to config/environment.rb <pre>config.gem 'absract_feature_branch', :version => '0.4.0'</pre>
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? 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).
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.4.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.4.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-21"
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
 
@@ -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
- feature_status = abstract_feature_branch_environment_value(feature_name)
7
- feature_status = AbstractFeatureBranch.features[Rails.env.to_s][feature_name.to_s] if feature_status.nil?
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.features[Rails.env.to_s][feature_name.to_s]
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 an Abstract Feature Branch configuration file in your application."
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.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-21 00:00:00.000000000 Z
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: