active_force 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/lib/active_force/active_query.rb +2 -2
- data/lib/active_force/association/association.rb +8 -0
- data/lib/active_force/association/eager_load_projection_builder.rb +12 -4
- data/lib/active_force/query.rb +2 -2
- data/lib/active_force/sobject.rb +9 -1
- data/lib/active_force/version.rb +1 -1
- data/spec/active_force/active_query_spec.rb +66 -1
- data/spec/active_force/query_spec.rb +24 -8
- data/spec/active_force/sobject/includes_spec.rb +26 -2
- data/spec/active_force/sobject_spec.rb +18 -0
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6de1df55a3215360cd78f76fa3f55d05ca9e8f9f9603d5ab4257667a5fd961a5
|
4
|
+
data.tar.gz: e85f2ae5bd075070adc261ec3a6d566eea2f9cf19c4d4423fa1b859cafdabcc4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5268395d3fcc92a705b909026b6de6deb36b4c6a9b622d0ee63307ed08d16142e96abca2bafb902c55fca02955d75c34aa3348eb84fc14c47784d3548b492f89
|
7
|
+
data.tar.gz: 3ea676afd55bb62601e211250c487a86e7902f1dee1f8b6fdc41410b903ce2052e5a4f893cc869798ba3bf5c1270aad2c84ea21c24dd137909e494e7c0564ff9
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,13 @@
|
|
2
2
|
|
3
3
|
## Not released
|
4
4
|
|
5
|
+
## 0.18.0
|
6
|
+
|
7
|
+
- Fix eager loading of scoped associations. (https://github.com/Beyond-Finance/active_force/pull/67)
|
8
|
+
- Adding `.blank?`, `.present?`, and `.any?` delegators to `ActiveQuery`. (https://github.com/Beyond-Finance/active_force/pull/68)
|
9
|
+
- Adding `update` and `update!` class methods on `SObject`. (https://github.com/Beyond-Finance/active_force/pull/66)
|
10
|
+
- Allow an argument to `last` allowing the query to select the `last(n)` records. Default is 1. (https://github.com/Beyond-Finance/active_force/pull/66)
|
11
|
+
|
5
12
|
## 0.17.0
|
6
13
|
|
7
14
|
- Fix bug with has_many queries due to query method chaining mutating in-place (https://github.com/Beyond-Finance/active_force/pull/10)
|
@@ -21,9 +21,9 @@ module ActiveForce
|
|
21
21
|
attr_reader :sobject, :association_mapping, :belongs_to_association_mapping
|
22
22
|
|
23
23
|
def_delegators :sobject, :sfdc_client, :build, :table_name, :mappings
|
24
|
-
def_delegators :to_a, :each, :map, :inspect, :pluck, :each_with_object
|
24
|
+
def_delegators :to_a, :blank?, :present?, :any?, :each, :map, :inspect, :pluck, :each_with_object
|
25
25
|
|
26
|
-
def initialize
|
26
|
+
def initialize(sobject, custom_table_name = nil)
|
27
27
|
@sobject = sobject
|
28
28
|
@association_mapping = {}
|
29
29
|
@belongs_to_association_mapping = {}
|
@@ -26,6 +26,14 @@ module ActiveForce
|
|
26
26
|
options[:relationship_name] || relation_model.to_s.constantize.table_name
|
27
27
|
end
|
28
28
|
|
29
|
+
def scoped_as
|
30
|
+
options[:scoped_as] || nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def scoped?
|
34
|
+
options[:scoped_as].present?
|
35
|
+
end
|
36
|
+
|
29
37
|
###
|
30
38
|
# Does this association's relation_model represent
|
31
39
|
# +sfdc_table_name+? Examples of +sfdc_table_name+
|
@@ -1,5 +1,6 @@
|
|
1
1
|
module ActiveForce
|
2
2
|
module Association
|
3
|
+
class InvalidEagerLoadAssociation < StandardError; end
|
3
4
|
class EagerLoadProjectionBuilder
|
4
5
|
class << self
|
5
6
|
def build(association, parent_association_field = nil)
|
@@ -34,6 +35,13 @@ module ActiveForce
|
|
34
35
|
def projections
|
35
36
|
raise "Must define #{self.class.name}#projections"
|
36
37
|
end
|
38
|
+
|
39
|
+
def apply_association_scope(query)
|
40
|
+
return query unless association.scoped?
|
41
|
+
raise InvalidEagerLoadAssociation, "Cannot use scopes that expect arguments: #{association.relation_name}" if association.scoped_as.arity.positive?
|
42
|
+
|
43
|
+
query.instance_exec(&association.scoped_as)
|
44
|
+
end
|
37
45
|
end
|
38
46
|
|
39
47
|
class HasManyAssociationProjectionBuilder < AbstractProjectionBuilder
|
@@ -43,17 +51,17 @@ module ActiveForce
|
|
43
51
|
# to be pluralized
|
44
52
|
def projections
|
45
53
|
relationship_name = association.sfdc_association_field
|
46
|
-
query =
|
54
|
+
query = ActiveQuery.new(association.relation_model, relationship_name)
|
47
55
|
query.fields association.relation_model.fields
|
48
|
-
["(#{query.to_s})"]
|
56
|
+
["(#{apply_association_scope(query).to_s})"]
|
49
57
|
end
|
50
58
|
end
|
51
59
|
|
52
60
|
class HasOneAssociationProjectionBuilder < AbstractProjectionBuilder
|
53
61
|
def projections
|
54
|
-
query =
|
62
|
+
query = ActiveQuery.new(association.relation_model, association.sfdc_association_field)
|
55
63
|
query.fields association.relation_model.fields
|
56
|
-
["(#{query.to_s})"]
|
64
|
+
["(#{apply_association_scope(query).to_s})"]
|
57
65
|
end
|
58
66
|
end
|
59
67
|
|
data/lib/active_force/query.rb
CHANGED
data/lib/active_force/sobject.rb
CHANGED
@@ -129,6 +129,14 @@ module ActiveForce
|
|
129
129
|
new(args).create!
|
130
130
|
end
|
131
131
|
|
132
|
+
def self.update(id, attributes)
|
133
|
+
new(attributes.merge(id: id)).update
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.update!(id, attributes)
|
137
|
+
new(attributes.merge(id: id)).update!
|
138
|
+
end
|
139
|
+
|
132
140
|
def save!
|
133
141
|
run_callbacks :save do
|
134
142
|
if persisted?
|
@@ -231,7 +239,7 @@ module ActiveForce
|
|
231
239
|
end
|
232
240
|
|
233
241
|
def default_attributes
|
234
|
-
@attributes.each_value.select do |value|
|
242
|
+
@attributes.each_value.select do |value|
|
235
243
|
value.is_a?(ActiveModel::Attribute::UserProvidedDefault) || value.instance_values["original_attribute"].is_a?(ActiveModel::Attribute::UserProvidedDefault)
|
236
244
|
end.map(&:name)
|
237
245
|
end
|
data/lib/active_force/version.rb
CHANGED
@@ -18,7 +18,6 @@ describe ActiveForce::ActiveQuery do
|
|
18
18
|
]
|
19
19
|
end
|
20
20
|
|
21
|
-
|
22
21
|
before do
|
23
22
|
allow(active_query).to receive(:sfdc_client).and_return client
|
24
23
|
allow(active_query).to receive(:build).and_return Object.new
|
@@ -40,6 +39,72 @@ describe ActiveForce::ActiveQuery do
|
|
40
39
|
end
|
41
40
|
end
|
42
41
|
|
42
|
+
describe '#blank? delegation' do
|
43
|
+
before do
|
44
|
+
allow(client).to receive(:query).and_return(api_result)
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'when there are no records' do
|
48
|
+
let(:api_result) { [] }
|
49
|
+
|
50
|
+
it 'returns true' do
|
51
|
+
result = active_query.where("Text_Label = 'foo'").blank?
|
52
|
+
expect(result).to be true
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'when records are returned' do
|
57
|
+
it 'returns false' do
|
58
|
+
result = active_query.where("Text_Label = 'foo'").blank?
|
59
|
+
expect(result).to be false
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe '#present? delegation' do
|
65
|
+
before do
|
66
|
+
allow(client).to receive(:query).and_return(api_result)
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'when there are no records' do
|
70
|
+
let(:api_result) { [] }
|
71
|
+
|
72
|
+
it 'returns false' do
|
73
|
+
result = active_query.where("Text_Label = 'foo'").present?
|
74
|
+
expect(result).to be false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'when there are records' do
|
79
|
+
it 'returns true' do
|
80
|
+
result = active_query.where("Text_Label = 'foo'").present?
|
81
|
+
expect(result).to be true
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe '#any? delegation' do
|
87
|
+
before do
|
88
|
+
allow(client).to receive(:query).and_return(api_result)
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'when there are no records' do
|
92
|
+
let(:api_result) { [] }
|
93
|
+
|
94
|
+
it 'returns true' do
|
95
|
+
result = active_query.where("Text_Label = 'foo'").any?
|
96
|
+
expect(result).to be false
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'when records are returned' do
|
101
|
+
it 'returns false' do
|
102
|
+
result = active_query.where("Text_Label = 'foo'").any?
|
103
|
+
expect(result).to be true
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
43
108
|
describe "select only some field using mappings" do
|
44
109
|
it "should return a query only with selected field" do
|
45
110
|
new_query = active_query.select(:field)
|
@@ -168,14 +168,30 @@ describe ActiveForce::Query do
|
|
168
168
|
end
|
169
169
|
|
170
170
|
describe '.last' do
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
171
|
+
context 'without any argument' do
|
172
|
+
it 'should return the query for the last record' do
|
173
|
+
expect(query.last.to_s).to eq 'SELECT Id, name, etc FROM table_name ORDER BY Id DESC LIMIT 1'
|
174
|
+
end
|
175
|
+
|
176
|
+
it "should not update the original query" do
|
177
|
+
new_query = query.last
|
178
|
+
expect(query.to_s).to eq "SELECT Id, name, etc FROM table_name"
|
179
|
+
expect(new_query.to_s).to eq 'SELECT Id, name, etc FROM table_name ORDER BY Id DESC LIMIT 1'
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
context 'with an argument' do
|
184
|
+
let(:last_argument) { 3 }
|
185
|
+
|
186
|
+
it 'should return the query for the last n records' do
|
187
|
+
expect(query.last(last_argument).to_s).to eq "SELECT Id, name, etc FROM table_name ORDER BY Id DESC LIMIT #{last_argument}"
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should not update the original query" do
|
191
|
+
new_query = query.last last_argument
|
192
|
+
expect(query.to_s).to eq "SELECT Id, name, etc FROM table_name"
|
193
|
+
expect(new_query.to_s).to eq "SELECT Id, name, etc FROM table_name ORDER BY Id DESC LIMIT #{last_argument}"
|
194
|
+
end
|
179
195
|
end
|
180
196
|
end
|
181
197
|
|
@@ -225,6 +225,13 @@ module ActiveForce
|
|
225
225
|
end
|
226
226
|
end
|
227
227
|
|
228
|
+
context 'when assocation has a scope' do
|
229
|
+
it 'formulates the correct SOQL query with the scope applied' do
|
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')"
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
228
235
|
context 'with namespaced SObjects' do
|
229
236
|
it 'formulates the correct SOQL query' do
|
230
237
|
soql = Salesforce::Quota.includes(:prez_clubs).where(id: '123').to_s
|
@@ -286,9 +293,26 @@ module ActiveForce
|
|
286
293
|
end
|
287
294
|
end
|
288
295
|
|
296
|
+
context 'has_one' do
|
297
|
+
context 'when assocation has a scope' do
|
298
|
+
it 'formulates the correct SOQL query with the scope applied' do
|
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')"
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
end
|
305
|
+
|
289
306
|
context 'when invalid associations are passed' do
|
290
|
-
|
291
|
-
|
307
|
+
context 'when the association is not defined' do
|
308
|
+
it 'raises an error' do
|
309
|
+
expect { Quota.includes(:invalid).find('123') }.to raise_error(ActiveForce::Association::InvalidAssociationError, 'Association named invalid was not found on Quota')
|
310
|
+
end
|
311
|
+
end
|
312
|
+
context 'when the association is scoped and accepts an argument' do
|
313
|
+
it 'raises and error' do
|
314
|
+
expect { Post.includes(:reply_comments).find('1234')}.to raise_error(ActiveForce::Association::InvalidEagerLoadAssociation)
|
315
|
+
end
|
292
316
|
end
|
293
317
|
end
|
294
318
|
end
|
@@ -313,6 +313,24 @@ describe ActiveForce::SObject do
|
|
313
313
|
expect(Whizbang.create(text: 'some text')).to be_instance_of(Whizbang)
|
314
314
|
end
|
315
315
|
end
|
316
|
+
|
317
|
+
describe 'self.update' do
|
318
|
+
it 'uses the client to update the correct record' do
|
319
|
+
expect(client).to receive(:update!)
|
320
|
+
.with(Whizbang.table_name, { 'Id' => '12345678', 'Text_Label' => 'my text', 'Updated_From__c' => 'Rails' })
|
321
|
+
.and_return(true)
|
322
|
+
Whizbang.update('12345678', text: 'my text')
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
describe 'self.update!' do
|
327
|
+
it 'uses the client to update the correct record' do
|
328
|
+
expect(client).to receive(:update!)
|
329
|
+
.with(Whizbang.table_name, { 'Id' => '123456789', 'Text_Label' => 'some other text', 'Updated_From__c' => 'Rails' })
|
330
|
+
.and_return(true)
|
331
|
+
Whizbang.update('123456789', text: 'some other text')
|
332
|
+
end
|
333
|
+
end
|
316
334
|
end
|
317
335
|
|
318
336
|
describe '.count' do
|
metadata
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_force
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.18.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eloy Espinaco
|
8
8
|
- Pablo Oldani
|
9
9
|
- Armando Andini
|
10
10
|
- José Piccioni
|
11
|
-
autorequire:
|
11
|
+
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2023-
|
14
|
+
date: 2023-10-02 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: activemodel
|
@@ -193,7 +193,7 @@ metadata:
|
|
193
193
|
bug_tracker_uri: https://github.com/Beyond-Finance/active_force/issues
|
194
194
|
changelog_uri: https://github.com/Beyond-Finance/active_force/blob/main/CHANGELOG.md
|
195
195
|
source_code_uri: https://github.com/Beyond-Finance/active_force
|
196
|
-
post_install_message:
|
196
|
+
post_install_message:
|
197
197
|
rdoc_options: []
|
198
198
|
require_paths:
|
199
199
|
- lib
|
@@ -208,8 +208,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
208
208
|
- !ruby/object:Gem::Version
|
209
209
|
version: '0'
|
210
210
|
requirements: []
|
211
|
-
rubygems_version: 3.
|
212
|
-
signing_key:
|
211
|
+
rubygems_version: 3.3.26
|
212
|
+
signing_key:
|
213
213
|
specification_version: 4
|
214
214
|
summary: Help you implement models persisting on Sales Force within Rails using RESTForce
|
215
215
|
test_files:
|