{"id":13462897,"url":"https://github.com/lucascaton/enumerate_it","last_synced_at":"2025-05-14T10:09:49.000Z","repository":{"id":841981,"uuid":"565843","full_name":"lucascaton/enumerate_it","owner":"lucascaton","description":"Enumerations for Ruby with some magic powers! 🎩","archived":false,"fork":false,"pushed_at":"2025-03-28T23:36:03.000Z","size":476,"stargazers_count":350,"open_issues_count":1,"forks_count":44,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-04-03T20:43:55.672Z","etag":null,"topics":["enumeration","rails","ruby","ruby-enumerations"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lucascaton.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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-03-16T23:25:21.000Z","updated_at":"2025-04-01T14:34:38.000Z","dependencies_parsed_at":"2024-05-17T06:26:28.897Z","dependency_job_id":"14f8649e-36ec-4584-8c2b-055072333ffa","html_url":"https://github.com/lucascaton/enumerate_it","commit_stats":{"total_commits":515,"total_committers":34,"mean_commits":"15.147058823529411","dds":0.3941747572815534,"last_synced_commit":"871aa803c1ef018e5935a8de29a0729793a2e188"},"previous_names":[],"tags_count":57,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucascaton%2Fenumerate_it","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucascaton%2Fenumerate_it/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucascaton%2Fenumerate_it/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lucascaton%2Fenumerate_it/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lucascaton","download_url":"https://codeload.github.com/lucascaton/enumerate_it/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248338026,"owners_count":21087150,"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":["enumeration","rails","ruby","ruby-enumerations"],"created_at":"2024-07-31T13:00:40.569Z","updated_at":"2025-05-14T10:09:48.992Z","avatar_url":"https://github.com/lucascaton.png","language":"Ruby","readme":"# EnumerateIt\n\nEnumerations for Ruby with some magic powers! 🎩\n\n[![CI Status](https://github.com/lucascaton/enumerate_it/workflows/CI/badge.svg)](https://github.com/lucascaton/enumerate_it/actions?query=workflow%3ACI)\n[![Gem Version](https://badge.fury.io/rb/enumerate_it.svg)](https://rubygems.org/gems/enumerate_it)\n[![Downloads](https://img.shields.io/gem/dt/enumerate_it.svg)](https://rubygems.org/gems/enumerate_it)\n[![Changelog](https://img.shields.io/badge/changelog--brightgreen.svg?style=flat)](https://github.com/lucascaton/enumerate_it/releases)\n\n**EnumerateIt** helps you declare and use enumerations in a very simple and\nflexible way.\n\n### Why would I want a gem if Rails already has native enumeration support?\n\nFirstly, although **EnumerateIt** works well with **Rails**, it isn't required!\nThis means you can add it to any **Ruby** project! Secondly, you can\n[define your enumerations in classes](#creating-enumerations), allowing you to\n**add behavior** and **reuse** them! 😀\n\n---\n\n\u003c!-- Tocer[start]: Auto-generated, don't remove. --\u003e\n\n## Table of Contents\n\n  - [Installation](#installation)\n  - [Using with Rails](#using-with-rails)\n  - [Creating enumerations](#creating-enumerations)\n    - [Sorting enumerations](#sorting-enumerations)\n  - [Using enumerations](#using-enumerations)\n  - [FAQ](#faq)\n      - [Why define enumerations outside the class that uses them?](#why-define-enumerations-outside-the-class-that-uses-them)\n      - [Can I use `enumerate_it` gem without Rails?](#can-i-use-enumerate_it-gem-without-rails)\n      - [What versions of Ruby and Rails are supported?](#what-versions-of-ruby-and-rails-are-supported)\n      - [Can I set a value to always be at the end of a sorted list?](#can-i-set-a-value-to-always-be-at-the-end-of-a-sorted-list)\n  - [I18n](#i18n)\n    - [Translate a namespaced enumeration](#translate-a-namespaced-enumeration)\n  - [Handling a legacy database](#handling-a-legacy-database)\n  - [Changelog](#changelog)\n  - [Note on Patches/Pull Requests](#note-on-patchespull-requests)\n  - [Copyright](#copyright)\n\n\u003c!-- Tocer[finish]: Auto-generated, don't remove. --\u003e\n\n## Installation\n\n```bash\ngem install enumerate_it\n```\n\n## Using with Rails\n\nAdd the gem to your `Gemfile`:\n\n```ruby\ngem 'enumerate_it'\n```\n\nYou can use a Rails generator to create both an enumeration and its locale file:\n\n```bash\nrails generate enumerate_it:enum --help\n```\n\n## Creating enumerations\n\nEnumerations are created as classes and should be placed inside the\n`app/enumerations` folder.\n\nYou can pass an array of symbols, where each symbol's value will be its\nstringified version:\n\n```ruby\nclass RelationshipStatus \u003c EnumerateIt::Base\n  associate_values(\n    :single,\n    :married,\n    :divorced\n  )\nend\n```\n\nThis will generate some nice stuff:\n\n- Constants for each enumeration value:\n\n  ```ruby\n  RelationshipStatus::SINGLE\n  #=\u003e 'single'\n\n  RelationshipStatus::MARRIED\n  #=\u003e 'married'\n  ```\n\n- A list of all enumeration codes:\n\n  ```ruby\n  RelationshipStatus.list\n  #=\u003e ['divorced', 'married', 'single']\n  ```\n\n- A JSON representation:\n\n  ```ruby\n  RelationshipStatus.to_json\n  #=\u003e \"[{\\\"value\\\":\\\"divorced\\\",\\\"label\\\":\\\"Divorced\\\"},{\\\"value\\\":\\\"married\\\", ...\n  ```\n\n- An array of options for Rails helpers, such as `select`, `select_tag`, etc.:\n\n  ```ruby\n  RelationshipStatus.to_a\n  #=\u003e [['Divorced', 'divorced'], ['Married', 'married'], ['Single', 'single']]\n  ```\n\n- You can retrieve a list with values for a group of enumeration constants.\n\n  ```ruby\n  RelationshipStatus.values_for %w(MARRIED SINGLE)\n  #=\u003e ['married', 'single']\n  ```\n\n- You can retrieve the value for a specific enumeration constant:\n\n  ```ruby\n  RelationshipStatus.value_for('MARRIED')\n  #=\u003e 'married'\n  ```\n\n- You can retrieve the symbol used to declare a specific enumeration value:\n\n  ```ruby\n  RelationshipStatus.key_for(RelationshipStatus::MARRIED)\n  #=\u003e :married\n  ```\n\n- You can iterate over the list of the enumeration's values:\n\n  ```ruby\n  RelationshipStatus.each_value { |value| ... }\n  ```\n\n- You can iterate over the list of the enumeration's translations:\n\n  ```ruby\n  RelationshipStatus.each_translation { |translation| ... }\n  ```\n\n- You can also retrieve all the translations of the enumeration:\n\n  ```ruby\n  RelationshipStatus.translations\n  ```\n\n- You can ask for the enumeration's length:\n\n  ```ruby\n  RelationshipStatus.length\n  #=\u003e 3\n  ```\n\n### Sorting enumerations\n\nWhen calling methods like `to_a`, `to_json` and `list`, values are sorted in the\norder they were passed to `associate_values`, by default.\n\nYou can override this with the `sort_by` class method:\n\n```ruby\nclass RelationshipStatus \u003c EnumerateIt::Base\n  associate_values :single, :married\n\n  sort_by :translation\nend\n```\n\nAccepted values for `sort_by`:\n\n| Value                    | Behavior                                                                              |\n| :----------------------- | :------------------------------------------------------------------------------------ |\n| `:none`                  | Uses the original order from `associate_values`                                       |\n| `:name`                  | Sorts by the name of each enumeration option                                          |\n| `:translation`           | Sorts by their translations                                                           |\n| `:normalize_translation` | Sorts by their translations normalized with NFKD unicode method (without accents)     |\n| `:value`                 | Sorts by assigned values (useful for [legacy databases](#handling-a-legacy-database)) |\n\n## Using enumerations\n\nThe cool part is that you can use these enumerations in any class, whether\nActiveRecord-based or not:\n\n```ruby\n# ActiveRecord instance\nclass Person \u003c ApplicationRecord\n  has_enumeration_for :relationship_status\nend\n```\n\n```ruby\n# Non-ActiveRecord instance\nclass Person\n  extend EnumerateIt\n  attr_accessor :relationship_status\n\n  has_enumeration_for :relationship_status\nend\n```\n\n\u003e **Note:** If the enumeration class name differs from the attribute name, use\n\u003e the `with` option:\n\u003e\n\u003e `has_enumeration_for :relationship_status, with: RelationshipStatus`\n\nThis will create:\n\n- A \"humanized\" version of the hash's key to humanize the attribute's value:\n\n  ```ruby\n  p = Person.new\n  p.relationship_status = RelationshipStatus::DIVORCED\n  p.relationship_status_humanize\n  #=\u003e 'Divorced'\n  ```\n\n- A translation for your options, if you include a locale to represent it (see\n  more in the [I18n section](#i18n)).\n\n  ```ruby\n  p = Person.new\n  p.relationship_status = RelationshipStatus::DIVORCED\n  p.relationship_status_humanize\n  #=\u003e 'Divorciado'\n  ```\n\n- The associated enumerations, which can be retrieved with the `enumerations`\n  class method:\n\n  ```ruby\n  Person.enumerations\n  #=\u003e { relationship_status: RelationshipStatus }\n  ```\n\n- A helper method for each enumeration option, if you pass the `create_helpers`\n  option as `true`:\n\n  ```ruby\n  class Person \u003c ApplicationRecord\n    has_enumeration_for :relationship_status, with: RelationshipStatus, create_helpers: true\n  end\n\n  p = Person.new\n  p.relationship_status = RelationshipStatus::MARRIED\n\n  p.married?\n  #=\u003e true\n\n  p.divorced?\n  #=\u003e false\n  ```\n\n  It's also possible to \"namespace\" the created helper methods, passing a hash\n  to the `create_helpers` option. This can be useful when two or more of the\n  enumerations used share the same constants:\n\n  ```ruby\n  class Person \u003c ApplicationRecord\n    has_enumeration_for :relationship_status,\n      with: RelationshipStatus, create_helpers: { prefix: true }\n  end\n\n  p = Person.new\n  p.relationship_status = RelationshipStatus::MARRIED\n\n  p.relationship_status_married?\n  #=\u003e true\n\n  p.relationship_status_divorced?\n  #=\u003e false\n  ```\n\n  You can define polymorphic behavior for the enumeration values, so you can\n  define a class for each of them:\n\n  ```ruby\n  class RelationshipStatus \u003c EnumerateIt::Base\n    associate_values :married, :single\n\n    class Married\n      def saturday_night\n        'At home with the kids'\n      end\n    end\n\n    class Single\n      def saturday_night\n        'Party hard!'\n      end\n    end\n  end\n\n  class Person \u003c ApplicationRecord\n    has_enumeration_for :relationship_status,\n      with: RelationshipStatus, create_helpers: { polymorphic: true }\n  end\n\n  p = Person.new\n  p.relationship_status = RelationshipStatus::MARRIED\n  p.relationship_status_object.saturday_night\n  #=\u003e 'At home with the kids'\n\n  p.relationship_status = RelationshipStatus::SINGLE\n  p.relationship_status_object.saturday_night\n  #=\u003e 'Party hard!'\n  ```\n\n  You can also change the suffix `_object`, using the `suffix` option:\n\n  ```ruby\n  class Person \u003c ApplicationRecord\n    has_enumeration_for :relationship_status,\n      with: RelationshipStatus, create_helpers: { polymorphic: { suffix: '_mode' } }\n  end\n\n  p.relationship_status_mode.saturday_night\n  ```\n\n  The `create_helpers` also creates some mutator helper methods, that can be\n  used to change the attribute's value.\n\n  ```ruby\n  p = Person.new\n  p.married!\n\n  p.married?\n  #=\u003e true\n  ```\n\n- A scope method for each enumeration option if you pass the `create_scopes`\n  option as `true`:\n\n  ```ruby\n  class Person \u003c ApplicationRecord\n    has_enumeration_for :relationship_status, with: RelationshipStatus, create_scopes: true\n  end\n\n  Person.married.to_sql\n  #=\u003e SELECT \"users\".* FROM \"users\" WHERE \"users\".\"relationship_status\" = \"married\"\n  ```\n\n  The `:create_scopes` also accepts `prefix` option.\n\n  ```ruby\n  class Person \u003c ApplicationRecord\n    has_enumeration_for :relationship_status,\n      with: RelationshipStatus, create_scopes: { prefix: true }\n  end\n\n  Person.relationship_status_married.to_sql\n  ```\n\n- An inclusion validation (if your class can manage validations and responds to\n  `validates_inclusion_of`):\n\n  ```ruby\n  class Person \u003c ApplicationRecord\n    has_enumeration_for :relationship_status, with: RelationshipStatus\n  end\n\n  p = Person.new(relationship_status: 'invalid')\n  p.valid?\n  #=\u003e false\n  p.errors[:relationship_status]\n  #=\u003e 'is not included in the list'\n  ```\n\n- A presence validation (if your class can manage validations and responds to\n  `validates_presence_of` and you pass the `required` options as `true`):\n\n  ```ruby\n  class Person \u003c ApplicationRecord\n    has_enumeration_for :relationship_status, required: true\n  end\n\n  p = Person.new relationship_status: nil\n  p.valid?\n  #=\u003e false\n  p.errors[:relationship_status]\n  #=\u003e \"can't be blank\"\n  ```\n\n  If you pass the `skip_validation` option as `true`, it will not create any\n  validations:\n\n  ```ruby\n  class Person \u003c ApplicationRecord\n    has_enumeration_for :relationship_status, with: RelationshipStatus, skip_validation: true\n  end\n\n  p = Person.new(relationship_status: 'invalid')\n  p.valid?\n  #=\u003e true\n  ```\n\nRemember that you can add validations to any kind of class and not only\n`ActiveRecord` ones.\n\n## FAQ\n\n#### Why define enumerations outside the class that uses them?\n\n- It's clearer.\n- You can add behavior to the enumeration class.\n- You can reuse the enumeration inside other classes.\n\n#### Can I use `enumerate_it` gem without Rails?\n\nYou sure can! 😄\n\n#### What versions of Ruby and Rails are supported?\n\n- **Ruby**: `3.0+`\n- **Rails**: `6.0+`\n\nAll versions are tested via\n[GitHub Actions](https://github.com/lucascaton/enumerate_it/blob/HEAD/.github/workflows/ci.yml).\n\n#### Can I set a value to always be at the end of a sorted list?\n\nYes,\n[see more details here](https://github.com/lucascaton/enumerate_it/issues/60).\n\n## I18n\n\nI18n lookup is provided for both `_humanized` and `Enumeration#to_a` methods,\ngiven the hash key is a Symbol. The I18n strings are located on\n`enumerations.\u003cenumeration_name\u003e.\u003ckey\u003e`:\n\n```yaml\n# Your locale file\npt-BR:\n  enumerations:\n    relationship_status:\n      married: Casado\n```\n\n```ruby\nclass RelationshipStatus \u003c EnumerateIt::Base\n  associate_values(\n    :married,\n    :single\n  )\nend\n\np = Person.new\np.relationship_status = RelationshipStatus::MARRIED\np.relationship_status_humanize # Existent key\n#=\u003e 'Casado'\n\np.relationship_status = RelationshipStatus::SINGLE\np.relationship_status_humanize # Non-existent key\n#=\u003e 'Single'\n```\n\nYou can also translate specific values:\n\n```ruby\nstatus = RelationshipStatus::MARRIED\nRelationshipStatus.t(status)\n#=\u003e 'Casado'\n```\n\n### Translate a namespaced enumeration\n\nIn order to translate an enumeration in a specific namespace (say\n`Design::Color`), use the following structure:\n\n```yaml\npt-BR:\n  enumerations:\n    \"design/color\":\n      blue: Azul\n      red: Vermelho\n```\n\n## Handling a legacy database\n\n**EnumerateIt** can help you build a Rails application around a legacy database\nwhich was filled with those small and unchangeable tables used to create foreign\nkey constraints everywhere, like the following example:\n\n```sql\nTable \"public.relationship_status\"\n\n  Column     |     Type      | Modifiers\n-------------+---------------+-----------\n code        | character(1)  | not null\n description | character(11) |\n\nIndexes:\n  \"relationship_status_pkey\" PRIMARY KEY, btree (code)\n\nSELECT * FROM relationship_status;\n\ncode |  description\n---- +--------------\n1    | Single\n2    | Married\n3    | Divorced\n```\n\nYou might also have something like a `users` table with a `relationship_status`\ncolumn and a foreign key pointing to the `relationship_status` table.\n\nWhile this is a good thing from the database normalization perspective, managing\nthese values in tests is very hard. Doing database joins just to get the\ndescription of some value is absurd. And, more than this, referencing them in\nthe code using\n[magic numbers](\u003chttps://en.wikipedia.org/wiki/Magic_number_(programming)\u003e) was\nterrible and meaningless: what does it mean when we say that someone or\nsomething is `2`?\n\nTo solve this, you can pass a **hash** to your enumeration values:\n\n```ruby\nclass RelationshipStatus \u003c EnumerateIt::Base\n  associate_values(\n    single:   1,\n    married:  2,\n    divorced: 3\n  )\nend\n```\n\n```ruby\nRelationshipStatus::MARRIED\n#=\u003e 2\n```\n\nYou can also sort it by its **value** using `sort_by :value`.\n\n## Changelog\n\nChanges follows the [Semantic Versioning](https://semver.org/) specification and\nyou can see them on the [releases page](../../releases).\n\n## Note on Patches/Pull Requests\n\n- Fork the project.\n- Make your feature addition or bug fix.\n- Add tests for it. This is important so we don't break it in a future version\n  unintentionally.\n- [Optional] Run the tests against a specific Gemfile:\n  `bundle exec appraisal rails_8.0 rake spec`.\n- Run the tests against all supported versions: `bundle exec rake`\n- Commit, but please do not mess with `Rakefile`, version, or history.\n- Send a Pull Request. Bonus points for topic branches.\n\n## Copyright\n\nCopyright (c) 2010-2025 Cássio Marques and Lucas Caton. See `LICENSE` file for\ndetails.\n","funding_links":[],"categories":["Active Record Plugins","Ruby"],"sub_categories":["Active Record Enumerations"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flucascaton%2Fenumerate_it","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flucascaton%2Fenumerate_it","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flucascaton%2Fenumerate_it/lists"}