{"id":13379272,"url":"https://github.com/fetlife/rollout","last_synced_at":"2025-05-13T19:04:08.751Z","repository":{"id":980003,"uuid":"781300","full_name":"fetlife/rollout","owner":"fetlife","description":"Feature flippers.","archived":false,"fork":false,"pushed_at":"2024-03-01T11:52:28.000Z","size":232,"stargazers_count":2894,"open_issues_count":9,"forks_count":212,"subscribers_count":62,"default_branch":"master","last_synced_at":"2025-04-17T03:02:57.230Z","etag":null,"topics":["feature-flags","percentage","redis","rollout","ruby"],"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/fetlife.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2010-07-18T04:25:36.000Z","updated_at":"2025-04-16T07:51:04.000Z","dependencies_parsed_at":"2023-01-13T10:49:25.961Z","dependency_job_id":"f3ba4b9b-6b70-433f-b34a-c10b4da1b606","html_url":"https://github.com/fetlife/rollout","commit_stats":{"total_commits":267,"total_committers":53,"mean_commits":5.037735849056604,"dds":0.7865168539325843,"last_synced_commit":"a88d9b2f72a67576d00669cf1ef63912aa3cde51"},"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fetlife%2Frollout","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fetlife%2Frollout/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fetlife%2Frollout/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fetlife%2Frollout/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fetlife","download_url":"https://codeload.github.com/fetlife/rollout/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250439290,"owners_count":21430823,"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","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":["feature-flags","percentage","redis","rollout","ruby"],"created_at":"2024-07-30T07:02:17.655Z","updated_at":"2025-04-24T08:59:27.515Z","avatar_url":"https://github.com/fetlife.png","language":"Ruby","readme":"# rollout\n\nFast feature flags based on Redis.\n\n[![Gem Version](https://badge.fury.io/rb/rollout.svg)](https://badge.fury.io/rb/rollout)\n[![CircleCI](https://circleci.com/gh/fetlife/rollout.svg?style=svg)](https://circleci.com/gh/fetlife/rollout)\n[![Code Climate](https://codeclimate.com/github/FetLife/rollout/badges/gpa.svg)](https://codeclimate.com/github/FetLife/rollout)\n[![Test Coverage](https://codeclimate.com/github/FetLife/rollout/badges/coverage.svg)](https://codeclimate.com/github/FetLife/rollout/coverage)\n\n## Install it\n\n```bash\ngem install rollout\n```\n\n## How it works\n\nInitialize a rollout object. I assign it to a global var.\n\n```ruby\nrequire 'redis'\n\n$redis = Redis.new\n$rollout = Rollout.new($redis)\n```\n\nor even simpler\n\n```ruby\nrequire 'redis'\n$rollout = Rollout.new($redis) # Will use REDIS_URL env var or default redis url\n```\n\n\nUpdate data specific to a feature:\n\n```ruby\n$rollout.set_feature_data(:chat, description: 'foo', release_date: 'bar', whatever: 'baz')\n```\n\nCheck whether a feature is active for a particular user:\n\n```ruby\n$rollout.active?(:chat, User.first) # =\u003e true/false\n```\n\nCheck whether a feature is active globally:\n\n```ruby\n$rollout.active?(:chat)\n```\n\nYou can activate features using a number of different mechanisms.\n\n## Groups\n\nRollout ships with one group by default: \"all\", which does exactly what it\nsounds like.\n\nYou can activate the all group for the chat feature like this:\n\n```ruby\n$rollout.activate_group(:chat, :all)\n```\n\nYou might also want to define your own groups. We have one for our caretakers:\n\n```ruby\n$rollout.define_group(:caretakers) do |user|\n  user.caretaker?\nend\n```\n\nYou can activate multiple groups per feature.\n\nDeactivate groups like this:\n\n```ruby\n$rollout.deactivate_group(:chat, :all)\n```\n\nGroups need to be defined every time your app starts. The logic is not persisted\nanywhere.\n\n## Specific Users\n\nYou might want to let a specific user into a beta test or something. If that\nuser isn't part of an existing group, you can let them in specifically:\n\n```ruby\n$rollout.activate_user(:chat, @user)\n```\n\nDeactivate them like this:\n\n```ruby\n$rollout.deactivate_user(:chat, @user)\n```\n\n## User Percentages\n\nIf you're rolling out a new feature, you might want to test the waters by\nslowly enabling it for a percentage of your users.\n\n```ruby\n$rollout.activate_percentage(:chat, 20)\n```\n\nThe algorithm for determining which users get let in is this:\n\n```ruby\nCRC32(user.id) \u003c (2**32 - 1) / 100.0 * percentage\n```\n\nSo, for 20%, users 0, 1, 10, 11, 20, 21, etc would be allowed in. Those users\nwould remain in as the percentage increases.\n\nDeactivate all percentages like this:\n\n```ruby\n$rollout.deactivate_percentage(:chat)\n```\n\n_Note that activating a feature for 100% of users will also make it active\n\"globally\". That is when calling Rollout#active? without a user object._\n\nIn some cases you might want to have a feature activated for a random set of\nusers. It can come specially handy when using Rollout for split tests.\n\n```ruby\n$rollout = Rollout.new($redis, randomize_percentage: true)\n```\n\nWhen on `randomize_percentage` will make sure that 50% of users for feature A\nare selected independently from users for feature B.\n\n## Global actions\n\nWhile groups can come in handy, the actual global setter for a feature does not require a group to be passed.\n\n```ruby\n$rollout.activate(:chat)\n```\n\nIn that case you can check the global availability of a feature using the following\n\n```ruby\n$rollout.active?(:chat)\n```\n\nAnd if something is wrong you can set a feature off for everybody using\n\nDeactivate everybody at once:\n\n```ruby\n$rollout.deactivate(:chat)\n```\n\nFor many of our features, we keep track of error rates using redis, and\ndeactivate them automatically when a threshold is reached to prevent service\nfailures from cascading. See https://github.com/jamesgolick/degrade for the\nfailure detection code.\n\n## Check Rollout Feature\n\nYou can inspect the state of your feature using:\n\n```ruby\n\u003e\u003e $rollout.get(:chat)\n=\u003e #\u003cRollout::Feature:0x00007f99fa4ec528 @data={}, @groups=[:caretakers], @name=:chat, @options={}, @percentage=0.05, @users=[\"1\"]\u003e\n```\n\n## Namespacing\n\nRollout separates its keys from other keys in the data store using the\n\"feature\" keyspace.\n\nIf you're using redis, you can namespace keys further to support multiple\nenvironments by using the\n[redis-namespace](https://github.com/resque/redis-namespace) gem.\n\n```ruby\n$ns = Redis::Namespace.new(Rails.env, redis: $redis)\n$rollout = Rollout.new($ns)\n$rollout.activate_group(:chat, :all)\n```\n\nThis example would use the \"development:feature:chat:groups\" key.\n\n## Frontend / UI\n\n* [rollout-ui](https://github.com/fetlife/rollout-ui)\n* [Rollout-Dashboard](https://github.com/fiverr/rollout_dashboard/)\n\n## Implementations in other languages\n\n*   Python: https://github.com/asenchi/proclaim\n*   PHP: https://github.com/opensoft/rollout\n*   Clojure: https://github.com/yeller/shoutout\n*   Perl: https://metacpan.org/pod/Toggle\n*   Golang: https://github.com/SalesLoft/gorollout\n\n\n## Contributors\n\n*   James Golick - Creator - https://github.com/jamesgolick\n*   Eric Rafaloff - Maintainer - https://github.com/EricR\n\n\n## Copyright\n\nCopyright (c) 2010-InfinityAndBeyond BitLove, Inc. See LICENSE for details.\n","funding_links":[],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffetlife%2Frollout","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffetlife%2Frollout","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffetlife%2Frollout/lists"}