abstract_feature_branch 1.2.2 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +9 -19
- data/CHANGELOG.md +11 -0
- data/LICENSE.txt +1 -1
- data/README.md +71 -43
- data/TODO.md +3 -0
- data/VERSION +1 -1
- data/abstract_feature_branch.gemspec +33 -24
- data/img/BigAstronaut-Logo.png +0 -0
- data/img/EarlyShares-Logo.svg +22 -0
- data/img/Factor75-Logo.svg +54 -0
- data/lib/abstract_feature_branch/configuration.rb +16 -7
- data/lib/abstract_feature_branch.rb +72 -6
- data/lib/ext/feature_branch.rb +4 -3
- data/lib/generators/templates/config/initializers/abstract_feature_branch.rb +4 -5
- data/ruby187.Gemfile +6 -5
- data/spec/ext/feature_branch__feature_branch_per_user_spec.rb +18 -9
- data/spec/ext/feature_branch__feature_branch_spec.rb +23 -0
- data/spec/ext/feature_branch__feature_enabled_spec.rb +14 -0
- metadata +81 -26
- data/ruby187.Gemfile.lock +0 -63
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8f0025726fa5c4de15b39b5b51ba6e180cf156ccccf86d661e3f9e4982d899a4
|
4
|
+
data.tar.gz: 78dae9664bcce67b70f2768ff9e866d9b47155ec1c8ef7c9d0f7d341f4866249
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 96649ca6005883f9765ac18d115e08eb1ac8c9aa165cba039b07aa3d843b29bc8f70cf8ec768bdb6e491e2982eb04f9f0959210418feeccd5da242df6c57bdd9
|
7
|
+
data.tar.gz: 0d305129dc28c997d740e1a6f0dbe2dfefddd1a379dfc64c4fc42287c3fbaf965dde0358287a91346af7aef201a6b1480be5d02f66dcff91464d64cf7f9e850e
|
data/.travis.yml
CHANGED
@@ -1,40 +1,30 @@
|
|
1
1
|
language: ruby
|
2
2
|
services:
|
3
3
|
- redis-server
|
4
|
+
addons:
|
5
|
+
code_climate:
|
6
|
+
repo_token: 535ff40cf55554362b2f48b85e913a7362f0cf3e51638455dab456006258c5a0
|
4
7
|
rvm:
|
8
|
+
- 2.2.1
|
9
|
+
- 2.1.5
|
5
10
|
- 2.0.0
|
6
11
|
- 1.9.3
|
7
12
|
- 1.8.7
|
8
13
|
- ree
|
9
|
-
- jruby-18mode
|
10
|
-
- jruby-19mode
|
11
|
-
- rbx-2.1.1
|
12
14
|
gemfile:
|
13
15
|
- Gemfile
|
14
16
|
- ruby187.Gemfile
|
15
17
|
matrix:
|
16
18
|
exclude:
|
19
|
+
- rvm: 2.2.1
|
20
|
+
gemfile: ruby187.Gemfile
|
21
|
+
- rvm: 2.1.5
|
22
|
+
gemfile: ruby187.Gemfile
|
17
23
|
- rvm: 2.0.0
|
18
24
|
gemfile: ruby187.Gemfile
|
19
25
|
- rvm: 1.9.3
|
20
26
|
gemfile: ruby187.Gemfile
|
21
27
|
- rvm: 1.8.7
|
22
28
|
gemfile: Gemfile
|
23
|
-
- rvm: 1.8.7
|
24
|
-
gemfile: ruby187.Gemfile
|
25
|
-
- rvm: ree
|
26
|
-
gemfile: Gemfile
|
27
29
|
- rvm: ree
|
28
|
-
gemfile: ruby187.Gemfile
|
29
|
-
- rvm: jruby-18mode
|
30
|
-
gemfile: Gemfile
|
31
|
-
- rvm: jruby-18mode
|
32
|
-
gemfile: ruby187.Gemfile
|
33
|
-
- rvm: jruby-19mode
|
34
|
-
gemfile: ruby187.Gemfile
|
35
|
-
- rvm: jruby-19mode
|
36
|
-
gemfile: Gemfile
|
37
|
-
- rvm: rbx-2.1.1
|
38
30
|
gemfile: Gemfile
|
39
|
-
- rvm: rbx-2.1.1
|
40
|
-
gemfile: ruby187.Gemfile
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# Change Log
|
2
|
+
|
3
|
+
## 1.3.0
|
4
|
+
|
5
|
+
- Officially support newer `redis` client gem version 5
|
6
|
+
- Support (general-user) Redis Overrides (similar to Environment Variable Overrides)
|
7
|
+
- Remove `redis` gem from required dependencies to allow using `abstract_feature_branch` without Redis
|
8
|
+
- Make configuration of Redis optional in generated Rails initializer
|
9
|
+
- Provide alias of `AbstractFeatureBranch::Configuration#feature_store` to `AbstractFeatureBranch::Configuration#user_features_storage` (plus corresponding aliases `feature_store=` and `initialize_feature_store`)
|
10
|
+
- Document support for Rails 7 and Redis Server 7
|
11
|
+
- Add gem post install instructions, including how to run the Rails generators and install/use Redis
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,21 +1,13 @@
|
|
1
|
-
Abstract Feature Branch
|
2
|
-
=======================
|
1
|
+
# Abstract Feature Branch 1.3.0
|
3
2
|
[![Gem Version](https://badge.fury.io/rb/abstract_feature_branch.png)](http://badge.fury.io/rb/abstract_feature_branch)
|
4
3
|
[![Build Status](https://api.travis-ci.org/AndyObtiva/abstract_feature_branch.png?branch=master)](https://travis-ci.org/AndyObtiva/abstract_feature_branch)
|
5
4
|
[![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
5
|
[![Code Climate](https://codeclimate.com/github/AndyObtiva/abstract_feature_branch.png)](https://codeclimate.com/github/AndyObtiva/abstract_feature_branch)
|
7
6
|
|
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
|
-
|
13
|
-
|
14
7
|
abstract_feature_branch is a Rails gem that enables developers to easily branch by abstraction as per this pattern:
|
15
8
|
http://paulhammant.com/blog/branch_by_abstraction.html
|
16
9
|
|
17
|
-
It is a productivity and fault tolerance enhancing team practice
|
18
|
-
teams at large corporations, such as [Sears](http://www.sears.com) and [Groupon](http://www.groupon.com).
|
10
|
+
It is a productivity and fault tolerance enhancing team practice.
|
19
11
|
|
20
12
|
It provides the ability to wrap blocks of code with an abstract feature branch name, and then
|
21
13
|
specify in a configuration file which features to be switched on or off.
|
@@ -34,9 +26,10 @@ context-specific feature files if needed.
|
|
34
26
|
|
35
27
|
Requirements
|
36
28
|
------------
|
37
|
-
- Ruby
|
38
|
-
-
|
39
|
-
-
|
29
|
+
- Ruby (between `~> 3.1.0` and `~> 1.8.7`) ([click for a list of tested Ruby versions](https://travis-ci.org/AndyObtiva/abstract_feature_branch))
|
30
|
+
- [Optional] Rails (between `~> 7.0` and `~> 2.0`)
|
31
|
+
- [Optional] Redis Server (between `~> 7.0` and `~> 2.0`)
|
32
|
+
- [Optional] Redis client gem (between `~> 5.0` and `~> 3.0`)
|
40
33
|
|
41
34
|
Setup
|
42
35
|
-----
|
@@ -44,25 +37,27 @@ Setup
|
|
44
37
|
### Rails Application Use
|
45
38
|
|
46
39
|
1. Configure Rubygem
|
47
|
-
-
|
48
|
-
-
|
40
|
+
- With `rails` between `~> 7.0` and `~> 2.0`: Add the following to Gemfile <pre>gem 'abstract_feature_branch', '~> 1.3.0'</pre>
|
41
|
+
- With `rails` `~> 2.0` only: Add the following to config/environment.rb <pre>config.gem 'abstract_feature_branch', :version => '1.3.0'</pre>
|
49
42
|
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>
|
50
|
-
3.
|
51
|
-
4.
|
43
|
+
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
|
+
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
|
+
5. [Optional] Redis Server (between `~> 7.0` and `~> 3.0`): Install view [Homebrew](https://brew.sh/) with `brew install redis`
|
46
|
+
6. [Optional] `redis` client gem (between `~> 5.0` and `~> 3.0`): Add the following to Gemfile above `abstract_feature_branch` <pre>gem 'redis', '~> 5.0.5'</pre>
|
52
47
|
|
53
48
|
### Ruby Application General Use
|
54
49
|
|
55
|
-
1. <pre>gem install abstract_feature_branch -v 1.
|
50
|
+
1. <pre>gem install abstract_feature_branch -v 1.3.0</pre>
|
56
51
|
2. Add code <code>require 'abstract_feature_branch'</code>
|
57
52
|
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).
|
58
|
-
4.
|
59
|
-
5.
|
60
|
-
6.
|
61
|
-
7.
|
62
|
-
8.
|
63
|
-
9.
|
64
|
-
10.
|
65
|
-
11.
|
53
|
+
4. [Optional] Create <code>config/features.local.yml</code> under <code>AbstractFeatureBranch.application_root</code> (more details under [**instructions**](#instructions))
|
54
|
+
5. [Optional] Create <code>config/features/[context_path].yml</code> under <code>AbstractFeatureBranch.application_root</code> (more details under [**instructions**](#instructions))
|
55
|
+
6. [Optional] Add code <code>AbstractFeatureBranch.application_root = "[your_application_path]"</code> to configure the location of feature files (it defaults to <code>'.'</code>)
|
56
|
+
7. [Optional] Add code <code>AbstractFeatureBranch.application_environment = "[your_application_environment]"</code> (it defaults to <code>'development'</code>). Alternatively, you can set <code>ENV['APP_ENV']</code> before the <code>require</code> statement or an an external environment variable.
|
57
|
+
8. [Optional] Add code <code>AbstractFeatureBranch.logger = "[your_application_logger]"</code> (it defaults to a new instance of Ruby <code>Logger</code>. Must use a logger with <code>info</code> and <code>warn</code> methods).
|
58
|
+
9. [Optional] Add code <code>AbstractFeatureBranch.cacheable = {[environment] => [true/false]}</code> to indicate cacheability of loaded feature files for enhanced performance (it defaults to true for every environment other than development).
|
59
|
+
10. [Optional] Add code <code>AbstractFeatureBranch.load_application_features</code> to pre-load application features for improved first-use performance
|
60
|
+
11. [Optional] Add code <code>AbstractFeatureBranch.feature_store = Redis.new(options)</code> to configure Redis for overrides or per-user feature enablement
|
66
61
|
|
67
62
|
Instructions
|
68
63
|
------------
|
@@ -264,6 +259,45 @@ The benefits can be achieved more easily via <code>config/features.local.yml</co
|
|
264
259
|
However, environment variable overrides are implemented to support overriding feature configuration for a Heroku deployed
|
265
260
|
application more easily.
|
266
261
|
|
262
|
+
Redis Overrides
|
263
|
+
---------------
|
264
|
+
|
265
|
+
Prerequisites: Redis server and client (`redis` gem) and optional Redis configuration of `AbstractFeatureBranch.feature_store` in `config/initializers/abstract_feature_branch.rb`
|
266
|
+
|
267
|
+
To be able to override feature configuration in a production environment, you can utilize Redis Overrides.
|
268
|
+
|
269
|
+
Alternatively, you may use Redis Overrides as your main source of feature configuration if you prefer that instead of relying on YAML files.
|
270
|
+
|
271
|
+
You can override feature configuration with Redis hash values by calling `AbstractFeatureBranch#set_store_feature` in `rails console` (or `irb` after requiring `redis` and `abstract_feature_branch`):
|
272
|
+
|
273
|
+
```ruby
|
274
|
+
AbstractFeatureBranch.set_store_feature('feature1', true)
|
275
|
+
```
|
276
|
+
|
277
|
+
Behind the scenes, that is the equivalent of the following Redis client invocation, which stores a hash value in a `abstract_feature_branch` key:
|
278
|
+
|
279
|
+
```ruby
|
280
|
+
AbstractFeatureBranch.configuration.feature_store.hset('abstract_feature_branch', 'feature1', 'true')
|
281
|
+
```
|
282
|
+
|
283
|
+
To remove a Redis override, you can run the following in `rails console` (or `irb`):
|
284
|
+
|
285
|
+
```ruby
|
286
|
+
AbstractFeatureBranch.delete_store_feature('feature1')
|
287
|
+
```
|
288
|
+
|
289
|
+
To get a Redis override value, you can run the following in `rails console` (or `irb`):
|
290
|
+
|
291
|
+
```ruby
|
292
|
+
AbstractFeatureBranch.get_store_feature('feature1')
|
293
|
+
```
|
294
|
+
|
295
|
+
To get an array of all Redis Override features, you can run the following in `rails console` (or `irb`):
|
296
|
+
|
297
|
+
```ruby
|
298
|
+
AbstractFeatureBranch.get_store_features
|
299
|
+
```
|
300
|
+
|
267
301
|
Heroku
|
268
302
|
------
|
269
303
|
|
@@ -284,7 +318,7 @@ Removing an environment variable override:
|
|
284
318
|
### Recommendation
|
285
319
|
|
286
320
|
It is recommended that you use environment variable overrides on Heroku only as an emergency or temporary measure.
|
287
|
-
Afterward, make the change
|
321
|
+
Afterward, make the change official in config/features.yml, deploy, and remove the environment variable override for the long term.
|
288
322
|
|
289
323
|
### Gotcha with abstract feature branching in CSS and JS files
|
290
324
|
|
@@ -308,18 +342,18 @@ the former if overlap in features occurs:
|
|
308
342
|
3. Context-specific local feature file overrides: <code>config/features/**/*.local.yml</code>
|
309
343
|
4. Main local feature file override: <code>config/features.local.yml</code>
|
310
344
|
5. Environment variable overrides
|
345
|
+
6. Redis overrides
|
311
346
|
|
312
347
|
Rails Initializer
|
313
348
|
-----------------
|
314
349
|
|
315
|
-
Here is the content of the generated initializer (<code>config/initializers/abstract_feature_branch.rb</code>), which contains instructions on how to customize via [dependency injection](http://en.wikipedia.org/wiki/Dependency_injection):
|
350
|
+
Here is the content of the generated initializer [with `redis` client gem added] (<code>config/initializers/abstract_feature_branch.rb</code>), which contains instructions on how to customize via [dependency injection](http://en.wikipedia.org/wiki/Dependency_injection):
|
316
351
|
|
317
|
-
>
|
352
|
+
> # Storage system for features (other than YAML/Env-Vars). Right now, only Redis is supported.
|
353
|
+
> # AbstractFeatureBranch.feature_store = Redis.new
|
318
354
|
>
|
319
|
-
> # Storage for user features, customizable over here. Right now, only a Redis client is supported.
|
320
355
|
> # The following example line works with Heroku Redis To Go while still operating on local Redis for local development
|
321
|
-
> # AbstractFeatureBranch.
|
322
|
-
> AbstractFeatureBranch.user_features_storage = Redis.new
|
356
|
+
> # AbstractFeatureBranch.feature_store = Redis.new(:url => ENV['REDISTOGO_URL'])
|
323
357
|
>
|
324
358
|
> # Application root where config/features.yml or config/features/ is found
|
325
359
|
> AbstractFeatureBranch.application_root = Rails.root
|
@@ -342,8 +376,6 @@ Here is the content of the generated initializer (<code>config/initializers/abst
|
|
342
376
|
> # Pre-load application features to improve performance of first web-page hit
|
343
377
|
> AbstractFeatureBranch.load_application_features unless Rails.env.development?
|
344
378
|
|
345
|
-
TODO Document in Heroku (:url => ENV['REDISTOGO_URL'])
|
346
|
-
|
347
379
|
Rake Task
|
348
380
|
---------
|
349
381
|
|
@@ -437,15 +469,14 @@ after invoking the rake task, **verify** that your feature file contents are to
|
|
437
469
|
task changes.
|
438
470
|
|
439
471
|
Feature Branches vs Branch by Abstraction
|
440
|
-
|
472
|
+
-----------------------------------------
|
441
473
|
|
442
|
-
Although feature branches and branching by abstraction are similar, there are different situations that recommend each approach.
|
474
|
+
Although feature branches and branching by abstraction are similar, there are different situations that recommend each approach.
|
443
475
|
|
444
|
-
Feature branching leverages your version control software (VCS) to create a branch that is independent of your main branch. Once you write your feature, you integrate it with the rest of your code base.
|
476
|
+
Feature branching leverages your version control software (VCS) to create a branch that is independent of your main branch. Once you write your feature, you integrate it with the rest of your code base. Feature branching is ideal for developing features that can be completed within the one or two iterations. But it can become cumbersome with larger features due to the fact your code is isolated and quickly falls out of sync with your main branch. You will have to regularly rebase with your main branch or devote substantial time to resolving merge conflicts.
|
445
477
|
|
446
478
|
Branching by abstraction, on the other hand, is ideal for substantial features, i.e. ones which take many iterations to complete. This approach to branching takes place outside of your VCS. Instead, you build your feature, but wrap the code inside configurable flags. These configuration flags will allow for different behavior, depending on the runtime environment. For example, a feature would be set to "on" when your app runs in development mode, but "off" when running in "production" mode. This approach avoids the pain of constantly rebasing or resolving a myriad of merge conflict when you do attempt to integrate your feature into the larger app.
|
447
479
|
|
448
|
-
|
449
480
|
Contributing to abstract_feature_branch
|
450
481
|
---------------------------------------
|
451
482
|
|
@@ -459,17 +490,14 @@ Contributing to abstract_feature_branch
|
|
459
490
|
|
460
491
|
Committers
|
461
492
|
---------------------------------------
|
462
|
-
* [
|
493
|
+
* [Andy Maleh (Author)](https://github.com/AndyObtiva)
|
463
494
|
|
464
495
|
Contributors
|
465
496
|
---------------------------------------
|
466
|
-
* [Christian Nennemann](https://github.com/XORwell)
|
467
|
-
* [Ben Downey](https://github.com/bnd5k)
|
468
497
|
* [Mark Moschel](https://github.com/mmosche2)
|
469
498
|
|
470
499
|
Copyright
|
471
500
|
---------------------------------------
|
472
501
|
|
473
|
-
Copyright (c)
|
502
|
+
Copyright (c) 2012-2022 Andy Maleh. See [LICENSE.txt](LICENSE.txt) for
|
474
503
|
further details.
|
475
|
-
|
data/TODO.md
ADDED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.3.0
|
@@ -2,25 +2,30 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
+
# stub: abstract_feature_branch 1.3.0 ruby lib
|
5
6
|
|
6
7
|
Gem::Specification.new do |s|
|
7
|
-
s.name = "abstract_feature_branch"
|
8
|
-
s.version = "1.
|
8
|
+
s.name = "abstract_feature_branch".freeze
|
9
|
+
s.version = "1.3.0"
|
9
10
|
|
10
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.
|
12
|
-
s.
|
13
|
-
s.
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
|
+
s.require_paths = ["lib".freeze]
|
13
|
+
s.authors = ["Annas \"Andy\" Maleh".freeze]
|
14
|
+
s.date = "2022-12-11"
|
15
|
+
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.\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".freeze
|
14
16
|
s.extra_rdoc_files = [
|
17
|
+
"CHANGELOG.md",
|
15
18
|
"LICENSE.txt",
|
16
19
|
"README.md"
|
17
20
|
]
|
18
21
|
s.files = [
|
19
22
|
".coveralls.yml",
|
20
23
|
".travis.yml",
|
24
|
+
"CHANGELOG.md",
|
21
25
|
"LICENSE.txt",
|
22
26
|
"README.md",
|
23
27
|
"RELEASE_NOTES.md",
|
28
|
+
"TODO.md",
|
24
29
|
"VERSION",
|
25
30
|
"abstract_feature_branch.gemspec",
|
26
31
|
"config/features/admin.local.yml",
|
@@ -29,6 +34,9 @@ Gem::Specification.new do |s|
|
|
29
34
|
"config/features/internal/wiki.yml",
|
30
35
|
"config/features/public.local.yml",
|
31
36
|
"config/features/public.yml",
|
37
|
+
"img/BigAstronaut-Logo.png",
|
38
|
+
"img/EarlyShares-Logo.svg",
|
39
|
+
"img/Factor75-Logo.svg",
|
32
40
|
"lib/abstract_feature_branch.rb",
|
33
41
|
"lib/abstract_feature_branch/configuration.rb",
|
34
42
|
"lib/abstract_feature_branch/file_beautifier.rb",
|
@@ -41,7 +49,6 @@ Gem::Specification.new do |s|
|
|
41
49
|
"lib/generators/templates/config/initializers/abstract_feature_branch.rb",
|
42
50
|
"lib/generators/templates/lib/tasks/abstract_feature_branch.rake",
|
43
51
|
"ruby187.Gemfile",
|
44
|
-
"ruby187.Gemfile.lock",
|
45
52
|
"spec/abstract_feature_branch/file_beautifier_spec.rb",
|
46
53
|
"spec/ext/feature_branch__feature_branch_per_user_spec.rb",
|
47
54
|
"spec/ext/feature_branch__feature_branch_spec.rb",
|
@@ -64,28 +71,30 @@ Gem::Specification.new do |s|
|
|
64
71
|
"spec/fixtures/application_ugly_config_reference/config/features/public.local.yml",
|
65
72
|
"spec/fixtures/application_ugly_config_reference/config/features/public.yml"
|
66
73
|
]
|
67
|
-
s.homepage = "http://github.com/AndyObtiva/abstract_feature_branch"
|
68
|
-
s.licenses = ["MIT"]
|
69
|
-
s.
|
70
|
-
s.rubygems_version = "
|
71
|
-
s.summary = "abstract_feature_branch is a Rails gem that enables developers to easily branch by abstraction as per this pattern: http://paulhammant.com/blog/branch_by_abstraction.html"
|
74
|
+
s.homepage = "http://github.com/AndyObtiva/abstract_feature_branch".freeze
|
75
|
+
s.licenses = ["MIT".freeze]
|
76
|
+
s.post_install_message = "\nRails-only post-install instructions:\n\n1) Run the following command to generate the Rails initializer and basic feature files:\n\nrails g abstract_feature_branch:install\n\n2) Optionally, you may run this command to generate feature files per context:\n\nrails g abstract_feature_branch:context context_path\n \n3) Optionally, install Redis server with [Homebrew](https://brew.sh/) by running:\n\nbrew install redis\n\n4) Optionally, install redis client gem (required with Redis server) by adding the following line to Gemfile above abstract_feature_branch:\n\ngem 'redis', '~> 5.0.5'\n\nAfterwards, run:\n\nbundle\n\n5) Optionally, customize configuration in config/initializers/abstract_feature_branch.rb\n\n(can be useful for changing location of feature files in Rails application,\nconfiguring Redis to use for overrides and per-user feature enablement,\nand/or troubleshooting specific Rails environment feature configurations)\n\n".freeze
|
77
|
+
s.rubygems_version = "3.3.6".freeze
|
78
|
+
s.summary = "abstract_feature_branch is a Rails gem that enables developers to easily branch by abstraction as per this pattern: http://paulhammant.com/blog/branch_by_abstraction.html".freeze
|
72
79
|
|
73
80
|
if s.respond_to? :specification_version then
|
74
81
|
s.specification_version = 4
|
82
|
+
end
|
75
83
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
s.add_dependency(%q<jeweler>, ["= 1.8.8"])
|
84
|
-
end
|
84
|
+
if s.respond_to? :add_runtime_dependency then
|
85
|
+
s.add_runtime_dependency(%q<deep_merge>.freeze, ["~> 1.0.0"])
|
86
|
+
s.add_development_dependency(%q<jeweler>.freeze, ["~> 2.3.9"])
|
87
|
+
s.add_development_dependency(%q<bundler>.freeze, [">= 2.1.4"])
|
88
|
+
s.add_development_dependency(%q<rspec>.freeze, ["= 2.14.1"])
|
89
|
+
s.add_development_dependency(%q<rdoc>.freeze, ["= 5.1.0"])
|
90
|
+
s.add_development_dependency(%q<psych>.freeze, ["= 3.3.4"])
|
85
91
|
else
|
86
|
-
s.add_dependency(%q<deep_merge
|
87
|
-
s.add_dependency(%q<
|
88
|
-
s.add_dependency(%q<
|
92
|
+
s.add_dependency(%q<deep_merge>.freeze, ["~> 1.0.0"])
|
93
|
+
s.add_dependency(%q<jeweler>.freeze, ["~> 2.3.9"])
|
94
|
+
s.add_dependency(%q<bundler>.freeze, [">= 2.1.4"])
|
95
|
+
s.add_dependency(%q<rspec>.freeze, ["= 2.14.1"])
|
96
|
+
s.add_dependency(%q<rdoc>.freeze, ["= 5.1.0"])
|
97
|
+
s.add_dependency(%q<psych>.freeze, ["= 3.3.4"])
|
89
98
|
end
|
90
99
|
end
|
91
100
|
|
Binary file
|
@@ -0,0 +1,22 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
2
|
+
<svg width="181px" height="26px" viewBox="0 0 181 26" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
|
3
|
+
<!-- Generator: Sketch 3.1 (8751) - http://www.bohemiancoding.com/sketch -->
|
4
|
+
<title>es_logo</title>
|
5
|
+
<desc>Created with Sketch.</desc>
|
6
|
+
<defs></defs>
|
7
|
+
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
|
8
|
+
<g id="es_logo" sketch:type="MSLayerGroup" transform="translate(1.000000, 0.000000)">
|
9
|
+
<path d="M-0.0666666667,0.342105263 L15.2666667,0.342105263 L15.2666667,2.12105263 L1.8,2.12105263 L1.8,9.1 L13.7333333,9.1 L13.7333333,10.8789474 L1.8,10.8789474 L1.8,18.6789474 L15.4,18.6789474 L15.4,20.4578947 L-0.0666666667,20.4578947" id="Shape" fill="#3c3f40" sketch:type="MSShapeGroup"></path>
|
10
|
+
<path d="M31.4666667,20.4578947 L31.4666667,18.3368421 C30.1333333,19.9105263 28.2,20.7315789 25.8,20.7315789 C23.5333333,20.7315789 21.6666667,19.9789474 20.3333333,18.5421053 C19.0666667,17.1736842 18.4666667,15.3947368 18.4666667,13.2052632 C18.4666667,11.0157895 19.0666667,9.16842105 20.3333333,7.8 C21.6666667,6.29473684 23.5333333,5.54210526 25.8666667,5.54210526 C28.2666667,5.54210526 30.0666667,6.29473684 31.4,7.86842105 L31.4,5.61052632 L33.3333333,5.61052632 L33.3333333,20.3894737 L31.4666667,20.3894737 L31.4666667,20.4578947 Z M30,8.96315789 C29,7.93684211 27.6,7.38947368 25.7333333,7.38947368 C24,7.38947368 22.6666667,7.93684211 21.6666667,9.1 C20.7333333,10.1947368 20.3333333,11.5631579 20.3333333,13.2052632 C20.3333333,14.9157895 20.8,16.2842105 21.6666667,17.3105263 C22.6,18.4052632 24,18.9526316 25.7333333,18.9526316 C27.4666667,18.9526316 28.8,18.4052632 29.8666667,17.3105263 C30.9333333,16.2157895 31.4,14.8473684 31.4,13.2052632 C31.4666667,11.4263158 31,9.98947368 30,8.96315789 L30,8.96315789 Z" id="Shape" fill="#3C3F40" sketch:type="MSShapeGroup"></path>
|
11
|
+
<path d="M42,9.64736842 C41.2,10.9473684 40.8,12.5894737 40.8,14.5736842 L40.8,20.4578947 L38.8666667,20.4578947 L38.8666667,5.95263158 L40.8,5.95263158 L40.8,7.93684211 C42.1333333,6.36315789 44.2,5.61052632 46.9333333,5.61052632 L46.9333333,7.38947368 C44.6,7.38947368 43,8.14210526 42,9.64736842" id="Shape" fill="#3C3F40" sketch:type="MSShapeGroup"></path>
|
12
|
+
<rect id="Rectangle-path" fill="#3C3F40" sketch:type="MSShapeGroup" x="51.3333333" y="0.684210526" width="1.93333333" height="20.1157895"></rect>
|
13
|
+
<path d="M64.4,25.7947368 C62.6666667,25.7947368 61.2,25.4526316 60.0666667,24.7684211 C58.6666667,23.8789474 57.9333333,22.3736842 57.8,20.2526316 L59.7333333,20.2526316 C59.8666667,21.5526316 60.2666667,22.5105263 61,23.1263158 C61.7333333,23.7421053 62.9333333,24.0157895 64.4666667,24.0157895 C67.9333333,24.0157895 69.6666667,22.5789474 69.6666667,19.7052632 L69.6666667,19.1578947 C68.4666667,20.4578947 66.8,21.0736842 64.6666667,21.0736842 C62.8,21.0736842 61.3333333,20.5947368 60.2,19.7052632 C59.0666667,18.6789474 58.4666667,17.3105263 58.4666667,15.5315789 L58.4666667,5.95263158 L60.4,5.95263158 L60.4,15.4631579 C60.4,17.9947368 61.8,19.2947368 64.6666667,19.2947368 C66.3333333,19.2947368 67.6,18.8157895 68.4,17.7894737 C69.2,16.9 69.6,15.6 69.6,13.9578947 L69.6,5.95263158 L71.5333333,5.95263158 L71.5333333,19.7052632 C71.5333333,23.7421053 69.1333333,25.7947368 64.4,25.7947368" id="Shape" fill="#3C3F40" sketch:type="MSShapeGroup"></path>
|
14
|
+
<path d="M85.6666667,20.6631579 C83.0666667,20.6631579 81.0666667,20.1842105 79.4666667,19.2947368 C77.4666667,18.1315789 76.5333333,16.3526316 76.5333333,13.8894737 L76.5333333,13 L78.4666667,13 L78.4666667,13.8210526 C78.4666667,17.2421053 80.8666667,18.9526316 85.7333333,18.9526316 C87.4,18.9526316 88.8,18.6789474 89.9333333,18.2 C91.3333333,17.5157895 92.0666667,16.5578947 92.0666667,15.2578947 C92.0666667,14.3684211 91.8,13.6842105 91.3333333,13.2052632 C90.9333333,12.7947368 90.2,12.4526316 89.1333333,12.1105263 C88.5333333,11.9052632 87.2,11.6315789 85,11.1526316 C82.5333333,10.6052632 80.8,10.1263158 79.9333333,9.64736842 C78.1333333,8.68947368 77.2,7.32105263 77.2,5.47368421 C77.2,1.98421053 79.8666667,0.205263158 85.1333333,0.205263158 C90.6666667,0.205263158 93.4666667,2.46315789 93.5333333,6.97894737 L91.6666667,6.97894737 C91.5333333,4.92631579 90.8666667,3.55789474 89.6666667,2.87368421 C88.6666667,2.32631579 87.1333333,2.05263158 85.1333333,2.05263158 C81.1333333,2.05263158 79.0666667,3.21578947 79.0666667,5.47368421 C79.0666667,6.56842105 79.6,7.45789474 80.6666667,8.00526316 C81.4,8.41578947 82.7333333,8.75789474 84.6,9.1 C88.1333333,9.78421053 90.5333333,10.4684211 91.7333333,11.1526316 C93.2,12.0421053 93.9333333,13.4105263 93.9333333,15.3263158 C93.9333333,17.2421053 92.9333333,18.6789474 91,19.6368421 C89.5333333,20.3210526 87.7333333,20.6631579 85.6666667,20.6631579" id="Shape" fill="#6BA651" sketch:type="MSShapeGroup"></path>
|
15
|
+
<path d="M110.266667,20.4578947 L110.266667,10.8105263 C110.266667,9.57894737 109.866667,8.62105263 109.133333,8.00526316 C108.4,7.38947368 107.333333,7.04736842 106,7.04736842 C104.466667,7.04736842 103.2,7.52631579 102.333333,8.41578947 C101.466667,9.37368421 101,10.6052632 101,12.1105263 L101,20.3894737 L99.0666667,20.3894737 L99.0666667,0.273684211 L101,0.273684211 L101,7.11578947 C102.2,5.88421053 103.866667,5.2 106,5.2 C107.733333,5.2 109.2,5.61052632 110.333333,6.5 C111.6,7.45789474 112.2,8.75789474 112.2,10.5368421 L112.2,20.3894737 L110.266667,20.3894737 L110.266667,20.4578947 Z" id="Shape" fill="#6BA651" sketch:type="MSShapeGroup"></path>
|
16
|
+
<path d="M140.333333,9.64736842 C139.533333,10.9473684 139.133333,12.5894737 139.133333,14.5736842 L139.133333,20.4578947 L137.2,20.4578947 L137.2,5.95263158 L139.133333,5.95263158 L139.133333,7.93684211 C140.466667,6.36315789 142.533333,5.61052632 145.266667,5.61052632 L145.266667,7.38947368 C142.933333,7.38947368 141.333333,8.14210526 140.333333,9.64736842" id="Shape" fill="#6BA651" sketch:type="MSShapeGroup"></path>
|
17
|
+
<path d="M150,13.9578947 C150.266667,17.3105263 152.133333,19.0210526 155.6,19.0210526 C158.266667,19.0210526 160.066667,17.8578947 160.933333,15.6 L162.933333,15.6 C162.266667,17.6526316 161.266667,19.0210526 159.866667,19.7736842 C158.6,20.4578947 157.2,20.8 155.533333,20.8 C153.066667,20.8 151.2,20.1157895 149.933333,18.6789474 C148.733333,17.3789474 148.133333,15.5315789 148.133333,13.2736842 C148.133333,11.0842105 148.8,9.23684211 150.133333,7.8 C151.466667,6.36315789 153.333333,5.61052632 155.666667,5.61052632 C157.933333,5.61052632 159.8,6.22631579 161.133333,7.52631579 C162.533333,8.82631579 163.2,10.5368421 163.2,12.7263158 L163.2,13.9578947 L150,13.9578947 L150,13.9578947 Z M159.733333,8.89473684 C158.733333,7.93684211 157.4,7.38947368 155.733333,7.38947368 C154.2,7.38947368 152.933333,7.8 151.933333,8.62105263 C150.933333,9.44210526 150.333333,10.6052632 150.133333,12.1789474 L161.2,12.1789474 C161,10.8105263 160.533333,9.71578947 159.733333,8.89473684 L159.733333,8.89473684 Z" id="Shape" fill="#6BA651" sketch:type="MSShapeGroup"></path>
|
18
|
+
<path d="M177.666667,20.0473684 C176.666667,20.5947368 175.333333,20.8684211 173.733333,20.8684211 C169.266667,20.8684211 167.066667,18.8157895 167.066667,14.6421053 L169.066667,14.6421053 C169.066667,16.2157895 169.466667,17.3105263 170.2,17.9947368 C170.933333,18.6789474 172.133333,19.0210526 173.8,19.0210526 C174.933333,19.0210526 175.866667,18.8157895 176.6,18.4736842 C177.466667,18.0631579 177.866667,17.3789474 177.866667,16.5578947 C177.866667,15.5315789 176.933333,14.7789474 175.133333,14.3684211 C175,14.3684211 174,14.2315789 172.266667,13.8894737 C169.266667,13.4105263 167.733333,12.1105263 167.733333,9.92105263 C167.733333,8.48421053 168.333333,7.38947368 169.533333,6.56842105 C170.6,5.88421053 171.8,5.54210526 173.266667,5.54210526 C177.533333,5.54210526 179.666667,7.38947368 179.733333,11.0842105 L177.733333,11.0842105 C177.733333,9.78421053 177.4,8.82631579 176.733333,8.21052632 C176.066667,7.66315789 174.933333,7.32105263 173.466667,7.32105263 C172.466667,7.32105263 171.6,7.52631579 170.866667,7.93684211 C170.066667,8.41578947 169.733333,9.03157895 169.733333,9.85263158 C169.733333,10.8789474 170.6,11.5631579 172.333333,11.8368421 C172.866667,11.9052632 173.866667,12.1105263 175.4,12.3842105 C178.333333,12.9315789 179.8,14.2315789 179.8,16.4210526 C179.733333,18.0631579 179.066667,19.2263158 177.666667,20.0473684" id="Shape" fill="#6BA651" sketch:type="MSShapeGroup"></path>
|
19
|
+
<path d="M128.8,20.3894737 L128.8,18.2684211 C127.466667,19.8421053 125.533333,20.6631579 123.133333,20.6631579 C120.866667,20.6631579 119,19.9105263 117.666667,18.4736842 C116.4,17.1052632 115.8,15.3263158 115.8,13.1368421 C115.8,10.9473684 116.4,9.1 117.666667,7.73157895 C119,6.22631579 120.866667,5.47368421 123.2,5.47368421 C125.6,5.47368421 127.4,6.22631579 128.733333,7.8 L128.733333,5.54210526 L130.666667,5.54210526 L130.666667,20.3210526 L128.8,20.3210526 L128.8,20.3894737 Z M127.333333,8.89473684 C126.333333,7.86842105 124.933333,7.32105263 123.066667,7.32105263 C121.333333,7.32105263 120,7.86842105 119,9.03157895 C118.066667,10.1263158 117.666667,11.4947368 117.666667,13.1368421 C117.666667,14.8473684 118.133333,16.2157895 119,17.2421053 C119.933333,18.3368421 121.333333,18.8842105 123.066667,18.8842105 C124.8,18.8842105 126.133333,18.3368421 127.2,17.2421053 C128.266667,16.1473684 128.733333,14.7789474 128.733333,13.1368421 C128.8,11.3578947 128.333333,9.92105263 127.333333,8.89473684 L127.333333,8.89473684 Z" id="Shape" fill="#6BA651" sketch:type="MSShapeGroup"></path>
|
20
|
+
</g>
|
21
|
+
</g>
|
22
|
+
</svg>
|
@@ -0,0 +1,54 @@
|
|
1
|
+
<?xml version="1.0" encoding="utf-8"?>
|
2
|
+
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
3
|
+
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
4
|
+
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
5
|
+
width="627.062px" height="612px" viewBox="0 0 627.062 612" enable-background="new 0 0 627.062 612" xml:space="preserve">
|
6
|
+
<g>
|
7
|
+
<path d="M563.713,302.966c0,139.834-113.353,253.183-253.19,253.183c-139.837,0-253.19-113.349-253.19-253.183
|
8
|
+
c0-139.834,113.354-253.201,253.19-253.201C450.36,49.765,563.713,163.132,563.713,302.966"/>
|
9
|
+
<path fill="#FFFFFF" d="M310.522,69.572c-128.674,0-233.384,104.703-233.384,233.38c0,128.688,104.71,233.376,233.384,233.376
|
10
|
+
c128.691,0,233.376-104.688,233.376-233.376C543.898,174.275,439.214,69.572,310.522,69.572 M310.522,590.299
|
11
|
+
c-158.457,0-287.336-128.903-287.336-287.347c0-158.436,128.879-287.333,287.336-287.333s287.336,128.897,287.336,287.333
|
12
|
+
C597.858,461.396,468.979,590.299,310.522,590.299"/>
|
13
|
+
<path fill="#C1D82F" d="M310.522,14.185c-159.217,0-288.771,129.543-288.771,288.768c0,159.231,129.554,288.778,288.771,288.778
|
14
|
+
c159.228,0,288.789-129.547,288.789-288.778C599.312,143.728,469.75,14.185,310.522,14.185z M542.457,302.952
|
15
|
+
c0,127.893-104.042,231.944-231.935,231.944c-127.886,0-231.938-104.052-231.938-231.944
|
16
|
+
c0-127.889,104.052-231.934,231.938-231.934C438.415,71.018,542.457,175.063,542.457,302.952z M310.522,17.062v51.082
|
17
|
+
c-129.476,0-234.818,105.332-234.818,234.808H24.622C24.622,145.308,152.893,17.062,310.522,17.062z"/>
|
18
|
+
<path fill="#C1D82F" d="M187.809,232.519c-4.814,0-8.731,3.662-8.731,8.194l-0.06,36.076h24.112l0.039-12.285
|
19
|
+
c0-5.239,3.503-8.251,7.798-8.251l70.894-0.074l-92.888,181.206h28.785l93.164-185.182v-19.684H187.809z"/>
|
20
|
+
<path fill="#C1D82F" d="M413.941,232.625c0,0-59.977-0.042-65.402,0c-5.434,0.042-8.997-0.127-12.531,1.647
|
21
|
+
c-5.989,3.005-7.198,9.94-7.863,15.986c-0.809,7.332-8.392,84.803-7.738,85.118l11.857,5.903c0,0,17.644-10.237,38.31-10.237
|
22
|
+
c26.75,0,44.118,17.046,44.118,42.188c0,23.123-19.291,41.626-43.842,41.626c-28.126,0-42.693-22.405-42.693-22.405l-14.579,20.903
|
23
|
+
c0,0,19.234,27.227,58.636,27.227c40.559,0,70.621-30.055,70.621-68.503c0-36.418-27.019-64.741-67.301-64.741
|
24
|
+
c-18.318,0-27.578,6.37-27.578,6.37s5.112-50.206,5.112-50.237c0.368-3.761,3.515-6.685,7.184-6.685c0,0,36.563-0.007,39.618-0.007
|
25
|
+
c3.98,0,6.733,3.574,6.833,6.674c0.096,3.09,0,13.285,0,13.285h22.695l0.057-31.504c0-6.622-5.246-10.605-8.746-11.765
|
26
|
+
C418.499,232.721,416.204,232.59,413.941,232.625"/>
|
27
|
+
<polygon fill="#FFFFFF" points="188.699,166.494 222.912,166.494 221.039,176.371 199.5,176.371 197.382,186.146 217.659,186.146
|
28
|
+
215.746,196.009 195.487,196.009 191.436,217.463 178.745,217.463 "/>
|
29
|
+
<path fill="#FFFFFF" d="M232.121,198.999h12.921l-2.888-19.549L232.121,198.999z M246.644,208.212h-19.185l-4.762,9.251h-12.695
|
30
|
+
l27.136-51.043h14.161l8.498,51.043h-11.789L246.644,208.212z"/>
|
31
|
+
<polygon fill="#FFFFFF" points="302.773,166.469 343.007,166.455 340.988,176.615 326.926,176.615 320.24,217.318 308.999,217.318
|
32
|
+
315.617,176.615 301.172,176.619 "/>
|
33
|
+
<path fill="#FFFFFF" d="M409.081,189.772c2.867,0,5.056-0.714,6.529-2.131c1.492-1.432,2.234-3.524,2.234-6.282
|
34
|
+
c0-1.195-0.587-2.881-2.305-3.984c-1.599-1.021-4.179-1.015-8.159-1.015c-3.16,0-4.214,0.053-4.214,0.053l-1.901,13.359H409.081z
|
35
|
+
M400.184,199.098l-2.588,18.397l-11.963-0.042l7.883-50.892h20.532c5.401,0,9.495,1.071,12.267,3.224
|
36
|
+
c2.811,2.156,4.214,5.324,4.214,9.509c0,4.235-1.11,7.707-3.315,10.45c-2.185,2.733-5.176,4.292-8.923,4.702
|
37
|
+
c1.722,0.367,3.164,1.304,4.288,2.786c1.142,1.481,2.146,3.747,2.994,6.819l3.659,13.444h-12.617l-3.153-11.588
|
38
|
+
c-0.771-2.587-1.658-4.383-2.722-5.352c-1.071-0.969-2.612-1.457-4.646-1.457H400.184z"/>
|
39
|
+
<path fill="#FFFFFF" d="M284.809,166.494c-8.947,0-17.1,3.797-18.758,13.239c-0.007,0.042-3.58,21.041-3.609,21.31
|
40
|
+
c-0.205,2.139-0.145,4.179,0.039,6.441c0.178,1.796,1.086,3.669,2.153,5.115c1.075,1.372,2.364,2.454,3.906,3.228
|
41
|
+
c3.182,1.623,7.232,1.669,11.817,1.715c0.732,0,10.994,0,10.994,0l1.057-10.206c-0.66,0-16.187,0.131-16.879-0.042
|
42
|
+
c-1.055-0.261-1.69-1.025-2.051-1.919c-0.389-0.873-0.1-2.287,0.061-3.245c0.018-0.032,4.195-23.342,4.195-23.342
|
43
|
+
c0.283-1.704,1.541-2.178,3.143-2.167c3.16,0.06,15.982,0.018,15.982,0.018l1.835-10.125
|
44
|
+
C298.694,166.512,287.092,166.494,284.809,166.494"/>
|
45
|
+
<path fill="#FFFFFF" d="M372.779,203.796c-0.438,2.17-1.63,3.581-3.609,3.574c-0.676,0-3.786,0.032-7.611,0.032
|
46
|
+
c-4.15,0-7.819-0.032-8.509-0.032c-1.934,0.007-2.804-1.576-2.472-3.574c0.262-1.63,1.651-10.832,2.157-14.144
|
47
|
+
c0.626-3.761,1.347-8.148,1.583-9.329c0.39-2.266,1.57-3.715,3.454-3.793c0.039,0,3.8-0.05,8.177-0.05
|
48
|
+
c4.369-0.007,8.088,0.042,8.088,0.042c1.984,0,2.801,1.379,2.489,3.535c0,0-0.891,6.498-1.796,12.896
|
49
|
+
C373.832,199.342,372.779,203.796,372.779,203.796 M360.018,217.644c4.879-0.05,11.613-0.474,15.992-3.153
|
50
|
+
c4.091-2.521,6.354-6.356,7.251-12.394l3.221-20.345c0.799-6.059-0.039-9.859-3.358-12.373c-3.766-2.885-11.064-3.167-16.004-3.167
|
51
|
+
l-0.01,0.018c-4.89,0.046-11.603,0.488-15.982,3.15c-4.107,2.535-6.335,6.359-7.25,12.39l-3.211,20.327
|
52
|
+
c-1.008,6.038,0.008,9.874,3.303,12.394c3.538,2.679,10.152,3.104,15.01,3.153"/>
|
53
|
+
</g>
|
54
|
+
</svg>
|
@@ -41,15 +41,24 @@ module AbstractFeatureBranch
|
|
41
41
|
:production => true
|
42
42
|
}
|
43
43
|
end
|
44
|
-
|
45
|
-
|
44
|
+
|
45
|
+
def feature_store
|
46
|
+
@feature_store ||= initialize_feature_store
|
46
47
|
end
|
47
|
-
|
48
|
-
|
48
|
+
alias user_features_storage feature_store
|
49
|
+
|
50
|
+
def feature_store=(feature_store)
|
51
|
+
@feature_store = feature_store
|
49
52
|
end
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
+
alias user_features_storage= feature_store=
|
54
|
+
|
55
|
+
def initialize_feature_store
|
56
|
+
self.feature_store = Redis.new
|
57
|
+
rescue => e
|
58
|
+
logger.debug { "Redis is not enabled!" }
|
59
|
+
logger.debug { e.full_message }
|
60
|
+
nil
|
53
61
|
end
|
62
|
+
alias initialize_user_features_storage initialize_feature_store
|
54
63
|
end
|
55
64
|
end
|
@@ -11,26 +11,42 @@ rescue Bundler::BundlerError => e
|
|
11
11
|
end
|
12
12
|
require 'logger' unless defined?(Rails) && Rails.logger
|
13
13
|
require 'deep_merge' unless {}.respond_to?(:deep_merge!)
|
14
|
+
require 'forwardable'
|
14
15
|
|
15
16
|
require File.join(File.dirname(__FILE__), 'abstract_feature_branch', 'configuration')
|
16
17
|
|
17
18
|
module AbstractFeatureBranch
|
18
19
|
ENV_FEATURE_PREFIX = "abstract_feature_branch_"
|
20
|
+
REDIS_HKEY = "abstract_feature_branch"
|
19
21
|
|
20
22
|
class << self
|
21
23
|
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
|
24
|
+
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, :feature_store, :feature_store=, :initialize_feature_store, :user_features_storage, :user_features_storage=, :initialize_user_features_storage
|
23
25
|
|
24
26
|
def configuration
|
25
27
|
@configuration ||= Configuration.new
|
26
28
|
end
|
27
29
|
|
30
|
+
def redis_overrides
|
31
|
+
load_redis_overrides
|
32
|
+
end
|
33
|
+
def load_redis_overrides
|
34
|
+
return {} if feature_store.nil?
|
35
|
+
|
36
|
+
redis_feature_hash = get_store_features.inject({}) do |output, feature|
|
37
|
+
output.merge(feature => get_store_feature(feature))
|
38
|
+
end
|
39
|
+
|
40
|
+
downcase_keys(redis_feature_hash)
|
41
|
+
end
|
42
|
+
|
28
43
|
def environment_variable_overrides
|
29
44
|
@environment_variable_overrides ||= load_environment_variable_overrides
|
30
45
|
end
|
31
46
|
def load_environment_variable_overrides
|
32
|
-
@environment_variable_overrides = featureize_keys(
|
47
|
+
@environment_variable_overrides = featureize_keys(downcase_keys(booleanize_values(select_feature_keys(ENV))))
|
33
48
|
end
|
49
|
+
|
34
50
|
def local_features
|
35
51
|
@local_features ||= load_local_features
|
36
52
|
end
|
@@ -38,6 +54,7 @@ module AbstractFeatureBranch
|
|
38
54
|
@local_features = {}
|
39
55
|
load_specific_features(@local_features, '.local.yml')
|
40
56
|
end
|
57
|
+
|
41
58
|
def features
|
42
59
|
@features ||= load_features
|
43
60
|
end
|
@@ -45,6 +62,7 @@ module AbstractFeatureBranch
|
|
45
62
|
@features = {}
|
46
63
|
load_specific_features(@features, '.yml')
|
47
64
|
end
|
65
|
+
|
48
66
|
# performance optimization via caching of feature values resolved through environment variable overrides and local features
|
49
67
|
def environment_features(environment)
|
50
68
|
@environment_features ||= {}
|
@@ -54,7 +72,9 @@ module AbstractFeatureBranch
|
|
54
72
|
@environment_features ||= {}
|
55
73
|
features[environment] ||= {}
|
56
74
|
local_features[environment] ||= {}
|
57
|
-
@environment_features[environment] = features[environment].
|
75
|
+
@environment_features[environment] = features[environment].
|
76
|
+
merge(local_features[environment]).
|
77
|
+
merge(environment_variable_overrides)
|
58
78
|
end
|
59
79
|
def application_features
|
60
80
|
unload_application_features unless cacheable?
|
@@ -77,12 +97,58 @@ module AbstractFeatureBranch
|
|
77
97
|
value = (application_environment != 'development') if value.nil?
|
78
98
|
value
|
79
99
|
end
|
100
|
+
|
101
|
+
# Sets feature value (true or false) in storage (e.g. Redis client)
|
102
|
+
def set_store_feature(feature, value)
|
103
|
+
raise 'Feature storage (e.g. Redis) is not setup!' if feature_store.nil?
|
104
|
+
feature = feature.to_s
|
105
|
+
return delete_store_feature(feature) if value.nil?
|
106
|
+
value = 'true' if value == true
|
107
|
+
value = 'false' if value == false
|
108
|
+
feature_store.hset(REDIS_HKEY, feature, value)
|
109
|
+
end
|
110
|
+
|
111
|
+
# Gets feature value (true or false) from storage (e.g. Redis client)
|
112
|
+
def get_store_feature(feature)
|
113
|
+
raise 'Feature storage (e.g. Redis) is not setup!' if feature_store.nil?
|
114
|
+
feature = feature.to_s
|
115
|
+
value = feature_store.hget(REDIS_HKEY, feature)
|
116
|
+
if value.nil?
|
117
|
+
matching_feature = get_store_features.find { |store_feature| store_feature.downcase == feature.downcase }
|
118
|
+
value = feature_store.hget(REDIS_HKEY, matching_feature) if matching_feature
|
119
|
+
end
|
120
|
+
return nil if value.nil?
|
121
|
+
return 'per_user' if value.to_s.downcase == 'per_user'
|
122
|
+
value.to_s.downcase == 'true'
|
123
|
+
end
|
124
|
+
|
125
|
+
# Gets feature value (true or false) from storage (e.g. Redis client)
|
126
|
+
def delete_store_feature(feature)
|
127
|
+
raise 'Feature storage (e.g. Redis) is not setup!' if feature_store.nil?
|
128
|
+
feature = feature.to_s
|
129
|
+
feature_store.hdel(REDIS_HKEY, feature)
|
130
|
+
end
|
131
|
+
|
132
|
+
# Gets features array (all features) from storage (e.g. Redis client)
|
133
|
+
def get_store_features
|
134
|
+
raise 'Feature storage (e.g. Redis) is not setup!' if feature_store.nil?
|
135
|
+
feature_store.hkeys(REDIS_HKEY)
|
136
|
+
end
|
137
|
+
|
138
|
+
# Gets features array (all features) from storage (e.g. Redis client)
|
139
|
+
def clear_store_features
|
140
|
+
raise 'Feature storage (e.g. Redis) is not setup!' if feature_store.nil?
|
141
|
+
feature_store.hkeys(REDIS_HKEY).each do |feature|
|
142
|
+
feature_store.hdel(REDIS_HKEY, feature)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
80
146
|
def toggle_features_for_user(user_id, features)
|
81
147
|
features.each do |name, value|
|
82
148
|
if value
|
83
|
-
|
149
|
+
feature_store.sadd("#{ENV_FEATURE_PREFIX}#{name.to_s.downcase}", user_id)
|
84
150
|
else
|
85
|
-
|
151
|
+
feature_store.srem("#{ENV_FEATURE_PREFIX}#{name.to_s.downcase}", user_id)
|
86
152
|
end
|
87
153
|
end
|
88
154
|
end
|
@@ -103,7 +169,7 @@ module AbstractFeatureBranch
|
|
103
169
|
end
|
104
170
|
|
105
171
|
def select_feature_keys(hash)
|
106
|
-
hash.reject {|k, v| !k.start_with?(ENV_FEATURE_PREFIX)} # using reject for Ruby 1.8 compatibility as select returns an array in it
|
172
|
+
hash.reject {|k, v| !k.downcase.start_with?(ENV_FEATURE_PREFIX)} # using reject for Ruby 1.8 compatibility as select returns an array in it
|
107
173
|
end
|
108
174
|
|
109
175
|
def booleanize_values(hash)
|
data/lib/ext/feature_branch.rb
CHANGED
@@ -10,9 +10,10 @@ class Object
|
|
10
10
|
def self.feature_enabled?(feature_name, user_id = nil)
|
11
11
|
normalized_feature_name = feature_name.to_s.downcase
|
12
12
|
|
13
|
-
|
13
|
+
redis_override_value = AbstractFeatureBranch.get_store_feature(normalized_feature_name) rescue nil
|
14
|
+
value = !redis_override_value.nil? ? redis_override_value : AbstractFeatureBranch.application_features[normalized_feature_name]
|
14
15
|
if value == 'per_user'
|
15
|
-
value = AbstractFeatureBranch.user_features_storage.sismember("#{AbstractFeatureBranch::ENV_FEATURE_PREFIX}#{normalized_feature_name}", user_id)
|
16
|
+
value = !user_id.nil? && AbstractFeatureBranch.user_features_storage.sismember("#{AbstractFeatureBranch::ENV_FEATURE_PREFIX}#{normalized_feature_name}", user_id)
|
16
17
|
end
|
17
18
|
value
|
18
19
|
end
|
@@ -26,4 +27,4 @@ class Object
|
|
26
27
|
def feature_enabled?(feature_name, user_id = nil)
|
27
28
|
Object.feature_enabled?(feature_name.to_s, user_id)
|
28
29
|
end
|
29
|
-
end
|
30
|
+
end
|
@@ -1,9 +1,8 @@
|
|
1
|
-
|
1
|
+
# Storage system for features (other than YAML/Env-Vars). Right now, only Redis is supported.
|
2
|
+
# AbstractFeatureBranch.feature_store = Redis.new
|
2
3
|
|
3
|
-
# Storage for user features, customizable over here. Right now, only a Redis client is supported.
|
4
4
|
# The following example line works with Heroku Redis To Go while still operating on local Redis for local development
|
5
|
-
# AbstractFeatureBranch.
|
6
|
-
AbstractFeatureBranch.user_features_storage = Redis.new
|
5
|
+
# AbstractFeatureBranch.feature_store = Redis.new(:url => ENV['REDISTOGO_URL'])
|
7
6
|
|
8
7
|
# Application root where config/features.yml or config/features/ is found
|
9
8
|
AbstractFeatureBranch.application_root = Rails.root
|
@@ -24,4 +23,4 @@ AbstractFeatureBranch.cacheable = {
|
|
24
23
|
}
|
25
24
|
|
26
25
|
# Pre-load application features to improve performance of first web-page hit
|
27
|
-
AbstractFeatureBranch.load_application_features unless Rails.env.development?
|
26
|
+
AbstractFeatureBranch.load_application_features unless Rails.env.development?
|
data/ruby187.Gemfile
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
source 'http://rubygems.org'
|
2
2
|
|
3
|
-
gem 'deep_merge', '1.0.0', :require => false #avoid loading
|
3
|
+
gem 'deep_merge', '1.0.0', :require => false #avoid loading to use only if Rails is unavailable
|
4
4
|
gem 'redis', '~> 3.0.0', :require => false
|
5
5
|
|
6
6
|
group :development do
|
7
7
|
gem 'jeweler', '1.8.8'
|
8
|
-
end
|
9
|
-
|
10
|
-
group :test do
|
11
8
|
gem 'rspec', '2.14.1'
|
12
|
-
|
9
|
+
|
10
|
+
#Ruby 1.8.7 compatible versions
|
11
|
+
gem "nokogiri", "~> 1.5.0"
|
12
|
+
gem "highline", "~> 1.6.21"
|
13
|
+
end
|
@@ -25,16 +25,22 @@ describe 'feature_branch object extensions' do
|
|
25
25
|
rescue => e
|
26
26
|
#noop
|
27
27
|
end
|
28
|
+
AbstractFeatureBranch.user_features_storage.keys.each do |key|
|
29
|
+
AbstractFeatureBranch.user_features_storage.del(key)
|
30
|
+
end
|
28
31
|
end
|
29
32
|
describe '#feature_branch' do
|
30
33
|
context 'per user' do
|
31
34
|
it 'feature branches correctly after storing feature configuration per user in a separate process (ensuring persistence)' do
|
32
35
|
user_id = 'email1@example.com'
|
33
|
-
|
36
|
+
ruby_code = <<-RUBY_CODE
|
37
|
+
$:.unshift('.')
|
38
|
+
require 'redis'
|
39
|
+
require 'lib/abstract_feature_branch'
|
34
40
|
AbstractFeatureBranch.initialize_user_features_storage
|
35
|
-
AbstractFeatureBranch.toggle_features_for_user(user_id, :feature1 => false, :feature3 => true, :feature6 => true, :feature7 => false)
|
36
|
-
|
37
|
-
|
41
|
+
AbstractFeatureBranch.toggle_features_for_user('#{user_id}', :feature1 => false, :feature3 => true, :feature6 => true, :feature7 => false)
|
42
|
+
RUBY_CODE
|
43
|
+
system "ruby -e \"#{ruby_code}\""
|
38
44
|
features_enabled = []
|
39
45
|
feature_branch :feature1, user_id do
|
40
46
|
features_enabled << :feature1
|
@@ -63,12 +69,15 @@ describe 'feature_branch object extensions' do
|
|
63
69
|
end
|
64
70
|
it 'update feature branching (disabling some features) after having stored feature configuration per user in a separate process (ensuring persistence)' do
|
65
71
|
user_id = 'email1@example.com'
|
66
|
-
|
72
|
+
ruby_code = <<-RUBY_CODE
|
73
|
+
$:.unshift('.')
|
74
|
+
require 'redis'
|
75
|
+
require 'lib/abstract_feature_branch'
|
67
76
|
AbstractFeatureBranch.initialize_user_features_storage
|
68
|
-
AbstractFeatureBranch.toggle_features_for_user(user_id, :feature6 => true, :feature7 => false)
|
69
|
-
AbstractFeatureBranch.toggle_features_for_user(user_id, :feature6 => false, :feature7 => true)
|
70
|
-
|
71
|
-
|
77
|
+
AbstractFeatureBranch.toggle_features_for_user('#{user_id}', :feature6 => true, :feature7 => false)
|
78
|
+
AbstractFeatureBranch.toggle_features_for_user('#{user_id}', :feature6 => false, :feature7 => true)
|
79
|
+
RUBY_CODE
|
80
|
+
system "ruby -e \"#{ruby_code}\""
|
72
81
|
features_enabled = []
|
73
82
|
feature_branch :feature6, user_id do
|
74
83
|
features_enabled << :feature6
|
@@ -15,6 +15,9 @@ describe 'feature_branch object extensions' do
|
|
15
15
|
AbstractFeatureBranch.application_root = @app_root_backup
|
16
16
|
AbstractFeatureBranch.application_environment = @app_env_backup
|
17
17
|
AbstractFeatureBranch.unload_application_features
|
18
|
+
AbstractFeatureBranch.user_features_storage.keys.each do |key|
|
19
|
+
AbstractFeatureBranch.user_features_storage.del(key)
|
20
|
+
end
|
18
21
|
end
|
19
22
|
describe '#feature_branch' do
|
20
23
|
context 'class level behavior (case-insensitive string or symbol feature names)' do
|
@@ -63,6 +66,26 @@ describe 'feature_branch object extensions' do
|
|
63
66
|
features_enabled.should_not include(:feature2)
|
64
67
|
features_enabled.should include(:feature3)
|
65
68
|
end
|
69
|
+
it 'allows redis variables (case-insensitive booleans) to override configuration file' do
|
70
|
+
AbstractFeatureBranch.unload_application_features
|
71
|
+
AbstractFeatureBranch.user_features_storage.hset('abstract_feature_branch', 'FEATURE1', 'FALSE')
|
72
|
+
AbstractFeatureBranch.user_features_storage.hset('abstract_feature_branch', 'Feature2', 'False')
|
73
|
+
AbstractFeatureBranch.user_features_storage.hset('abstract_feature_branch', 'feature3', 'true')
|
74
|
+
AbstractFeatureBranch.load_application_features
|
75
|
+
features_enabled = []
|
76
|
+
feature_branch :feature1 do
|
77
|
+
features_enabled << :feature1
|
78
|
+
end
|
79
|
+
feature_branch :feature2 do
|
80
|
+
features_enabled << :feature2
|
81
|
+
end
|
82
|
+
feature_branch :feature3 do
|
83
|
+
features_enabled << :feature3
|
84
|
+
end
|
85
|
+
features_enabled.should_not include(:feature1)
|
86
|
+
features_enabled.should_not include(:feature2)
|
87
|
+
features_enabled.should include(:feature3)
|
88
|
+
end
|
66
89
|
it 'allows local configuration file to override main configuration file' do
|
67
90
|
features_enabled = []
|
68
91
|
feature_branch :feature4 do
|
@@ -15,6 +15,9 @@ describe 'feature_branch object extensions' do
|
|
15
15
|
AbstractFeatureBranch.application_root = @app_root_backup
|
16
16
|
AbstractFeatureBranch.application_environment = @app_env_backup
|
17
17
|
AbstractFeatureBranch.unload_application_features
|
18
|
+
AbstractFeatureBranch.user_features_storage.keys.each do |key|
|
19
|
+
AbstractFeatureBranch.user_features_storage.del(key)
|
20
|
+
end
|
18
21
|
end
|
19
22
|
describe '#feature_enabled?' do
|
20
23
|
it 'determines whether a feature is enabled or not in features configuration (case-insensitive string or symbol feature names)' do
|
@@ -35,6 +38,17 @@ describe 'feature_branch object extensions' do
|
|
35
38
|
feature_enabled?(:feature3).should == true
|
36
39
|
feature_enabled?(:feature4a).should == true #not overridden
|
37
40
|
end
|
41
|
+
it 'allows redis variables (case-insensitive booleans) to override configuration file' do
|
42
|
+
AbstractFeatureBranch.unload_application_features
|
43
|
+
AbstractFeatureBranch.user_features_storage.hset('abstract_feature_branch', 'FEATURE1', 'FALSE')
|
44
|
+
AbstractFeatureBranch.user_features_storage.hset('abstract_feature_branch', 'Feature2', 'False')
|
45
|
+
AbstractFeatureBranch.user_features_storage.hset('abstract_feature_branch', 'feature3', 'true')
|
46
|
+
AbstractFeatureBranch.load_application_features
|
47
|
+
feature_enabled?(:feature1).should == false
|
48
|
+
feature_enabled?(:feature2).should == false
|
49
|
+
feature_enabled?(:feature3).should == true
|
50
|
+
feature_enabled?(:feature4a).should == true #not overridden
|
51
|
+
end
|
38
52
|
it 'allows local configuration file to override main configuration file in test environment' do
|
39
53
|
feature_enabled?(:feature4).should == false
|
40
54
|
feature_enabled?(:feature5).should == true
|
metadata
CHANGED
@@ -1,63 +1,104 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: abstract_feature_branch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Annas "Andy" Maleh
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-12-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: deep_merge
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 1.0.0
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 1.0.0
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: jeweler
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: 3.
|
34
|
-
type: :
|
33
|
+
version: 2.3.9
|
34
|
+
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - ~>
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: 3.
|
40
|
+
version: 2.3.9
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.1.4
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.1.4
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.14.1
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.14.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rdoc
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 5.1.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 5.1.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: psych
|
43
85
|
requirement: !ruby/object:Gem::Requirement
|
44
86
|
requirements:
|
45
87
|
- - '='
|
46
88
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
89
|
+
version: 3.3.4
|
48
90
|
type: :development
|
49
91
|
prerelease: false
|
50
92
|
version_requirements: !ruby/object:Gem::Requirement
|
51
93
|
requirements:
|
52
94
|
- - '='
|
53
95
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
96
|
+
version: 3.3.4
|
55
97
|
description: |
|
56
98
|
abstract_feature_branch is a Rails gem that enables developers to easily branch by abstraction as per this pattern:
|
57
99
|
http://paulhammant.com/blog/branch_by_abstraction.html
|
58
100
|
|
59
|
-
It is a productivity and fault tolerance enhancing team practice
|
60
|
-
teams at large corporations, such as Sears and Groupon.
|
101
|
+
It is a productivity and fault tolerance enhancing team practice.
|
61
102
|
|
62
103
|
It provides the ability to wrap blocks of code with an abstract feature branch name, and then
|
63
104
|
specify in a configuration file which features to be switched on or off.
|
@@ -73,18 +114,21 @@ description: |
|
|
73
114
|
abstract_feature_branch additionally supports DDD's pattern of
|
74
115
|
Bounded Contexts by allowing developers to configure
|
75
116
|
context-specific feature files if needed.
|
76
|
-
email:
|
117
|
+
email:
|
77
118
|
executables: []
|
78
119
|
extensions: []
|
79
120
|
extra_rdoc_files:
|
121
|
+
- CHANGELOG.md
|
80
122
|
- LICENSE.txt
|
81
123
|
- README.md
|
82
124
|
files:
|
83
|
-
- .coveralls.yml
|
84
|
-
- .travis.yml
|
125
|
+
- ".coveralls.yml"
|
126
|
+
- ".travis.yml"
|
127
|
+
- CHANGELOG.md
|
85
128
|
- LICENSE.txt
|
86
129
|
- README.md
|
87
130
|
- RELEASE_NOTES.md
|
131
|
+
- TODO.md
|
88
132
|
- VERSION
|
89
133
|
- abstract_feature_branch.gemspec
|
90
134
|
- config/features/admin.local.yml
|
@@ -93,6 +137,9 @@ files:
|
|
93
137
|
- config/features/internal/wiki.yml
|
94
138
|
- config/features/public.local.yml
|
95
139
|
- config/features/public.yml
|
140
|
+
- img/BigAstronaut-Logo.png
|
141
|
+
- img/EarlyShares-Logo.svg
|
142
|
+
- img/Factor75-Logo.svg
|
96
143
|
- lib/abstract_feature_branch.rb
|
97
144
|
- lib/abstract_feature_branch/configuration.rb
|
98
145
|
- lib/abstract_feature_branch/file_beautifier.rb
|
@@ -105,7 +152,6 @@ files:
|
|
105
152
|
- lib/generators/templates/config/initializers/abstract_feature_branch.rb
|
106
153
|
- lib/generators/templates/lib/tasks/abstract_feature_branch.rake
|
107
154
|
- ruby187.Gemfile
|
108
|
-
- ruby187.Gemfile.lock
|
109
155
|
- spec/abstract_feature_branch/file_beautifier_spec.rb
|
110
156
|
- spec/ext/feature_branch__feature_branch_per_user_spec.rb
|
111
157
|
- spec/ext/feature_branch__feature_branch_spec.rb
|
@@ -131,24 +177,33 @@ homepage: http://github.com/AndyObtiva/abstract_feature_branch
|
|
131
177
|
licenses:
|
132
178
|
- MIT
|
133
179
|
metadata: {}
|
134
|
-
post_install_message:
|
180
|
+
post_install_message: "\nRails-only post-install instructions:\n\n1) Run the following
|
181
|
+
command to generate the Rails initializer and basic feature files:\n\nrails g abstract_feature_branch:install\n\n2)
|
182
|
+
Optionally, you may run this command to generate feature files per context:\n\nrails
|
183
|
+
g abstract_feature_branch:context context_path\n \n3) Optionally, install Redis
|
184
|
+
server with [Homebrew](https://brew.sh/) by running:\n\nbrew install redis\n\n4)
|
185
|
+
Optionally, install redis client gem (required with Redis server) by adding the
|
186
|
+
following line to Gemfile above abstract_feature_branch:\n\ngem 'redis', '~> 5.0.5'\n\nAfterwards,
|
187
|
+
run:\n\nbundle\n\n5) Optionally, customize configuration in config/initializers/abstract_feature_branch.rb\n\n(can
|
188
|
+
be useful for changing location of feature files in Rails application,\nconfiguring
|
189
|
+
Redis to use for overrides and per-user feature enablement,\nand/or troubleshooting
|
190
|
+
specific Rails environment feature configurations)\n\n"
|
135
191
|
rdoc_options: []
|
136
192
|
require_paths:
|
137
193
|
- lib
|
138
194
|
required_ruby_version: !ruby/object:Gem::Requirement
|
139
195
|
requirements:
|
140
|
-
- -
|
196
|
+
- - ">="
|
141
197
|
- !ruby/object:Gem::Version
|
142
198
|
version: '0'
|
143
199
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
144
200
|
requirements:
|
145
|
-
- -
|
201
|
+
- - ">="
|
146
202
|
- !ruby/object:Gem::Version
|
147
203
|
version: '0'
|
148
204
|
requirements: []
|
149
|
-
|
150
|
-
|
151
|
-
signing_key:
|
205
|
+
rubygems_version: 3.3.6
|
206
|
+
signing_key:
|
152
207
|
specification_version: 4
|
153
208
|
summary: 'abstract_feature_branch is a Rails gem that enables developers to easily
|
154
209
|
branch by abstraction as per this pattern: http://paulhammant.com/blog/branch_by_abstraction.html'
|
data/ruby187.Gemfile.lock
DELETED
@@ -1,63 +0,0 @@
|
|
1
|
-
GEM
|
2
|
-
remote: http://rubygems.org/
|
3
|
-
specs:
|
4
|
-
addressable (2.3.5)
|
5
|
-
builder (3.2.2)
|
6
|
-
deep_merge (1.0.0)
|
7
|
-
diff-lcs (1.2.5)
|
8
|
-
faraday (0.8.9)
|
9
|
-
multipart-post (~> 1.2.0)
|
10
|
-
git (1.2.6)
|
11
|
-
github_api (0.10.1)
|
12
|
-
addressable
|
13
|
-
faraday (~> 0.8.1)
|
14
|
-
hashie (>= 1.2)
|
15
|
-
multi_json (~> 1.4)
|
16
|
-
nokogiri (~> 1.5.2)
|
17
|
-
oauth2
|
18
|
-
hashie (2.0.5)
|
19
|
-
highline (1.6.20)
|
20
|
-
httpauth (0.2.0)
|
21
|
-
jeweler (1.8.8)
|
22
|
-
builder
|
23
|
-
bundler (~> 1.0)
|
24
|
-
git (>= 1.2.5)
|
25
|
-
github_api (= 0.10.1)
|
26
|
-
highline (>= 1.6.15)
|
27
|
-
nokogiri (= 1.5.10)
|
28
|
-
rake
|
29
|
-
rdoc
|
30
|
-
json (1.8.1)
|
31
|
-
jwt (0.1.10)
|
32
|
-
multi_json (>= 1.5)
|
33
|
-
multi_json (1.8.4)
|
34
|
-
multi_xml (0.5.5)
|
35
|
-
multipart-post (1.2.0)
|
36
|
-
nokogiri (1.5.10)
|
37
|
-
oauth2 (0.9.2)
|
38
|
-
faraday (~> 0.8)
|
39
|
-
httpauth (~> 0.2)
|
40
|
-
jwt (~> 0.1.4)
|
41
|
-
multi_json (~> 1.0)
|
42
|
-
multi_xml (~> 0.5)
|
43
|
-
rack (~> 1.2)
|
44
|
-
rack (1.5.2)
|
45
|
-
rake (10.1.1)
|
46
|
-
rdoc (4.1.1)
|
47
|
-
json (~> 1.4)
|
48
|
-
rspec (2.14.1)
|
49
|
-
rspec-core (~> 2.14.0)
|
50
|
-
rspec-expectations (~> 2.14.0)
|
51
|
-
rspec-mocks (~> 2.14.0)
|
52
|
-
rspec-core (2.14.7)
|
53
|
-
rspec-expectations (2.14.4)
|
54
|
-
diff-lcs (>= 1.1.3, < 2.0)
|
55
|
-
rspec-mocks (2.14.4)
|
56
|
-
|
57
|
-
PLATFORMS
|
58
|
-
ruby
|
59
|
-
|
60
|
-
DEPENDENCIES
|
61
|
-
deep_merge (= 1.0.0)
|
62
|
-
jeweler (= 1.8.8)
|
63
|
-
rspec (= 2.14.1)
|