Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Small documentation updates #430

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
4 changes: 3 additions & 1 deletion docs/aggregation.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Aggregations

There are 2 types of aggregation functions: ones which are working without a GROUP clause and returns single values (e.g. `max`, `min`, `count`) and ones, working with GROUP clause and returning arrays of values.
There are 2 types of aggregation functions:
1. working without a GROUP clause and returns single values (e.g. `max`, `min`, `count`)
2. working _with_ a GROUP clause and returning arrays of values

#### Max

Expand Down
6 changes: 3 additions & 3 deletions docs/model_sti.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ end

Requirements:

- created table for STI should include **all** fields of all subclasses (that's why it is cold STI);
- STI table has to have field named as `type` of any string type which will be able to store class name of child models;
- The created table for STI should include **all** fields of all subclasses (that's why it is called Single Table Inheritance);
- STI table has to have field named as `type` of any `String` type which will be able to store class name of child models;
- parent class should have definition for `type` field;

To extract from DB several subclasses in one request - just use parent class to query:
Expand All @@ -41,4 +41,4 @@ To extract from DB several subclasses in one request - just use parent class to
Profile.all.where { _login.like("%eter%") }
```

Each retrieved object will respect values in `type` field and appropriate class object will be builded (including invoking of `after_initialize` callbacks).
Each retrieved object will respect values in `type` field and appropriate class object will be built (including invoking of `after_initialize` callbacks).
44 changes: 43 additions & 1 deletion docs/relations.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ contact.remove_addresses(address)
| association_foreign |--/
```

As you can see primary field of related model can't be specified - defined primary key (in the mapping) will be got.
As you can see primary field of related model can't be specified so the defined primary key (in the mapping) will be used.

Also `has_many`, `belongs_to` and `has_one` relations have `dependent` parameter - defines extra callback for cleaning up related data after destroying parent one. Allowed types are:

Expand All @@ -60,6 +60,48 @@ Also `has_many`, `belongs_to` and `has_one` relations have `dependent` parameter

`has_many` and `has_one` relations also accepts `inverse_of` option which presents inverse relation name. Specifying this option will automatically load owner object during any relation loading (because of `ModelQuery#includes` or `ModelQuery#eager_load` or even plaint `SomeModel#relation_name` method call).

## Examples of non-standard relationships

Sometimes relationships between tables use non-standard attributes or may require more than a single field as keys for the relationship. In situations like this you can use the `request` parameter on the relationship. This parameter takes a block using the `where` statement syntax, and can even use the other model column as a reference.

```crystal
class Address < Jennifer::Model::Base
with_timestamps

belongs_to :person, foreign: :guid, primary: guid

# In this example model, the `type` field is a string of "mailing_address" or "physical_address"
mapping(
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you prefer to have mapping at the bottom of the class? Is it because it leaves more space for other information at the top of the file? Did you tested it with MTI?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example came from how I was able to get Jennifer working with a DB that was already configured to work this way in a Rails app. It didn't properly use STI, but kind of mocked the behavior.

As far as where the mapping macro goes, I like to organize my models so the relationship definitions are at the top of the file, the mapping macro goes below that to make seeing the fields easy, and below that, I define any model methods. Just a personal preference because it makes finding relationships quick and easy.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently all examples follow the opposite notation - mapping first and relations next. Could you please change the order here so it is consistent with other pages?

id: Primary64,
guid: String,
address1: String,
type: String,
created_at: Time,
updated_at: Time
)
end


class Person < Jennifer::Model::Base
with_timestamps

has_many :addresses, foreign: guid, primary: guid
has_one :mailing_address, request: { Address._type == "mailing_address" }, foreign: :guid, primary: :guid
has_one :physical_address, request: { Address._type == "physical_address" }, foreign: :guid, primary: :guid

mapping(
id: Primary64,
guid: String,
first_name: String,
last_name: String,
updated_at: Time,
created_at: Time
)
end
```

With this setup, using `person.addresses` will return an array of `Address`es. Doing `person.mailing_address` will return a single `Address` object, or `nil` if no record was found.

## Polymorphic Relations

Polymorphic relation can be easily configured:
Expand Down
2 changes: 1 addition & 1 deletion docs/serialization.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ There are multiple approaches to implement model serialization to a required for

## General

Jennifer defines some hidden instance attributes in defined models for own use. Sometimes we would like to operate with a class where we have full access to all defined attributes/methods. For this case the easiest way is to use [JenniferTwin](https://github.com/imdrasil/jennifer_twin) lib. Using it you can dump Jennifer model instance to a separate object that is totally under your control. One of the cases when this approach may come in handy when ther is a need to use attribute annotations, like [MessagePack::Serializable](https://github.com/crystal-community/msgpack-crystal) or [JSON::Serializable](https://crystal-lang.org/api/0.31.1/JSON/Serializable.html).
Jennifer defines some hidden instance attributes in defined models for own use. Sometimes we would like to operate with a class where we have full access to all defined attributes/methods. For this case the easiest way is to use [JenniferTwin](https://github.com/imdrasil/jennifer_twin) lib. Using it you can dump Jennifer model instance to a separate object that is totally under your control. One of the cases when this approach may come in handy when ther is a need to use attribute annotations, like [MessagePack::Serializable](https://github.com/crystal-community/msgpack-crystal) or [JSON::Serializable](https://crystal-lang.org/api/latest/JSON/Serializable.html).

```crystal
class User < Jennifer::Model::Base
Expand Down