active_force 0.21.0 → 0.21.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: acbf91e00afed35fd89a0dc1505ebb40d275883bca6aba9237dad7d554e10b7b
4
- data.tar.gz: 488fe99179b48383967688ecbfc2c2f93f3f565ec177e972fb6e9ad5e68b5d8e
3
+ metadata.gz: 0a2d59fa533f638635afa0e8654fae4f77540310fba228ae697e05eaaf965525
4
+ data.tar.gz: 0570306ac71f575994b07aace62ad9579d0103f812eceb59aaa83b1a6ddc1911
5
5
  SHA512:
6
- metadata.gz: 1b29a58711a0d6b63c82604c1bc2f3bfdc9b06e435468016fa7055bdd8ace5c779d09d4f11fdc32e7292ffd8c41a5784698610c1a6184116044bfa77b1c93cb5
7
- data.tar.gz: 1bb0165385e5f06c7767d9463842e9daf742e11184e7518b008142162f8fb46bb2d35d5a1ba8f64be702cf169f5d9a6a9bfb3b8a1e83331fc11114c2ae30846a
6
+ metadata.gz: ceccbbfb84f6f80190d11ae8722ba8fbbe423cc4164b0567df2fd912f7e45150ddfb4092da3af528d431f03527676dd897e5565f6d5b18fdf70492d489667896
7
+ data.tar.gz: 91962473ab612ca13f755f7b94b891d8c6f849677999ef0333a98924b9f6a001566cd602fc3ee3c9c8e1b6cdcef039fbfeb13e4f1a8f2e284877e5d771a84e3c
data/CHANGELOG.md CHANGED
@@ -2,6 +2,9 @@
2
2
 
3
3
  ## Not released
4
4
 
5
+ ## 0.21.1
6
+ - Fixes #91. Applies scopes to eager-loaded associations when they are nested. (https://github.com/Beyond-Finance/active_force/pull/92)
7
+
5
8
  ## 0.21.0
6
9
 
7
10
  - Uninitialized attributes will ERROR instead of returning as `nil` (https://github.com/Beyond-Finance/active_force/pull/78)
@@ -62,8 +62,9 @@ module ActiveForce
62
62
  private
63
63
 
64
64
  def build_relation(association, nested_includes)
65
- sub_query = Query.new(association.sfdc_association_field)
66
- sub_query.fields association.relation_model.fields
65
+ builder_class = ActiveForce::Association::EagerLoadProjectionBuilder.projection_builder_class(association)
66
+ projection_builder = builder_class.new(association)
67
+ sub_query = projection_builder.query_with_association_fields
67
68
  association_mapping[association.sfdc_association_field.downcase] = association.relation_name
68
69
  nested_includes_query = self.class.build(nested_includes, association.relation_model)
69
70
  sub_query.fields nested_includes_query[:fields]
@@ -6,6 +6,13 @@ module ActiveForce
6
6
  def build(association, parent_association_field = nil)
7
7
  new(association, parent_association_field).projections
8
8
  end
9
+
10
+ def projection_builder_class(association)
11
+ klass = association.class.name.demodulize
12
+ ActiveForce::Association.const_get "#{klass}ProjectionBuilder"
13
+ rescue NameError
14
+ raise "No projection builder exists for #{klass}"
15
+ end
9
16
  end
10
17
 
11
18
  attr_reader :association, :parent_association_field
@@ -16,12 +23,10 @@ module ActiveForce
16
23
  end
17
24
 
18
25
  def projections
19
- klass = association.class.name.split('::').last
20
- builder_class = ActiveForce::Association.const_get "#{klass}ProjectionBuilder"
26
+ builder_class = self.class.projection_builder_class(association)
21
27
  builder_class.new(association, parent_association_field).projections
22
- rescue NameError
23
- raise "Don't know how to build projections for #{klass}"
24
28
  end
29
+
25
30
  end
26
31
 
27
32
  class AbstractProjectionBuilder
@@ -42,26 +47,28 @@ module ActiveForce
42
47
 
43
48
  query.instance_exec(&association.scoped_as)
44
49
  end
45
- end
46
50
 
47
- class HasManyAssociationProjectionBuilder < AbstractProjectionBuilder
48
51
  ###
49
52
  # Use ActiveForce::Query to build a subquery for the SFDC
50
53
  # relationship name. Per SFDC convention, the name needs
51
54
  # to be pluralized
52
- def projections
55
+ def query_with_association_fields
53
56
  relationship_name = association.sfdc_association_field
54
57
  query = ActiveQuery.new(association.relation_model, relationship_name)
55
58
  query.fields association.relation_model.fields
56
- ["(#{apply_association_scope(query).to_s})"]
59
+ apply_association_scope(query)
60
+ end
61
+ end
62
+
63
+ class HasManyAssociationProjectionBuilder < AbstractProjectionBuilder
64
+ def projections
65
+ ["(#{query_with_association_fields.to_s})"]
57
66
  end
58
67
  end
59
68
 
60
69
  class HasOneAssociationProjectionBuilder < AbstractProjectionBuilder
61
70
  def projections
62
- query = ActiveQuery.new(association.relation_model, association.sfdc_association_field)
63
- query.fields association.relation_model.fields
64
- ["(#{apply_association_scope(query).to_s})"]
71
+ ["(#{query_with_association_fields.to_s})"]
65
72
  end
66
73
  end
67
74
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveForce
4
- VERSION = '0.21.0'
4
+ VERSION = '0.21.1'
5
5
  end
@@ -386,7 +386,7 @@ describe ActiveForce::SObject do
386
386
  it 'allows passing a foreign key' do
387
387
  Comment.belongs_to :post, foreign_key: :fancy_post_id
388
388
  allow(comment).to receive(:fancy_post_id).and_return "2"
389
- expect(client).to receive(:query).with("SELECT Id, Title__c, BlogId FROM Post__c WHERE (Id = '2') LIMIT 1")
389
+ expect(client).to receive(:query).with("SELECT Id, Title__c, BlogId, IsActive FROM Post__c WHERE (Id = '2') LIMIT 1")
390
390
  comment.post
391
391
  Comment.belongs_to :post # reset association to original value
392
392
  end
@@ -228,7 +228,7 @@ module ActiveForce
228
228
  context 'when assocation has a scope' do
229
229
  it 'formulates the correct SOQL query with the scope applied' do
230
230
  soql = Post.includes(:impossible_comments).where(id: '1234').to_s
231
- 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')"
231
+ expect(soql).to eq "SELECT Id, Title__c, BlogId, IsActive, (SELECT Id, PostId, PosterId__c, FancyPostId, Body__c FROM Comments__r WHERE (1 = 0)) FROM Post__c WHERE (Id = '1234')"
232
232
  end
233
233
  end
234
234
 
@@ -297,7 +297,7 @@ module ActiveForce
297
297
  context 'when assocation has a scope' do
298
298
  it 'formulates the correct SOQL query with the scope applied' do
299
299
  soql = Post.includes(:last_comment).where(id: '1234').to_s
300
- 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')"
300
+ expect(soql).to eq "SELECT Id, Title__c, BlogId, IsActive, (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')"
301
301
  end
302
302
  end
303
303
 
@@ -397,9 +397,41 @@ module ActiveForce
397
397
  end
398
398
  end
399
399
 
400
+ context 'when the associations have scopes' do
401
+ it 'generates the correct SOQL query' do
402
+ soql = Blog.includes(active_posts: :impossible_comments).where(id: '123').to_s
403
+ expect(soql).to eq <<-SOQL.squish
404
+ SELECT Id, Name, Link__c,
405
+ (SELECT Id, Title__c, BlogId, IsActive,
406
+ (SELECT Id, PostId, PosterId__c, FancyPostId, Body__c
407
+ FROM Comments__r WHERE (1 = 0))
408
+ FROM Posts__r
409
+ WHERE (IsActive = true))
410
+ FROM Blog__c
411
+ WHERE (Id = '123')
412
+ SOQL
413
+ end
414
+
415
+ it 'builds the associated objects and caches them' do
416
+ response = [build_restforce_sobject({
417
+ 'Id' => '123',
418
+ 'Posts__r' => build_restforce_collection([
419
+ {'Id' => '213', 'IsActive' => true, 'Comments__r' => [{'Id' => '987'}]},
420
+ {'Id' => '214', 'IsActive' => true, 'Comments__r' => [{'Id' => '456'}]}
421
+ ])
422
+ })]
423
+ allow(client).to receive(:query).once.and_return response
424
+ blog = Blog.includes(active_posts: :impossible_comments).find '123'
425
+ expect(blog.active_posts).to be_an Array
426
+ expect(blog.active_posts.all? { |o| o.is_a? Post }).to eq true
427
+ expect(blog.active_posts.first.impossible_comments.first).to be_a Comment
428
+ expect(blog.active_posts.first.impossible_comments.first.id).to eq '987'
429
+ end
430
+ end
431
+
400
432
  context 'with namespaced sobjects' do
401
433
  it 'formulates the correct SOQL query' do
402
- soql = Salesforce::Account.includes({partner_opportunities: :owner}).where(id: '123').to_s
434
+ soql = Salesforce::Account.includes({opportunities: :owner}).where(id: '123').to_s
403
435
  expect(soql).to eq <<-SOQL.squish
404
436
  SELECT Id, Business_Partner__c,
405
437
  (SELECT Id, OwnerId, AccountId, Business_Partner__c, Owner.Id
@@ -417,11 +449,11 @@ module ActiveForce
417
449
  {'Id' => '214', 'AccountId' => '123', 'OwnerId' => '321', 'Business_Partner__c' => '123', 'Owner' => {'Id' => '321'}} ])
418
450
  })]
419
451
  allow(client).to receive(:query).once.and_return response
420
- account = Salesforce::Account.includes({partner_opportunities: :owner}).find '123'
421
- expect(account.partner_opportunities).to be_an Array
422
- expect(account.partner_opportunities.all? { |o| o.is_a? Salesforce::Opportunity }).to eq true
423
- expect(account.partner_opportunities.first.owner).to be_a Salesforce::User
424
- expect(account.partner_opportunities.first.owner.id).to eq '321'
452
+ account = Salesforce::Account.includes({opportunities: :owner}).find '123'
453
+ expect(account.opportunities).to be_an Array
454
+ expect(account.opportunities.all? { |o| o.is_a? Salesforce::Opportunity }).to eq true
455
+ expect(account.opportunities.first.owner).to be_a Salesforce::User
456
+ expect(account.opportunities.first.owner.id).to eq '321'
425
457
  end
426
458
  end
427
459
 
@@ -631,7 +663,7 @@ module ActiveForce
631
663
  soql = Comment.includes(post: :blog).where(id: '123').to_s
632
664
  expect(soql).to eq <<-SOQL.squish
633
665
  SELECT Id, PostId, PosterId__c, FancyPostId, Body__c,
634
- PostId.Id, PostId.Title__c, PostId.BlogId,
666
+ PostId.Id, PostId.Title__c, PostId.BlogId, PostId.IsActive,
635
667
  PostId.BlogId.Id, PostId.BlogId.Name, PostId.BlogId.Link__c
636
668
  FROM Comment__c
637
669
  WHERE (Id = '123')
@@ -11,6 +11,7 @@ class Post < ActiveForce::SObject
11
11
  self.table_name = "Post__c"
12
12
  field :title
13
13
  field :blog_id, from: "BlogId"
14
+ field :is_active, from: "IsActive", as: :boolean
14
15
  has_many :comments
15
16
  has_many :impossible_comments, model: Comment, scoped_as: ->{ where('1 = 0') }
16
17
  has_many :reply_comments, model: Comment, scoped_as: ->(post){ where(body: "RE: #{post.title}").order('CreationDate DESC') }
@@ -25,6 +26,7 @@ class Blog < ActiveForce::SObject
25
26
  field :name, from: 'Name'
26
27
  field :link, from: 'Link__c'
27
28
  has_many :posts
29
+ has_many :active_posts, model: 'Post', scoped_as: -> { where(is_active: true) }
28
30
  end
29
31
  class Territory < ActiveForce::SObject
30
32
  field :quota_id, from: "Quota__c"
@@ -149,6 +151,7 @@ module Salesforce
149
151
  end
150
152
  class Account < ActiveForce::SObject
151
153
  field :business_partner
154
+ has_many :opportunities, model: Opportunity
152
155
  has_many :partner_opportunities, model: Opportunity, scoped_as: ->(account){ where(business_partner: account.business_partner).includes(:owner) }
153
156
  end
154
157
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_force
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.21.0
4
+ version: 0.21.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eloy Espinaco
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2024-02-15 00:00:00.000000000 Z
14
+ date: 2024-05-17 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: activemodel