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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fb2107fd2312b62c1864e7b60ce08c24620229c0
4
- data.tar.gz: e9a07cf69f21a778a4f9245e5c53e3b2e71f4d53
3
+ metadata.gz: e0ac105f82f311d41f4de3e74b47d80a97f256ae
4
+ data.tar.gz: 2a6c82543d466d7f0783e325d4a351e81ccac8d3
5
5
  SHA512:
6
- metadata.gz: bca12505dd8a199f432261d7498ed5378b2199a36236bca57cb13613c3f83b58acfefba5faa18cf9a223d5bdc29a451326429b9ae31bfa9f0923475d5d2e8c0a
7
- data.tar.gz: f1f7d0a7db3534745891a034fd357557b93299b9ac86fb4879715a5bae84a53d825684dfe49d00557b3137f51b165d0cd3d6ca26cd9744178226c2f511fca650
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.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>
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
- 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.
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.5.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.5.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-22"
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"])
@@ -0,0 +1,14 @@
1
+ defaults: &defaults
2
+ admin_feature3: true
3
+
4
+ development:
5
+ <<: *defaults
6
+
7
+ test:
8
+ <<: *defaults
9
+
10
+ staging:
11
+ <<: *defaults
12
+
13
+ production:
14
+ <<: *defaults
@@ -0,0 +1,16 @@
1
+ defaults: &defaults
2
+ admin_feature1: true
3
+ admin_feature2: false
4
+ admin_feature3: false
5
+
6
+ development:
7
+ <<: *defaults
8
+
9
+ test:
10
+ <<: *defaults
11
+
12
+ staging:
13
+ <<: *defaults
14
+
15
+ production:
16
+ <<: *defaults
@@ -0,0 +1,14 @@
1
+ defaults: &defaults
2
+ wiki_feature3: true
3
+
4
+ development:
5
+ <<: *defaults
6
+
7
+ test:
8
+ <<: *defaults
9
+
10
+ staging:
11
+ <<: *defaults
12
+
13
+ production:
14
+ <<: *defaults
@@ -0,0 +1,16 @@
1
+ defaults: &defaults
2
+ wiki_feature1: true
3
+ wiki_feature2: false
4
+ wiki_feature3: false
5
+
6
+ development:
7
+ <<: *defaults
8
+
9
+ test:
10
+ <<: *defaults
11
+
12
+ staging:
13
+ <<: *defaults
14
+
15
+ production:
16
+ <<: *defaults
@@ -0,0 +1,14 @@
1
+ defaults: &defaults
2
+ public_feature3: true
3
+
4
+ development:
5
+ <<: *defaults
6
+
7
+ test:
8
+ <<: *defaults
9
+
10
+ staging:
11
+ <<: *defaults
12
+
13
+ production:
14
+ <<: *defaults
@@ -0,0 +1,16 @@
1
+ defaults: &defaults
2
+ public_feature1: true
3
+ public_feature2: false
4
+ public_feature3: false
5
+
6
+ development:
7
+ <<: *defaults
8
+
9
+ test:
10
+ <<: *defaults
11
+
12
+ staging:
13
+ <<: *defaults
14
+
15
+ production:
16
+ <<: *defaults
@@ -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 ||= Hash[YAML.load_file(File.join(Rails.root, 'config', 'features.local.yml')).map {|k, v| [k, v && downcase_keys(v)]}]
21
- @local_features
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 ||= Hash[YAML.load_file(File.join(Rails.root, 'config', 'features.yml')).map {|k, v| [k, v && downcase_keys(v)]}]
25
- @features
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 "Creates Abstract Feature Branch configuration files in your application."
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 config/features.yml
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.5.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-22 00:00:00.000000000 Z
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