openstax_active_force 1.0.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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.mailmap +3 -0
  4. data/.rspec +2 -0
  5. data/.travis.yml +3 -0
  6. data/CHANGELOG.md +98 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +174 -0
  10. data/Rakefile +6 -0
  11. data/active_force.gemspec +30 -0
  12. data/lib/active_attr/dirty.rb +33 -0
  13. data/lib/active_force/active_query.rb +155 -0
  14. data/lib/active_force/association/association.rb +50 -0
  15. data/lib/active_force/association/belongs_to_association.rb +26 -0
  16. data/lib/active_force/association/eager_load_projection_builder.rb +60 -0
  17. data/lib/active_force/association/has_many_association.rb +33 -0
  18. data/lib/active_force/association/relation_model_builder.rb +70 -0
  19. data/lib/active_force/association.rb +28 -0
  20. data/lib/active_force/attribute.rb +30 -0
  21. data/lib/active_force/mapping.rb +78 -0
  22. data/lib/active_force/query.rb +110 -0
  23. data/lib/active_force/sobject.rb +210 -0
  24. data/lib/active_force/standard_types.rb +357 -0
  25. data/lib/active_force/table.rb +37 -0
  26. data/lib/active_force/version.rb +3 -0
  27. data/lib/active_force.rb +13 -0
  28. data/lib/generators/active_force/model/USAGE +8 -0
  29. data/lib/generators/active_force/model/model_generator.rb +62 -0
  30. data/lib/generators/active_force/model/templates/model.rb.erb +5 -0
  31. data/spec/active_force/active_query_spec.rb +178 -0
  32. data/spec/active_force/association/relation_model_builder_spec.rb +62 -0
  33. data/spec/active_force/association_spec.rb +157 -0
  34. data/spec/active_force/attribute_spec.rb +27 -0
  35. data/spec/active_force/callbacks_spec.rb +20 -0
  36. data/spec/active_force/mapping_spec.rb +18 -0
  37. data/spec/active_force/query_spec.rb +126 -0
  38. data/spec/active_force/sobject/includes_spec.rb +290 -0
  39. data/spec/active_force/sobject/table_name_spec.rb +27 -0
  40. data/spec/active_force/sobject_spec.rb +398 -0
  41. data/spec/active_force/table_spec.rb +25 -0
  42. data/spec/active_force_spec.rb +7 -0
  43. data/spec/fixtures/sobject/single_sobject_hash.yml +26 -0
  44. data/spec/spec_helper.rb +16 -0
  45. data/spec/support/fixture_helpers.rb +45 -0
  46. data/spec/support/restforce_factories.rb +9 -0
  47. data/spec/support/sobjects.rb +97 -0
  48. data/spec/support/whizbang.rb +30 -0
  49. metadata +196 -0
@@ -0,0 +1,398 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveForce::SObject do
4
+ let(:sobject_hash) { YAML.load(fixture('sobject/single_sobject_hash')) }
5
+ let(:client) { double 'Client' }
6
+
7
+ before do
8
+ ActiveForce.sfdc_client = client
9
+ end
10
+
11
+ describe ".new" do
12
+ it 'should assigns values when are passed by parameters' do
13
+ expect(Whizbang.new({ text: 'some text' }).text).to eq 'some text'
14
+ end
15
+ end
16
+
17
+ describe ".build" do
18
+ let(:sobject){ Whizbang.build sobject_hash }
19
+
20
+ it "build a valid sobject from a JSON" do
21
+ expect(sobject).to be_an_instance_of Whizbang
22
+ end
23
+
24
+ it "sets the values' types from the sf_type" do
25
+ expect(sobject.boolean).to be_an_instance_of TrueClass
26
+ expect(sobject.checkbox).to be_an_instance_of FalseClass
27
+ expect(sobject.date).to be_an_instance_of Date
28
+ expect(sobject.datetime).to be_an_instance_of DateTime
29
+ expect(sobject.percent).to be_an_instance_of Float
30
+ expect(sobject.text).to be_an_instance_of String
31
+ expect(sobject.picklist_multiselect).to be_an_instance_of String
32
+ end
33
+ end
34
+
35
+ describe ".field" do
36
+ it "add a mappings" do
37
+ expect(Whizbang.mappings).to include(
38
+ checkbox: 'Checkbox_Label',
39
+ text: 'Text_Label',
40
+ date: 'Date_Label',
41
+ datetime: 'DateTime_Label',
42
+ picklist_multiselect: 'Picklist_Multiselect_Label'
43
+ )
44
+ end
45
+
46
+ it "set an attribute" do
47
+ %w[checkbox text date datetime picklist_multiselect].each do |name|
48
+ expect(Whizbang.attribute_names).to include(name)
49
+ end
50
+ end
51
+
52
+ it "uses Salesforce API naming conventions by default" do
53
+ expect(Whizbang.mappings[:estimated_close_date]).to eq 'Estimated_Close_Date__c'
54
+ end
55
+
56
+ describe 'having an id' do
57
+ it 'has one by default' do
58
+ expect(Territory.new).to respond_to(:id)
59
+ expect(Territory.mappings[:id]).to eq 'Id'
60
+ end
61
+
62
+ it 'can be overridden' do
63
+ expect(Quota.new).to respond_to(:id)
64
+ expect(Quota.mappings[:id]).to eq 'Bar_Id__c'
65
+ end
66
+ end
67
+
68
+ context 'as: :multi_picklist' do
69
+ before do
70
+ class IceCream < ActiveForce::SObject
71
+ field :flavors, as: :multi_picklist
72
+ end
73
+ sundae.clear_changes_information
74
+ sundae.flavors = %w(chocolate vanilla strawberry)
75
+ end
76
+
77
+ context 'on create' do
78
+ let(:sundae) { IceCream.new }
79
+ it 'formats the picklist values' do
80
+ expect(client).to receive(:create!).with('IceCream__c', {'Flavors__c' => 'chocolate;vanilla;strawberry'})
81
+ sundae.save
82
+ end
83
+ end
84
+
85
+ context 'on update' do
86
+ let(:sundae) { IceCream.new(id: '1') }
87
+ it 'formats the picklist values' do
88
+ expect(client).to receive(:update!).with('IceCream__c', {'Flavors__c' => 'chocolate;vanilla;strawberry', 'Id' => '1'})
89
+ sundae.save
90
+ end
91
+ end
92
+
93
+ end
94
+ end
95
+
96
+ describe "CRUD" do
97
+ let(:instance){ Whizbang.new(id: '1') }
98
+
99
+ describe '#update' do
100
+
101
+ context 'with valid attributes' do
102
+ before do
103
+ expected_args = [
104
+ Whizbang.table_name,
105
+ {'Text_Label' => 'some text', 'Boolean_Label' => false, 'Id' => '1', "Updated_From__c"=>"Rails"}
106
+ ]
107
+ expect(client).to receive(:update!).with(*expected_args).and_return('id')
108
+ end
109
+
110
+ it 'delegates to the Client with create! and sets the id' do
111
+ expect(instance.update( text: 'some text', boolean: false )).to eq(true)
112
+ expect(instance.text).to eq('some text')
113
+ end
114
+ end
115
+
116
+ context 'with invalid attributes' do
117
+ it 'sets the error on the instance' do
118
+ expect(instance.update( boolean: true )).to eq(false)
119
+ expect(instance.errors).to be_present
120
+ expect(instance.errors.full_messages.count).to eq(1)
121
+ expect(instance.errors.full_messages[0]).to eq("Percent can't be blank")
122
+ end
123
+ end
124
+ end
125
+
126
+ describe ".update!" do
127
+ context 'with valid attributes' do
128
+ describe 'and without a ClientError' do
129
+ before do
130
+ expected_args = [
131
+ Whizbang.table_name,
132
+ {'Text_Label' => 'some text', 'Boolean_Label' => false, 'Id' => '1', "Updated_From__c"=>"Rails"}
133
+ ]
134
+ expect(client).to receive(:update!).with(*expected_args).and_return('id')
135
+ end
136
+ it 'saves successfully' do
137
+ expect(instance.update!( text: 'some text', boolean: false )).to eq(true)
138
+ expect(instance.text).to eq('some text')
139
+ end
140
+ end
141
+
142
+ describe 'and with a ClientError' do
143
+ let(:faraday_error){ Faraday::Error::ClientError.new('Some String') }
144
+
145
+ before{ expect(client).to receive(:update!).and_raise(faraday_error) }
146
+
147
+ it 'raises an error' do
148
+ expect{ instance.update!( text: 'some text', boolean: false ) }.to raise_error(Faraday::Error::ClientError)
149
+ end
150
+ end
151
+ end
152
+
153
+ context 'with invalid attributes' do
154
+ let(:instance){ Whizbang.new boolean: true }
155
+
156
+ it 'raises an error' do
157
+ expect{ instance.update!( text: 'some text', boolean: true ) }.to raise_error(ActiveForce::RecordInvalid)
158
+ end
159
+ end
160
+ end
161
+
162
+ describe '#create' do
163
+ context 'with valid attributes' do
164
+ before do
165
+ expect(client).to receive(:create!).and_return('id')
166
+ end
167
+
168
+ it 'delegates to the Client with create! and sets the id' do
169
+ expect(instance.create).to be_instance_of(Whizbang)
170
+ expect(instance.id).to eq('id')
171
+ end
172
+ end
173
+
174
+
175
+ context 'with invalid attributes' do
176
+ let(:instance){ Whizbang.new boolean: true }
177
+
178
+ it 'sets the error on the instance' do
179
+ expect(instance.create).to be_instance_of(Whizbang)
180
+ expect(instance.id).to eq(nil)
181
+ expect(instance.errors).to be_present
182
+ expect(instance.errors.full_messages.count).to eq(1)
183
+ expect(instance.errors.full_messages[0]).to eq("Percent can't be blank")
184
+ end
185
+ end
186
+ end
187
+
188
+ describe '#create!' do
189
+ context 'with valid attributes' do
190
+ describe 'and without a ClientError' do
191
+
192
+ before{ expect(client).to receive(:create!).and_return('id') }
193
+
194
+ it 'saves successfully' do
195
+ expect(instance.create!).to be_instance_of(Whizbang)
196
+ expect(instance.id).to eq('id')
197
+ end
198
+ end
199
+
200
+ describe 'and with a ClientError' do
201
+ let(:faraday_error){ Faraday::Error::ClientError.new('Some String') }
202
+
203
+ before{ expect(client).to receive(:create!).and_raise(faraday_error) }
204
+
205
+ it 'raises an error' do
206
+ expect{ instance.create! }.to raise_error(Faraday::Error::ClientError)
207
+ end
208
+ end
209
+ end
210
+
211
+ context 'with invalid attributes' do
212
+ let(:instance){ Whizbang.new boolean: true }
213
+
214
+ it 'raises an error' do
215
+ expect{ instance.create! }.to raise_error(ActiveForce::RecordInvalid)
216
+ end
217
+ end
218
+ end
219
+
220
+ describe "#destroy" do
221
+ it "should send client :destroy! with its id" do
222
+ expect(client).to receive(:destroy!).with 'Whizbang__c', '1'
223
+ instance.destroy
224
+ end
225
+ end
226
+
227
+ describe 'self.create' do
228
+ before do
229
+ expect(client).to receive(:create!).with(Whizbang.table_name, 'Text_Label' => 'some text', 'Updated_From__c'=>'Rails').and_return('id')
230
+ end
231
+
232
+ it 'should create a new instance' do
233
+ expect(Whizbang.create(text: 'some text')).to be_instance_of(Whizbang)
234
+ end
235
+ end
236
+ end
237
+
238
+ describe "#count" do
239
+ let(:count_response){ [Restforce::Mash.new(expr0: 1)] }
240
+
241
+ it "responds to count" do
242
+ expect(Whizbang).to respond_to(:count)
243
+ end
244
+
245
+ it "sends the query to the client" do
246
+ expect(client).to receive(:query).and_return(count_response)
247
+ expect(Whizbang.count).to eq(1)
248
+ end
249
+
250
+ end
251
+
252
+ describe "#find_by" do
253
+ it "should query the client, with the SFDC field names and correctly enclosed values" do
254
+ expect(client).to receive(:query).with("SELECT #{Whizbang.fields.join ', '} FROM Whizbang__c WHERE (Id = 123) AND (Text_Label = 'foo') LIMIT 1")
255
+ Whizbang.find_by id: 123, text: "foo"
256
+ end
257
+ end
258
+
259
+ describe '#reload' do
260
+ let(:client) do
261
+ double("sfdc_client", query: [Restforce::Mash.new(Id: 1, Name: 'Jeff')])
262
+ end
263
+ let(:quota){ Quota.new(id: '1') }
264
+ let(:territory){ Territory.new(id: '1', quota_id: '1') }
265
+
266
+ before do
267
+ ActiveForce.sfdc_client = client
268
+ end
269
+
270
+ it 'clears cached associations' do
271
+ soql = "SELECT Id, Bar_Id__c FROM Quota__c WHERE (Id = '1') LIMIT 1"
272
+ expect(client).to receive(:query).twice.with soql
273
+ allow(Territory).to receive(:find){ territory }
274
+ territory.quota
275
+ territory.quota
276
+ territory.reload
277
+ territory.quota
278
+ end
279
+
280
+ it "refreshes the object's attributes" do
281
+ territory.name = 'Walter'
282
+ expect(territory.name).to eq 'Walter'
283
+ territory.reload
284
+ expect(territory.name).to eq 'Jeff'
285
+ expect(territory.changed_attributes).to be_empty
286
+ end
287
+
288
+ it 'returns the same object' do
289
+ allow(Territory).to receive(:find){ Territory.new }
290
+ expected = territory
291
+ expect(territory.reload).to eql expected
292
+ end
293
+ end
294
+
295
+ describe '#persisted?' do
296
+ context 'with an id' do
297
+ let(:instance){ Territory.new(id: '00QV0000004jeqNMAT') }
298
+
299
+ it 'returns true' do
300
+ expect(instance).to be_persisted
301
+ end
302
+ end
303
+
304
+ context 'without an id' do
305
+ let(:instance){ Territory.new }
306
+
307
+ it 'returns false' do
308
+ expect(instance).to_not be_persisted
309
+ end
310
+ end
311
+ end
312
+
313
+ describe 'logger output' do
314
+ let(:instance){ Whizbang.new }
315
+
316
+ before do
317
+ allow(instance).to receive(:create!).and_raise(Faraday::Error::ClientError.new(double))
318
+ end
319
+
320
+ it 'catches and logs the error' do
321
+ expect(instance).to receive(:logger_output).and_return(false)
322
+ instance.save
323
+ end
324
+ end
325
+
326
+ describe ".save!" do
327
+ let(:instance){ Whizbang.new }
328
+
329
+ context 'with valid attributes' do
330
+ describe 'and without a ClientError' do
331
+ before{ expect(client).to receive(:create!).and_return('id') }
332
+ it 'saves successfully' do
333
+ expect(instance.save!).to eq(true)
334
+ end
335
+ end
336
+
337
+ describe 'and with a ClientError' do
338
+ let(:faraday_error){ Faraday::Error::ClientError.new('Some String') }
339
+
340
+ before{ expect(client).to receive(:create!).and_raise(faraday_error) }
341
+
342
+ it 'raises an error' do
343
+ expect{ instance.save! }.to raise_error(Faraday::Error::ClientError)
344
+ end
345
+ end
346
+ end
347
+
348
+ context 'with invalid attributes' do
349
+ let(:instance){ Whizbang.new boolean: true }
350
+
351
+ it 'raises an error' do
352
+ expect{ instance.save! }.to raise_error(ActiveForce::RecordInvalid)
353
+ end
354
+ end
355
+ end
356
+
357
+ describe ".save" do
358
+ let(:instance){ Whizbang.new }
359
+
360
+ context 'with valid attributes' do
361
+ describe 'and without a ClientError' do
362
+ before{ expect(client).to receive(:create!).and_return('id') }
363
+ it 'saves successfully' do
364
+ expect(instance.save).to eq(true)
365
+ end
366
+ end
367
+
368
+ describe 'and with a ClientError' do
369
+ let(:faraday_error){ Faraday::Error::ClientError.new('Some String') }
370
+ before{ expect(client).to receive(:create!).and_raise(faraday_error) }
371
+ it 'returns false' do
372
+ expect(instance.save).to eq(false)
373
+ end
374
+ it 'sets the error on the instance' do
375
+ instance.save
376
+ expect(instance.errors).to be_present
377
+ expect(instance.errors.full_messages.count).to eq(1)
378
+ expect(instance.errors.full_messages[0]).to eq('Some String')
379
+ end
380
+ end
381
+ end
382
+
383
+ context 'with invalid attributes' do
384
+ let(:instance){ Whizbang.new boolean: true }
385
+
386
+ it 'does not save' do
387
+ expect(instance.save).to eq(false)
388
+ end
389
+
390
+ it 'sets the error on the instance' do
391
+ instance.save
392
+ expect(instance.errors).to be_present
393
+ expect(instance.errors.full_messages.count).to eq(1)
394
+ expect(instance.errors.full_messages[0]).to eq("Percent can't be blank")
395
+ end
396
+ end
397
+ end
398
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveForce::Table do
4
+ describe '#table_name' do
5
+ let(:table) { ActiveForce::Table }
6
+
7
+ it 'Use the class name adding "__c"' do
8
+ expect(table.new('Custom').name).to eq('Custom__c')
9
+ end
10
+
11
+ it 'with standard SObject types it does not add the "__c"' do
12
+ expect(table.new('Account').name).to eq('Account')
13
+ end
14
+
15
+ context 'with a namespace' do
16
+ it "the namespace is not included" do
17
+ expect(table.new('Foo::Bar').name).to eq('Bar__c')
18
+ end
19
+
20
+ it 'standard types are inferred correctly' do
21
+ expect(table.new('Foo::Account').name).to eq('Account')
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveForce do
4
+ it 'should have a version number' do
5
+ expect(ActiveForce::VERSION).to_not be_nil
6
+ end
7
+ end
@@ -0,0 +1,26 @@
1
+ attributes:
2
+ type: 'Whizbang'
3
+ url: '/services/data/v20.0/sobjects/Whizbang/foo'
4
+ Checkbox_Label: false
5
+ Text_Label: 'Hi there!'
6
+ Date_Label: '2010-01-01'
7
+ DateTime_Label: '2011-07-07T00:37:00.000+0000'
8
+ Picklist_Multiselect_Label: 'four;six'
9
+ Percent_Label: 20
10
+ Boolean_Label: true
11
+
12
+ ParentWhizbang__r:
13
+ Name: 'Parent Whizbang'
14
+ Text_Label: 'Hello'
15
+ attributes:
16
+ type: 'Whizbang'
17
+ url: '/services/data/v20.0/sobjects/Whizbang/bar'
18
+
19
+ Whizbangs__r:
20
+ totalSize: 1
21
+ done: true
22
+ records:
23
+ - attributes:
24
+ type: 'Whizbang__c'
25
+ url: '/services/data/v24.0/sobjects/Whizbang__c/a00E0000004D5lsIAC'
26
+ Name: 'Child Whizbang'
@@ -0,0 +1,16 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'active_force'
3
+ Dir["./spec/support/**/*"].sort.each { |f| require f }
4
+ require 'pry'
5
+
6
+ ActiveSupport::Inflector.inflections do |inflect|
7
+ inflect.plural 'quota', 'quotas'
8
+ inflect.plural 'Quota', 'Quotas'
9
+ inflect.singular 'quota', 'quota'
10
+ inflect.singular 'Quota', 'Quota'
11
+ end
12
+
13
+ RSpec.configure do |config|
14
+ config.order = :random
15
+ config.include RestforceFactories
16
+ end
@@ -0,0 +1,45 @@
1
+ module FixtureHelpers
2
+ module InstanceMethods
3
+
4
+ def stub_api_request(endpoint, options={})
5
+ options = {
6
+ :method => :get,
7
+ :status => 200,
8
+ :api_version => Restforce.configuration.api_version
9
+ }.merge(options)
10
+
11
+ stub = stub_request(options[:method], %r{/services/data/v#{options[:api_version]}/#{endpoint}})
12
+ stub = stub.with(:body => options[:with_body]) if options[:with_body] && !RUBY_VERSION.match(/^1.8/)
13
+ stub = stub.to_return(:status => options[:status], :body => fixture(options[:fixture]), :headers => { 'Content-Type' => 'application/json'}) if options[:fixture]
14
+ stub
15
+ end
16
+
17
+ def stub_login_request(options={})
18
+ stub = stub_request(:post, "https://login.salesforce.com/services/oauth2/token")
19
+ stub = stub.with(:body => options[:with_body]) if options[:with_body] && !RUBY_VERSION.match(/^1.8/)
20
+ stub
21
+ end
22
+
23
+ def fixture(f)
24
+ File.read(File.expand_path("../../fixtures/#{f}.yml", __FILE__))
25
+ end
26
+
27
+ end
28
+
29
+ module ClassMethods
30
+ def requests(endpoint, options={})
31
+ before do
32
+ (@requests ||= []) << stub_api_request(endpoint, options)
33
+ end
34
+
35
+ after do
36
+ @requests.each { |request| expect(request).to have_been_requested }
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ RSpec.configure do |config|
43
+ config.include FixtureHelpers::InstanceMethods
44
+ config.extend FixtureHelpers::ClassMethods
45
+ end
@@ -0,0 +1,9 @@
1
+ module RestforceFactories
2
+ def build_restforce_collection(array)
3
+ Restforce::Collection.new({ 'records' => array }, nil)
4
+ end
5
+
6
+ def build_restforce_sobject(hash)
7
+ Restforce::SObject.new(hash)
8
+ end
9
+ end
@@ -0,0 +1,97 @@
1
+
2
+ class Comment < ActiveForce::SObject
3
+ self.table_name = "Comment__c"
4
+ field :post_id, from: "PostId"
5
+ field :poster_id, from: 'PosterId__c'
6
+ field :fancy_post_id, from: 'FancyPostId'
7
+ field :body
8
+ belongs_to :post
9
+ end
10
+ class Post < ActiveForce::SObject
11
+ self.table_name = "Post__c"
12
+ field :title
13
+ has_many :comments
14
+ has_many :impossible_comments, model: Comment, scoped_as: ->{ where('1 = 0') }
15
+ has_many :reply_comments, model: Comment, scoped_as: ->(post){ where(body: "RE: #{post.title}").order('CreationDate DESC') }
16
+ has_many :ugly_comments, { model: Comment }
17
+ has_many :poster_comments, { foreign_key: :poster_id, model: Comment }
18
+ end
19
+ class Territory < ActiveForce::SObject
20
+ field :quota_id, from: "Quota__c"
21
+ field :name, from: 'Name'
22
+ belongs_to :quota
23
+ end
24
+ class PrezClub < ActiveForce::SObject
25
+ field :quota_id, from: 'QuotaId'
26
+ belongs_to :quota
27
+ end
28
+ class Quota < ActiveForce::SObject
29
+ field :id, from: 'Bar_Id__c'
30
+ has_many :prez_clubs
31
+ has_many :territories
32
+ end
33
+ class Opportunity < ActiveForce::SObject
34
+ field :account_id, from: 'AccountId'
35
+ belongs_to :account
36
+ end
37
+ class Account < ActiveForce::SObject
38
+ field :owner_id, from: 'OwnerId'
39
+ has_many :opportunities
40
+ belongs_to :owner
41
+ end
42
+ class Owner < ActiveForce::SObject
43
+ has_many :accounts
44
+ end
45
+ class Custom < ActiveForce::SObject; end
46
+ class EnforcedTableName < ActiveForce::SObject
47
+ self.table_name = 'Forced__c'
48
+ end
49
+
50
+ module Foo
51
+ class Bar < ActiveForce::SObject; end
52
+ class Opportunity < ActiveForce::SObject
53
+ field :account_id, from: 'AccountId'
54
+ field :partner_account_id, from: 'Partner_Account_Id__c'
55
+ end
56
+ class Account < ActiveForce::SObject
57
+ has_many :opportunities, model: Foo::Opportunity
58
+ has_many :partner_opportunities, foreign_key: :partner_account_id, model: Foo::Opportunity
59
+ end
60
+ class Lead < ActiveForce::SObject; end
61
+ class Attachment < ActiveForce::SObject
62
+ field :lead_id, from: 'Lead_Id__c'
63
+ field :fancy_lead_id, from: 'LeadId'
64
+ belongs_to :lead, model: Foo::Lead
65
+ belongs_to :fancy_lead, model: Foo::Lead, foreign_key: :fancy_lead_id
66
+ end
67
+ end
68
+
69
+ module Salesforce
70
+ class PrezClub < ActiveForce::SObject
71
+ field :quota_id, from: 'QuotaId'
72
+ end
73
+ class Quota < ActiveForce::SObject
74
+ has_many :prez_clubs, model: PrezClub
75
+ end
76
+ class Widget < ActiveForce::SObject
77
+ self.table_name = 'Tegdiw__c'
78
+ end
79
+ class Territory < ActiveForce::SObject
80
+ field :quota_id, from: "QuotaId"
81
+ field :widget_id, from: 'WidgetId'
82
+ belongs_to :quota, model: Salesforce::Quota, foreign_key: :quota_id
83
+ belongs_to :widget, model: Salesforce::Widget, foreign_key: :widget_id
84
+ end
85
+ class User < ActiveForce::SObject
86
+ end
87
+ class Opportunity < ActiveForce::SObject
88
+ field :owner_id, from: 'OwnerId'
89
+ field :account_id, from: 'AccountId'
90
+ field :business_partner
91
+ belongs_to :owner, model: Salesforce::User, foreign_key: :owner_id, relationship_name: 'Owner'
92
+ end
93
+ class Account < ActiveForce::SObject
94
+ field :business_partner
95
+ has_many :partner_opportunities, model: Opportunity, scoped_as: ->(account){ where(business_partner: account.business_partner).includes(:owner) }
96
+ end
97
+ end
@@ -0,0 +1,30 @@
1
+ class Whizbang < ActiveForce::SObject
2
+
3
+ field :id, from: 'Id'
4
+ field :checkbox, from: 'Checkbox_Label', as: :boolean
5
+ field :text, from: 'Text_Label'
6
+ field :date, from: 'Date_Label', as: :date
7
+ field :datetime, from: 'DateTime_Label', as: :datetime
8
+ field :picklist_multiselect, from: 'Picklist_Multiselect_Label', as: :multipicklist
9
+ field :boolean, from: 'Boolean_Label', as: :boolean
10
+ field :percent, from: 'Percent_Label', as: :percent
11
+ field :estimated_close_date, as: :datetime
12
+ field :updated_from, as: :datetime
13
+ field :dirty_attribute, as: :boolean
14
+
15
+ before_save :set_as_updated_from_rails
16
+ after_save :mark_dirty
17
+
18
+ validates :percent, presence: true, if: :boolean
19
+
20
+ private
21
+
22
+ def set_as_updated_from_rails
23
+ self.updated_from = 'Rails'
24
+ end
25
+
26
+ def mark_dirty
27
+ self.dirty_attribute = true
28
+ end
29
+
30
+ end