active_force 0.5.0 → 0.6.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.
@@ -0,0 +1,7 @@
1
+ require 'active_force/sobject'
2
+
3
+ class <%= @class_name %> < ActiveForce::SObject
4
+ <% attributes.each do |attribute| -%>
5
+ <%= attribute_line attribute %>
6
+ <% end -%>
7
+ end
@@ -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
- sobject = double("sobject")
7
- allow(sobject).to receive(:table_name).and_return "table_name"
8
- allow(sobject).to receive(:fields).and_return []
9
- allow(sobject).to receive(:mappings).and_return({field: "Field__c"})
10
- sobject
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
- post = Post.new
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
- comment = Comment.new
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 retrun a SOQL statment" do
53
+ it "should return a SOQL statment" do
54
54
  soql = "SELECT Id, PostId FROM Comment__c WHERE PostId = '1'"
55
- expect(post.comments.to_s).to eq soql
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: :post }
74
- Comment.field :post, from: 'PostId'
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
- allow(client).to receive(:query).and_return [Restforce::Mash.new(id: 1)]
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
- it "doesn't include a namespace" do
29
- module Foo
30
- class Bar < ActiveForce::SObject
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
- expect(Foo::Bar.table_name).to eq('Bar__c')
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
- expect(client).to receive(:update!).and_return('id')
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