abstract_feature_branch 0.5.0 → 0.6.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 +42 -7
- data/VERSION +1 -1
- data/abstract_feature_branch.gemspec +13 -2
- data/config/features/admin.local.yml +14 -0
- data/config/features/admin.yml +16 -0
- data/config/features/internal/wiki.local.yml +14 -0
- data/config/features/internal/wiki.yml +16 -0
- data/config/features/public.local.yml +14 -0
- data/config/features/public.yml +16 -0
- data/lib/abstract_feature_branch.rb +21 -4
- data/lib/generators/abstract_feature_branch/context_generator.rb +12 -0
- data/lib/generators/abstract_feature_branch/install_generator.rb +5 -3
- data/lib/generators/templates/config/features.example.yml +19 -0
- data/lib/generators/templates/config/features.local.yml +1 -1
- data/lib/generators/templates/config/features.yml +1 -7
- metadata +24 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e0ac105f82f311d41f4de3e74b47d80a97f256ae
|
4
|
+
data.tar.gz: 2a6c82543d466d7f0783e325d4a351e81ccac8d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e19186625bb9665d2606069de65e9ae9131cf4287c6ec2a467a235ce4698ccba85b818e17d9f07cec343f65c77207956161c4ae1a5ef7883de2067642746bfd
|
7
|
+
data.tar.gz: 6dcf6ac83b3ce412f9b5fd0de2999889042ca94b0ab21a31c7268386471ef178de6a3e6256cdd6ad1efb66ab0fdf801e0448511a41673fa2a247b34da665a1a4
|
data/README.md
CHANGED
@@ -4,6 +4,9 @@ Abstract Feature Branch
|
|
4
4
|
abstract_feature_branch is a Rails gem that enables developers to easily branch by
|
5
5
|
abstraction as per this pattern: http://paulhammant.com/blog/branch_by_abstraction.html
|
6
6
|
|
7
|
+
It is a productivity and fault tolerance enhancing team practice that has been utilized by professional software development
|
8
|
+
teams at large corporations, such as Sears and Groupon.
|
9
|
+
|
7
10
|
It gives ability to wrap blocks of code with an abstract feature branch name, and then
|
8
11
|
specify which features to be switched on or off in a configuration file.
|
9
12
|
|
@@ -15,6 +18,10 @@ switched on in production, but do enable them in staging and locally.
|
|
15
18
|
This gives developers the added benefit of being able to switch a feature off after
|
16
19
|
release should big problems arise for a high risk feature.
|
17
20
|
|
21
|
+
abstract_feature_branch additionally supports [DDD](http://www.domaindrivendesign.org)'s pattern of
|
22
|
+
[bounded contexts](http://dddcommunity.org/uncategorized/bounded-context/), but allowing developers to configure
|
23
|
+
context-specific feature files if needed.
|
24
|
+
|
18
25
|
Requirements
|
19
26
|
------------
|
20
27
|
- Ruby ~> 2.0.0, ~> 1.9 or ~> 1.8.7
|
@@ -24,9 +31,10 @@ Setup
|
|
24
31
|
-----
|
25
32
|
|
26
33
|
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.
|
34
|
+
- Rails (~> 4.0.0 or ~> 3.0): Add the following to Gemfile <pre>gem 'abstract_feature_branch', '0.6.0'</pre>
|
35
|
+
- Rails (~> 2.0): Add the following to config/environment.rb <pre>config.gem 'absract_feature_branch', :version => '0.6.0'</pre>
|
29
36
|
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>
|
37
|
+
3. (Optional) Generate <code>config/features/[context_path].yml</code> in your Rails app directory by running <pre>rails g abstract_feature_branch:context context_path</pre>
|
30
38
|
|
31
39
|
Instructions
|
32
40
|
------------
|
@@ -36,6 +44,14 @@ Instructions
|
|
36
44
|
<code>config/features.local.yml</code> contains local overrides for the configuration, ignored by git, thus useful for temporary
|
37
45
|
local feature switching for development/testing/troubleshooting purposes.
|
38
46
|
|
47
|
+
Optional context specific <code>config/features/[context_path].yml</code> contain feature configuration for specific application contexts.
|
48
|
+
For example: admin, public, or even internal/wiki. Useful for better organization especially once <code>config/features.yml</code> grows too big (e.g. 20+ features)
|
49
|
+
|
50
|
+
Optional context specific <code>config/features/[context_path].local.yml</code> contain local overrides for context-specific feature configuration.
|
51
|
+
These files are rarely necessary as any feature (even a context feature) can be overridden in <code>config/features.local.yml</code>,
|
52
|
+
so these additional <code>*.local.yml</code> files are only recommended to be utilized once <code>config/features.local.yml</code> grows
|
53
|
+
too big (e.g. 20+ features).
|
54
|
+
|
39
55
|
Here are the contents of the generated sample config/features.yml, which you can modify with your own features, each
|
40
56
|
enabled (true) or disabled (false) per environment (e.g. production).
|
41
57
|
|
@@ -156,6 +172,9 @@ simply switching off the URL route to them. Example:
|
|
156
172
|
it is recommended that its feature branching code is plucked out of the code base to simplify the code
|
157
173
|
for better maintenance as the need is not longer there for feature branching at that point.
|
158
174
|
|
175
|
+
- Split <code>config/features.yml</code> into multiple context-specific feature files once it grows too big (e.g. 20+ features) by
|
176
|
+
utilizing the context generator mentioned above: <pre>rails g abstract_feature_branch:context context_path</pre>
|
177
|
+
|
159
178
|
- When working on a new feature locally that the developer does not want others on the team to see yet, the feature
|
160
179
|
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
180
|
|
@@ -178,13 +197,14 @@ Example:
|
|
178
197
|
The first command adds an environment variable override for <code>feature1</code> that enables it regardless of any
|
179
198
|
feature configuration, and the second command starts the rails server with <code>feature1</code> enabled.
|
180
199
|
|
181
|
-
To remove environment variable override, you may run:
|
200
|
+
To remove an environment variable override, you may run:
|
182
201
|
|
183
202
|
> unset ABSTRACT_FEATURE_BRANCH_FEATURE1
|
184
203
|
> rails s
|
185
204
|
|
186
|
-
|
187
|
-
implemented to support overriding feature configuration for a Heroku deployed
|
205
|
+
The benefits can be achieved more easily via <code>config/features.local.yml</code> mentioned above.
|
206
|
+
However, environment variable overrides are implemented to support overriding feature configuration for a Heroku deployed
|
207
|
+
application more easily.
|
188
208
|
|
189
209
|
Heroku
|
190
210
|
------
|
@@ -218,9 +238,24 @@ variable overrides to alter the style or JavaScript behavior of a page back and
|
|
218
238
|
is to do additional abstract feature branching in HTML templates (e.g. ERB or HAML templates) to link to different
|
219
239
|
CSS classes or invoke different JavaScript methods per branch of HTML for example.
|
220
240
|
|
241
|
+
Feature Configuration Load Order
|
242
|
+
--------------------------------
|
243
|
+
|
244
|
+
For better knowledge and clarity, here is the order in which feature configuration is loaded, with the latter sources overriding
|
245
|
+
the former if overlap in features occurs:
|
246
|
+
|
247
|
+
1. Context-specific feature files: <code>config/features/**/*.yml</code>
|
248
|
+
2. Main feature file: <code>config/features.yml</code>
|
249
|
+
3. Context-specific local feature file overrides: <code>config/features/**/*.local.yml</code>
|
250
|
+
4. Main local feature file override: <code>config/features.local.yml</code>
|
251
|
+
5. Environment variable overrides
|
252
|
+
|
221
253
|
Release Notes
|
222
254
|
-------------
|
223
255
|
|
256
|
+
Version 0.6.0:
|
257
|
+
- Added a context generator and support for reading feature configuration from context files config/features/**/*.yml and config/features/**/*.local.yml
|
258
|
+
|
224
259
|
Version 0.5.0:
|
225
260
|
- Added support for local configuration feature ignored by git + some performance optimizations via configuration caching and better algorithms.
|
226
261
|
|
@@ -256,10 +291,10 @@ Version 0.2.0:
|
|
256
291
|
Upcoming
|
257
292
|
--------
|
258
293
|
|
259
|
-
- Support the option of having multiple features.yml files, one per environment, as opposed to one for all environments
|
260
294
|
- Support general Ruby (non-Rails) use
|
261
|
-
- Support contexts of features to group features, once they grow beyond a certain size, in separate files, one per context
|
262
295
|
- Add rake task to reorder feature entries in feature.yml alphabetically
|
296
|
+
- Support runtime read of features yml in development for easy testing purposes (trading off performance)
|
297
|
+
- Support configuring per environment whether features yml is read at runtime or not (given performance trade-off)
|
263
298
|
|
264
299
|
Contributing to abstract_feature_branch
|
265
300
|
---------------------------------------
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.6.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.6.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-23"
|
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",
|
@@ -20,9 +20,17 @@ Gem::Specification.new do |s|
|
|
20
20
|
"README.md",
|
21
21
|
"VERSION",
|
22
22
|
"abstract_feature_branch.gemspec",
|
23
|
+
"config/features/admin.local.yml",
|
24
|
+
"config/features/admin.yml",
|
25
|
+
"config/features/internal/wiki.local.yml",
|
26
|
+
"config/features/internal/wiki.yml",
|
27
|
+
"config/features/public.local.yml",
|
28
|
+
"config/features/public.yml",
|
23
29
|
"lib/abstract_feature_branch.rb",
|
24
30
|
"lib/ext/feature_branch.rb",
|
31
|
+
"lib/generators/abstract_feature_branch/context_generator.rb",
|
25
32
|
"lib/generators/abstract_feature_branch/install_generator.rb",
|
33
|
+
"lib/generators/templates/config/features.example.yml",
|
26
34
|
"lib/generators/templates/config/features.local.yml",
|
27
35
|
"lib/generators/templates/config/features.yml"
|
28
36
|
]
|
@@ -36,15 +44,18 @@ Gem::Specification.new do |s|
|
|
36
44
|
s.specification_version = 4
|
37
45
|
|
38
46
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
47
|
+
s.add_runtime_dependency(%q<deep_merge>, ["= 1.0.0"])
|
39
48
|
s.add_development_dependency(%q<rspec>, ["= 2.11.0"])
|
40
49
|
s.add_development_dependency(%q<rdoc>, ["= 3.12.2"])
|
41
50
|
s.add_development_dependency(%q<jeweler>, ["= 1.8.8"])
|
42
51
|
else
|
52
|
+
s.add_dependency(%q<deep_merge>, ["= 1.0.0"])
|
43
53
|
s.add_dependency(%q<rspec>, ["= 2.11.0"])
|
44
54
|
s.add_dependency(%q<rdoc>, ["= 3.12.2"])
|
45
55
|
s.add_dependency(%q<jeweler>, ["= 1.8.8"])
|
46
56
|
end
|
47
57
|
else
|
58
|
+
s.add_dependency(%q<deep_merge>, ["= 1.0.0"])
|
48
59
|
s.add_dependency(%q<rspec>, ["= 2.11.0"])
|
49
60
|
s.add_dependency(%q<rdoc>, ["= 3.12.2"])
|
50
61
|
s.add_dependency(%q<jeweler>, ["= 1.8.8"])
|
@@ -8,6 +8,7 @@ rescue Bundler::BundlerError => e
|
|
8
8
|
$stderr.puts "Run `bundle install` to install missing gems"
|
9
9
|
exit e.status_code
|
10
10
|
end
|
11
|
+
require 'deep_merge' unless {}.respond_to?(:deep_merge!)
|
11
12
|
|
12
13
|
module AbstractFeatureBranch
|
13
14
|
def self.environment_variable_overrides
|
@@ -17,12 +18,24 @@ module AbstractFeatureBranch
|
|
17
18
|
@environment_variable_overrides = featureize_keys(select_feature_keys(booleanize_values(downcase_keys(ENV))))
|
18
19
|
end
|
19
20
|
def self.local_features
|
20
|
-
@local_features ||=
|
21
|
-
|
21
|
+
@local_features ||= load_local_features
|
22
|
+
end
|
23
|
+
def self.load_local_features
|
24
|
+
@local_features = {}
|
25
|
+
Dir.glob(File.join(Rails.root, 'config', 'features', '**', '*.local.yml')).each do |feature_configuration_file|
|
26
|
+
@local_features.deep_merge!(downcase_feature_hash_keys(YAML.load_file(feature_configuration_file)))
|
27
|
+
end
|
28
|
+
@local_features.deep_merge!(downcase_feature_hash_keys(YAML.load_file(File.join(Rails.root, 'config', 'features.local.yml'))))
|
22
29
|
end
|
23
30
|
def self.features
|
24
|
-
@features ||=
|
25
|
-
|
31
|
+
@features ||= load_features
|
32
|
+
end
|
33
|
+
def self.load_features
|
34
|
+
@features = {}
|
35
|
+
Dir.glob(File.join(Rails.root, 'config', 'features', '**', '*.yml')).each do |feature_configuration_file|
|
36
|
+
@features.deep_merge!(downcase_feature_hash_keys(YAML.load_file(feature_configuration_file)))
|
37
|
+
end
|
38
|
+
@features.deep_merge!(downcase_feature_hash_keys(YAML.load_file(File.join(Rails.root, 'config', 'features.yml'))))
|
26
39
|
end
|
27
40
|
# performance optimization via caching of feature values resolved through environment variable overrides and local features
|
28
41
|
def self.environment_features(environment)
|
@@ -52,6 +65,10 @@ module AbstractFeatureBranch
|
|
52
65
|
def self.downcase_keys(hash)
|
53
66
|
Hash[hash.map {|k, v| [k.downcase, v]}]
|
54
67
|
end
|
68
|
+
|
69
|
+
def self.downcase_feature_hash_keys(hash)
|
70
|
+
Hash[hash.map {|k, v| [k, v && downcase_keys(v)]}]
|
71
|
+
end
|
55
72
|
end
|
56
73
|
|
57
74
|
require File.join(File.dirname(__FILE__), 'ext', 'feature_branch')
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module AbstractFeatureBranch
|
2
|
+
module Generators
|
3
|
+
class ContextGenerator < Rails::Generators::NamedBase
|
4
|
+
source_root File.expand_path("../../templates", __FILE__)
|
5
|
+
|
6
|
+
desc "Creates a configuration file for a specific application context (e.g. admin). Takes context path as argument (e.g. admin or internal/wiki) to create config/features/#{context_path}.yml"
|
7
|
+
def copy_config
|
8
|
+
template "config/features.yml", "config/features/#{file_path}.yml"
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -3,14 +3,16 @@ module AbstractFeatureBranch
|
|
3
3
|
class InstallGenerator < Rails::Generators::Base
|
4
4
|
source_root File.expand_path("../../templates", __FILE__)
|
5
5
|
|
6
|
-
desc "
|
6
|
+
desc "Installs Abstract Feature Branch by generating basic configuration files, including git ignored local one."
|
7
7
|
def copy_config
|
8
|
-
template "config/features.yml", "config/features.yml"
|
8
|
+
template "config/features.example.yml", "config/features.yml"
|
9
9
|
template "config/features.local.yml", "config/features.local.yml"
|
10
10
|
append_to_file '.gitignore', <<-GIT_IGNORE_CONTENT
|
11
11
|
|
12
12
|
#abstract_feature_branch local configuration file
|
13
|
-
config/features.local.yml
|
13
|
+
/config/features.local.yml
|
14
|
+
/config/features/*.local.yml
|
15
|
+
/config/features/**/*.local.yml
|
14
16
|
GIT_IGNORE_CONTENT
|
15
17
|
end
|
16
18
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
defaults: &defaults
|
2
|
+
feature1: true
|
3
|
+
feature2: true
|
4
|
+
feature3: false
|
5
|
+
|
6
|
+
development:
|
7
|
+
<<: *defaults
|
8
|
+
|
9
|
+
test:
|
10
|
+
<<: *defaults
|
11
|
+
|
12
|
+
staging:
|
13
|
+
<<: *defaults
|
14
|
+
feature2: false
|
15
|
+
|
16
|
+
production:
|
17
|
+
<<: *defaults
|
18
|
+
feature1: false
|
19
|
+
feature2: false
|
@@ -1,5 +1,5 @@
|
|
1
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
|
2
|
+
# It is recommended to use this file only for temporary overrides. Once done, make final change in main .yml
|
3
3
|
defaults: &defaults
|
4
4
|
|
5
5
|
development:
|
@@ -1,7 +1,4 @@
|
|
1
1
|
defaults: &defaults
|
2
|
-
feature1: true
|
3
|
-
feature2: true
|
4
|
-
feature3: false
|
5
2
|
|
6
3
|
development:
|
7
4
|
<<: *defaults
|
@@ -11,9 +8,6 @@ test:
|
|
11
8
|
|
12
9
|
staging:
|
13
10
|
<<: *defaults
|
14
|
-
feature2: false
|
15
11
|
|
16
12
|
production:
|
17
|
-
<<: *defaults
|
18
|
-
feature1: false
|
19
|
-
feature2: false
|
13
|
+
<<: *defaults
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
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.6.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-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: deep_merge
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.0.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.0.0
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: rspec
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -74,9 +88,17 @@ files:
|
|
74
88
|
- README.md
|
75
89
|
- VERSION
|
76
90
|
- abstract_feature_branch.gemspec
|
91
|
+
- config/features/admin.local.yml
|
92
|
+
- config/features/admin.yml
|
93
|
+
- config/features/internal/wiki.local.yml
|
94
|
+
- config/features/internal/wiki.yml
|
95
|
+
- config/features/public.local.yml
|
96
|
+
- config/features/public.yml
|
77
97
|
- lib/abstract_feature_branch.rb
|
78
98
|
- lib/ext/feature_branch.rb
|
99
|
+
- lib/generators/abstract_feature_branch/context_generator.rb
|
79
100
|
- lib/generators/abstract_feature_branch/install_generator.rb
|
101
|
+
- lib/generators/templates/config/features.example.yml
|
80
102
|
- lib/generators/templates/config/features.local.yml
|
81
103
|
- lib/generators/templates/config/features.yml
|
82
104
|
homepage: http://github.com/AndyObtiva/abstract_feature_branch
|