active_force 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +9 -2
- data/CHANGELOG.md +6 -0
- data/README.md +43 -41
- data/active_force.gemspec +1 -1
- data/lib/active_attr/dirty.rb +0 -5
- data/lib/active_force/active_query.rb +66 -7
- data/lib/active_force/association/association.rb +4 -0
- data/lib/active_force/association/belongs_to_association.rb +11 -3
- data/lib/active_force/association/has_many_association.rb +6 -4
- data/lib/active_force/query.rb +5 -0
- data/lib/active_force/sobject.rb +45 -32
- data/lib/active_force/standard_types.rb +357 -0
- data/lib/active_force/table.rb +33 -0
- data/lib/active_force/version.rb +1 -1
- data/lib/generators/active_force/{active_force_model → model}/USAGE +0 -0
- data/lib/generators/active_force/model/model_generator.rb +61 -0
- data/lib/generators/active_force/model/templates/model.rb.erb +7 -0
- data/spec/active_force/active_query_spec.rb +79 -12
- data/spec/active_force/association_spec.rb +94 -16
- data/spec/active_force/callbacks_spec.rb +42 -0
- data/spec/active_force/query_spec.rb +10 -0
- data/spec/active_force/sobject/table_name_spec.rb +15 -4
- data/spec/active_force/sobject_spec.rb +72 -3
- data/spec/active_force/table_spec.rb +28 -0
- metadata +13 -8
- data/lib/generators/active_force/active_force_model/active_force_model_generator.rb +0 -47
- data/lib/generators/active_force/active_force_model/templates/model.rb.erb +0 -11
@@ -2,18 +2,15 @@ require 'spec_helper'
|
|
2
2
|
require 'active_force/active_query'
|
3
3
|
|
4
4
|
describe ActiveForce::ActiveQuery do
|
5
|
-
let(:sobject)
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
let(:client){
|
14
|
-
double("client")
|
15
|
-
}
|
16
|
-
|
5
|
+
let(:sobject) do
|
6
|
+
double("sobject", {
|
7
|
+
table_name: "table_name",
|
8
|
+
fields: [],
|
9
|
+
mappings: mappings
|
10
|
+
})
|
11
|
+
end
|
12
|
+
let(:mappings){ { field: "Field__c", other_field: "Other_Field" } }
|
13
|
+
let(:client){ double("client") }
|
17
14
|
let(:active_query){ ActiveForce::ActiveQuery.new(sobject) }
|
18
15
|
|
19
16
|
before do
|
@@ -37,6 +34,13 @@ describe ActiveForce::ActiveQuery do
|
|
37
34
|
end
|
38
35
|
end
|
39
36
|
|
37
|
+
describe "select only some field using mappings" do
|
38
|
+
it "should return a query only with selected field" do
|
39
|
+
active_query.select(:field)
|
40
|
+
expect(active_query.to_s).to eq("SELECT Field__c FROM table_name")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
40
44
|
describe "condition mapping" do
|
41
45
|
it "maps conditions for a .where" do
|
42
46
|
active_query.where(field: 123)
|
@@ -47,6 +51,69 @@ describe ActiveForce::ActiveQuery do
|
|
47
51
|
active_query.where field: "hello"
|
48
52
|
expect(active_query.to_s).to end_with("Field__c = 'hello'")
|
49
53
|
end
|
54
|
+
|
55
|
+
it "puts NULL when a field is set as nil" do
|
56
|
+
active_query.where field: nil
|
57
|
+
expect(active_query.to_s).to end_with("Field__c = NULL")
|
58
|
+
end
|
59
|
+
|
60
|
+
describe 'bind parameters' do
|
61
|
+
let(:mappings) do
|
62
|
+
super().merge({
|
63
|
+
other_field: 'Other_Field__c',
|
64
|
+
name: 'Name'
|
65
|
+
})
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'accepts bind parameters' do
|
69
|
+
active_query.where('Field__c = ?', 123)
|
70
|
+
expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE Field__c = 123")
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'accepts nil bind parameters' do
|
74
|
+
active_query.where('Field__c = ?', nil)
|
75
|
+
expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE Field__c = NULL")
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'accepts multiple bind parameters' do
|
79
|
+
active_query.where('Field__c = ? AND Other_Field__c = ? AND Name = ?', 123, 321, 'Bob')
|
80
|
+
expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE Field__c = 123 AND Other_Field__c = 321 AND Name = 'Bob'")
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'complains when there given an incorrect number of bind parameters' do
|
84
|
+
expect{
|
85
|
+
active_query.where('Field__c = ? AND Other_Field__c = ? AND Name = ?', 123, 321)
|
86
|
+
}.to raise_error(ActiveForce::PreparedStatementInvalid, 'wrong number of bind variables (2 for 3)')
|
87
|
+
end
|
88
|
+
|
89
|
+
context 'named bind parameters' do
|
90
|
+
it 'accepts bind parameters' do
|
91
|
+
active_query.where('Field__c = :field', field: 123)
|
92
|
+
expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE Field__c = 123")
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'accepts nil bind parameters' do
|
96
|
+
active_query.where('Field__c = :field', field: nil)
|
97
|
+
expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE Field__c = NULL")
|
98
|
+
end
|
99
|
+
|
100
|
+
it 'accepts multiple bind parameters' do
|
101
|
+
active_query.where('Field__c = :field AND Other_Field__c = :other_field AND Name = :name', field: 123, other_field: 321, name: 'Bob')
|
102
|
+
expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE Field__c = 123 AND Other_Field__c = 321 AND Name = 'Bob'")
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'accepts multiple bind parameters orderless' do
|
106
|
+
active_query.where('Field__c = :field AND Other_Field__c = :other_field AND Name = :name', name: 'Bob', other_field: 321, field: 123)
|
107
|
+
expect(active_query.to_s).to eq("SELECT Id FROM table_name WHERE Field__c = 123 AND Other_Field__c = 321 AND Name = 'Bob'")
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'complains when there given an incorrect number of bind parameters' do
|
111
|
+
expect{
|
112
|
+
active_query.where('Field__c = :field AND Other_Field__c = :other_field AND Name = :name', field: 123, other_field: 321)
|
113
|
+
}.to raise_error(ActiveForce::PreparedStatementInvalid, 'missing value for :name in Field__c = :field AND Other_Field__c = :other_field AND Name = :name')
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
50
117
|
end
|
51
118
|
|
52
119
|
describe "#find_by" do
|
@@ -4,20 +4,15 @@ require 'active_force/association'
|
|
4
4
|
describe ActiveForce::SObject do
|
5
5
|
|
6
6
|
let :post do
|
7
|
-
|
8
|
-
allow(post).to receive(:id).and_return "1"
|
9
|
-
post
|
7
|
+
Post.new(id: "1")
|
10
8
|
end
|
11
9
|
|
12
10
|
let :comment do
|
13
|
-
|
14
|
-
allow(comment).to receive(:id).and_return "1"
|
15
|
-
allow(comment).to receive(:post_id).and_return "1"
|
16
|
-
comment
|
11
|
+
Comment.new(id: "1", post_id: "1")
|
17
12
|
end
|
18
13
|
|
19
14
|
let :client do
|
20
|
-
double("sfdc_client")
|
15
|
+
double("sfdc_client", query: [Restforce::Mash.new(id: 1)])
|
21
16
|
end
|
22
17
|
|
23
18
|
before do
|
@@ -34,7 +29,6 @@ describe ActiveForce::SObject do
|
|
34
29
|
end
|
35
30
|
|
36
31
|
describe "has_many_query" do
|
37
|
-
|
38
32
|
before do
|
39
33
|
class Post < ActiveForce::SObject
|
40
34
|
has_many :comments
|
@@ -49,13 +43,46 @@ describe ActiveForce::SObject do
|
|
49
43
|
expect(post.comments).to be_a ActiveForce::ActiveQuery
|
50
44
|
end
|
51
45
|
|
46
|
+
it 'makes only one API call to fetch the associated object' do
|
47
|
+
expect(client).to receive(:query).once
|
48
|
+
post.comments.to_a
|
49
|
+
post.comments.to_a
|
50
|
+
end
|
51
|
+
|
52
52
|
describe 'to_s' do
|
53
|
-
it "should
|
53
|
+
it "should return a SOQL statment" do
|
54
54
|
soql = "SELECT Id, PostId FROM Comment__c WHERE PostId = '1'"
|
55
|
-
|
55
|
+
expect(post.comments.to_s).to eq soql
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
+
context 'when the SObject is namespaced' do
|
60
|
+
let(:account){ Foo::Account.new(id: '1') }
|
61
|
+
|
62
|
+
before do
|
63
|
+
module Foo
|
64
|
+
class Opportunity < ActiveForce::SObject
|
65
|
+
field :account_id, from: 'AccountId'
|
66
|
+
end
|
67
|
+
|
68
|
+
class Account < ActiveForce::SObject
|
69
|
+
has_many :opportunities, model: Foo::Opportunity
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'correctly infers the foreign key and forms the correct query' do
|
75
|
+
soql = "SELECT Id, AccountId FROM Opportunity WHERE AccountId = '1'"
|
76
|
+
expect(account.opportunities.to_s).to eq soql
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'uses an explicit foreign key if it is supplied' do
|
80
|
+
Foo::Opportunity.field :partner_account_id, from: 'Partner_Account_Id__c'
|
81
|
+
Foo::Account.has_many :opportunities, foreign_key: :partner_account_id, model: Foo::Opportunity
|
82
|
+
soql = "SELECT Id, AccountId, Partner_Account_Id__c FROM Opportunity WHERE Partner_Account_Id__c = '1'"
|
83
|
+
expect(account.opportunities.to_s).to eq soql
|
84
|
+
end
|
85
|
+
end
|
59
86
|
end
|
60
87
|
|
61
88
|
describe 'has_many(options)' do
|
@@ -70,8 +97,8 @@ describe ActiveForce::SObject do
|
|
70
97
|
end
|
71
98
|
|
72
99
|
it 'should allow to change the foreign key' do
|
73
|
-
Post.has_many :comments, { foreign_key: :
|
74
|
-
Comment.field :
|
100
|
+
Post.has_many :comments, { foreign_key: :poster }
|
101
|
+
Comment.field :poster, from: 'PostId'
|
75
102
|
soql = "SELECT Id, PostId FROM Comment__c WHERE PostId = '1'"
|
76
103
|
expect(post.comments.to_s).to eq soql
|
77
104
|
end
|
@@ -90,13 +117,11 @@ describe ActiveForce::SObject do
|
|
90
117
|
end
|
91
118
|
|
92
119
|
describe "belongs_to" do
|
93
|
-
|
94
120
|
before do
|
95
|
-
|
121
|
+
Comment.belongs_to :post
|
96
122
|
end
|
97
123
|
|
98
124
|
it "should get the resource it belongs to" do
|
99
|
-
Comment.belongs_to :post
|
100
125
|
expect(comment.post).to be_instance_of(Post)
|
101
126
|
end
|
102
127
|
|
@@ -110,5 +135,58 @@ describe ActiveForce::SObject do
|
|
110
135
|
comment.post
|
111
136
|
end
|
112
137
|
|
138
|
+
it 'makes only one API call to fetch the associated object' do
|
139
|
+
expect(client).to receive(:query).once
|
140
|
+
comment.post
|
141
|
+
comment.post
|
142
|
+
end
|
143
|
+
|
144
|
+
it 'accepts assignment of an existing object as an association' do
|
145
|
+
expect(client).to_not receive(:query)
|
146
|
+
other_post = Post.new(id: "2")
|
147
|
+
comment.post = other_post
|
148
|
+
expect(comment.post_id).to eq other_post.id
|
149
|
+
expect(comment.post).to eq other_post
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'when the SObject is namespaced' do
|
153
|
+
let(:attachment){ Foo::Attachment.new(id: '1', lead_id: '2') }
|
154
|
+
before do
|
155
|
+
module Foo
|
156
|
+
class Lead < ActiveForce::SObject; end
|
157
|
+
|
158
|
+
class Attachment < ActiveForce::SObject
|
159
|
+
field :lead_id, from: 'Lead_Id__c'
|
160
|
+
belongs_to :lead, model: Foo::Lead
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'generates the correct query' do
|
166
|
+
expect(client).to receive(:query).with("SELECT Id FROM Lead WHERE Id = '2' LIMIT 1")
|
167
|
+
attachment.lead
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'instantiates the correct object' do
|
171
|
+
expect(attachment.lead).to be_instance_of(Foo::Lead)
|
172
|
+
end
|
173
|
+
|
174
|
+
context 'when given a foreign key' do
|
175
|
+
let(:attachment){ Foo::Attachment.new(id: '1', fancy_lead_id: '2') }
|
176
|
+
before do
|
177
|
+
module Foo
|
178
|
+
class Attachment < ActiveForce::SObject
|
179
|
+
field :fancy_lead_id, from: 'LeadId'
|
180
|
+
belongs_to :lead, model: Foo::Lead, foreign_key: :fancy_lead_id
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'generates the correct query' do
|
186
|
+
expect(client).to receive(:query).with("SELECT Id FROM Lead WHERE Id = '2' LIMIT 1")
|
187
|
+
attachment.lead
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
113
191
|
end
|
114
192
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'active_force/sobject'
|
3
|
+
|
4
|
+
describe ActiveForce::SObject do
|
5
|
+
let(:client) { double 'Client', create!: 'id' }
|
6
|
+
|
7
|
+
before do
|
8
|
+
allow(ActiveForce::SObject).to receive(:sfdc_client).and_return client
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "save" do
|
12
|
+
|
13
|
+
it 'call action callback when save a record' do
|
14
|
+
class Whizbanged < ActiveForce::SObject
|
15
|
+
|
16
|
+
field :updated_from
|
17
|
+
field :dirty_attribute
|
18
|
+
|
19
|
+
before_save :set_as_updated_from_rails
|
20
|
+
after_save :mark_dirty
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def set_as_updated_from_rails
|
25
|
+
self.updated_from = 'Rails'
|
26
|
+
end
|
27
|
+
|
28
|
+
def mark_dirty
|
29
|
+
self.dirty_attribute = true
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
whizbanged = Whizbanged.new
|
35
|
+
whizbanged.save
|
36
|
+
expect(whizbanged.updated_from).to eq 'Rails'
|
37
|
+
expect(whizbanged.dirty_attribute).to eq true
|
38
|
+
expect(whizbanged.changed.include? 'dirty_attribute').to eq true
|
39
|
+
expect(whizbanged.changed.include? 'updated_from').to eq false
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -11,6 +11,16 @@ describe ActiveForce::Query do
|
|
11
11
|
after do
|
12
12
|
end
|
13
13
|
|
14
|
+
describe '.select' do
|
15
|
+
it 'use column sent on the select method' do
|
16
|
+
expect(@query.select('name').all.to_s).to eq "SELECT name FROM table_name"
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'use columns sent on the select method' do
|
20
|
+
expect(@query.select(['id','name']).all.to_s).to eq "SELECT id, name FROM table_name"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
14
24
|
describe ".all" do
|
15
25
|
it "table should return table name" do
|
16
26
|
expect(@query.all.table).to eq(@query.table)
|
@@ -25,13 +25,24 @@ describe ActiveForce::SObject do
|
|
25
25
|
expect(EnforcedTableName.table_name).to eq('Forced__c')
|
26
26
|
end
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
context 'with a namespace' do
|
29
|
+
it "the namespace is not included" do
|
30
|
+
module Foo
|
31
|
+
class Bar < ActiveForce::SObject
|
32
|
+
end
|
31
33
|
end
|
34
|
+
|
35
|
+
expect(Foo::Bar.table_name).to eq('Bar__c')
|
32
36
|
end
|
33
37
|
|
34
|
-
|
38
|
+
it 'standard types are inferred correctly' do
|
39
|
+
module Foo
|
40
|
+
class Account < ActiveForce::SObject
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
expect(Foo::Account.table_name).to eq('Account')
|
45
|
+
end
|
35
46
|
end
|
36
47
|
end
|
37
48
|
end
|
@@ -55,16 +55,46 @@ describe ActiveForce::SObject do
|
|
55
55
|
expect(Quota.mappings[:id]).to eq 'Bar_Id__c'
|
56
56
|
end
|
57
57
|
end
|
58
|
+
|
59
|
+
context 'as: :multi_picklist' do
|
60
|
+
before do
|
61
|
+
class IceCream < ActiveForce::SObject
|
62
|
+
field :flavors, as: :multi_picklist
|
63
|
+
end
|
64
|
+
sundae.flavors = %w(chocolate vanilla strawberry)
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'on create' do
|
68
|
+
let(:sundae) { IceCream.new }
|
69
|
+
it 'formats the picklist values' do
|
70
|
+
expect(client).to receive(:create!).with('IceCream__c', {'Flavors__c' => 'chocolate;vanilla;strawberry'})
|
71
|
+
sundae.save
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
context 'on update' do
|
76
|
+
let(:sundae) { IceCream.new(id: '1') }
|
77
|
+
it 'formats the picklist values' do
|
78
|
+
expect(client).to receive(:update!).with('IceCream__c', {'Flavors__c' => 'chocolate;vanilla;strawberry', 'Id' => '1'})
|
79
|
+
sundae.save
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
58
84
|
end
|
59
85
|
|
60
86
|
describe '#update' do
|
61
87
|
|
62
88
|
subject do
|
63
|
-
Whizbang.new
|
89
|
+
Whizbang.new(id: '1')
|
64
90
|
end
|
65
91
|
|
66
92
|
before do
|
67
|
-
|
93
|
+
expected_args = [
|
94
|
+
Whizbang.table_name,
|
95
|
+
{'Text_Label' => 'some text', 'Id' => '1'}
|
96
|
+
]
|
97
|
+
expect(client).to receive(:update!).with(*expected_args).and_return('id')
|
68
98
|
end
|
69
99
|
|
70
100
|
it 'delegates to the Client with create!' do
|
@@ -97,7 +127,7 @@ describe ActiveForce::SObject do
|
|
97
127
|
describe 'self.create' do
|
98
128
|
|
99
129
|
before do
|
100
|
-
expect(client).to receive(:create!).and_return('id')
|
130
|
+
expect(client).to receive(:create!).with(Whizbang.table_name, 'Text_Label' => 'some text').and_return('id')
|
101
131
|
end
|
102
132
|
|
103
133
|
it 'should create a new instance' do
|
@@ -126,4 +156,43 @@ describe ActiveForce::SObject do
|
|
126
156
|
Whizbang.find_by id: 123, text: "foo"
|
127
157
|
end
|
128
158
|
end
|
159
|
+
|
160
|
+
describe '#reload' do
|
161
|
+
let(:client) do
|
162
|
+
double("sfdc_client", query: [Restforce::Mash.new(Id: 1, Name: 'Jeff')])
|
163
|
+
end
|
164
|
+
let(:quota){ Quota.new(id: '1') }
|
165
|
+
let(:territory){ Territory.new(id: '1', quota_id: '1') }
|
166
|
+
|
167
|
+
before do
|
168
|
+
Territory.belongs_to :quota, model: Quota
|
169
|
+
Territory.field :quota_id, from: 'Quota_Id'
|
170
|
+
allow(ActiveForce::SObject).to receive(:sfdc_client).and_return client
|
171
|
+
end
|
172
|
+
|
173
|
+
it 'clears cached associations' do
|
174
|
+
soql = "SELECT Id, Bar_Id__c FROM Quota__c WHERE Id = '1' LIMIT 1"
|
175
|
+
expect(client).to receive(:query).twice.with soql
|
176
|
+
allow(Territory).to receive(:find){ territory }
|
177
|
+
territory.quota
|
178
|
+
territory.quota
|
179
|
+
territory.reload
|
180
|
+
territory.quota
|
181
|
+
end
|
182
|
+
|
183
|
+
it "refreshes the object's attributes" do
|
184
|
+
Territory.field :name, from: 'Name'
|
185
|
+
territory.name = 'Walter'
|
186
|
+
expect(territory.name).to eq 'Walter'
|
187
|
+
territory.reload
|
188
|
+
expect(territory.name).to eq 'Jeff'
|
189
|
+
expect(territory.changed_attributes).to be_empty
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'returns the same object' do
|
193
|
+
allow(Territory).to receive(:find){ Territory.new }
|
194
|
+
expected = territory
|
195
|
+
expect(territory.reload).to eql expected
|
196
|
+
end
|
197
|
+
end
|
129
198
|
end
|