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.
bundle add discriminable
or
gem install discriminable
class Order < ActiveRecord::Base
include Discriminable
discriminable_attribute :state
end
class Cart < Order
discriminable_value :open
end
Cart.create
# => #<Cart id: 1, state: "open">
Order.all
# => #<ActiveRecord::Relation [#<Cart id: 1, state: "open">]>
class Order < ActiveRecord::Base
include Discriminable
enum state: { open: 0, processing: 1, invoiced: 2 }
discriminable_attribute :state
end
class Cart < Order
discriminable_value :open
end
class Invoice < Order
discriminable_value :invoiced
end
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.
class Property < ActiveRecord::Base
include Discriminable
alias_attribute :kind, :kind_with_legacy_postfix
# Aliased attributes are supported when specifying the discriminable attribute
discriminable_attribute :kind
end
class NumberProperty < Property
discriminable_value 1
end
Sometimes, in a real project, you may want to map a number of values to a single class. This is possible by specifying:
class OptionProperty < Property
# The first mention becomes the default value
discriminable_values 2, 3, 4
end
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.
values | string | integer | boolean | enum | decimal | … |
---|---|---|---|---|---|---|
single | 🟡 class.name only |
🔴 | 🔴 | 🔴 | 🔴 | 🔴 |
multiple | 🔴 | 🔴 | 🔴 | 🔴 | 🔴 | 🔴 |
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.
Rails 5+ is required.
Also, in order to make this work in development
environment the class hierarchy needs to be loaded.
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.
The gem is available as open source under the terms of the MIT License.
Everyone interacting in the Discriminable project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the code of conduct.
The idea for this Gem was influenced by “Bye Bye STI, Hello Discriminable Model” by Randy Burkes. This Gem has started out with his code.
See also:
- Rails single table inheritance and DelegatedType
- Sequel single-table inheritance plugin
- Discriminator gem.
- Java JPA discrimanator
- Python Django model inheritance and proxy