restforce-db 2.0.0 → 2.0.1

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 (29) hide show
  1. checksums.yaml +4 -4
  2. data/lib/restforce/db/accumulator.rb +13 -0
  3. data/lib/restforce/db/field_processor.rb +70 -0
  4. data/lib/restforce/db/instances/salesforce.rb +10 -0
  5. data/lib/restforce/db/record_types/salesforce.rb +2 -2
  6. data/lib/restforce/db/synchronizer.rb +2 -1
  7. data/lib/restforce/db/version.rb +1 -1
  8. data/lib/restforce/db.rb +1 -0
  9. data/test/cassettes/Restforce_DB_Associator/_run/given_a_BelongsTo_association/given_another_record_for_association/when_the_Salesforce_association_is_out_of_date/updates_the_association_ID_in_Salesforce.yml +135 -82
  10. data/test/cassettes/Restforce_DB_Associator/_run/given_a_BelongsTo_association/given_another_record_for_association/when_the_database_association_is_out_of_date/updates_the_associated_record_in_the_database.yml +152 -99
  11. data/test/cassettes/Restforce_DB_Cleaner/_run/given_a_synchronized_Salesforce_record/when_the_mapping_has_no_conditions/does_not_drop_the_synchronized_database_record.yml +120 -0
  12. data/test/cassettes/Restforce_DB_Cleaner/_run/given_a_synchronized_Salesforce_record/when_the_record_does_not_meet_the_mapping_conditions/for_a_non-Passive_strategy/drops_the_synchronized_database_record.yml +34 -36
  13. data/test/cassettes/Restforce_DB_Cleaner/_run/given_a_synchronized_Salesforce_record/when_the_record_meets_the_mapping_conditions/does_not_drop_the_synchronized_database_record.yml +35 -37
  14. data/test/cassettes/Restforce_DB_Initializer/_run/given_an_existing_database_record/for_an_Always_strategy/populates_Salesforce_with_the_new_record.yml +96 -44
  15. data/test/cassettes/Restforce_DB_Instances_Salesforce/_update_/updates_the_local_record_with_the_passed_attributes.yml +88 -35
  16. data/test/cassettes/Restforce_DB_Instances_Salesforce/_update_/updates_the_record_in_Salesforce_with_the_passed_attributes.yml +96 -43
  17. data/test/cassettes/Restforce_DB_RecordTypes_Salesforce/_create_/creates_a_record_in_Salesforce_from_the_passed_database_record_s_attributes.yml +81 -28
  18. data/test/cassettes/Restforce_DB_RecordTypes_Salesforce/_create_/updates_the_database_record_with_the_Salesforce_record_s_ID.yml +81 -28
  19. data/test/cassettes/Restforce_DB_Synchronizer/_run/given_a_Salesforce_record_with_an_associated_database_record/updates_the_database_record.yml +88 -35
  20. data/test/cassettes/Restforce_DB_Synchronizer/_run/given_a_Salesforce_record_with_an_associated_database_record/updates_the_salesforce_record.yml +96 -43
  21. data/test/lib/restforce/db/accumulator_test.rb +16 -0
  22. data/test/lib/restforce/db/cleaner_test.rb +15 -9
  23. data/test/lib/restforce/db/collector_test.rb +13 -15
  24. data/test/lib/restforce/db/field_processor_test.rb +51 -0
  25. data/test/lib/restforce/db/initializer_test.rb +10 -13
  26. data/test/lib/restforce/db/runner_cache_test.rb +1 -0
  27. data/test/support/utilities.rb +1 -0
  28. metadata +5 -3
  29. data/test/cassettes/Restforce_DB_Instances_Salesforce/_copy_/updates_the_record_with_the_attributes_from_the_copied_object.yml +0 -194
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e92a8e2216150a27646cdd8525a951100055ad99
4
- data.tar.gz: 07ade758ff323cd721daede50850a724562df0b1
3
+ metadata.gz: bd30e064e5e85ed7a3324e54e08797f34df84c84
4
+ data.tar.gz: 43bf5c31a7517094be1fbb875c701d9b580a4121
5
5
  SHA512:
6
- metadata.gz: be4a146095c1555becbb048bd0944b56d37fe8c14231cc2e27355f30c372526ced00f8767ed486d14117af61dc47c8b4d55c28d95b99d74723a4f6ec83993c4e
7
- data.tar.gz: d5a0929ca903969593e9037961a9ba2874329fa9f3f30e58fee67fa993c1b6d3bdc9bec2a84b06ccd0dee6c0a210261b95b2a9ff0212b80e6f65dc472068c8ca
6
+ metadata.gz: 9c8c3b7f0ce2cd131c643f2d33f7c15c719b820522e67696bb6d6a4eeb9ef80a77d4a841d8a7d0bf7a8a66fd9caf252357e29cd245f4257823a9d85c55cb9e63
7
+ data.tar.gz: e75e5b35ed86a07426eea1926ae642d7176167a7de858934a3e251399efe0f85af30fca97e1ea12f93ff9fbcddf7bf925bd08221bb1158e9520987da5ea16d46
@@ -44,6 +44,19 @@ module Restforce
44
44
  end
45
45
  end
46
46
 
47
+ # Public: Do the canonical attributes stored in this Accumulator differ
48
+ # from those in the passed comparison Hash?
49
+ #
50
+ # comparison - A Hash mapping of attributes to values.
51
+ #
52
+ # Returns a Boolean.
53
+ def changed?(comparison)
54
+ attributes.any? do |attribute, value|
55
+ next unless comparison.key?(attribute)
56
+ comparison[attribute] != value
57
+ end
58
+ end
59
+
47
60
  end
48
61
 
49
62
  end
@@ -0,0 +1,70 @@
1
+ module Restforce
2
+
3
+ module DB
4
+
5
+ # Restforce::DB::FieldProcessor encapsulates logic for preventing
6
+ # information for unwriteable fields from being submitted to Salesforce.
7
+ class FieldProcessor
8
+
9
+ # Internal: Get a global cache with which to store/fetch the writable
10
+ # fields for each Salesforce SObject Type.
11
+ #
12
+ # Returns a Hash.
13
+ def self.field_cache
14
+ @field_cache ||= {}
15
+ end
16
+
17
+ # Internal: Clear out the global field cache.
18
+ #
19
+ # Returns nothing.
20
+ def self.reset
21
+ @field_cache = {}
22
+ end
23
+
24
+ # Public: Get a restricted version of the passed attributes Hash, with
25
+ # unwritable fields stripped out.
26
+ #
27
+ # sobject_type - A String name of an SObject Type in Salesforce.
28
+ # attributes - A Hash with keys corresponding to Salesforce field names.
29
+ #
30
+ # Returns a Hash.
31
+ def process(sobject_type, attributes)
32
+ attributes.each_with_object({}) do |(field, value), processed|
33
+ next unless writable?(sobject_type, field)
34
+ processed[field] = value
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ # Internal: Is the passed attribute writable for the passed SObject Type?
41
+ #
42
+ # sobject_type - A String name of an SObject Type in Salesforce.
43
+ # field - A String Salesforce field API name.
44
+ #
45
+ # Returns a Boolean.
46
+ def writable?(sobject_type, field)
47
+ field_statuses(sobject_type)[field]
48
+ end
49
+
50
+ # Internal: Get a collection of all fields for the passed Salesforce
51
+ # SObject Type, with an indication of whether or not they are writable.
52
+ #
53
+ # sobject_type - A String name of an SObject Type in Salesforce.
54
+ #
55
+ # Returns a Hash.
56
+ def field_statuses(sobject_type)
57
+ self.class.field_cache[sobject_type] ||= begin
58
+ fields = Restforce::DB.client.describe(sobject_type).fields
59
+
60
+ fields.each_with_object({}) do |field, output|
61
+ output[field["name"]] = field["updateable"]
62
+ end
63
+ end
64
+ end
65
+
66
+ end
67
+
68
+ end
69
+
70
+ end
@@ -21,6 +21,16 @@ module Restforce
21
21
  @record.Id
22
22
  end
23
23
 
24
+ # Public: Update the instance with the passed attributes.
25
+ #
26
+ # attributes - A Hash mapping attribute names to values.
27
+ #
28
+ # Returns self.
29
+ # Raises if the update fails for any reason.
30
+ def update!(attributes)
31
+ super FieldProcessor.new.process(@record_type, attributes)
32
+ end
33
+
24
34
  # Public: Get the time of the last update to this record.
25
35
  #
26
36
  # Returns a Time-compatible object.
@@ -17,8 +17,8 @@ module Restforce
17
17
  # Returns a Restforce::DB::Instances::Salesforce instance.
18
18
  # Raises on any error from Salesforce.
19
19
  def create!(from_record)
20
- attributes = @mapping.convert(@record_type, from_record.attributes)
21
- record_id = DB.client.create!(@record_type, attributes)
20
+ from_attributes = FieldProcessor.new.process(@record_type, from_record.attributes)
21
+ record_id = DB.client.create!(@record_type, from_attributes)
22
22
 
23
23
  from_record.update!(@mapping.lookup_column => record_id).after_sync
24
24
 
@@ -49,10 +49,11 @@ module Restforce
49
49
  #
50
50
  # Returns nothing.
51
51
  def update(instance, accumulator)
52
+ return unless accumulator.changed?(instance.attributes)
53
+
52
54
  current_attributes = accumulator.current(instance.attributes)
53
55
  attributes = @mapping.convert(instance.record_type, current_attributes)
54
56
 
55
- return if attributes.empty?
56
57
  instance.update!(attributes)
57
58
  end
58
59
 
@@ -3,7 +3,7 @@ module Restforce
3
3
  # :nodoc:
4
4
  module DB
5
5
 
6
- VERSION = "2.0.0"
6
+ VERSION = "2.0.1"
7
7
 
8
8
  end
9
9
 
data/lib/restforce/db.rb CHANGED
@@ -16,6 +16,7 @@ require "restforce/db/associations/foreign_key"
16
16
  require "restforce/db/associations/has_many"
17
17
  require "restforce/db/associations/has_one"
18
18
 
19
+ require "restforce/db/field_processor"
19
20
  require "restforce/db/instances/base"
20
21
  require "restforce/db/instances/active_record"
21
22
  require "restforce/db/instances/salesforce"