restforce-db 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +39 -8
  3. data/lib/restforce/db/instances/active_record.rb +0 -9
  4. data/lib/restforce/db/instances/base.rb +7 -5
  5. data/lib/restforce/db/instances/salesforce.rb +0 -9
  6. data/lib/restforce/db/mapping.rb +91 -31
  7. data/lib/restforce/db/model.rb +5 -20
  8. data/lib/restforce/db/record_types/active_record.rb +38 -5
  9. data/lib/restforce/db/record_types/base.rb +2 -2
  10. data/lib/restforce/db/record_types/salesforce.rb +3 -3
  11. data/lib/restforce/db/version.rb +1 -1
  12. data/lib/restforce/db/worker.rb +7 -10
  13. data/lib/restforce/db.rb +0 -1
  14. data/test/cassettes/Restforce_DB/accessing_Salesforce/uses_the_configured_credentials.yml +5 -5
  15. data/test/cassettes/Restforce_DB_Instances_Salesforce/_copy_/updates_the_record_with_the_attributes_from_the_copied_object.yml +36 -35
  16. data/test/cassettes/Restforce_DB_Instances_Salesforce/_update_/updates_the_local_record_with_the_passed_attributes.yml +34 -33
  17. data/test/cassettes/Restforce_DB_Instances_Salesforce/_update_/updates_the_record_in_Salesforce_with_the_passed_attributes.yml +45 -44
  18. data/test/cassettes/Restforce_DB_RecordTypes_Salesforce/_create_/creates_a_record_in_Salesforce_from_the_passed_database_record_s_attributes.yml +28 -28
  19. data/test/cassettes/Restforce_DB_RecordTypes_Salesforce/_create_/updates_the_database_record_with_the_Salesforce_record_s_ID.yml +28 -28
  20. data/test/cassettes/Restforce_DB_RecordTypes_Salesforce/_find/finds_existing_records_in_Salesforce.yml +29 -28
  21. data/test/cassettes/Restforce_DB_RecordTypes_Salesforce/_find/returns_nil_when_no_matching_record_exists.yml +12 -12
  22. data/test/cassettes/Restforce_DB_Synchronizer/_run/given_a_Salesforce_record_with_an_associated_database_record/when_synchronization_is_stale/updates_the_database_record.yml +36 -36
  23. data/test/cassettes/Restforce_DB_Synchronizer/_run/given_a_Salesforce_record_with_an_associated_database_record/when_synchronization_is_up-to-date/does_not_update_the_database_record.yml +28 -104
  24. data/test/cassettes/Restforce_DB_Synchronizer/_run/given_an_existing_Salesforce_record/{populates_the_database_with_the_new_record.yml → for_a_non-root_mapping/does_not_create_a_database_record.yml} +28 -28
  25. data/test/cassettes/Restforce_DB_Synchronizer/_run/given_an_existing_Salesforce_record/for_a_root_mapping/creates_a_matching_database_record.yml +158 -0
  26. data/test/cassettes/Restforce_DB_Synchronizer/_run/given_an_existing_database_record/populates_Salesforce_with_the_new_record.yml +44 -44
  27. data/test/lib/restforce/db/instances/active_record_test.rb +4 -2
  28. data/test/lib/restforce/db/instances/salesforce_test.rb +5 -6
  29. data/test/lib/restforce/db/mapping_test.rb +40 -15
  30. data/test/lib/restforce/db/model_test.rb +6 -23
  31. data/test/lib/restforce/db/record_types/active_record_test.rb +45 -17
  32. data/test/lib/restforce/db/record_types/salesforce_test.rb +6 -7
  33. data/test/lib/restforce/db/synchronizer_test.rb +31 -17
  34. data/test/support/active_record.rb +20 -1
  35. data/test/support/utilities.rb +29 -0
  36. data/test/test_helper.rb +0 -12
  37. metadata +4 -4
  38. data/lib/restforce/db/record_type.rb +0 -77
  39. data/test/lib/restforce/db/record_type_test.rb +0 -26
@@ -3,9 +3,9 @@ require_relative "../../../../test_helper"
3
3
  describe Restforce::DB::RecordTypes::ActiveRecord do
4
4
 
5
5
  configure!
6
+ mappings!
6
7
 
7
- let(:mapping) { Restforce::DB::Mapping.new }
8
- let(:record_type) { Restforce::DB::RecordTypes::ActiveRecord.new(CustomObject, mapping) }
8
+ let(:record_type) { mapping.database_record_type }
9
9
  let(:salesforce_id) { "a001a000001E1vREAL" }
10
10
 
11
11
  describe "#sync!" do
@@ -19,10 +19,6 @@ describe Restforce::DB::RecordTypes::ActiveRecord do
19
19
  end
20
20
  let(:instance) { record_type.sync!(sync_from).record }
21
21
 
22
- before do
23
- mapping.add_mappings(name: "Name", example: "Example_Field__c")
24
- end
25
-
26
22
  describe "without an existing database record" do
27
23
 
28
24
  it "creates a new database record from the passed Salesforce record" do
@@ -35,7 +31,7 @@ describe Restforce::DB::RecordTypes::ActiveRecord do
35
31
 
36
32
  describe "with an existing database record" do
37
33
  let!(:sync_to) do
38
- CustomObject.create!(
34
+ database_model.create!(
39
35
  name: "Existing name",
40
36
  example: "Existing sample text",
41
37
  salesforce_id: salesforce_id,
@@ -53,32 +49,64 @@ describe Restforce::DB::RecordTypes::ActiveRecord do
53
49
  end
54
50
 
55
51
  describe "#create!" do
52
+ let(:attributes) do
53
+ {
54
+ name: "Some name",
55
+ example: "Some text",
56
+ }
57
+ end
58
+ let(:record) { nil }
56
59
  let(:create_from) do
57
- Struct.new(:id, :last_update, :attributes).new(
60
+ Struct.new(:id, :last_update, :attributes, :record).new(
58
61
  salesforce_id,
59
62
  Time.now,
60
- name: "Some name",
61
- example: "Some text",
63
+ attributes,
64
+ record,
62
65
  )
63
66
  end
64
67
  let(:instance) { record_type.create!(create_from).record }
65
68
 
66
- before do
67
- mapping.add_mappings(name: "Name", example: "Example_Field__c")
68
- end
69
-
70
69
  it "creates a record in the database from the passed Salesforce record's attributes" do
71
70
  expect(instance.salesforce_id).to_equal salesforce_id
72
- expect(instance.name).to_equal create_from.attributes[:name]
73
- expect(instance.example).to_equal create_from.attributes[:example]
71
+ expect(instance.name).to_equal attributes[:name]
72
+ expect(instance.example).to_equal attributes[:example]
74
73
  expect(instance.synchronized_at).to_not_be_nil
75
74
  end
75
+
76
+ describe "given a mapped association" do
77
+ let(:record) { Struct.new(:Friend__c).new(association_id) }
78
+ let(:association_id) { "a001a000001EFRIEND" }
79
+ let(:associations) { { user: "Friend__c" } }
80
+
81
+ before do
82
+ mapping = Restforce::DB::Mapping.new(User, "Contact", fields: { email: "Email" })
83
+ salesforce_record_type = mapping.salesforce_record_type
84
+
85
+ # Stub out the `#find` method on the record type
86
+ def salesforce_record_type.find(id)
87
+ Struct.new(:id, :last_update, :attributes).new(
88
+ id,
89
+ Time.now,
90
+ email: "somebody@example.com",
91
+ )
92
+ end
93
+ end
94
+
95
+ it "creates the associated record from the related Salesforce record's attributes" do
96
+ user = instance.reload.user
97
+
98
+ expect(user).to_not_be_nil
99
+ expect(user.email).to_equal("somebody@example.com")
100
+ expect(user.salesforce_id).to_equal(association_id)
101
+ expect(user.synchronized_at).to_not_be_nil
102
+ end
103
+ end
76
104
  end
77
105
 
78
106
  describe "#find" do
79
107
 
80
108
  it "finds existing records in the database by their salesforce id" do
81
- CustomObject.create!(salesforce_id: salesforce_id)
109
+ database_model.create!(salesforce_id: salesforce_id)
82
110
  expect(record_type.find(salesforce_id)).to_be_instance_of Restforce::DB::Instances::ActiveRecord
83
111
  end
84
112
 
@@ -3,23 +3,22 @@ require_relative "../../../../test_helper"
3
3
  describe Restforce::DB::RecordTypes::Salesforce do
4
4
 
5
5
  configure!
6
+ mappings!
6
7
 
7
- let(:mapping) { Restforce::DB::Mapping.new }
8
- let(:record_type) { Restforce::DB::RecordTypes::Salesforce.new("CustomObject__c", mapping) }
8
+ let(:record_type) { mapping.salesforce_record_type }
9
9
 
10
10
  describe "#create!", :vcr do
11
11
  let(:database_record) do
12
- CustomObject.create!(
12
+ database_model.create!(
13
13
  name: "Something",
14
14
  example: "Something else",
15
15
  )
16
16
  end
17
- let(:sync_from) { Restforce::DB::Instances::ActiveRecord.new(database_record, mapping) }
17
+ let(:sync_from) { Restforce::DB::Instances::ActiveRecord.new(database_model, database_record, mapping) }
18
18
  let(:instance) { record_type.create!(sync_from).record }
19
19
 
20
20
  before do
21
- mapping.add_mappings name: "Name", example: "Example_Field__c"
22
- Salesforce.records << ["CustomObject__c", instance.Id]
21
+ Salesforce.records << [salesforce_model, instance.Id]
23
22
  end
24
23
 
25
24
  it "creates a record in Salesforce from the passed database record's attributes" do
@@ -33,7 +32,7 @@ describe Restforce::DB::RecordTypes::Salesforce do
33
32
  end
34
33
 
35
34
  describe "#find", :vcr do
36
- let(:id) { Salesforce.create!("CustomObject__c") }
35
+ let(:id) { Salesforce.create!(salesforce_model) }
37
36
 
38
37
  it "finds existing records in Salesforce" do
39
38
  expect(record_type.find(id)).to_be_instance_of Restforce::DB::Instances::Salesforce
@@ -3,11 +3,9 @@ require_relative "../../../test_helper"
3
3
  describe Restforce::DB::Synchronizer do
4
4
 
5
5
  configure!
6
+ mappings!
6
7
 
7
- let(:mapping) { Restforce::DB::Mapping.new(name: "Name", example: "Example_Field__c") }
8
- let(:database_type) { Restforce::DB::RecordTypes::ActiveRecord.new(CustomObject, mapping) }
9
- let(:salesforce_type) { Restforce::DB::RecordTypes::Salesforce.new("CustomObject__c", mapping) }
10
- let(:synchronizer) { Restforce::DB::Synchronizer.new(database_type, salesforce_type) }
8
+ let(:synchronizer) { mapping.synchronizer }
11
9
 
12
10
  describe "#initialize" do
13
11
  before { Restforce::DB.last_run = Time.now }
@@ -27,39 +25,55 @@ describe Restforce::DB::Synchronizer do
27
25
  end
28
26
  let(:salesforce_id) do
29
27
  Salesforce.create!(
30
- "CustomObject__c",
31
- mapping.convert(:salesforce, attributes),
28
+ salesforce_model,
29
+ mapping.convert(salesforce_model, attributes),
32
30
  )
33
31
  end
34
32
 
35
33
  describe "given an existing Salesforce record" do
36
34
  before do
37
35
  salesforce_id
38
- synchronizer.run
39
36
  end
40
37
 
41
- it "populates the database with the new record" do
42
- record = CustomObject.last
38
+ describe "for a root mapping" do
39
+ before do
40
+ synchronizer.run
41
+ end
42
+
43
+ it "creates a matching database record" do
44
+ record = database_model.last
43
45
 
44
- expect(record.name).to_equal attributes[:name]
45
- expect(record.example).to_equal attributes[:example]
46
- expect(record.salesforce_id).to_equal salesforce_id
46
+ expect(record.name).to_equal attributes[:name]
47
+ expect(record.example).to_equal attributes[:example]
48
+ expect(record.salesforce_id).to_equal salesforce_id
49
+ end
50
+ end
51
+
52
+ describe "for a non-root mapping" do
53
+ before do
54
+ mapping.root = false
55
+ synchronizer.run
56
+ end
57
+
58
+ it "does not create a database record" do
59
+ expect(database_model.last).to_be_nil
60
+ end
47
61
  end
48
62
  end
49
63
 
50
64
  describe "given an existing database record" do
51
- let(:database_record) { CustomObject.create!(attributes) }
65
+ let(:database_record) { database_model.create!(attributes) }
52
66
  let(:salesforce_id) { database_record.reload.salesforce_id }
53
67
 
54
68
  before do
55
69
  database_record
56
70
  synchronizer.run
57
71
 
58
- Salesforce.records << ["CustomObject__c", salesforce_id]
72
+ Salesforce.records << [salesforce_model, salesforce_id]
59
73
  end
60
74
 
61
75
  it "populates Salesforce with the new record" do
62
- record = salesforce_type.find(salesforce_id).record
76
+ record = mapping.salesforce_record_type.find(salesforce_id).record
63
77
 
64
78
  expect(record.Name).to_equal attributes[:name]
65
79
  expect(record.Example_Field__c).to_equal attributes[:example]
@@ -75,14 +89,14 @@ describe Restforce::DB::Synchronizer do
75
89
  }
76
90
  end
77
91
  let(:database_record) do
78
- CustomObject.create!(database_attributes.merge(salesforce_id: salesforce_id))
92
+ database_model.create!(database_attributes.merge(salesforce_id: salesforce_id))
79
93
  end
80
94
 
81
95
  describe "when synchronization is stale" do
82
96
  before do
83
97
  # Set the synchronization timestamp to 5 seconds before the Salesforce
84
98
  # modification timestamp.
85
- updated = salesforce_type.find(salesforce_id).last_update
99
+ updated = mapping.salesforce_record_type.find(salesforce_id).last_update
86
100
  database_record.update!(synchronized_at: updated - 5)
87
101
 
88
102
  synchronizer.run
@@ -7,15 +7,34 @@ ActiveRecord::Base.establish_connection(
7
7
  )
8
8
 
9
9
  ActiveRecord::Schema.define do
10
+
10
11
  create_table :custom_objects do |table|
11
12
  table.column :name, :string
12
13
  table.column :example, :string
14
+ table.column :user_id, :integer
13
15
  table.column :salesforce_id, :string
14
16
  table.column :synchronized_at, :datetime
15
17
  table.timestamps null: false
16
18
  end
17
19
 
18
20
  add_index :custom_objects, :salesforce_id
21
+
22
+ create_table :users do |table|
23
+ table.column :email, :string
24
+ table.column :salesforce_id, :string
25
+ table.column :synchronized_at, :datetime
26
+ table.timestamps null: false
27
+ end
28
+
29
+ add_index :users, :salesforce_id
30
+
31
+ end
32
+
33
+ # :nodoc:
34
+ class CustomObject < ActiveRecord::Base
35
+
36
+ belongs_to :user
37
+
19
38
  end
20
39
 
21
- class CustomObject < ActiveRecord::Base; end
40
+ class User < ActiveRecord::Base; end
@@ -0,0 +1,29 @@
1
+ # :nodoc:
2
+ def configure!
3
+ before do
4
+ Salesforce.configure!
5
+ end
6
+
7
+ after do
8
+ Restforce::DB::Mapping.collection = {}
9
+ DatabaseCleaner.clean
10
+ Salesforce.clean!
11
+ end
12
+ end
13
+
14
+ # :nodoc:
15
+ def mappings!
16
+ let(:database_model) { CustomObject }
17
+ let(:salesforce_model) { "CustomObject__c" }
18
+ let(:fields) { { name: "Name", example: "Example_Field__c" } }
19
+ let(:associations) { {} }
20
+ let!(:mapping) do
21
+ Restforce::DB::Mapping.new(
22
+ database_model,
23
+ salesforce_model,
24
+ root: true,
25
+ fields: fields,
26
+ associations: associations,
27
+ )
28
+ end
29
+ end
data/test/test_helper.rb CHANGED
@@ -11,15 +11,3 @@ secrets_file = File.expand_path("../config/secrets.yml", __FILE__)
11
11
  Secrets = YAML.load_file(secrets_file)
12
12
 
13
13
  Dir[File.join(File.dirname(__FILE__), "support/**/*.rb")].each { |f| require f }
14
-
15
- # :nodoc:
16
- def configure!
17
- before do
18
- Salesforce.configure!
19
- end
20
-
21
- after do
22
- DatabaseCleaner.clean
23
- Salesforce.clean!
24
- end
25
- end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: restforce-db
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Horner
@@ -227,7 +227,6 @@ files:
227
227
  - lib/restforce/db/instances/salesforce.rb
228
228
  - lib/restforce/db/mapping.rb
229
229
  - lib/restforce/db/model.rb
230
- - lib/restforce/db/record_type.rb
231
230
  - lib/restforce/db/record_types/active_record.rb
232
231
  - lib/restforce/db/record_types/base.rb
233
232
  - lib/restforce/db/record_types/salesforce.rb
@@ -247,14 +246,14 @@ files:
247
246
  - test/cassettes/Restforce_DB_RecordTypes_Salesforce/_find/returns_nil_when_no_matching_record_exists.yml
248
247
  - test/cassettes/Restforce_DB_Synchronizer/_run/given_a_Salesforce_record_with_an_associated_database_record/when_synchronization_is_stale/updates_the_database_record.yml
249
248
  - test/cassettes/Restforce_DB_Synchronizer/_run/given_a_Salesforce_record_with_an_associated_database_record/when_synchronization_is_up-to-date/does_not_update_the_database_record.yml
250
- - test/cassettes/Restforce_DB_Synchronizer/_run/given_an_existing_Salesforce_record/populates_the_database_with_the_new_record.yml
249
+ - test/cassettes/Restforce_DB_Synchronizer/_run/given_an_existing_Salesforce_record/for_a_non-root_mapping/does_not_create_a_database_record.yml
250
+ - test/cassettes/Restforce_DB_Synchronizer/_run/given_an_existing_Salesforce_record/for_a_root_mapping/creates_a_matching_database_record.yml
251
251
  - test/cassettes/Restforce_DB_Synchronizer/_run/given_an_existing_database_record/populates_Salesforce_with_the_new_record.yml
252
252
  - test/lib/restforce/db/configuration_test.rb
253
253
  - test/lib/restforce/db/instances/active_record_test.rb
254
254
  - test/lib/restforce/db/instances/salesforce_test.rb
255
255
  - test/lib/restforce/db/mapping_test.rb
256
256
  - test/lib/restforce/db/model_test.rb
257
- - test/lib/restforce/db/record_type_test.rb
258
257
  - test/lib/restforce/db/record_types/active_record_test.rb
259
258
  - test/lib/restforce/db/record_types/salesforce_test.rb
260
259
  - test/lib/restforce/db/synchronizer_test.rb
@@ -263,6 +262,7 @@ files:
263
262
  - test/support/active_record.rb
264
263
  - test/support/database_cleaner.rb
265
264
  - test/support/salesforce.rb
265
+ - test/support/utilities.rb
266
266
  - test/support/vcr.rb
267
267
  - test/test_helper.rb
268
268
  homepage: https://www.github.com/tablexi/restforce-db
@@ -1,77 +0,0 @@
1
- module Restforce
2
-
3
- module DB
4
-
5
- # Restforce::DB::RecordType is an abstraction for a two-way binding between
6
- # an ActiveRecord class and a Salesforce object type. It provides an
7
- # interface for mapping database columns to Salesforce fields.
8
- class RecordType
9
-
10
- class << self
11
-
12
- include Enumerable
13
- attr_accessor :collection
14
-
15
- # Public: Get the Restforce::DB::RecordType entry for the specified
16
- # database model.
17
- #
18
- # database_model - A Class compatible with ActiveRecord::Base.
19
- #
20
- # Returns a Restforce::DB::RecordType.
21
- def [](database_model)
22
- collection[database_model]
23
- end
24
-
25
- # Public: Iterate through all registered Restforce::DB::RecordTypes.
26
- #
27
- # Yields one RecordType for each database-to-Salesforce mapping.
28
- # Returns nothing.
29
- def each
30
- collection.each do |database_model, record_type|
31
- yield database_model.name, record_type
32
- end
33
- end
34
-
35
- end
36
-
37
- self.collection ||= {}
38
- attr_reader :mapping, :synchronizer
39
-
40
- # Public: Initialize and register a Restforce::DB::RecordType.
41
- #
42
- # database_model - A Class compatible with ActiveRecord::Base.
43
- # salesforce_model - A String name of an object type in Salesforce.
44
- # mappings - A Hash of mappings between database columns and
45
- # fields in Salesforce.
46
- def initialize(database_model, salesforce_model, **mappings)
47
- @mapping = Mapping.new(mappings)
48
- @database_record_type = RecordTypes::ActiveRecord.new(database_model, @mapping)
49
- @salesforce_record_type = RecordTypes::Salesforce.new(salesforce_model, @mapping)
50
- @synchronizer = Synchronizer.new(@database_record_type, @salesforce_record_type)
51
-
52
- self.class.collection[database_model] = self
53
- end
54
-
55
- # Public: Append the passed mappings to this model.
56
- #
57
- # mappings - A Hash of database column names mapped to Salesforce fields.
58
- #
59
- # Returns nothing.
60
- def add_mappings(mappings)
61
- @mapping.add_mappings mappings
62
- end
63
-
64
- # Public: Synchronize the records between the database and Salesforce.
65
- #
66
- # options - A Hash of options to pass to the synchronizer.
67
- #
68
- # Returns nothing.
69
- def synchronize(options = {})
70
- @synchronizer.run(options)
71
- end
72
-
73
- end
74
-
75
- end
76
-
77
- end
@@ -1,26 +0,0 @@
1
- require_relative "../../../test_helper"
2
-
3
- describe Restforce::DB::RecordType do
4
-
5
- configure!
6
-
7
- let(:database_model) { CustomObject }
8
- let(:salesforce_model) { "CustomObject__c" }
9
- let!(:record_type) { Restforce::DB::RecordType.new(database_model, salesforce_model) }
10
-
11
- describe "#initialize" do
12
-
13
- it "registers the record type in the master collection" do
14
- expect(Restforce::DB::RecordType[database_model]).to_equal(record_type)
15
- end
16
- end
17
-
18
- describe ".each" do
19
-
20
- # Restforce::DB::RecordType actually implements Enumerable, so we're just
21
- # going with a trivially testable portion of the Enumerable API.
22
- it "yields the registered record types" do
23
- expect(Restforce::DB::RecordType.first).to_equal [database_model.name, record_type]
24
- end
25
- end
26
- end