I have a moderately complicated data model in postgresql with lots of relations, and I need to gather them all together for display in a view.
I have a Conversation
which associates with a few other objects, which have their own associations, and I need some or all of those loaded depending on the view being rendered. For example, Conversation
has many Messages
belongs to User
, and in a single-conversation view (GET /conversations/1234
) I want to include the recent messages and the users that sent them, while in the conversation-list view (GET /conversations
) I want only the users and the one most recent message. I’m struggling with 1) how to write relations and repositories that might involve complex queries like LATERAL JOINS, and 2) how do I share those queries with other repositories/relations?
Some specific questions I’ve had that don’t have satisfying answers in the documentation include:
- Is there a way to reuse or combine the methods on the repository? For example, to get a single conversation with all the associated objects, I have a method
#by_slug_with_embeds(slug)
. To get all the conversations with those associations loaded, I have a separate method#with_embeds
. A feature I need to add next is to limit the conversations by account, do I just add it to those methods, eg#for_account_by_slug_with_embeds(account, slug)
? If I need to add another, this method is starting to get unwieldy. Do I instead name it for its purpose, like#for_index(account:)
and#for_show(account:, slug:)
? - I have a custom
Conversation
ROM::Struct model that these get loaded into. A single Conversation has amessages
attribute containing all it messages, but to save memory and load, the Conversations within a list should not load those, and instead have a singlelatest_message
attribute. Are these a single Model with two optional attribute, or two separate models that have very similar attributes?
I’ve found some examples to crib from in Hanami [1], but few for ROM itself. I imagine the patterns are similar since Hanami is just ROM underneath, but are the any ROM-specific examples I’m missing?
class ConversationsRelation < ROM::Relation[:sql]
gateway :default
schema(:conversations, infer: true) do
associations do
belongs_to :contact
belongs_to :account
has_many :messages
has_many :conversation_events
end
end
def with_embeds
combine(:account, :conversation_events, contact: :phone, messages: :user)
end
end
class ConversationRepository < ROM::Repository::Root
root :conversations
commands :create, update: :by_pk, delete: :by_pk, mapper: :conversation
def by_slug_with_embeds(slug)
conversations.with_embeds.by_pk(slug).one!
end
end
And for comparison, this is a cleaned-up version of an ActiveRecord-based query object I’m trying to replace with ROM functionality: https://gist.github.com/paul/07d62974e4c8e401f00af58a98b8e9fa (The actual one is way gnarlier, and getting worse by the iteration).