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

Fix eager loading of scoped associations. Previously the scope was no… #67

Merged
merged 4 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

## Not released

- Fix eager loading of scoped associations. (https://github.com/Beyond-Finance/active_force/pull/67)
- Adding `.blank?`, `.present?`, and `.any?` delegators to `ActiveQuery`. (https://github.com/Beyond-Finance/active_force/pull/68)
- Adding `update` and `update!` class methods on `SObject`. (https://github.com/Beyond-Finance/active_force/pull/66)

Expand Down
8 changes: 8 additions & 0 deletions lib/active_force/association/association.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ def relationship_name
options[:relationship_name] || relation_model.to_s.constantize.table_name
end

def scoped_as
options[:scoped_as] || nil
end

def scoped?
options[:scoped_as].present?
end

###
# Does this association's relation_model represent
# +sfdc_table_name+? Examples of +sfdc_table_name+
Expand Down
16 changes: 12 additions & 4 deletions lib/active_force/association/eager_load_projection_builder.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module ActiveForce
module Association
class InvalidEagerLoadAssociation < StandardError; end
class EagerLoadProjectionBuilder
class << self
def build(association, parent_association_field = nil)
Expand Down Expand Up @@ -34,6 +35,13 @@ def initialize(association, parent_association_field = nil)
def projections
raise "Must define #{self.class.name}#projections"
end

def apply_association_scope(query)
return query unless association.scoped?
raise InvalidEagerLoadAssociation, "Cannot use scopes that expect arguments: #{association.relation_name}" if association.scoped_as.arity.positive?

query.instance_exec(&association.scoped_as)
asedge marked this conversation as resolved.
Show resolved Hide resolved
end
end

class HasManyAssociationProjectionBuilder < AbstractProjectionBuilder
Expand All @@ -43,17 +51,17 @@ class HasManyAssociationProjectionBuilder < AbstractProjectionBuilder
# to be pluralized
def projections
relationship_name = association.sfdc_association_field
query = Query.new relationship_name
query = ActiveQuery.new(association.relation_model, relationship_name)
asedge marked this conversation as resolved.
Show resolved Hide resolved
query.fields association.relation_model.fields
["(#{query.to_s})"]
["(#{apply_association_scope(query).to_s})"]
end
end

class HasOneAssociationProjectionBuilder < AbstractProjectionBuilder
def projections
query = Query.new association.sfdc_association_field
query = ActiveQuery.new(association.relation_model, association.sfdc_association_field)
query.fields association.relation_model.fields
["(#{query.to_s})"]
["(#{apply_association_scope(query).to_s})"]
end
end

Expand Down
28 changes: 26 additions & 2 deletions spec/active_force/sobject/includes_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,13 @@ module ActiveForce
end
end

context 'when assocation has a scope' do
it 'formulates the correct SOQL query with the scope applied' do
soql = Post.includes(:impossible_comments).where(id: '1234').to_s
expect(soql).to eq "SELECT Id, Title__c, BlogId, (SELECT Id, PostId, PosterId__c, FancyPostId, Body__c FROM Comments__r WHERE (1 = 0)) FROM Post__c WHERE (Id = '1234')"
end
end

context 'with namespaced SObjects' do
it 'formulates the correct SOQL query' do
soql = Salesforce::Quota.includes(:prez_clubs).where(id: '123').to_s
Expand Down Expand Up @@ -286,9 +293,26 @@ module ActiveForce
end
end

context 'has_one' do
context 'when assocation has a scope' do
it 'formulates the correct SOQL query with the scope applied' do
soql = Post.includes(:last_comment).where(id: '1234').to_s
expect(soql).to eq "SELECT Id, Title__c, BlogId, (SELECT Id, PostId, PosterId__c, FancyPostId, Body__c FROM Comment__r WHERE (NOT ((Body__c = NULL))) ORDER BY CreatedDate DESC) FROM Post__c WHERE (Id = '1234')"
end
end

end

context 'when invalid associations are passed' do
it 'raises an error' do
expect { Quota.includes(:invalid).find('123') }.to raise_error(ActiveForce::Association::InvalidAssociationError, 'Association named invalid was not found on Quota')
context 'when the association is not defined' do
it 'raises an error' do
expect { Quota.includes(:invalid).find('123') }.to raise_error(ActiveForce::Association::InvalidAssociationError, 'Association named invalid was not found on Quota')
end
end
context 'when the association is scoped and accepts an argument' do
it 'raises and error' do
expect { Post.includes(:reply_comments).find('1234')}.to raise_error(ActiveForce::Association::InvalidEagerLoadAssociation)
end
end
end
end
Expand Down