openstax_active_force 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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