Core » Relations
Relations are really the heart of ROM. They provide APIs for reading the data from various databases, and low-level interfaces for making changes in the databases. Relations are adapter-specific, which means that each adapter provides its own relation specialization, exposing interfaces that make it easy to leverage the features of your database. At the same time, these relations encapsulate data access, so that details about how it's done don't leak into your application domain layer.
In typical setup of an application using ROM, relations are defined as explicit classes. You can put them in separate files, namespace them or not, and configure them when it's needed (especially useful when using a legacy database with non-standard naming conventions).
The most important responsibility of relations is to expose a clear API for reading data. Every relation method should return another relation, we call them relation views. These views can be defined in ways that make them composable by including join-keys in the resulting tuples. This is not limited to SQL, you can compose data from different sources.
Even when you use an adapter that can infer relations from your database schema,
it is valuable to define relations classes explicitly. Let's say we have
table in an SQL database, here's how you would define a relation class for it:
class Users < ROM::Relation[:sql] end
Notice two things:
:sqlidentifier to resolve relation type for
Usersclass name is used by default to infer
datasetname and set it to
Every method in a relation should return another relation, this happens automatically
whenever you use a query interface provided by adapters. In our example we use
rom-sql, let's define a relation view using SQL query DSL:
class Users < ROM::Relation def listing select(:id, :name, :email).order(:name) end end
Now let's see how you can use relation schemas.