restforce-db 1.0.4 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +38 -22
  3. data/lib/restforce/db/associations/base.rb +152 -0
  4. data/lib/restforce/db/associations/belongs_to.rb +70 -0
  5. data/lib/restforce/db/associations/foreign_key.rb +80 -0
  6. data/lib/restforce/db/associations/has_many.rb +37 -0
  7. data/lib/restforce/db/associations/has_one.rb +33 -0
  8. data/lib/restforce/db/dsl.rb +25 -13
  9. data/lib/restforce/db/mapping.rb +2 -3
  10. data/lib/restforce/db/record_types/active_record.rb +3 -8
  11. data/lib/restforce/db/record_types/salesforce.rb +23 -8
  12. data/lib/restforce/db/strategies/associated.rb +58 -0
  13. data/lib/restforce/db/version.rb +1 -1
  14. data/lib/restforce/db.rb +6 -1
  15. data/restforce-db.gemspec +0 -1
  16. data/test/cassettes/Restforce_DB_Associations_BelongsTo/with_an_inverse_mapping/_build/returns_an_associated_record_populated_with_the_Salesforce_attributes.yml +271 -0
  17. data/test/cassettes/Restforce_DB_Associations_BelongsTo/with_an_inverse_mapping/_synced_for_/when_a_matching_associated_record_has_been_synchronized/returns_true.yml +271 -0
  18. data/test/cassettes/Restforce_DB_Associations_BelongsTo/with_an_inverse_mapping/_synced_for_/when_no_matching_associated_record_has_been_synchronized/returns_false.yml +271 -0
  19. data/test/cassettes/Restforce_DB_Associations_HasMany/with_an_inverse_mapping/_build/builds_a_number_of_associated_records_from_the_data_in_Salesforce.yml +439 -0
  20. data/test/cassettes/Restforce_DB_Associations_HasMany/with_an_inverse_mapping/_synced_for_/when_a_matching_associated_record_has_been_synchronized/returns_true.yml +439 -0
  21. data/test/cassettes/Restforce_DB_Associations_HasMany/with_an_inverse_mapping/_synced_for_/when_no_matching_associated_record_has_been_synchronized/returns_false.yml +196 -0
  22. data/test/cassettes/Restforce_DB_Associations_HasOne/with_an_inverse_mapping/_build/returns_an_associated_record_populated_with_the_Salesforce_attributes.yml +271 -0
  23. data/test/cassettes/Restforce_DB_Associations_HasOne/with_an_inverse_mapping/_synced_for_/when_a_matching_associated_record_has_been_synchronized/returns_true.yml +271 -0
  24. data/test/cassettes/Restforce_DB_Associations_HasOne/with_an_inverse_mapping/_synced_for_/when_no_matching_associated_record_has_been_synchronized/returns_false.yml +271 -0
  25. data/test/cassettes/Restforce_DB_Strategies_Associated/_build_/given_an_inverse_mapping/with_a_synchronized_association_record/wants_to_build_a_new_record.yml +275 -0
  26. data/test/cassettes/Restforce_DB_Strategies_Associated/_build_/given_an_inverse_mapping/with_an_existing_database_record/does_not_want_to_build_a_new_record.yml +237 -0
  27. data/test/cassettes/Restforce_DB_Strategies_Associated/_build_/given_an_inverse_mapping/with_no_synchronized_association_record/does_not_want_to_build_a_new_record.yml +275 -0
  28. data/test/lib/restforce/db/associations/belongs_to_test.rb +82 -0
  29. data/test/lib/restforce/db/associations/has_many_test.rb +96 -0
  30. data/test/lib/restforce/db/associations/has_one_test.rb +80 -0
  31. data/test/lib/restforce/db/dsl_test.rb +22 -3
  32. data/test/lib/restforce/db/mapping_test.rb +10 -8
  33. data/test/lib/restforce/db/record_types/active_record_test.rb +12 -5
  34. data/test/lib/restforce/db/runner_test.rb +2 -3
  35. data/test/lib/restforce/db/strategies/always_test.rb +2 -2
  36. data/test/lib/restforce/db/strategies/associated_test.rb +78 -0
  37. data/test/lib/restforce/db/strategies/passive_test.rb +1 -1
  38. data/test/lib/restforce/db/tracker_test.rb +0 -2
  39. data/test/support/active_record.rb +25 -2
  40. data/test/support/salesforce.rb +1 -1
  41. data/test/support/utilities.rb +5 -7
  42. data/test/test_helper.rb +0 -1
  43. metadata +24 -18
  44. data/lib/restforce/db/associations/active_record.rb +0 -68
  45. data/test/lib/restforce/db/associations/active_record_test.rb +0 -43
@@ -0,0 +1,80 @@
1
+ require_relative "../../../../test_helper"
2
+
3
+ describe Restforce::DB::Associations::HasOne do
4
+
5
+ configure!
6
+ mappings!
7
+
8
+ let(:association) { Restforce::DB::Associations::HasOne.new(:custom_object, through: "Friend__c") }
9
+
10
+ it "sets the lookup field" do
11
+ expect(association.lookup).to_equal "Friend__c"
12
+ end
13
+
14
+ describe "#fields" do
15
+
16
+ it "returns nothing (since the lookup field is external)" do
17
+ expect(association.fields).to_equal []
18
+ end
19
+ end
20
+
21
+ describe "with an inverse mapping", :vcr do
22
+ let(:inverse_mapping) do
23
+ Restforce::DB::Mapping.new(User, "Contact").tap do |m|
24
+ m.fields = { email: "Email" }
25
+ m.associations << association
26
+ end
27
+ end
28
+ let(:user_salesforce_id) do
29
+ Salesforce.create!(
30
+ inverse_mapping.salesforce_model,
31
+ "Email" => "somebody@example.com",
32
+ "LastName" => "Somebody",
33
+ )
34
+ end
35
+ let(:object_salesforce_id) do
36
+ Salesforce.create!(mapping.salesforce_model, "Friend__c" => user_salesforce_id)
37
+ end
38
+
39
+ before do
40
+ Restforce::DB::Registry << mapping
41
+ Restforce::DB::Registry << inverse_mapping
42
+ mapping.associations << Restforce::DB::Associations::BelongsTo.new(:user, through: "Friend__c")
43
+
44
+ object_salesforce_id
45
+ end
46
+
47
+ describe "#synced_for?" do
48
+ let(:salesforce_instance) { inverse_mapping.salesforce_record_type.find(user_salesforce_id) }
49
+
50
+ describe "when no matching associated record has been synchronized" do
51
+
52
+ it "returns false" do
53
+ expect(association).to_not_be :synced_for?, salesforce_instance
54
+ end
55
+ end
56
+
57
+ describe "when a matching associated record has been synchronized" do
58
+ before do
59
+ mapping.database_model.create!(salesforce_id: object_salesforce_id)
60
+ end
61
+
62
+ it "returns true" do
63
+ expect(association).to_be :synced_for?, salesforce_instance
64
+ end
65
+ end
66
+ end
67
+
68
+ describe "#build" do
69
+ let(:database_record) { User.new }
70
+ let(:salesforce_record) { inverse_mapping.salesforce_record_type.find(user_salesforce_id).record }
71
+ let(:associated) { association.build(database_record, salesforce_record) }
72
+
73
+ it "returns an associated record, populated with the Salesforce attributes" do
74
+ expect(associated.user).to_equal database_record
75
+ expect(associated.salesforce_id).to_equal object_salesforce_id
76
+ end
77
+ end
78
+ end
79
+
80
+ end
@@ -52,7 +52,10 @@ describe Restforce::DB::DSL do
52
52
  end
53
53
 
54
54
  it "adds an association to the created mapping" do
55
- expect(mapping.associations).to_equal some_association: "Some_Field__c"
55
+ association = mapping.associations.first
56
+ expect(association).to_be_instance_of Restforce::DB::Associations::BelongsTo
57
+ expect(association.name).to_equal :some_association
58
+ expect(association.lookup).to_equal "Some_Field__c"
56
59
  end
57
60
  end
58
61
 
@@ -61,8 +64,24 @@ describe Restforce::DB::DSL do
61
64
  dsl.has_one :other_association, through: "Other_Field__c"
62
65
  end
63
66
 
64
- it "sets the `through` attribute for the created mapping" do
65
- expect(mapping.through).to_equal "Other_Field__c"
67
+ it "adds an association to the created mapping" do
68
+ association = mapping.associations.first
69
+ expect(association).to_be_instance_of Restforce::DB::Associations::HasOne
70
+ expect(association.name).to_equal :other_association
71
+ expect(association.lookup).to_equal "Other_Field__c"
72
+ end
73
+ end
74
+
75
+ describe "#has_many" do
76
+ before do
77
+ dsl.has_many :multiple_associations, through: "External_Field__c"
78
+ end
79
+
80
+ it "adds an association to the created mapping" do
81
+ association = mapping.associations.first
82
+ expect(association).to_be_instance_of Restforce::DB::Associations::HasMany
83
+ expect(association.name).to_equal :multiple_associations
84
+ expect(association.lookup).to_equal "External_Field__c"
66
85
  end
67
86
  end
68
87
 
@@ -6,19 +6,15 @@ describe Restforce::DB::Mapping do
6
6
 
7
7
  let(:database_model) { CustomObject }
8
8
  let(:salesforce_model) { "CustomObject__c" }
9
- let(:associations) { {} }
10
9
  let(:fields) do
11
10
  {
12
11
  column_one: "SF_Field_One__c",
13
12
  column_two: "SF_Field_Two__c",
14
13
  }
15
14
  end
16
- let(:through) { nil }
17
- let!(:mapping) do
15
+ let(:mapping) do
18
16
  Restforce::DB::Mapping.new(database_model, salesforce_model).tap do |m|
19
- m.fields = fields
20
- m.associations = associations
21
- m.through = through
17
+ m.fields = fields
22
18
  end
23
19
  end
24
20
 
@@ -39,10 +35,16 @@ describe Restforce::DB::Mapping do
39
35
  end
40
36
 
41
37
  describe "given a set of associations" do
42
- let(:associations) { { user: "Owner" } }
38
+ let(:association) do
39
+ Restforce::DB::Associations::BelongsTo.new(:user, through: "Owner")
40
+ end
41
+
42
+ before do
43
+ mapping.associations << association
44
+ end
43
45
 
44
46
  it "lists the field and association lookups" do
45
- expect(mapping.salesforce_fields).to_equal(fields.values + associations.values)
47
+ expect(mapping.salesforce_fields).to_equal(fields.values + association.fields)
46
48
  end
47
49
  end
48
50
  end
@@ -40,16 +40,23 @@ describe Restforce::DB::RecordTypes::ActiveRecord do
40
40
  describe "given a mapped association" do
41
41
  let(:record) { Struct.new(:Friend__c).new(association_id) }
42
42
  let(:association_id) { "a001a000001EFRIEND" }
43
- let(:associations) { { user: "Friend__c" } }
44
43
 
45
44
  before do
46
- mapping = Restforce::DB::Mapping.new(User, "Contact").tap do |m|
47
- m.through = "Friend__c"
45
+ mapping.associations << Restforce::DB::Associations::BelongsTo.new(
46
+ :user,
47
+ through: "Friend__c",
48
+ )
49
+
50
+ associated_mapping = Restforce::DB::Mapping.new(User, "Contact").tap do |m|
48
51
  m.fields = { email: "Email" }
52
+ m.associations << Restforce::DB::Associations::HasOne.new(
53
+ :custom_object,
54
+ through: "Friend__c",
55
+ )
49
56
  end
50
- Restforce::DB::Registry << mapping
57
+ Restforce::DB::Registry << associated_mapping
51
58
 
52
- salesforce_record_type = mapping.salesforce_record_type
59
+ salesforce_record_type = associated_mapping.salesforce_record_type
53
60
 
54
61
  # Stub out the `#find` method on the record type
55
62
  def salesforce_record_type.find(id)
@@ -1,12 +1,10 @@
1
1
  require_relative "../../../test_helper"
2
2
 
3
3
  describe Restforce::DB::Runner do
4
- before { Restforce::DB.last_run = Time.now }
5
- after { Restforce::DB.last_run = nil }
6
-
7
4
  let(:runner) { Restforce::DB::Runner.new }
8
5
 
9
6
  describe "#initialize" do
7
+ before { Restforce::DB.last_run = Time.now }
10
8
 
11
9
  it "prefills the Collector's last_run timestamp with the global configuration" do
12
10
  expect(runner.last_run).to_equal Restforce::DB.last_run
@@ -14,6 +12,7 @@ describe Restforce::DB::Runner do
14
12
  end
15
13
 
16
14
  describe "#tick!" do
15
+ before { Restforce::DB.last_run = Time.now }
17
16
 
18
17
  it "updates the run timestamps" do
19
18
  prior_run = runner.last_run
@@ -18,7 +18,7 @@ describe Restforce::DB::Strategies::Always do
18
18
  let(:record) { mapping.salesforce_record_type.find(salesforce_id) }
19
19
 
20
20
  it "wants to build a new matching record" do
21
- expect(strategy.build?(record)).to_equal true
21
+ expect(strategy).to_be :build?, record
22
22
  end
23
23
 
24
24
  describe "with a corresponding database record" do
@@ -30,7 +30,7 @@ describe Restforce::DB::Strategies::Always do
30
30
  end
31
31
 
32
32
  it "does not want to build a new record" do
33
- expect(strategy.build?(record)).to_equal false
33
+ expect(strategy).to_not_be :build?, record
34
34
  end
35
35
  end
36
36
  end
@@ -0,0 +1,78 @@
1
+ require_relative "../../../../test_helper"
2
+
3
+ describe Restforce::DB::Strategies::Associated do
4
+
5
+ configure!
6
+ mappings!
7
+
8
+ let(:strategy) { Restforce::DB::Strategies::Associated.new(with: :custom_object) }
9
+
10
+ it "is not a passive strategy" do
11
+ expect(strategy).to_not_be :passive?
12
+ end
13
+
14
+ describe "#build?", :vcr do
15
+
16
+ describe "given an inverse mapping" do
17
+ let(:inverse_mapping) do
18
+ Restforce::DB::Mapping.new(Detail, "CustomObjectDetail__c").tap do |m|
19
+ m.fields = { name: "Name" }
20
+ m.associations << Restforce::DB::Associations::BelongsTo.new(
21
+ :custom_object,
22
+ through: "CustomObject__c",
23
+ )
24
+ end
25
+ end
26
+ let(:object_salesforce_id) { Salesforce.create!(mapping.salesforce_model) }
27
+ let(:detail_salesforce_id) do
28
+ Salesforce.create!(
29
+ inverse_mapping.salesforce_model,
30
+ "CustomObject__c" => object_salesforce_id,
31
+ )
32
+ end
33
+ let(:record) { inverse_mapping.salesforce_record_type.find(detail_salesforce_id) }
34
+
35
+ before do
36
+ Restforce::DB::Registry << mapping
37
+ Restforce::DB::Registry << inverse_mapping
38
+ mapping.associations << Restforce::DB::Associations::HasMany.new(
39
+ :details,
40
+ through: "CustomObject__c",
41
+ )
42
+ end
43
+
44
+ describe "with no synchronized association record" do
45
+
46
+ it "does not want to build a new record" do
47
+ expect(strategy).to_not_be :build?, record
48
+ end
49
+ end
50
+
51
+ describe "with an existing database record" do
52
+ before do
53
+ Detail.create!(
54
+ salesforce_id: detail_salesforce_id,
55
+ synchronized_at: Time.now,
56
+ )
57
+ end
58
+
59
+ it "does not want to build a new record" do
60
+ expect(strategy).to_not_be :build?, record
61
+ end
62
+ end
63
+
64
+ describe "with a synchronized association record" do
65
+ before do
66
+ CustomObject.create!(
67
+ salesforce_id: object_salesforce_id,
68
+ synchronized_at: Time.now,
69
+ )
70
+ end
71
+
72
+ it "wants to build a new record" do
73
+ expect(strategy).to_be :build?, record
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -11,7 +11,7 @@ describe Restforce::DB::Strategies::Passive do
11
11
 
12
12
  it "returns false for any record" do
13
13
  record = Object.new
14
- expect(strategy.build?(record)).to_equal false
14
+ expect(strategy).to_not_be :build?, record
15
15
  end
16
16
  end
17
17
  end
@@ -5,8 +5,6 @@ describe Restforce::DB::Tracker do
5
5
  let(:tracker) { Restforce::DB::Tracker.new(file.path) }
6
6
  let(:runtime) { Time.now }
7
7
 
8
- after { Restforce::DB.last_run = nil }
9
-
10
8
  describe "#initialize" do
11
9
 
12
10
  describe "when no timestamp has been recorded" do
@@ -19,6 +19,16 @@ ActiveRecord::Schema.define do
19
19
 
20
20
  add_index :custom_objects, :salesforce_id
21
21
 
22
+ create_table :details do |table|
23
+ table.column :name, :string
24
+ table.column :custom_object_id, :integer
25
+ table.column :salesforce_id, :string
26
+ table.column :synchronized_at, :datetime
27
+ table.timestamps null: false
28
+ end
29
+
30
+ add_index :details, :salesforce_id
31
+
22
32
  create_table :users do |table|
23
33
  table.column :email, :string
24
34
  table.column :salesforce_id, :string
@@ -33,8 +43,21 @@ end
33
43
  # :nodoc:
34
44
  class CustomObject < ActiveRecord::Base
35
45
 
36
- belongs_to :user
46
+ belongs_to :user, inverse_of: :custom_object, autosave: true
47
+ has_many :details, inverse_of: :custom_object
37
48
 
38
49
  end
39
50
 
40
- class User < ActiveRecord::Base; end
51
+ # :nodoc:
52
+ class Detail < ActiveRecord::Base
53
+
54
+ belongs_to :custom_object, inverse_of: :details
55
+
56
+ end
57
+
58
+ # :nodoc:
59
+ class User < ActiveRecord::Base
60
+
61
+ has_one :custom_object, inverse_of: :user
62
+
63
+ end
@@ -24,7 +24,7 @@ class Salesforce
24
24
  # Returns a Salesforce record ID.
25
25
  def self.create!(salesforce_model, attributes = nil)
26
26
  attributes ||= { "Name" => "Sample object" }
27
- salesforce_id = Restforce::DB.client.create(salesforce_model, attributes)
27
+ salesforce_id = Restforce::DB.client.create!(salesforce_model, attributes)
28
28
  Salesforce.records << [salesforce_model, salesforce_id]
29
29
 
30
30
  salesforce_id
@@ -6,6 +6,8 @@ def configure!
6
6
 
7
7
  after do
8
8
  Restforce::DB::Registry.clean!
9
+ Restforce::DB.last_run = nil
10
+
9
11
  DatabaseCleaner.clean
10
12
  Salesforce.clean!
11
13
  end
@@ -16,15 +18,11 @@ def mappings!
16
18
  let(:database_model) { CustomObject }
17
19
  let(:salesforce_model) { "CustomObject__c" }
18
20
  let(:fields) { { name: "Name", example: "Example_Field__c" } }
19
- let(:associations) { {} }
20
21
  let(:conditions) { [] }
21
- let(:through) { nil }
22
- let!(:mapping) do
22
+ let(:mapping) do
23
23
  Restforce::DB::Mapping.new(database_model, salesforce_model).tap do |m|
24
- m.through = through
25
- m.conditions = conditions
26
- m.fields = fields
27
- m.associations = associations
24
+ m.conditions = conditions
25
+ m.fields = fields
28
26
  end
29
27
  end
30
28
  end
data/test/test_helper.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require "cgi"
2
2
  require "minitest/autorun"
3
- require "minitest/bang"
4
3
  require "minitest/spec/expect"
5
4
  require "yaml"
6
5
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: restforce-db
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Horner
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-04-10 00:00:00.000000000 Z
11
+ date: 2015-04-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -94,20 +94,6 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
- - !ruby/object:Gem::Dependency
98
- name: minitest-bang
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: '0'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - ">="
109
- - !ruby/object:Gem::Version
110
- version: '0'
111
97
  - !ruby/object:Gem::Dependency
112
98
  name: minitest-spec-expect
113
99
  requirement: !ruby/object:Gem::Requirement
@@ -222,7 +208,11 @@ files:
222
208
  - lib/generators/templates/script
223
209
  - lib/restforce/db.rb
224
210
  - lib/restforce/db/accumulator.rb
225
- - lib/restforce/db/associations/active_record.rb
211
+ - lib/restforce/db/associations/base.rb
212
+ - lib/restforce/db/associations/belongs_to.rb
213
+ - lib/restforce/db/associations/foreign_key.rb
214
+ - lib/restforce/db/associations/has_many.rb
215
+ - lib/restforce/db/associations/has_one.rb
226
216
  - lib/restforce/db/attribute_map.rb
227
217
  - lib/restforce/db/collector.rb
228
218
  - lib/restforce/db/command.rb
@@ -240,6 +230,7 @@ files:
240
230
  - lib/restforce/db/registry.rb
241
231
  - lib/restforce/db/runner.rb
242
232
  - lib/restforce/db/strategies/always.rb
233
+ - lib/restforce/db/strategies/associated.rb
243
234
  - lib/restforce/db/strategies/passive.rb
244
235
  - lib/restforce/db/strategy.rb
245
236
  - lib/restforce/db/synchronizer.rb
@@ -249,6 +240,15 @@ files:
249
240
  - lib/restforce/extensions.rb
250
241
  - restforce-db.gemspec
251
242
  - test/cassettes/Restforce_DB/accessing_Salesforce/uses_the_configured_credentials.yml
243
+ - test/cassettes/Restforce_DB_Associations_BelongsTo/with_an_inverse_mapping/_build/returns_an_associated_record_populated_with_the_Salesforce_attributes.yml
244
+ - test/cassettes/Restforce_DB_Associations_BelongsTo/with_an_inverse_mapping/_synced_for_/when_a_matching_associated_record_has_been_synchronized/returns_true.yml
245
+ - test/cassettes/Restforce_DB_Associations_BelongsTo/with_an_inverse_mapping/_synced_for_/when_no_matching_associated_record_has_been_synchronized/returns_false.yml
246
+ - test/cassettes/Restforce_DB_Associations_HasMany/with_an_inverse_mapping/_build/builds_a_number_of_associated_records_from_the_data_in_Salesforce.yml
247
+ - test/cassettes/Restforce_DB_Associations_HasMany/with_an_inverse_mapping/_synced_for_/when_a_matching_associated_record_has_been_synchronized/returns_true.yml
248
+ - test/cassettes/Restforce_DB_Associations_HasMany/with_an_inverse_mapping/_synced_for_/when_no_matching_associated_record_has_been_synchronized/returns_false.yml
249
+ - test/cassettes/Restforce_DB_Associations_HasOne/with_an_inverse_mapping/_build/returns_an_associated_record_populated_with_the_Salesforce_attributes.yml
250
+ - test/cassettes/Restforce_DB_Associations_HasOne/with_an_inverse_mapping/_synced_for_/when_a_matching_associated_record_has_been_synchronized/returns_true.yml
251
+ - test/cassettes/Restforce_DB_Associations_HasOne/with_an_inverse_mapping/_synced_for_/when_no_matching_associated_record_has_been_synchronized/returns_false.yml
252
252
  - test/cassettes/Restforce_DB_Collector/_run/given_a_Salesforce_record_with_an_associated_database_record/returns_the_attributes_from_both_records.yml
253
253
  - test/cassettes/Restforce_DB_Collector/_run/given_an_existing_Salesforce_record/returns_the_attributes_from_the_Salesforce_record.yml
254
254
  - test/cassettes/Restforce_DB_Collector/_run/given_an_existing_database_record/returns_the_attributes_from_the_database_record.yml
@@ -269,11 +269,16 @@ files:
269
269
  - test/cassettes/Restforce_DB_RecordTypes_Salesforce/_find/returns_nil_when_no_matching_record_exists.yml
270
270
  - test/cassettes/Restforce_DB_Strategies_Always/_build_/given_a_Salesforce_record/wants_to_build_a_new_matching_record.yml
271
271
  - test/cassettes/Restforce_DB_Strategies_Always/_build_/given_a_Salesforce_record/with_a_corresponding_database_record/does_not_want_to_build_a_new_record.yml
272
+ - test/cassettes/Restforce_DB_Strategies_Associated/_build_/given_an_inverse_mapping/with_a_synchronized_association_record/wants_to_build_a_new_record.yml
273
+ - test/cassettes/Restforce_DB_Strategies_Associated/_build_/given_an_inverse_mapping/with_an_existing_database_record/does_not_want_to_build_a_new_record.yml
274
+ - test/cassettes/Restforce_DB_Strategies_Associated/_build_/given_an_inverse_mapping/with_no_synchronized_association_record/does_not_want_to_build_a_new_record.yml
272
275
  - test/cassettes/Restforce_DB_Synchronizer/_run/given_a_Salesforce_record_with_an_associated_database_record/updates_the_database_record.yml
273
276
  - test/cassettes/Restforce_DB_Synchronizer/_run/given_a_Salesforce_record_with_an_associated_database_record/updates_the_salesforce_record.yml
274
277
  - test/cassettes/Restforce_DB_Synchronizer/_run/given_a_Salesforce_record_with_no_associated_database_record/does_nothing_for_this_specific_mapping.yml
275
278
  - test/lib/restforce/db/accumulator_test.rb
276
- - test/lib/restforce/db/associations/active_record_test.rb
279
+ - test/lib/restforce/db/associations/belongs_to_test.rb
280
+ - test/lib/restforce/db/associations/has_many_test.rb
281
+ - test/lib/restforce/db/associations/has_one_test.rb
277
282
  - test/lib/restforce/db/attribute_map_test.rb
278
283
  - test/lib/restforce/db/collector_test.rb
279
284
  - test/lib/restforce/db/configuration_test.rb
@@ -288,6 +293,7 @@ files:
288
293
  - test/lib/restforce/db/registry_test.rb
289
294
  - test/lib/restforce/db/runner_test.rb
290
295
  - test/lib/restforce/db/strategies/always_test.rb
296
+ - test/lib/restforce/db/strategies/associated_test.rb
291
297
  - test/lib/restforce/db/strategies/passive_test.rb
292
298
  - test/lib/restforce/db/strategy_test.rb
293
299
  - test/lib/restforce/db/synchronizer_test.rb
@@ -1,68 +0,0 @@
1
- module Restforce
2
-
3
- module DB
4
-
5
- module Associations
6
-
7
- # Restforce::DB::Associations::ActiveRecord is a utility class which
8
- # encapsulates the logic for creating/populating a one-to-one association
9
- #
10
- class ActiveRecord
11
-
12
- attr_reader :associated
13
-
14
- # Public: Initialize a new Restforce::DB::Associations::ActiveRecord.
15
- #
16
- # record - The base ActiveRecord::Base instance for which the
17
- # association should be built.
18
- # association - The name of the association which should be built.
19
- def initialize(record, association)
20
- @record = record
21
- @associated = record.association(association).build
22
- end
23
-
24
- # Public: Build the associated record from the attributes found on the
25
- # passed Salesforce record's lookups.
26
- #
27
- # from_record - A Hashie::Mash representing a base Salesforce record.
28
- #
29
- # Returns the constructed associated record.
30
- def build(from_record)
31
- Registry[associated.class].each do |mapping|
32
- lookup_id = from_record[mapping.through]
33
- apply(mapping, lookup_id)
34
- end
35
-
36
- associated
37
- end
38
-
39
- private
40
-
41
- # Internal: Assemble the associated record, using the data from the
42
- # Salesforce record corresponding to a specific lookup ID.
43
- #
44
- # TODO: With some refactoring, this should be possible to handle as a
45
- # recursive call to the configured Mapping's database record type. Right
46
- # now, nested associations are unhandled.
47
- #
48
- # mapping - A Restforce::DB::Mapping.
49
- # lookup_id - A Salesforce ID corresponding to the record type in the
50
- # passed Mapping.
51
- #
52
- # Returns nothing.
53
- def apply(mapping, lookup_id)
54
- return if lookup_id.nil?
55
-
56
- salesforce_instance = mapping.salesforce_record_type.find(lookup_id)
57
- attributes = mapping.convert(associated.class, salesforce_instance.attributes)
58
-
59
- associated.assign_attributes(attributes.merge(mapping.lookup_column => lookup_id))
60
- end
61
-
62
- end
63
-
64
- end
65
-
66
- end
67
-
68
- end
@@ -1,43 +0,0 @@
1
- require_relative "../../../../test_helper"
2
-
3
- describe Restforce::DB::Associations::ActiveRecord do
4
-
5
- configure!
6
- mappings!
7
-
8
- let(:associations) { { user: "Friend__c" } }
9
- let(:record) { CustomObject.new }
10
- let(:association) { Restforce::DB::Associations::ActiveRecord.new(record, :user) }
11
-
12
- describe "#build" do
13
- let(:association_id) { "a001a000001EFRIEND" }
14
- let(:salesforce_record) { Hashie::Mash.new("Friend__c" => association_id) }
15
- let(:associated_record) { association.build(salesforce_record) }
16
-
17
- before do
18
- mapping = Restforce::DB::Mapping.new(User, "Contact").tap do |m|
19
- m.through = "Friend__c"
20
- m.fields = { email: "Email" }
21
- end
22
- Restforce::DB::Registry << mapping
23
-
24
- salesforce_record_type = mapping.salesforce_record_type
25
-
26
- # Stub out the `#find` method on the record type
27
- def salesforce_record_type.find(id)
28
- Struct.new(:id, :last_update, :attributes).new(
29
- id,
30
- Time.now,
31
- email: "somebody@example.com",
32
- )
33
- end
34
- end
35
-
36
- it "creates the associated record from the related Salesforce record's attributes" do
37
- expect(associated_record).to_not_be_nil
38
- expect(associated_record.email).to_equal("somebody@example.com")
39
- expect(associated_record.salesforce_id).to_equal(association_id)
40
- expect(associated_record).to_equal record.user
41
- end
42
- end
43
- end