-
Notifications
You must be signed in to change notification settings - Fork 11
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
Lazy dig #44
Lazy dig #44
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
# frozen_string_literal: true | ||
|
||
module AmsLazyRelationships::Core | ||
# Provides `lazy_dig` as an instance method for serializers, in order to make | ||
# possible to dig relationships in depth just like `Hash#dig` do, keeping the | ||
# laziness and N+1-free evaluation. | ||
module LazyDigMethod | ||
# @param relation_names [Array<Symbol>] the sequence of relation names | ||
# to dig through. | ||
# @return [ActiveRecord::Base, Array<ActiveRecord::Base>, nil] ActiveRecord | ||
# objects found by digging through the sequence of nested relationships. | ||
# Singular or plural nature of returned value depends from the | ||
# singular/plural nature of the chain of relation_names. | ||
# | ||
# @example | ||
# class AuthorSerializer < BaseSerializer | ||
# lazy_belongs_to :address | ||
# lazy_has_many :rewards | ||
# end | ||
# | ||
# class BlogPostSerializer < BaseSerializer | ||
# lazy_belongs_to :author | ||
# | ||
# attribute :author_address do | ||
# # returns single AR object or nil | ||
# lazy_dig(:author, :address)&.full_address | ||
# end | ||
# | ||
# attribute :author_rewards do | ||
# # returns an array of AR objects | ||
# lazy_dig(:author, :rewards).map(&:description) | ||
# end | ||
# end | ||
def lazy_dig(*relation_names) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It'd be good to add some documentation to this method - what args it accepts, what it returns and an example of use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added! ) |
||
relationships = { | ||
multiple: false, | ||
data: [{ | ||
serializer: self.class, | ||
object: object | ||
}] | ||
} | ||
|
||
relation_names.each do |relation_name| | ||
lazy_dig_relationship!(relation_name, relationships) | ||
end | ||
|
||
objects = relationships[:data].map { |r| r[:object] } | ||
|
||
relationships[:multiple] ? objects : objects.first | ||
end | ||
|
||
private | ||
|
||
def lazy_dig_relationship!(relation_name, relationships) | ||
relationships[:data].map! do |serializer:, object:| | ||
next_objects = lazy_dig_next_objects!(relation_name, serializer, object) | ||
next unless next_objects | ||
|
||
relationships[:multiple] ||= next_objects.respond_to?(:to_ary) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. well, we always initialize this flag by false at the beginning def lazy_dig(*relation_names)
relationships = {
multiple: false,
# ...
} The idea is to maintain the singular/plural nature of lazy_dig(:company, :logo) # returns single AR record or nil
lazy_dig(:companies, :logo) # returns the array of AR records
lazy_dig(:company, :logos) # returns the array of AR records In general,
. Then we decide what kind of return value will be as follows relationships[:multiple] ? objects : objects.first |
||
|
||
lazy_dig_next_relationships!(relation_name, serializer, next_objects) | ||
end | ||
|
||
relationships[:data].flatten! | ||
relationships[:data].compact! | ||
end | ||
|
||
def lazy_dig_next_objects!(relation_name, serializer, object) | ||
serializer&.send( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd even consider raising an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Honestly I don't have a strong opinion on this, even don't know is that good or bad for me personally ) I just wanted to simulate the My vote is to leave that as it is ) If we need more strict method (I'm not sure that we are though) - let it have a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Hm, thinking about raising the ArgumentError widely module AmsLazyRelationships::Core
module Evaluation
def load_lazy_relationship(relation_name, object)
lrm = lazy_relationships[relation_name]
# Raise ArgumentError ?.
return unless lrm There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Bajena Yeah, did that, added tests, please take a look ) |
||
:load_lazy_relationship, | ||
relation_name, | ||
object | ||
) | ||
end | ||
|
||
def lazy_dig_next_relationships!(relation_name, serializer, next_objects) | ||
Array.wrap(next_objects).map do |next_object| | ||
next_serializer = serializer.send( | ||
:lazy_serializer_for, | ||
next_object, | ||
relation_name: relation_name | ||
) | ||
|
||
{ | ||
serializer: next_serializer, | ||
object: next_object | ||
} | ||
end | ||
end | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea with renaming this. I'd even consider renaming it to sth like
collect_all_lazy_relationships
ortouch_all_lazy_relationships
, but this is good enough as well 👍