{"id":28089457,"url":"https://github.com/azeveco/togglefy","last_synced_at":"2025-05-13T12:58:27.289Z","repository":{"id":287480692,"uuid":"964866720","full_name":"azeveco/Togglefy","owner":"azeveco","description":"Togglefy is a feature management Rails gem to help you control which features an user or a group has access to.","archived":false,"fork":false,"pushed_at":"2025-05-08T21:54:32.000Z","size":172,"stargazers_count":3,"open_issues_count":8,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-08T22:31:38.574Z","etag":null,"topics":["feature","feature-flags","feature-toggles","gem","rails"],"latest_commit_sha":null,"homepage":"https://rubygems.org/gems/togglefy","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/azeveco.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":"CODE_OF_CONDUCT.md","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,"zenodo":null}},"created_at":"2025-04-11T23:36:22.000Z","updated_at":"2025-04-29T17:22:54.000Z","dependencies_parsed_at":"2025-05-08T22:25:00.119Z","dependency_job_id":"4d89f033-bff4-4bdd-aa88-418a7a1c7ed3","html_url":"https://github.com/azeveco/Togglefy","commit_stats":null,"previous_names":["azeveco/jane","azeveco/togglefy"],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/azeveco%2FTogglefy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/azeveco%2FTogglefy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/azeveco%2FTogglefy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/azeveco%2FTogglefy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/azeveco","download_url":"https://codeload.github.com/azeveco/Togglefy/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253948387,"owners_count":21988953,"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","feature-flags","feature-toggles","gem","rails"],"created_at":"2025-05-13T12:58:26.594Z","updated_at":"2025-05-13T12:58:27.275Z","avatar_url":"https://github.com/azeveco.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Togglefy\n\n![Gem](https://img.shields.io/gem/v/togglefy)\n![Downloads](https://img.shields.io/gem/dt/togglefy)\n\nTogglefy is a simple feature management solution to help you control which features an user or a group has access to.\n\nTogglefy is free, open source and you are welcome to help build it.\n\n## Installation\n\nAdd the gem manually to your Gemfile:\n\n```gemfile\ngem 'togglefy', '~\u003e 1.2'\n```\n\nOr install it and add to the application's Gemfile by executing:\n\n```bash\nbundle add togglefy\n```\n\nIf bundler is not being used to manage dependencies, install the gem by executing:\n\n```bash\ngem install togglefy\n```\n\n## Usage\n\n### First steps\n\n#### Installing inside the project\nAfter adding the gem to your project, you need to run the generate command to add the necessary files:\n```bash\nrails generate togglefy:install\n```\n\nThis command will create the migrations to create the tables inside your project.\n\nIf you use an older version of Rails (\u003c 5), then the migration files don't need you to specify the version.\n\nTo fix this, you will have to manually go to the two migration files of Togglefy: `create_feature` and `create_feature_assignments` and do the following:\n\nChange these lines from this:\n\n```ruby\nrails_version = \"#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}\"\n\nclass CreateTogglefyFeatures \u003c ActiveRecord::Migration[rails_version]\n```\n\n```ruby\nrails_version = \"#{Rails::VERSION::MAJOR}.#{Rails::VERSION::MINOR}\"\n\nclass CreateTogglefyFeatureAssignments \u003c ActiveRecord::Migration[rails_version]\n```\n\nTo this:\n\n```ruby\nclass CreateTogglefyFeatures \u003c ActiveRecord::Migration\n```\n\n```ruby\nclass CreateTogglefyFeatureAssignments \u003c ActiveRecord::Migration\n```\n\nPlease, don't remove/change anything else that's there or Togglefy may not work as expected.\n\nRun the migration to create these in your datase:\n```bash\nrails db:migrate\n```\nOr if you're using a legacy codebase:\n```bash\nrake db:migrate\n```\n\nThe models are stored inside Togglefy, so you don't need to create them inside your project unless you want to.\n\nIf that's something you want, you can check them following this path: `app/models/togglefy/`.\n\nAfter that, the next steps are also pretty simple.\n\n#### Including inside the Assignable\n\nAdd the following to your model that will have a relation with the features. It can be an `User`, `Account` or something you decide:\n```ruby\ninclude Togglefy::Assignable\n```\n\nThis will add the relationship between Togglefy's models and yours. Yours will be referred to as **assignable** throughout this documentation. If you want to check it in the source code, you can find it here: `lib/togglefy/assignable.rb` inside de `included` block.\n\nOlder versions (`\u003c= 1.0.2`) had the `Featureable` instead of the `Assignable`. The `Featureable` is now deprecated. You can still use it and it won't impact old users, but we highly recommend you to use the `Assignable` as it is semantically more accurate about what it does.\n\nWith that, everything is ready to use **Togglefy**, welcome!\n\n### Managing Features\n\n#### Creating Features\nTo create features it's as simple as drinking a nice cold beer after a hard day or drinking the entire bottle of coffee in a span of 1 hour:\n\n```ruby\nTogglefy::Feature.create(\n  name: \"Magic\",\n  description: \"You're a Wizard, Harry\"\n) # To create a simple Feature\n```\n\nIf you have tenant, groups (or roles), difference between environments, you can do the following:\n\n```ruby\nTogglefy::Feature.create(\n  name: \"Super Powers\",\n  description: \"With great power comes great responsibility\", \n  tenant_id: \"123abc\",\n  group: :admin,\n  environment: :production\n)\n```\n\nYou can also use:\n\n```ruby\nTogglefy.create(\n  name: \"Teleportation\",\n  description: \"Allows the assignable to teleport to anywhere\",\n  group: :jumper\n)\n\n# Or\n\nTogglefy.create_feature(\n  name: \"Teleportation\",\n  description: \"Allows the assignable to teleport to anywhere\",\n  group: :jumper\n)\n```\n\nYou don't have to fill all fields, the only one that is mandatory is the name, because is by using the name that we will create the unique identifier, which is the field we'll use to find, delete and more.\n\nThe identifier is the name, downcased and snake_cased 🐍\n\nWhenever you create a `Togglefy::Feature`, you can expect something like this:\n\n```ruby\nid: 1,\nname: \"Super Powers\",\nidentifier: \"super_powers\",\ndescription: \"With great power comes great responsibility\",\ncreated_at: \"2025-04-12 01:39:10.176561000 +0000\",\nupdated_at: \"2025-04-12 01:39:46.818928000 +0000\",\ntenant_id: \"123abc\",\ngroup: \"admin\",\nenvironment: \"production\",\nstatus: \"inactive\"\n```\n\n#### Updating a Feature\n\nTo update a feature is as simple as riding on a monocycle while balancing a cup of water on the top of a really tall person that's on your shoulders.\n\nHere's how you can do it:\n\n```ruby\nTogglefy.update(:super_powers, tenant_id: \"abc123\")\nTogglefy.update_feature(:super_powers, tenant_id: \"abc123\")\n```\n\nOr by finding the feature manually and then updating it like you always do with Rails:\n\n```ruby\nfeature = Togglefy::Feature.find_by(identifier: :super_powers)\n# or\nfeature = Togglefy.feature(:super_powers) # This is explained more in the \"Finding a specific feature\" section of this README\n\nfeature.update(tenant_id: \"123abc\")\n```\n\n#### Destroying Features\n\nIt looks like you're mean 😈\n\nSo here's how you can destroy a feature:\n\n```ruby\nTogglefy.destroy(:super_powers)\nTogglefy.destroy_feature(:super_powers)\nTogglefy::Feature.identifier(:super_powers).destroy\nTogglefy::Feature.find_by(identifier: :super_powers).destroy\n```\n\n#### Toggle Features\n\nYou can toggle a feature status to active or inactive by doing this:\n\n```ruby\nTogglefy.toggle(:super_powers)\nTogglefy.toggle_feature(:super_powers)\n```\n\n#### About `Togglefy::Feature` status\n\nAs you can see, the `Togglefy::Feature` also has a status and it is default to `inactive`. You can change this during creation.\n\nThe status holds the `inactive` or `active` values. This status is not to define if a assignable (any model that has the `include Togglefy::Assignable`) either has ou hasn't a feature, but to decide if this feature is available in the entire system.\n\nIt's up to you to define how you will implement it.\n\n* Is it disabled? Then this feature is likely unrelease\n* Or maybe if it is disabled, you can see the flag to active to an assignable but can't change the values?\n\nAgain, it's up to you!\n\nYou can change the status by:\n* Sending a value during creation\n* Updating the column\n* Doing a:\n  ```ruby\n  Togglefy::Feature.find_by(identifier: :super_powers).active!\n  Togglefy.feature(:super_powers).active!\n  Togglefy.active!(:super_powers)\n  Togglefy.activate_feature(:super_powers)\n\n  Togglefy::Feature.find_by(identifier: :super_powers).inactive!\n  Togglefy.feature(:super_powers).inactive!\n  Togglefy.inactive!(:super_powers)\n  Togglefy.inactivate_feature(:super_powers)\n  ```\n\n### Managing Assignables \u003c-\u003e Features\nNow that we know how to create features, let's check how we can manage them.\n\nAn assignable has some direct methods thanks to the `include Togglefy::Assignable`, which are (and let's use an user as an example of an assignable):\n\n```ruby\nuser.has_feature?(:super_powers) # Checks if user has a single feature\nuser.add_feature(:super_powers) # Adds a feature to user\nuser.remove_feature(:super_powers) # Removes a feature from an user\nuser.clear_features # Clears all features from an user\n```\n\nThe assignable \u003c-\u003e feature relation is held by the `Togglefy::FeatureAssignment` table/model.\n\nBut there's another way to manage assignables \u003c-\u003e features by using the `FeatureAssignableManager`. It's up to you to decide which one.\n\nHere are the examples:\n\n```ruby\nTogglefy.for(assignable).has?(:super_powers) # Checks if assignable (user || account || anything) has a single feature\nTogglefy.for(assignable).enable(:super_powers) # Enables/adds a feature to an assignable\nTogglefy.for(assignable).disable(:super_powers) # Disables/removes a feature from an assignable\nTogglefy.for(assignable).clear # Clears all features from an assignable\n```\n\nYou can also supercharge it by chaining methods, like:\n\n```ruby\n# Instead of doing this:\nTogglefy.for(assignable).disable(:alpha_access)\nTogglefy.for(assignable).enable(:beta_access)\n\n# You can go something like this:\nTogglefy.for(assignable).disable(:alpha_access).enable(:beta_access)\n```\n\nThis second method may look strange, but it's the default used by the gem and you will see that right now!\n\n#### Mass Enable/Disable of Features to/from Assignables\nYou can mass enable/disable features to/from Assignables.\n\nDoing that is simple! Let's assume that your assignable is an User model.\n\n```ruby\nTogglefy.mass_for(User).bulk.enable(:super_powers) # To enable a specific feature to all users\nTogglefy.mass_for(User).bulk.enable([:super_powers, :magic, ...]) # To enable two or more features to all users\nTogglefy.mass_for(User).bulk.enable(:super_powers, percentage: 20) # To enable a feature to 20% of all users\nTogglefy.mass_for(User).bulk.enable([:super_powers, :magic, ...], percentage: 50) # To enable two or more features to 50% of all users\n```\n\nThe same applies to the disable:\n\n```ruby\nTogglefy.mass_for(User).bulk.disable(:super_powers) # To disable a specific feature to all users\nTogglefy.mass_for(User).bulk.disable([:super_powers, :magic, ...]) # To disable two or more features to all users\nTogglefy.mass_for(User).bulk.disable(:super_powers, percentage: 5) # To disable a feature to 5% of all users\nTogglefy.mass_for(User).bulk.disable([:super_powers, :magic, ...], percentage: 75) # To disable two or more features to 75% of all users\n```\n\nThere are a few things to pay attention:\n* Whenever you do a enable/disable, it will only query for valid assignables, so:\n  * If you do a enable, it will query all assignables that don't have the feature(s) enabled\n  * If you do a disable, it will query all assignables that do already have the feature(s) enabled\n* You can also use filters for:\n  * `group || role`\n  * `environment || env`\n  * `tenant_id`\n  * You can check about filters aliases at [Aliases table](#aliases-table)\n\nThese will be applied to query features that match the identifiers + the filters sent.\n\nSo it would be something like:\n\n```ruby\nTogglefy.mass_for(User).bulk.enable(:super_powers, group: :admin, percentage: 20)\nTogglefy.mass_for(User).bulk.disable(:magic, group: :dev, env: :production, percentage: 75)\n```\n\n### Querying Features\nRemember when I told you a looooong time ago that the strange way is the default using by the gem? If you don't, no worries. It was a really loooong time ago, like `1.minute.ago`.\n\nIt's actually pretty simple. Each line of each code block will show you a way to query to achieve the same result in the context. You **don't** need to use all of options listed in each code block.\n\n#### Querying Features (plural)\n\nTo query `Togglefy::Feature` from a specific group (the same applies to environment and tenant), you would do something like this:\n\n```ruby\nTogglefy::Feature.where(group: :dev)\nTogglefy::Feature.for_group(:dev)\nTogglefy.for_group(:dev)\nTogglefy.for_role(:dev)\nTogglefy.for_filters(filters: {group: :dev})\nTogglefy.for_filters(filter: {role: :dev})\n```\n\nThe `for_filters` is recommended when you want to use more than one filter, like:\n\n```ruby\nTogglefy.for_filters(filters: {group: :admin, environment: :production})\n```\n\nThis will query me all `Togglefy::Feature`s that belongs to group admin and the production environment.\n\nThe `Togglefy.for_filters` can have the following filters:\n\n```ruby\nfilters: {\n  group:,\n  role:, # Acts as a group, explained in the Aliases section\n  environment:,\n  env:, # Acts as a group, explained in the Aliases section\n  tenant_id:,\n  status:,\n  identifier:\n}\n```\n\nThe `Togglefy.for_filters` will only apply the filters sent that are `nil` or `!value.blank?`.\n\nYou can send `nil` values too, like:\n\n```ruby\nTogglefy.for_tenant(nil) # This will query me all Togglefy::Features with tenant_id nil\n\nTogglefy.for_filters(filters: {group: :admin, environment: nil, tenant_id: nil})\n```\n\nThere's also another way to filter for `nil` values:\n\n```ruby\nTogglefy.without_group\nTogglefy.without_role\n\nTogglefy.without_environment\nTogglefy.without_env\n\nTogglefy.without_tenant\n```\n\n#### Querying by Status\nTo query Togglefy::Features by status (the same applies to inactive).\n\n```ruby\nTogglefy::Feature.where(status: :active)\nTogglefy::Feature.active\nTogglefy.with_status(:active)\n```\n\n#### Finding a specific feature\n```ruby\nTogglefy.feature(:super_powers)\nTogglefy::Feature.identifier(:super_powers)\nTogglefy::Feature.find_by(identifier: :super_powers)\n```\n\n#### Finding multiple features\n```ruby\nTogglefy.feature([:super_powers, :magic])\nTogglefy::Feature.identifier([:super_powers, :magic])\nTogglefy::Feature.where(identifier: [:super_powers, :magic])\n```\n\n#### List all features of an Assignable\nLet's pretend that your assignable is an User.\n\n```ruby\nuser = User.find(1)\nuser.features\n```\n\n#### Check all features an Assignable have/doesn't have\nAgain, let's pretend that your assignable is an User. This is the only case you need to send the feature id and not the identifier.\n\n```ruby\nUser.with_features(1)\nUser.with_features([2, 3])\nUser.without_features(1)\nUser.without_features([2, 3])\n```\n\n#### Querying all features\n```ruby\nTogglefy.features(:super_powers)\nTogglefy::Feature.all\n```\n\n#### Querying all features enabled to a klass\nLet's assume that you have two different assignables: User and Account.\n\nYou want to list all features being used by assignables of User type:\n\n```ruby\nTogglefy.for_type(User) # This returns all current FeatureAssignment with a User assignable\n```\n\n#### Aliases\n\nBy the way, did you notice that I wrote `group` and `role` to get group?\n\nThere are aliases for both group and environment that can be used outside of `Togglefy::Feature`. If you want to query `Togglefy::Feature` directly, use only the default name.\n* `group` can be written as `role` outside of `Togglefy::Feature`\n* `environment` can be written as `env` out side of `Togglefy::Feature`\n\n```ruby\nTogglefy.for_group(:dev)\nTogglefy.for_role(:dev)\nTogglefy.for_filters(filters: {group: :dev})\nTogglefy.for_filters(filter: {role: :dev})\n\nTogglefy.for_environment(:production)\nTogglefy.for_env(:production)\nTogglefy.for_filters(filters: {environment: :production})\nTogglefy.for_filters(filter: {env: :production})\n```\n\n## Aliases table\n\nHere's a table of all aliases available on Togglefy.\n\nYou can use either, as long as you respect the rules listed.\n\n| Original              | Alias                | Rules                                               |\n| --------------------- | -------------------- | --------------------------------------------------- |\n| `for_group`           | `for_role`           | Can't be used if doing a direct `Togglefy::Feature` |\n| `without_group`       | `without_role`       | Can't be used if doing a direct `Togglefy::Feature` |\n| `for_environment`     | `for_env`            | Can't be used if doing a direct `Togglefy::Feature` |\n| `without_environment` | `without_env`        | Can't be used if doing a direct `Togglefy::Feature` |\n| `group`               | `role`               | Used inside methods that receives filters           |\n| `environment`         | `env`                | Used inside methods that receives filters           |\n| `create`              | `create_feature`     | None                                                |\n| `update`              | `update_feature`     | None                                                |\n| `toggle`              | `toggle_feature`     | None                                                |\n| `active!`             | `activate_feature`   | None                                                |\n| `deactive!`           | `inactivate_feature` | None                                                |\n| `destroy`             | `destroy_feature`    | None                                                |\n\n\n## Development\n### Setup the environment\n1. Clone the repository\n2. Run `bin/setup` on the Gem's root directory to install dependencies and run the dummy Rails app migrations used for tests\n3. Create your branch and checkout to it\n\n### Running the tests\n1. Make sure that the dummy Rails app db and migrations were ran\n2. Run `bundle exec rspec` on the Gem's root directory to run all the tests\n3. If you want to specify a single file to run the tests: `bundle exec rspec path/to/spec/file.rb`\n4. If you want to specify a single test of a single file to run: `bundle exec spec path/to/spec/file.rb:42` where 42 represents the number of the line\n\n### Running RuboCop\n1. Make sure you're at the Gem's root directory\n2. Run `bundle exec rubocop` to run RuboCop on all files not ignored by the `.rubocop.yml` file\n3. Run `bundle exec rubocop app spec lib/something.rb` to run RuboCop on specific directories/files\n4. Run `bundle exec rubocop -a` to fix all the auto-correctable offenses listed by RuboCop\n\n### Other\n1. You can also run `bin/console` for an interactive prompt that will allow you to experiment.\n\n### Use local Togglefy Gem with a Rails application\n1. Open the Gemfile of your Rails application\n2. Add the following: `gem \"togglefy\", path: \"path/to/togglefy/directory\"`\n3. Now you can test everything inside of your Rails application\n\n#### If you make a change to the Togglefy's code and want to test it, make sure to:\n1. If you're running a Rails server: restart\n2. If you're running a Rails console: `reload!` or restart\n\n## Contributing\n\nBug reports and pull requests are welcome on GitHub at https://github.com/azeveco/togglefy. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/azeveco/togglefy/blob/master/CODE_OF_CONDUCT.md).\n\n* Where can I check for planned features but not started?\n  * You can check Togglefy's project here: https://github.com/users/azeveco/projects/2 to see the priorities, status and more\n  * Or you can check the issues: https://github.com/azeveco/Togglefy/issues\n* I have an idea of a feature! What do I do?\n  * First things first, check if there's an issue about it first. If it does, put your comments there. If it doesn't, do the following:\n    * Are you a developer that wants to develop it? Create a new issue, select the New Feature 🚀 template and fill out everything\n    * Are you a developer or just an user that would like to request a new feature? Create a new issue, select the Feature Request 💡 template and fill out everything\n* I have a bug report. Where can I... report it?\n  * Create an issue, select the Bug Report 🪲 template and fill out everything\n\nThere's a PR and Issues Templates. We recommend you to follow these strictly to help everyone.\n\n## License\n\nThe gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).\n\n## Code of Conduct\n\nEveryone interacting in the togglefy project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/azeveco/togglefy/blob/master/CODE_OF_CONDUCT.md).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fazeveco%2Ftogglefy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fazeveco%2Ftogglefy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fazeveco%2Ftogglefy/lists"}