abstract_feature_branch 1.2.1 → 1.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +10 -3
- data/VERSION +1 -1
- data/abstract_feature_branch.gemspec +3 -2
- data/lib/abstract_feature_branch.rb +19 -62
- data/lib/abstract_feature_branch/configuration.rb +55 -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: 171dc3b05d9257bd90a08f4675cb4be9649e0c1e
|
4
|
+
data.tar.gz: caa97d72c5866c47023ce82bba130b747e526936
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 92bc2cf8e35055a73a2e44a276b89cd5d3a27b2e499ed176243f836046392bb2f38109aa50b38a75b6c8ab254dc7695796ee0fb5b89d5e8ea652dc29ffc76edb
|
7
|
+
data.tar.gz: b63edde1fb4b6beeece611e173dc002a56ff00a577d10003d769b1f21945a9b9e924663b87dfdefe673b3093c741e4ce6becc65e9f7d5af85b6b32efee1de76f
|
data/README.md
CHANGED
@@ -3,6 +3,13 @@ Abstract Feature Branch
|
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/abstract_feature_branch.png)](http://badge.fury.io/rb/abstract_feature_branch)
|
4
4
|
[![Build Status](https://api.travis-ci.org/AndyObtiva/abstract_feature_branch.png?branch=master)](https://travis-ci.org/AndyObtiva/abstract_feature_branch)
|
5
5
|
[![Coverage Status](https://coveralls.io/repos/AndyObtiva/abstract_feature_branch/badge.png?branch=master)](https://coveralls.io/r/AndyObtiva/abstract_feature_branch?branch=master)
|
6
|
+
[![Code Climate](https://codeclimate.com/github/AndyObtiva/abstract_feature_branch.png)](https://codeclimate.com/github/AndyObtiva/abstract_feature_branch)
|
7
|
+
|
8
|
+
**As Featured In**
|
9
|
+
|
10
|
+
[![Factor 75](https://dzd6ppgm28vds.cloudfront.net/assets/logo-b190de0b423855600e216d490fc160ad.png)](https://www.factor75.com)..[![Character Business Card](https://characterbusinesscard.s3.amazonaws.com/assets/Character-Business-Card-Logo-Desktop-44eb97b18b0a100488ba9c322343b91a.png)](https://www.characterbusinesscard.com)..[![Early Shares](http://early-shares-assets-production.s3.amazonaws.com/assets/logo2x-67fadfc8bb942ba92cca60c464010f1f.png)](https://www.earlyshares.com)
|
11
|
+
|
12
|
+
|
6
13
|
|
7
14
|
abstract_feature_branch is a Rails gem that enables developers to easily branch by abstraction as per this pattern:
|
8
15
|
http://paulhammant.com/blog/branch_by_abstraction.html
|
@@ -37,15 +44,15 @@ Setup
|
|
37
44
|
### Rails Application Use
|
38
45
|
|
39
46
|
1. Configure Rubygem
|
40
|
-
- Rails (~> 4.0.0 or ~> 3.0): Add the following to Gemfile <pre>gem 'abstract_feature_branch', '1.
|
41
|
-
- Rails (~> 2.0): Add the following to config/environment.rb <pre>config.gem 'abstract_feature_branch', :version => '1.
|
47
|
+
- Rails (~> 4.0.0 or ~> 3.0): Add the following to Gemfile <pre>gem 'abstract_feature_branch', '1.2.2'</pre>
|
48
|
+
- Rails (~> 2.0): Add the following to config/environment.rb <pre>config.gem 'abstract_feature_branch', :version => '1.2.2'</pre>
|
42
49
|
2. Generate <code>config/initializers/abstract_feature_branch.rb</code>, <code>lib/tasks/abstract_feature_branch.rake</code>, <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>
|
43
50
|
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> (more details under [**instructions**](#instructions))
|
44
51
|
4. (Optional) Customize configuration in <code>config/initializers/abstract_feature_branch.rb</code> (can be useful for changing location of feature files in Rails application, configuring Redis for per-user feature enablement, or troubleshooting a specific Rails environment feature configuration)
|
45
52
|
|
46
53
|
### Ruby Application General Use
|
47
54
|
|
48
|
-
1. <pre>gem install abstract_feature_branch -v 1.
|
55
|
+
1. <pre>gem install abstract_feature_branch -v 1.2.2</pre>
|
49
56
|
2. Add code <code>require 'abstract_feature_branch'</code>
|
50
57
|
3. Create <code>config/features.yml</code> under <code>AbstractFeatureBranch.application_root</code> and fill it with content similar to that of the sample <code>config/features.yml</code> mentioned under [**instructions**](#instructions).
|
51
58
|
4. (Optional) Create <code>config/features.local.yml</code> under <code>AbstractFeatureBranch.application_root</code> (more details under [**instructions**](#instructions))
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.2.
|
1
|
+
1.2.2
|
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "abstract_feature_branch"
|
8
|
-
s.version = "1.2.
|
8
|
+
s.version = "1.2.2"
|
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 = "2014-
|
12
|
+
s.date = "2014-02-23"
|
13
13
|
s.description = "abstract_feature_branch is a Rails gem that enables developers to easily branch by abstraction as per this pattern:\nhttp://paulhammant.com/blog/branch_by_abstraction.html\n\nIt is a productivity and fault tolerance enhancing team practice that has been utilized by professional software development\nteams at large corporations, such as Sears and Groupon.\n\nIt provides the ability to wrap blocks of code with an abstract feature branch name, and then\nspecify in a configuration file which features to be switched on or off.\n\nThe goal is to build out upcoming features in the same source code repository branch, regardless of whether all are\ncompleted by the next release date or not, thus increasing team productivity by preventing integration delays.\nDevelopers then disable in-progress features until they are ready to be switched on in production, yet enable them\nlocally and in staging environments for in-progress testing.\n\nThis gives developers the added benefit of being able to switch a feature off after release should big problems arise\nfor a high risk feature.\n\nabstract_feature_branch additionally supports DDD's pattern of\nBounded Contexts by allowing developers to configure\ncontext-specific feature files if needed.\n"
|
14
14
|
s.extra_rdoc_files = [
|
15
15
|
"LICENSE.txt",
|
@@ -30,6 +30,7 @@ Gem::Specification.new do |s|
|
|
30
30
|
"config/features/public.local.yml",
|
31
31
|
"config/features/public.yml",
|
32
32
|
"lib/abstract_feature_branch.rb",
|
33
|
+
"lib/abstract_feature_branch/configuration.rb",
|
33
34
|
"lib/abstract_feature_branch/file_beautifier.rb",
|
34
35
|
"lib/ext/feature_branch.rb",
|
35
36
|
"lib/generators/abstract_feature_branch/context_generator.rb",
|
@@ -12,61 +12,19 @@ end
|
|
12
12
|
require 'logger' unless defined?(Rails) && Rails.logger
|
13
13
|
require 'deep_merge' unless {}.respond_to?(:deep_merge!)
|
14
14
|
|
15
|
+
require File.join(File.dirname(__FILE__), 'abstract_feature_branch', 'configuration')
|
16
|
+
|
15
17
|
module AbstractFeatureBranch
|
16
18
|
ENV_FEATURE_PREFIX = "abstract_feature_branch_"
|
17
19
|
|
18
20
|
class << self
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
def
|
23
|
-
@
|
24
|
-
end
|
25
|
-
def initialize_application_root
|
26
|
-
self.application_root = defined?(Rails) ? Rails.root : '.'
|
27
|
-
end
|
28
|
-
def application_environment
|
29
|
-
@application_environment ||= initialize_application_environment
|
30
|
-
end
|
31
|
-
def application_environment=(environment)
|
32
|
-
@application_environment = environment
|
33
|
-
end
|
34
|
-
def initialize_application_environment
|
35
|
-
self.application_environment = defined?(Rails) ? Rails.env.to_s : ENV['APP_ENV'] || 'development'
|
36
|
-
end
|
37
|
-
def logger
|
38
|
-
@logger ||= initialize_logger
|
39
|
-
end
|
40
|
-
def logger=(logger)
|
41
|
-
@logger = logger
|
42
|
-
end
|
43
|
-
def initialize_logger
|
44
|
-
self.logger = defined?(Rails) && Rails.logger ? Rails.logger : Logger.new(STDOUT)
|
45
|
-
end
|
46
|
-
def cacheable
|
47
|
-
@cacheable ||= initialize_cacheable
|
48
|
-
end
|
49
|
-
def cacheable=(cacheable)
|
50
|
-
@cacheable = cacheable
|
51
|
-
end
|
52
|
-
def initialize_cacheable
|
53
|
-
self.cacheable = {
|
54
|
-
:development => false,
|
55
|
-
:test => true,
|
56
|
-
:staging => true,
|
57
|
-
:production => true
|
58
|
-
}
|
59
|
-
end
|
60
|
-
def user_features_storage
|
61
|
-
@user_features_storage ||= initialize_user_features_storage
|
62
|
-
end
|
63
|
-
def user_features_storage=(user_features_storage)
|
64
|
-
@user_features_storage = user_features_storage
|
65
|
-
end
|
66
|
-
def initialize_user_features_storage
|
67
|
-
require 'redis'
|
68
|
-
self.user_features_storage = Redis.new
|
21
|
+
extend Forwardable
|
22
|
+
def_delegators :configuration, :application_root, :application_root=, :initialize_application_root, :application_environment, :application_environment=, :initialize_application_environment, :logger, :logger=, :initialize_logger, :cacheable, :cacheable=, :initialize_cacheable, :user_features_storage, :user_features_storage=, :initialize_user_features_storage
|
23
|
+
|
24
|
+
def configuration
|
25
|
+
@configuration ||= Configuration.new
|
69
26
|
end
|
27
|
+
|
70
28
|
def environment_variable_overrides
|
71
29
|
@environment_variable_overrides ||= load_environment_variable_overrides
|
72
30
|
end
|
@@ -78,24 +36,14 @@ module AbstractFeatureBranch
|
|
78
36
|
end
|
79
37
|
def load_local_features
|
80
38
|
@local_features = {}
|
81
|
-
|
82
|
-
@local_features.deep_merge!(downcase_feature_hash_keys(YAML.load_file(feature_configuration_file)))
|
83
|
-
end
|
84
|
-
main_local_features_file = File.join(application_root, 'config', 'features.local.yml')
|
85
|
-
@local_features.deep_merge!(downcase_feature_hash_keys(YAML.load_file(main_local_features_file))) if File.exists?(main_local_features_file)
|
86
|
-
@local_features
|
39
|
+
load_specific_features(@local_features, '.local.yml')
|
87
40
|
end
|
88
41
|
def features
|
89
42
|
@features ||= load_features
|
90
43
|
end
|
91
44
|
def load_features
|
92
45
|
@features = {}
|
93
|
-
|
94
|
-
@features.deep_merge!(downcase_feature_hash_keys(YAML.load_file(feature_configuration_file)))
|
95
|
-
end
|
96
|
-
main_features_file = File.join(application_root, 'config', 'features.yml')
|
97
|
-
@features.deep_merge!(downcase_feature_hash_keys(YAML.load_file(main_features_file))) if File.exists?(main_features_file)
|
98
|
-
@features
|
46
|
+
load_specific_features(@features, '.yml')
|
99
47
|
end
|
100
48
|
# performance optimization via caching of feature values resolved through environment variable overrides and local features
|
101
49
|
def environment_features(environment)
|
@@ -141,6 +89,15 @@ module AbstractFeatureBranch
|
|
141
89
|
|
142
90
|
private
|
143
91
|
|
92
|
+
def load_specific_features(features_hash, extension)
|
93
|
+
Dir.glob(File.join(application_root, 'config', 'features', '**', "*#{extension}")).each do |feature_configuration_file|
|
94
|
+
features_hash.deep_merge!(downcase_feature_hash_keys(YAML.load_file(feature_configuration_file)))
|
95
|
+
end
|
96
|
+
main_local_features_file = File.join(application_root, 'config', "features#{extension}")
|
97
|
+
features_hash.deep_merge!(downcase_feature_hash_keys(YAML.load_file(main_local_features_file))) if File.exists?(main_local_features_file)
|
98
|
+
features_hash
|
99
|
+
end
|
100
|
+
|
144
101
|
def featureize_keys(hash)
|
145
102
|
Hash[hash.map {|k, v| [k.sub(ENV_FEATURE_PREFIX, ''), v]}]
|
146
103
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module AbstractFeatureBranch
|
2
|
+
class Configuration
|
3
|
+
def application_root
|
4
|
+
@application_root ||= initialize_application_root
|
5
|
+
end
|
6
|
+
def application_root=(path)
|
7
|
+
@application_root = path
|
8
|
+
end
|
9
|
+
def initialize_application_root
|
10
|
+
self.application_root = defined?(Rails) ? Rails.root : '.'
|
11
|
+
end
|
12
|
+
def application_environment
|
13
|
+
@application_environment ||= initialize_application_environment
|
14
|
+
end
|
15
|
+
def application_environment=(environment)
|
16
|
+
@application_environment = environment
|
17
|
+
end
|
18
|
+
def initialize_application_environment
|
19
|
+
self.application_environment = defined?(Rails) ? Rails.env.to_s : ENV['APP_ENV'] || 'development'
|
20
|
+
end
|
21
|
+
def logger
|
22
|
+
@logger ||= initialize_logger
|
23
|
+
end
|
24
|
+
def logger=(logger)
|
25
|
+
@logger = logger
|
26
|
+
end
|
27
|
+
def initialize_logger
|
28
|
+
self.logger = defined?(Rails) && Rails.logger ? Rails.logger : Logger.new(STDOUT)
|
29
|
+
end
|
30
|
+
def cacheable
|
31
|
+
@cacheable ||= initialize_cacheable
|
32
|
+
end
|
33
|
+
def cacheable=(cacheable)
|
34
|
+
@cacheable = cacheable
|
35
|
+
end
|
36
|
+
def initialize_cacheable
|
37
|
+
self.cacheable = {
|
38
|
+
:development => false,
|
39
|
+
:test => true,
|
40
|
+
:staging => true,
|
41
|
+
:production => true
|
42
|
+
}
|
43
|
+
end
|
44
|
+
def user_features_storage
|
45
|
+
@user_features_storage ||= initialize_user_features_storage
|
46
|
+
end
|
47
|
+
def user_features_storage=(user_features_storage)
|
48
|
+
@user_features_storage = user_features_storage
|
49
|
+
end
|
50
|
+
def initialize_user_features_storage
|
51
|
+
require 'redis'
|
52
|
+
self.user_features_storage = Redis.new
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
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: 1.2.
|
4
|
+
version: 1.2.2
|
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: 2014-
|
11
|
+
date: 2014-02-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: deep_merge
|
@@ -94,6 +94,7 @@ files:
|
|
94
94
|
- config/features/public.local.yml
|
95
95
|
- config/features/public.yml
|
96
96
|
- lib/abstract_feature_branch.rb
|
97
|
+
- lib/abstract_feature_branch/configuration.rb
|
97
98
|
- lib/abstract_feature_branch/file_beautifier.rb
|
98
99
|
- lib/ext/feature_branch.rb
|
99
100
|
- lib/generators/abstract_feature_branch/context_generator.rb
|