README.md
# Discriminable [](https://rubygems.org/gems/discriminable)[](https://github.com/gregorw/discriminable/actions/workflows/main.yml)[](https://codeclimate.com/github/gregorw/discriminable/maintainability)[](https://codeclimate.com/github/gregorw/discriminable/test_coverage) This is a Ruby gem that implements single-table inheritance (STI) for ActiveRecord using string, integer and boolean column types. In other words, it allows you to use _any_ existing model attribute to discriminate between different subclasses in your class hierarchy. This is especially useful when dealing with **legacy databases** or **third-party systems**. Anytime you cannot use the default “inheritance column / class name” combo—`discriminable` is your friend. It also supports aliased attributes and _multiple_ values per subclass. ## Installation bundle add discriminable or gem install discriminable ## Usage ```rubyclass Order < ActiveRecord::Base include Discriminable discriminable_attribute :stateend class Cart < Order discriminable_value :openend Cart.create# => #<Cart id: 1, state: "open">Order.all# => #<ActiveRecord::Relation [#<Cart id: 1, state: "open">]>``` ## Features ### Compatible with enums ```rubyclass Order < ActiveRecord::Base include Discriminable enum state: { open: 0, processing: 1, invoiced: 2 } discriminable_attribute :stateend class Cart < Order discriminable_value :openend class Invoice < Order discriminable_value :invoicedend``` ### Aliased attributes In case you are working with a legacy database and cannot change the column name easily it’s easy to reference an aliased attribute in the `discriminable_attribute` definition. ```rubyclass Property < ActiveRecord::Base include Discriminable alias_attribute :kind, :kind_with_legacy_postfix # Aliased attributes are supported when specifying the discriminable attribute discriminable_attribute :kindend class NumberProperty < Property discriminable_value 1end``` ### Multiple values Sometimes, in a real project, you may want to map a number of values to a single class. This is possible by specifying: ```rubyclass OptionProperty < Property # The first mention becomes the default value discriminable_values 2, 3, 4end``` Note that when creating new records with e.g. `OptionProperty.create` a _default_ value needs to be set in the database for this discriminable class. The Discriminable gem uses the _first_ value in the list as the default. ## Comparison with standard Rails ### Rails STI | *values* | string | integer | boolean | enum | decimal | … ||--|--|--|--|--|--|--|| single | 🟡 `class.name` only | 🔴 | 🔴 | 🔴 | 🔴 | 🔴 || multiple | 🔴 | 🔴 | 🔴 | 🔴 | 🔴 | 🔴 | ### Discriminable Gem | *values* | string | integer | boolean | enum | decimal | … ||--|--|--|--|--|--| --|| single | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 || multiple | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 | 🟢 | “Multiple” means that more than one value can map to a single subclass. This may or may not be useful for your use case. In standard Rails, the a single class name obviously maps to a single class. ## Prerequisites Rails 5+ is required. Also, in order to make this work in `development` environment the class hierarchy needs to be loaded. ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/gregorw/discriminable. 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/gregorw/discriminable/blob/main/CODE_OF_CONDUCT.md). ## License The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). ## Code of Conduct Everyone interacting in the Discriminable project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/gregorw/discriminable/blob/main/CODE_OF_CONDUCT.md). ## Related work The idea for this Gem was influenced by [“Bye Bye STI, Hello Discriminable Model”](https://www.salsify.com/blog/engineering/bye-bye-sti-hello-discriminable-model) by Randy Burkes. This Gem has started out with [his code](https://gist.github.com/rlburkes/798e186acb2f93e787a5). See also: - Rails [single table inheritance](https://api.rubyonrails.org/classes/ActiveRecord/Inheritance.html) and [DelegatedType](https://api.rubyonrails.org/classes/ActiveRecord/DelegatedType.html)- Sequel [single-table inheritance plugin](https://sequel.jeremyevans.net/rdoc-plugins/classes/Sequel/Plugins/SingleTableInheritance.html)- [Discriminator](https://github.com/gdpelican/discriminator) gem.- Java [JPA discrimanator](https://openjpa.apache.org/builds/1.0.2/apache-openjpa-1.0.2/docs/manual/jpa_overview_mapping_discrim.html)- Python [Django model inheritance](https://docs.djangoproject.com/en/4.0/topics/db/models/#model-inheritance-1) and [proxy](https://dev.to/zachtylr21/model-inheritance-in-django-m0j)