|
|
> Tired of `polymorphic` or the infamous `type` column. Well, **NERVER MORE!!!**
|
|
|
|
|
|
This is one great feature from PostgreSQL, the ability of having a table with the most generic data, and then many other tables with the information necessary only for that specifi type of the main one. Rails do allow us to do that, using the `type` attribute, but its biggest problem is that columns from different types ended up getting mixed together. [PostgreSQL Docs](https://www.postgresql.org/docs/9.6/static/ddl-inherit.html)
|
|
|
|
|
|
This will allow you do work with inherited models as they are separated tables but yet sharing methods, scopes, validations, and all the other features from all the super models.
|
|
|
|
|
|
**CAUTION** PostgreSQL has some [**caveats**](https://www.postgresql.org/docs/9.1/static/ddl-inherit.html#DDL-INHERIT-CAVEATS) while using this resource. They will be address in later versions of this GEM.
|
|
|
|
|
|
# How it works
|
|
|
|
|
|
### Migration
|
|
|
|
|
|
First, you have to create a base table. Then, create many tables as wanted, specifying the `:inherited` key.
|
|
|
|
|
|
```ruby
|
|
|
create_table "activities" do |t|
|
|
|
t.string "title"
|
|
|
end
|
|
|
|
|
|
create_table "activity_books", inherits: :activities do |t|
|
|
|
t.belongs_to "author_id"
|
|
|
t.datetime "published_at"
|
|
|
end
|
|
|
|
|
|
create_table "activity_posts", inherits: :activities do |t|
|
|
|
t.belongs_to "post_id"
|
|
|
t.datetime "published_at"
|
|
|
end
|
|
|
```
|
|
|
|
|
|
### Querying
|
|
|
|
|
|
#### Single record
|
|
|
|
|
|
Any already loaded record can be casted to its original model using `cast_record` method. **Be careful** that this method rely on a `primary_key` to be correctly casted.
|
|
|
|
|
|
```ruby
|
|
|
ActivityPost.create(title: 'Post 1')
|
|
|
Activity.first # #<Activity id: 1, title: "Post 1" ...
|
|
|
Activity.first.cast_record # #<ActivityPost id: 1, title: "Post 1" ...
|
|
|
```
|
|
|
|
|
|
#### Multiple records
|
|
|
|
|
|
You can also inform the relation to automatically cast all the returned recors to their original model using `cast_records` while querying.
|
|
|
|
|
|
```ruby
|
|
|
Activity.create(title: 'Activity 1')
|
|
|
ActivityPost.create(title: 'Post 1')
|
|
|
ActivityBook.create(title: 'Book 1')
|
|
|
|
|
|
list = Activity.all.cast_records.load.to_a
|
|
|
list.first # #<Activity id: 1, title: "Activity 1" ...
|
|
|
list.second # #<ActivityPost id: 2, title: "Post 1" ...
|
|
|
list.third # #<ActivityBook id: 3, title: "Book 1" ...
|
|
|
```
|
|
|
|
|
|
The `cast_records` provides other filters like specifying which classes shoul be casted and if they shold be filtered while querying.
|
|
|
|
|
|
```ruby
|
|
|
# No filter applied
|
|
|
list = Activity.all.cast_records(ActivityPost).load.to_a
|
|
|
list.first # #<Activity id: 1, title: "Activity 1" ...
|
|
|
list.second # #<ActivityPost id: 2, title: "Post 1" ...
|
|
|
list.third # #<Activity id: 3, title: "Book 1" ...
|
|
|
|
|
|
# With filter applied
|
|
|
list = Activity.all.cast_records(ActivityPost, filter: true).load.to_a
|
|
|
list.first # #<Activity id: 1, title: "Activity 1" ...
|
|
|
list.second # #<ActivityPost id: 2, title: "Post 1" ...
|
|
|
list.third # nil
|
|
|
```
|
|
|
|
|
|
#### Non confliting records
|
|
|
|
|
|
With this feature, another method was introduced. The `itself_only` only allows queries on the base table to not include any inherited record. It uses the `FROM ONLY` SQL clause, so it's very performatic.
|
|
|
|
|
|
```ruby
|
|
|
list = Activity.itself_only.load.to_a
|
|
|
list.first # #<Activity id: 1, title: "Activity 1" ...
|
|
|
list.second # nil
|
|
|
list.third # nil
|
|
|
```
|
|
|
|
|
|
#### Returning the records' type
|
|
|
|
|
|
In order to perform any query with correct performance and maintainability, this feature uses [Auxiliary Statements](https://github.com/crashtech/torque-postgresql/wiki/Auxiliary-Statements) and [Dynamic Attributes](https://github.com/crashtech/torque-postgresql/wiki/Dynamic-Attributes).
|
|
|
|
|
|
The `_record_class` (with can be renames using the [`inheritance.record_class_column_name`](https://github.com/crashtech/torque-postgresql/wiki/Configuring#inheritance.record_class_column_name) setting) is used both as an Auxiliary Statement and a Dynamic Attribute to get the type of the records. While it's a great example of these provided features, you can always take advantake of this to get the type of the record.
|
|
|
|
|
|
```ruby
|
|
|
list = Activity.all.with(:_record_class).load.to_a
|
|
|
list.first._record_class # "activities"
|
|
|
list.second._record_class # "activity_posts"
|
|
|
list.third._record_class # "activity_books"
|
|
|
```
|
|
|
|
|
|
This will be improved in the next `v0.2.1` to accept an `type_activity?`.
|
|
|
|
|
|
#### Table name to Model name
|
|
|
|
|
|
Since the data that indicates the record type is a table name, this process rely on rails common translation from class name to table name. But this is tricky, because `activity_posts` can either be `ActivityPost` or `Activity::Post` (or even another model using `self.table_name = "activity_posts"`). This GEM try its best to translate the table name to a model name, chicking all the possibilities, so you might not face issues. **But**, you can always help it, and make the process more accurate or even faster.
|
|
|
|
|
|
Please, check the [Configuration Page](https://github.com/crashtech/torque-postgresql/wiki/Configuring) in order to see the options to improve this operation.
|
|
|
|
|
|
The most important setting is the [`irregular_models`](https://github.com/crashtech/torque-postgresql/wiki/Configuring#irregular_models), which can both help descibing the correct relationship between a table name and a model name, and improve the performance by avoinding the default behavior of searching for the model based on the table name. Although the table name once associated with an model name is cahced, be aware of this for setting irregular names.
|
|
|
|
|
|
```ruby
|
|
|
Torque::PostgreSQL.configure do |c|
|
|
|
c.irregular_models = {
|
|
|
'my_awesome_table_name' => 'SimpleModel'
|
|
|
}
|
|
|
end
|
|
|
``` |
|
|
\ No newline at end of file |