{"id":33178040,"url":"https://github.com/AndyObtiva/abstract_feature_branch","last_synced_at":"2025-11-20T21:01:14.502Z","repository":{"id":5328968,"uuid":"6512790","full_name":"AndyObtiva/abstract_feature_branch","owner":"AndyObtiva","description":"abstract_feature_branch is a Ruby gem that provides a variation on the Branch by Abstraction Pattern by Paul Hammant and the Feature Toggles Pattern by Martin Fowler (aka Feature Flags) to enable Continuous Integration and Trunk-Based Development. ","archived":false,"fork":false,"pushed_at":"2024-12-03T16:07:12.000Z","size":230,"stargazers_count":25,"open_issues_count":0,"forks_count":5,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-10-06T18:47:00.669Z","etag":null,"topics":["branch-by-abstraction","continuous-integration","fault-tolerance","feature-flags","feature-flipper","feature-flipping","feature-rollout","feature-toggles","productivity","rails","release-management","ruby","trunk-based-development"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AndyObtiva.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2012-11-02T21:20:38.000Z","updated_at":"2025-09-28T12:42:02.000Z","dependencies_parsed_at":"2024-01-17T08:00:08.681Z","dependency_job_id":"36e3db9a-d847-4285-9321-888d9f00b194","html_url":"https://github.com/AndyObtiva/abstract_feature_branch","commit_stats":{"total_commits":188,"total_committers":3,"mean_commits":"62.666666666666664","dds":0.03191489361702127,"last_synced_commit":"1229f068a9c86a8364b03c14e54a6667640c7aa1"},"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"purl":"pkg:github/AndyObtiva/abstract_feature_branch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndyObtiva%2Fabstract_feature_branch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndyObtiva%2Fabstract_feature_branch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndyObtiva%2Fabstract_feature_branch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndyObtiva%2Fabstract_feature_branch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AndyObtiva","download_url":"https://codeload.github.com/AndyObtiva/abstract_feature_branch/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndyObtiva%2Fabstract_feature_branch/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":285511776,"owners_count":27184237,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-20T02:00:05.334Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["branch-by-abstraction","continuous-integration","fault-tolerance","feature-flags","feature-flipper","feature-flipping","feature-rollout","feature-toggles","productivity","rails","release-management","ruby","trunk-based-development"],"created_at":"2025-11-16T03:00:30.883Z","updated_at":"2025-11-20T21:01:14.496Z","avatar_url":"https://github.com/AndyObtiva.png","language":"Ruby","funding_links":[],"categories":["Feature flipping"],"sub_categories":[],"readme":"# Abstract Feature Branch 1.6.0\n[![Gem Version](https://badge.fury.io/rb/abstract_feature_branch.png)](http://badge.fury.io/rb/abstract_feature_branch)\n[![Build Status](https://api.travis-ci.org/AndyObtiva/abstract_feature_branch.png?branch=master)](https://travis-ci.org/AndyObtiva/abstract_feature_branch)\n[![Coverage Status](https://coveralls.io/repos/AndyObtiva/abstract_feature_branch/badge.png?branch=master)](https://coveralls.io/r/AndyObtiva/abstract_feature_branch?branch=master)\n[![Code Climate](https://codeclimate.com/github/AndyObtiva/abstract_feature_branch.png)](https://codeclimate.com/github/AndyObtiva/abstract_feature_branch)\n\n[`abstract_feature_branch`](https://rubygems.org/gems/abstract_feature_branch) is a Ruby gem that provides a unique variation on the [Branch by Abstraction Pattern](http://paulhammant.com/blog/branch_by_abstraction.html) by [Paul Hammant](https://paulhammant.com/) and the [Feature Toggles Pattern](https://martinfowler.com/bliki/FeatureToggle.html) by [Martin Fowler](https://martinfowler.com/) to enhance team productivity and improve software fault tolerance.\n\nIt provides the ability to wrap blocks of code with an abstract feature branch name, and then\n[specify in a configuration file](#instructions) which features to be switched on or off.\n\nThe goal is to build out upcoming features in the same source code repository branch (i.e. [Continuous Integration](https://en.wikipedia.org/wiki/Continuous_integration) and [Trunk-Based Development](https://trunkbaseddevelopment.com)), 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\n[`abstract_feature_branch`](https://rubygems.org/gems/abstract_feature_branch) additionally supports [Domain Driven Design](https://en.wikipedia.org/wiki/Domain-driven_design)'s pattern of\n[Bounded Contexts](https://www.domainlanguage.com/wp-content/uploads/2016/05/DDD_Reference_2015-03.pdf) by allowing developers to configure\ncontext-specific feature files if needed.\n\n[`abstract_feature_branch`](https://rubygems.org/gems/abstract_feature_branch) is one of the simplest and most minimalistic \"Feature Flags\" Ruby gems out there as it enables you to get started very quickly by simply leveraging YAML files without having to set up a data store if you do not need it (albeit, you also have the option to use [Redis](https://redis.com) as a very fast in-memory data store).\n\nRequirements\n------------\n- Ruby (between `~\u003e 3.3.0` and `~\u003e 1.9.1`)\n- [Optional] Rails (between `~\u003e 7.0` and `~\u003e 2.0`)\n- [Optional] Redis Server (between `~\u003e 7.0` and `~\u003e 2.0`)\n- [Optional] Redis client gem (between `~\u003e 5.0` and `~\u003e 3.0`)\n\nSetup\n-----\n\n### Rails Application Use\n\n1. Configure Rubygem\n   - With `rails` between `~\u003e 7.0` and `~\u003e 2.0`: Add the following to Gemfile \u003cpre\u003egem 'abstract_feature_branch', '~\u003e 1.6.0'\u003c/pre\u003e\n   - With `rails` `~\u003e 2.0` only: Add the following to config/environment.rb \u003cpre\u003econfig.gem 'abstract_feature_branch', :version =\u003e '1.6.0'\u003c/pre\u003e\n2. Generate \u003ccode\u003econfig/initializers/abstract_feature_branch.rb\u003c/code\u003e, \u003ccode\u003elib/tasks/abstract_feature_branch.rake\u003c/code\u003e, \u003ccode\u003econfig/features.yml\u003c/code\u003e and \u003ccode\u003econfig/features.local.yml\u003c/code\u003e in your Rails app directory by running \u003cpre\u003erails g abstract_feature_branch:install\u003c/pre\u003e\n3. [Optional] Generate \u003ccode\u003econfig/features/[context_path].yml\u003c/code\u003e in your Rails app directory by running \u003cpre\u003erails g abstract_feature_branch:context context_path\u003c/pre\u003e (more details under [**instructions**](#instructions))\n4. [Optional] Customize configuration in \u003ccode\u003econfig/initializers/abstract_feature_branch.rb\u003c/code\u003e (can be useful for changing location of feature files in Rails application, configuring Redis with a Redis or ConnectionPool instance to use for overrides, and [scoped feature enablement](#scoped-feature-enablement) (e.g. per-user), or troubleshooting a specific Rails environment feature configuration)\n5. [Optional] Redis Server (between `~\u003e 7.0` and `~\u003e 3.0`): On the Mac, you can install simply via [Homebrew](https://brew.sh/) with `brew install redis`\n6. [Optional] `redis` client gem (between `~\u003e 5.0` and `~\u003e 3.0`): Add the following to Gemfile above [`abstract_feature_branch`](https://rubygems.org/gems/abstract_feature_branch) \u003cpre\u003egem 'redis', '~\u003e 5.0.5'\u003c/pre\u003e\n\n### Ruby Application General Use\n\n1. \u003cpre\u003egem install abstract_feature_branch -v 1.6.0\u003c/pre\u003e\n2. Add code \u003ccode\u003erequire 'abstract_feature_branch'\u003c/code\u003e\n3. Create \u003ccode\u003econfig/features.yml\u003c/code\u003e under \u003ccode\u003eAbstractFeatureBranch.application_root\u003c/code\u003e and fill it with content similar to that of the sample \u003ccode\u003econfig/features.yml\u003c/code\u003e mentioned under [**instructions**](#instructions).\n4. [Optional] Create \u003ccode\u003econfig/features.local.yml\u003c/code\u003e under \u003ccode\u003eAbstractFeatureBranch.application_root\u003c/code\u003e  (more details under [**instructions**](#instructions))\n5. [Optional] Create \u003ccode\u003econfig/features/[context_path].yml\u003c/code\u003e under \u003ccode\u003eAbstractFeatureBranch.application_root\u003c/code\u003e (more details under [**instructions**](#instructions))\n6. [Optional] Add code \u003ccode\u003eAbstractFeatureBranch.application_root = \"[your_application_path]\"\u003c/code\u003e to configure the location of feature files (it defaults to \u003ccode\u003e'.'\u003c/code\u003e)\n7. [Optional] Add code \u003ccode\u003eAbstractFeatureBranch.application_environment = \"[your_application_environment]\"\u003c/code\u003e (it defaults to \u003ccode\u003e'development'\u003c/code\u003e). Alternatively, you can set \u003ccode\u003eENV['APP_ENV']\u003c/code\u003e before the \u003ccode\u003erequire\u003c/code\u003e statement or an an external environment variable.\n8. [Optional] Add code \u003ccode\u003eAbstractFeatureBranch.logger = \"[your_application_logger]\"\u003c/code\u003e (it defaults to a new instance of Ruby \u003ccode\u003eLogger\u003c/code\u003e. Must use a logger with \u003ccode\u003einfo\u003c/code\u003e and \u003ccode\u003ewarn\u003c/code\u003e methods).\n9. [Optional] Add code \u003ccode\u003eAbstractFeatureBranch.cacheable = {[environment] =\u003e [true/false]}\u003c/code\u003e to indicate cacheability of loaded feature files for enhanced performance (it defaults to true for every environment other than development).\n10. [Optional] Add code \u003ccode\u003eAbstractFeatureBranch.load_application_features\u003c/code\u003e to pre-load application features for improved first-use performance\n11. [Optional] Add code \u003ccode\u003eAbstractFeatureBranch.feature_store = Redis.new(options)\u003c/code\u003e to configure Redis for overrides and/or [scoped feature enablement](#scoped-feature-enablement) (e.g. per-user)\n12. [Optional] Set \u003ccode\u003eAbstractFeatureBranch.feature_store_live_fetching = true\u003c/code\u003e to enable live fetching of features from store (e.g. Redis) to avoid need for app/server restart upon feature changes, with the trade-off of slightly more latency due to making calls to feature store over the network (this affects scoped feature enablement too, pre-caching all user IDs for features on app/server restart when disabled)\n\nInstructions\n------------\n\n\u003ccode\u003econfig/features.yml\u003c/code\u003e contains the main configuration for the application features.\n\n\u003ccode\u003econfig/features.local.yml\u003c/code\u003e contains local overrides for the configuration, ignored by git, thus useful for temporary\nlocal feature switching for development/testing/troubleshooting purposes.\n\nOptional context specific \u003ccode\u003econfig/features/[context_path].yml\u003c/code\u003e contain feature configuration for specific application contexts.\nFor example: admin, public, or even internal/wiki. Useful for better organization especially once \u003ccode\u003econfig/features.yml\u003c/code\u003e grows too big (e.g. 20+ features)\n\nOptional context specific \u003ccode\u003econfig/features/[context_path].local.yml\u003c/code\u003e contain local overrides for context-specific feature configuration.\nThese files are rarely necessary as any feature (even a context feature) can be overridden in \u003ccode\u003econfig/features.local.yml\u003c/code\u003e,\nso these additional \u003ccode\u003e*.local.yml\u003c/code\u003e files are only recommended to be utilized once \u003ccode\u003econfig/features.local.yml\u003c/code\u003e grows\ntoo big (e.g. 20+ features).\n\nHere are the contents of the generated sample \u003ccode\u003econfig/features.yml\u003c/code\u003e, which you can modify with your own features, each\nenabled (true) or disabled (false) per environment (e.g. production).\n\n\u003e     defaults: \u0026defaults\n\u003e       feature1: true\n\u003e       feature2: true\n\u003e       feature3: false\n\u003e       feature4: scoped\n\u003e\n\u003e     development:\n\u003e       \u003c\u003c: *defaults\n\u003e\n\u003e     test:\n\u003e       \u003c\u003c: *defaults\n\u003e\n\u003e     staging:\n\u003e       \u003c\u003c: *defaults\n\u003e       feature2: false\n\u003e\n\u003e     production:\n\u003e       \u003c\u003c: *defaults\n\u003e       feature1: false\n\u003e       feature2: false\n\nNotice in the sample file how the feature \"feature1\" was configured as true (enabled) by default, but\noverridden as false (disabled) in production. This is a recommended practice.\n\n- Declaratively feature branch logic to only run when feature1 is enabled:\n\nmulti-line logic:\n\u003e     feature_branch :feature1 do\n\u003e       # perform logic\n\u003e     end\n\nsingle-line logic:\n\u003e     feature_branch(:feature1) { # perform logic }\n\nNote that \u003ccode\u003efeature_branch\u003c/code\u003e returns `nil` and does not execute the block if the feature is disabled or non-existent.\n\n`feature_branch` supports multi-threaded code (i.e. usage from multiple parallel threads, as possible in JRuby).\n\n- Imperatively check if a feature is enabled or not:\n\n\u003e     if feature_enabled?(:feature1)\n\u003e       # perform logic\n\u003e     else\n\u003e       # perform alternate logic\n\u003e     end\n\nNote that \u003ccode\u003efeature_enabled?\u003c/code\u003e returns `false` if the feature is disabled and `nil` if the feature is non-existent (practically the same effect, but nil can sometimes be useful to detect if a feature is referenced).\n\n`feature_enabled?` supports multi-threaded code (i.e. usage from multiple parallel threads, as possible in JRuby).\n\n- List all configured features for a particular environment:\n\n\u003e     AbstractFeatureBranch.environment_features('development')\n\u003e     # =\u003e {\"feature1\"=\u003etrue, \"feature2\"=\u003efalse, \"feature3\"=\u003efalse, \"feature4\"=\u003etrue}\n\n### Scoped Feature Enablement\n\nIt is possible to restrict enablement of features per specific users (or per entities of any kind) by setting a feature value to \u003ccode\u003escoped\u003c/code\u003e, and then toggling features for specific users (or other entities).\n\n1. Use \u003ccode\u003etoggle_features_for_scope\u003c/code\u003e in Ruby code to enable features per scope ID (e.g. entity ID, comma-separated compound ID, JSON string, or value object), which must be a String or a value that is safely-convertable to a String like Integer (e.g. email address or database ID). This loads Redis client gem into memory and stores scoped feature configuration in Redis.\nIn the example below, current_user is a method that provides the current signed in user (e.g. using Rails [Devise] (https://github.com/plataformatec/devise) library).\n\n\u003e     scope = current_user.email\n\u003e     AbstractFeatureBranch.toggle_features_for_scope(scope, :feature1 =\u003e true, :feature2 =\u003e false, :feature3 =\u003e true, :feature5 =\u003e true)\n\nUse alternate version of \u003ccode\u003efeature_branch\u003c/code\u003e and \u003ccode\u003efeature_enabled?\u003c/code\u003e passing extra \u003ccode\u003escope\u003c/code\u003e argument\n\nExamples:\n\n\u003e     feature_branch :feature1, current_user.email do\n\u003e       # THIS WILL EXECUTE\n\u003e     end\n\n\u003e     if feature_enabled?(:feature2, current_user.email)\n\u003e       # THIS ONE WILL NOT EXECUTE\n\u003e     else\n\u003e       # THIS ONE WILL EXECUTE\n\u003e     end\n\n\u003e     feature_branch :feature1, another_user.email do\n\u003e       # THIS WILL NOT EXECUTE\n\u003e     end\n\n\u003e     if feature_enabled?(:feature2, another_user.email)\n\u003e       # THIS ONE WILL EXECUTE (assuming feature2 is enabled in features.yml)\n\u003e     else\n\u003e       # THIS ONE WILL NOT EXECUTE\n\u003e     end\n\nNote:\n\nIf a feature is enabled as \u003ccode\u003etrue\u003c/code\u003e or disabled as \u003ccode\u003efalse\u003c/code\u003e in features.yml (or one of the overrides\nlike features.local.yml or environment variable overrides), then it overrides toggled scoped feature restrictions, becoming\nenabled or disabled globally.\n\nAPI\n---\n\n`AbstractFeatureBranch.toggle_features_for_scope(scope, feature1: true, feature2: false, ...)`: API method that toggles features (Strings or Symbols) for a scope\n\n`AbstractFeatureBranch.toggled_features_for_scope(scope)`: API method that returns toggled features for a scope (String)\n\n`AbstractFeatureBranch.scopes_for_feature(feature)`: API method that returns scopes for a (scoped) feature (String or Symbol)\n\nRecommendations\n---------------\n\n- Wrap routes in routes.rb with feature blocks to disable entire MVC feature elements by\nsimply switching off the URL route to them. Example:\n\n\u003e     feature_branch :add_business_project do\n\u003e       resources :projects\n\u003e     end\n\n- Wrap visual links to these routes in ERB views. Example:\n\n\u003e     \u003c% feature_branch :add_business_project do %\u003e\n\u003e       \u003ch2\u003eSubmit a Business\u003c/h2\u003e\n\u003e       \u003cp\u003e\n\u003e         Please submit a business idea for review.\n\u003e       \u003c/p\u003e\n\u003e       \u003cul\u003e\n\u003e         \u003c% current_user.projects.each do |p| %\u003e\n\u003e         \u003cli\u003e\u003c%= link_to p.business_campaign_name, project_path(p) %\u003e\u003c/li\u003e\n\u003e         \u003c% end %\u003e\n\u003e       \u003c/ul\u003e\n\u003e       \u003ch4\u003e\n\u003e         \u003c%= link_to('Start', new_project_path, :id =\u003e \"business_background_invitation\", :class =\u003e 'button') %\u003e\n\u003e       \u003c/h4\u003e\n\u003e     \u003c% end %\u003e\n\n- In Rails 4, wrap newly added strong parameters in controllers for data security. Example:\n\n\u003e     params.require(:project).permit(\n\u003e       feature_branch(:project_gallery) {:exclude_display},\n\u003e       :name,\n\u003e       :description,\n\u003e       :website\n\u003e     )\n\n- In Rails 4 and 3.1+ with the asset pipeline, wrap newly added CSS or JavaScript using .erb format ([gotcha and alternative solution](#gotcha-with-abstract-feature-branching-in-css-and-js-files)). Example (renamed projects.css.scss to projects.css.scss.erb and wrapped CSS with an abstract feature branch block):\n\n\u003e     \u003c% feature_branch :project_gallery do %\u003e\n\u003e     .exclude_display {\n\u003e       margin-left: auto;\n\u003e       margin-right: auto;\n\u003e       label {\n\u003e         font-size: 1em;\n\u003e         text-align: center;\n\u003e       }\n\u003e       height: 47px;\n\u003e     }\n\u003e     \u003c% end %\u003e\n\u003e     label {\n\u003e       font-size: 1.5em;\n\u003e       margin-bottom: -15px;\n\u003e       margin-top: 3px;\n\u003e       display: inline;\n\u003e     }\n\n- Once a feature has been released and switched on in production, and it has worked well for a while (e.g. for two consecutive releases),\nit is **strongly recommended** that its feature branching code is plucked out of the codebase to simplify and improve\nfuture maintainability given that it is no longer needed at that point.\n\n- Once \u003ccode\u003econfig/features.yml\u003c/code\u003e grows too big (e.g. 20+ features), it is **strongly recommended** to split it into\nmultiple context-specific feature files by utilizing the context generator mentioned above: \u003cpre\u003erails g abstract_feature_branch:context context_path\u003c/pre\u003e\n\n- When working on a new feature locally that the developer does not want others on the team to see yet, the feature\ncan be enabled in \u003ccode\u003econfig/features.local.yml\u003c/code\u003e only as it is git ignored while the feature is disabled in \u003ccode\u003econfig/features.yml\u003c/code\u003e\n\n- When troubleshooting a deployed feature by simulating a non-development environment (e.g. staging or production) locally,\nthe developer can disable it temporarily in \u003ccode\u003econfig/features.local.yml\u003c/code\u003e (git ignored) under the non-development environment,\nperform tests on the feature, and then remove the local configuration once done.\n\nEnvironment Variable Overrides\n------------------------------\n\nYou can override feature configuration with environment variables by setting an environment variable with\na name matching this convention (case-insensitive):\nABSTRACT_FEATURE_BRANCH_[feature_name] and giving it the case-insensitive value \"TRUE\" or \"FALSE\"\n\nExample:\n\n\u003e     export ABSTRACT_FEATURE_BRANCH_FEATURE1=TRUE\n\u003e     rails s\n\nThe first command adds an environment variable override for \u003ccode\u003efeature1\u003c/code\u003e that enables it regardless of any\nfeature configuration, and the second command starts the rails server with \u003ccode\u003efeature1\u003c/code\u003e enabled.\n\nTo remove an environment variable override, you may run:\n\n\u003e     unset ABSTRACT_FEATURE_BRANCH_FEATURE1\n\u003e     rails s\n\nThe benefits can be achieved more easily via \u003ccode\u003econfig/features.local.yml\u003c/code\u003e mentioned above.\nHowever, environment variable overrides are implemented to support overriding feature configuration for a Heroku deployed\napplication more easily.\n\nRedis Overrides\n---------------\n\nPrerequisites: Redis server and client (`redis` gem) and Redis configuration of `AbstractFeatureBranch.feature_store` in `config/initializers/abstract_feature_branch.rb` (`Redis` `ConnectionPool` instance is recommended for Production environments)\n\nTo be able to override feature configuration in a production environment, you can utilize Redis Overrides.\n\nAlternatively, you may use Redis Overrides as your main source of feature configuration if you prefer that instead of relying on YAML files.\n\nKeep in mind that by default, Redis Overrides are fetched on app/server start to pre-cache for better performance.\n\nTo enable live fetching of Redis Overrides, set `AbstractFeatureBranch#feature_store_live_fetching` to `true` (e.g. in `config/initializers/abstract_feature_branch.rb`), but keep in mind the trade-off with more latency due to making calls to Redis Server over the network.\n\nYou 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`](https://rubygems.org/gems/abstract_feature_branch)):\n\n```ruby\nAbstractFeatureBranch.set_store_feature('feature1', true)\n```\n\nBehind the scenes, that is the equivalent of the following Redis client invocation, which stores a hash value in a `'abstract_feature_branch'` key:\n\n```ruby\nAbstractFeatureBranch.configuration.feature_store.hset('abstract_feature_branch', 'feature1', 'true')\n```\n\nTo remove a Redis override, you can run the following in `rails console` (or `irb`):\n\n```ruby\nAbstractFeatureBranch.delete_store_feature('feature1')\n```\n\nTo get a Redis override value, you can run the following in `rails console` (or `irb`):\n\n```ruby\nAbstractFeatureBranch.get_store_feature('feature1')\n```\n\nTo get an array of all Redis Override features, you can run the following in `rails console` (or `irb`):\n\n```ruby\nAbstractFeatureBranch.get_store_features\n```\n\nHeroku\n------\n\nEnvironment variable overrides can be extremely helpful on Heroku as they allow developers to enable/disable features\nat runtime without a redeploy.\n\n### Examples\n\nEnabling a new feature without a redeploy:\n\u003cpre\u003eheroku config:add ABSTRACT_FEATURE_BRANCH_FEATURE3=true -a heroku_application_name\u003c/pre\u003e\n\nDisabling a buggy recently deployed feature without a redeploy:\n\u003cpre\u003eheroku config:add ABSTRACT_FEATURE_BRANCH_FEATURE2=false -a heroku_application_name\u003c/pre\u003e\n\nRemoving an environment variable override:\n\u003cpre\u003eheroku config:remove ABSTRACT_FEATURE_BRANCH_FEATURE2 -a heroku_application_name\u003c/pre\u003e\n\n### Recommendation\n\nIt is recommended that you use environment variable overrides on Heroku only as an emergency or temporary measure.\nAfterward, make the change official in config/features.yml, deploy, and remove the environment variable override for the long term.\n\n### Gotcha with abstract feature branching in CSS and JS files\n\nIf you've used abstract feature branching in CSS or JS files via ERB, setting environment variable overrides won't\naffect them as you need asset recompilation in addition to it, which can only be triggered by changing a CSS or JS\nfile and redeploying on Heroku (even if it's just a minor change to force it). In any case, environment variable\noverrides have been recommended above as an emergency or temporary measure. If there is a need to rely on environment\nvariable overrides to alter the style or JavaScript behavior of a page back and forth without a redeploy, **one solution**\nis to do additional abstract feature branching in HTML templates (e.g. ERB or [HAML](http://haml.info) to\nlink to different stylesheets and JS files, use different CSS classes, or invoke different JavaScript methods per branch\nof HTML for example.)\n\nFeature Configuration Load Order\n--------------------------------\n\nFor better knowledge and clarity, here is the order in which feature configuration is loaded, with the latter sources overriding\nthe former if overlap in features occurs:\n\n1. Context-specific feature files: \u003ccode\u003econfig/features/**/*.yml\u003c/code\u003e\n2. Main feature file: \u003ccode\u003econfig/features.yml\u003c/code\u003e\n3. Context-specific local feature file overrides: \u003ccode\u003econfig/features/**/*.local.yml\u003c/code\u003e\n4. Main local feature file override: \u003ccode\u003econfig/features.local.yml\u003c/code\u003e\n5. Environment variable overrides\n6. Redis overrides\n\nRails Initializer\n-----------------\n\nHere is the content of the generated initializer [with `redis` client gem added] (\u003ccode\u003econfig/initializers/abstract_feature_branch.rb\u003c/code\u003e), which contains instructions on how to customize via [dependency injection](http://en.wikipedia.org/wiki/Dependency_injection):\n\n\u003e     # Storage system for features (other than YAML/Env-Vars). Right now, only Redis and ConnectionPool are supported.\n\u003e     # AbstractFeatureBranch.feature_store = Redis.new\n\u003e\n\u003e     # Storage can be a Redis ConnectionPool instance\n\u003e     # AbstractFeatureBranch.feature_store = ConnectionPool.new { Redis.new }\n\u003e\n\u003e     # The following example line works with Heroku Redis To Go while still operating on local Redis for local development\n\u003e     # AbstractFeatureBranch.feature_store = Redis.new(:url =\u003e ENV['REDISTOGO_URL'])\n\u003e\n\u003e     # Enable live fetching of feature configuration from storage system, to update features without app/server restart.\n\u003e     # false by default to only load features on app/server start for faster performance (requires restart on change)\n\u003e     AbstractFeatureBranch.feature_store_live_fetching = false\n\u003e\n\u003e     # Application root where config/features.yml or config/features/ is found\n\u003e     AbstractFeatureBranch.application_root = Rails.root\n\u003e\n\u003e     # Application environment (e.g. \"development\", \"staging\" or \"production\")\n\u003e     AbstractFeatureBranch.application_environment = Rails.env.to_s\n\u003e\n\u003e     # Abstract Feature Branch logger\n\u003e     AbstractFeatureBranch.logger = Rails.logger\n\u003e\n\u003e     # Cache feature files once read or re-read them at runtime on every use (helps development).\n\u003e     # Defaults to true if environment not specified, except for development, which defaults to false.\n\u003e     AbstractFeatureBranch.cacheable = {\n\u003e       :development =\u003e false,\n\u003e       :test =\u003e true,\n\u003e       :staging =\u003e true,\n\u003e       :production =\u003e true\n\u003e     }\n\u003e\n\u003e     # Pre-load application features to improve performance of first web-page hit\n\u003e     AbstractFeatureBranch.load_application_features unless Rails.env.development?\n\nRake Task\n---------\n\nAbstract Feature Branch comes with a rake task to beautify feature files that have grown unorganized by sorting features\nby name and getting rid of extra empty lines. It does so per section, without affecting the order of the sections\nthemselves.\n\nFor example, here is content before and after beautification.\n\nBefore:\n\n\u003e     defaults: \u0026defaults\n\u003e\n\u003e\n\u003e       gallery: true\n\u003e\n\u003e       carousel: true\n\u003e\n\u003e       third_party_integration: false\n\u003e       caching: true\n\u003e\n\u003e     development:\n\u003e       \u003c\u003c: *defaults\n\u003e\n\u003e     test:\n\u003e       \u003c\u003c: *defaults\n\u003e\n\u003e       caching: false\n\u003e\n\u003e     staging:\n\u003e       \u003c\u003c: *defaults\n\u003e       third_party_integration: true\n\u003e       caching: true\n\u003e\n\u003e     production:\n\u003e       \u003c\u003c: *defaults\n\u003e       third_party_integration: false\n\u003e\n\u003e       caching: false\n\nAfter:\n\n\u003e     defaults: \u0026defaults\n\u003e       caching: true\n\u003e       carousel: true\n\u003e       gallery: true\n\u003e       third_party_integration: false\n\u003e\n\u003e     development:\n\u003e       \u003c\u003c: *defaults\n\u003e\n\u003e     test:\n\u003e       \u003c\u003c: *defaults\n\u003e       caching: false\n\u003e\n\u003e     staging:\n\u003e       \u003c\u003c: *defaults\n\u003e       caching: true\n\u003e       third_party_integration: true\n\u003e\n\u003e     production:\n\u003e       \u003c\u003c: *defaults\n\u003e       caching: false\n\u003e       third_party_integration: false\n\nThis is very useful in bigger applications that have scores of features since it allows a developer to quickly scan\nfor alphabetical sorted feature names. Although file find is an alternative solution, having tidy organized feature\nnames can help increase overall team productivity in the long term.\n\nFor Rails application use, the rake task is generated under \u003ccode\u003elib/tasks/abstract_feature_branch.rake\u003c/code\u003e.\n\nFor Ruby application general use, here is the content of the rake task:\n\n\u003e     require 'abstract_feature_branch'\n\u003e     namespace :abstract_feature_branch do\n\u003e\n\u003e       desc \"Beautify YAML of specified feature file via file_path argument or all feature files when no argument specified (config/features.yml, config/features.local.yml, and config/features/**/*.yml) by sorting features by name and eliminating extra empty lines\"\n\u003e       task :beautify_files, :file_path do |_, args|\n\u003e         AbstractFeatureBranch::FileBeautifier.process(args[:file_path])\n\u003e       end\n\u003e\n\u003e     end\n\nThe rake task may be invoked in a number of ways:\n- \u003ccode\u003erake abstract_feature_branch:beautify_files\u003c/code\u003e beautifies all feature files under [application_root]/config\n- \u003ccode\u003erake abstract_feature_branch:beautify_files[file_path]\u003c/code\u003e beautifies a single feature file\n- \u003ccode\u003erake abstract_feature_branch:beautify_files[directory_path]\u003c/code\u003e beautifies all feature files under directory path recursively\n\nNote that the beautifier ignores comments at the top, but deletes entire line comments in the middle of a YAML file, so\nafter invoking the rake task, **verify** that your feature file contents are to your satisfaction before committing the\ntask changes.\n\nFeature Branches vs Branch by Abstraction\n-----------------------------------------\n\nAlthough feature branches and branching by abstraction are similar, there are different situations that recommend each approach.\n\nFeature 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.\n\nBranching 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 conflicts when you do attempt to integrate your feature into the larger app.\n\nContributing to abstract_feature_branch\n---------------------------------------\n\n* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.\n* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.\n* Fork the project.\n* Start a feature/bugfix branch.\n* Commit and push until you are happy with your contribution.\n* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.\n* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.\n\nCommitters\n---------------------------------------\n* [Andy Maleh (Author)](https://github.com/AndyObtiva)\n\nContributors\n---------------------------------------\n* [Mark Moschel](https://github.com/mmosche2)\n\nCopyright\n---------------------------------------\n\nCopyright (c) 2012-2023 Andy Maleh. See [LICENSE.txt](LICENSE.txt) for\nfurther details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAndyObtiva%2Fabstract_feature_branch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAndyObtiva%2Fabstract_feature_branch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAndyObtiva%2Fabstract_feature_branch/lists"}