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

Introduce association component #646

Merged
merged 1 commit into from
Jul 3, 2021
Merged

Introduce association component #646

merged 1 commit into from
Jul 3, 2021

Conversation

solnic
Copy link
Member

@solnic solnic commented Jul 3, 2021

This is the first step towards schema<=>association decoupling.
Associations are now lazy-loaded via components and relations use their
own association resolver rather than relying on schema.

SchemaDSL still provides association definitions but this can be easily
improved so that Relation class can define its associations outside of
the schema block.

Associations are scoped to relations but it's possible to store them as
first-class components like the rest. I haven't done that though because
I'm not sure if this will be useful.

Here's a practical outcome of this change:

# here we get a relation object but its associations are NOT loaded yet
rom.relations[:users]
#<ROM::Relation[Users] name=ROM::Relation::Name(users) dataset=#<Sequel::Postgres::Dataset: "SELECT \"users\".\"id\", \"users\".\"name\" FROM \"users\" ORDER BY \"users\".\"id\"">>

# we can inspect the association resolver though and it still doesn't load associations
rom.relations[:users].associations.keys
["associations.users.posts", "associations.users.aliased_posts", "associations.users.labels", "associations.users.books"]

# once something asks for a specific association, it will be lazy-loaded
rom.relations[:users].associations[:labels]
#<ROM::SQL::Associations::ManyToMany definition=#<ROM::Associations::Definitions::ManyToMany source=ROM::Relation::Name(users) target=ROM::Relation::Name(labels) result=:many> source=#<ROM::Relation[Users] name=ROM::Relation::Name(users) dataset=#<Sequel::Postgres::Dataset: "SELECT \"users\".\"id\", \"users\".\"name\" FROM \"users\" ORDER BY \"users\".\"id\"">> target=#<ROM::Relation[Labels] name=ROM::Relation::Name(labels) dataset=#<Sequel::Postgres::Dataset: "SELECT \"labels\".\"id\", \"labels\".\"name\" FROM \"labels\" ORDER BY \"labels\".\"id\"">>>

There are various improvements that should be made eventually. Probably
the most important one is adding Association#inverse_of(another) so
that resolving "another side" can be done in a more robust way.
For example if you have "users.has_many(:posts)" but on the other side you
have "posts.belongs_to(:user, as: :author)" then it's tricky to figure it
out without a proper abstraction. Luckily, we have all the information
in the right place to implement it.

This is the first step towards schema<=>association decoupling.
Associations are now lazy-loaded via components and relations use their
own association resolver rather than relying on schema.

SchemaDSL still provides association definitions but this can be easily
improved so that Relation class can define its associations outside of
the schema block.

Associations are scoped to relations but it's possible to store them as
first-class components like the rest. I haven't done that though because
I'm not sure if this will be useful.

Here's a practical outcome of this change:

```ruby
\# here we get a relation object but its associations are NOT loaded yet
rom.relations[:users]
\#<ROM::Relation[Users] name=ROM::Relation::Name(users) dataset=#<Sequel::Postgres::Dataset: "SELECT \"users\".\"id\", \"users\".\"name\" FROM \"users\" ORDER BY \"users\".\"id\"">>

\# we can inspect the association resolver though and it still doesn't load associations
rom.relations[:users].associations.keys
["associations.users.posts", "associations.users.aliased_posts", "associations.users.labels", "associations.users.books"]

\# once something asks for a specific association, it will be lazy-loaded
rom.relations[:users].associations[:labels]
\#<ROM::SQL::Associations::ManyToMany definition=#<ROM::Associations::Definitions::ManyToMany source=ROM::Relation::Name(users) target=ROM::Relation::Name(labels) result=:many> source=#<ROM::Relation[Users] name=ROM::Relation::Name(users) dataset=#<Sequel::Postgres::Dataset: "SELECT \"users\".\"id\", \"users\".\"name\" FROM \"users\" ORDER BY \"users\".\"id\"">> target=#<ROM::Relation[Labels] name=ROM::Relation::Name(labels) dataset=#<Sequel::Postgres::Dataset: "SELECT \"labels\".\"id\", \"labels\".\"name\" FROM \"labels\" ORDER BY \"labels\".\"id\"">>>
```

There are various improvements that should be made eventually. Probably
the most important one is adding `Association#inverse_of(another)` so
that resolving "another side" can be done in a more robust way.
For example if you have "users.has_many(:posts)" but on the other side you
have "posts.belongs_to(:user, as: :author)" then it's tricky to figure it
out without a proper abstraction. Luckily, we have all the information
in the right place to implement it.
@solnic solnic merged commit a16001e into master Jul 3, 2021
@solnic solnic deleted the association-components branch July 3, 2021 13:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant