Skip to content

Commit

Permalink
Fix eager loading of scoped associations. Previously the scope was no… (
Browse files Browse the repository at this point in the history
#67)

* Fix eager loading of scoped associations. Previously the scope was not applied.  Scopes that accept arguments cannot be used as they are expecting an instance to be passed to them and if we are eager loading there is no instance to reference.

* Update CHANGELOG.
  • Loading branch information
asedge authored Sep 29, 2023
1 parent 11a9e10 commit 5c26202
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 6 deletions.
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)
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)
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

0 comments on commit 5c26202

Please sign in to comment.