restforce-db 1.3.0 → 2.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.
- checksums.yaml +4 -4
- data/README.md +19 -9
- data/circle.yml +20 -0
- data/lib/restforce/db/accumulator.rb +5 -8
- data/lib/restforce/db/adapter.rb +39 -0
- data/lib/restforce/db/attribute_map.rb +25 -87
- data/lib/restforce/db/collector.rb +10 -10
- data/lib/restforce/db/dsl.rb +7 -7
- data/lib/restforce/db/mapping.rb +3 -4
- data/lib/restforce/db/synchronizer.rb +2 -2
- data/lib/restforce/db/version.rb +1 -1
- data/lib/restforce/db.rb +1 -0
- data/restforce-db.gemspec +1 -1
- data/test/lib/restforce/db/accumulator_test.rb +5 -8
- data/test/lib/restforce/db/adapter_test.rb +29 -0
- data/test/lib/restforce/db/attribute_map_test.rb +12 -62
- data/test/lib/restforce/db/dsl_test.rb +4 -5
- data/test/lib/restforce/db/record_types/active_record_test.rb +5 -5
- data/test/lib/restforce/db/synchronizer_test.rb +4 -9
- data/test/support/utilities.rb +8 -4
- metadata +9 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e92a8e2216150a27646cdd8525a951100055ad99
|
4
|
+
data.tar.gz: 07ade758ff323cd721daede50850a724562df0b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be4a146095c1555becbb048bd0944b56d37fe8c14231cc2e27355f30c372526ced00f8767ed486d14117af61dc47c8b4d55c28d95b99d74723a4f6ec83993c4e
|
7
|
+
data.tar.gz: d5a0929ca903969593e9037961a9ba2874329fa9f3f30e58fee67fa993c1b6d3bdc9bec2a84b06ccd0dee6c0a210261b95b2a9ff0212b80e6f65dc472068c8ca
|
data/README.md
CHANGED
@@ -44,12 +44,16 @@ class Restaurant < ActiveRecord::Base
|
|
44
44
|
|
45
45
|
module StyleAdapter
|
46
46
|
|
47
|
-
def self.
|
48
|
-
|
47
|
+
def self.to_database(attributes)
|
48
|
+
attributes.each_with_object({}) do |(key, value), final|
|
49
|
+
final[key] = value.chomp(" in Salesforce")
|
50
|
+
end
|
49
51
|
end
|
50
52
|
|
51
|
-
def self.
|
52
|
-
|
53
|
+
def self.from_database(attributes)
|
54
|
+
attributes.each_with_object({}) do |(key, value), final|
|
55
|
+
final[key] = "#{value} in Salesforce"
|
56
|
+
end
|
53
57
|
end
|
54
58
|
|
55
59
|
end
|
@@ -59,14 +63,12 @@ class Restaurant < ActiveRecord::Base
|
|
59
63
|
has_many :dishes, through: "Restaurant__c"
|
60
64
|
belongs_to :chef, through: %w(Chef__c Cuisine__c)
|
61
65
|
|
66
|
+
converts_with StyleAdapter
|
67
|
+
|
62
68
|
maps(
|
63
69
|
name: "Name",
|
64
70
|
style: "Style__c",
|
65
71
|
)
|
66
|
-
|
67
|
-
converts(
|
68
|
-
style: StyleAdapter,
|
69
|
-
)
|
70
72
|
end
|
71
73
|
|
72
74
|
end
|
@@ -135,9 +137,17 @@ Individual conditions supplied to `where` will be appended together with `AND` c
|
|
135
137
|
|
136
138
|
`maps` defines a set of direct field-to-field mappings. It takes a Hash as an argument; the keys should line up with your ActiveRecord attribute names, while the values should line up with the matching field names in Salesforce.
|
137
139
|
|
140
|
+
Your ActiveRecord class _must_ expose readers for each attribute in the mapping, and generally _should_ expose matching writers, though you can use an adapter object (see "Field Conversions" below) to obviate the need for the latter.
|
141
|
+
|
138
142
|
#### Field Conversions
|
139
143
|
|
140
|
-
`
|
144
|
+
`converts_with` defines a mapping conversion adapter. The only requirement for an adapter is that it respond to the methods `#to_database` and `#from_database`.
|
145
|
+
|
146
|
+
- `#to_database` will be handed a "normalized" Hash, with the standard Symbol mapped attributes as keys, and the values as they are stored in Salesforce. It should return a modified version of the Hash which can be passed to `assign_attributes` for a record.
|
147
|
+
|
148
|
+
- `#from_database` will be handed a Hash with the standard Symbol mapped attributes as keys, and the values for those attributes as they are returned by the ActiveRecord object. It should return a modified version of the Hash with values suitable for storage in Salesforce.
|
149
|
+
|
150
|
+
By default, `Restforce::DB::Adapter` will be used, which simply converts times into String ISO-8601 timestamps before passing them off to Salesforce.
|
141
151
|
|
142
152
|
#### Associations
|
143
153
|
|
data/circle.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
checkout:
|
2
|
+
post:
|
3
|
+
- mkdir -p test/config
|
4
|
+
- cp lib/generators/templates/config.yml test/config/secrets.yml
|
5
|
+
machine:
|
6
|
+
ruby:
|
7
|
+
version: 2.2.2
|
8
|
+
dependencies:
|
9
|
+
pre:
|
10
|
+
# Get the most recent bundler
|
11
|
+
- gem install bundler
|
12
|
+
database:
|
13
|
+
override:
|
14
|
+
# Do nothing; we don't need any database setup.
|
15
|
+
- echo "SKIPPING"
|
16
|
+
test:
|
17
|
+
override:
|
18
|
+
- bundle exec rubocop
|
19
|
+
- bundle exec rake test
|
20
|
+
|
@@ -31,19 +31,16 @@ module Restforce
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
# Public: Get a Hash representing the
|
35
|
-
#
|
36
|
-
# attributes Hash.
|
34
|
+
# Public: Get a Hash representing the current values for the items in the
|
35
|
+
# passed Hash, as a subset of this Accumulator's attributes Hash.
|
37
36
|
#
|
38
37
|
# comparison - A Hash mapping of attributes to values.
|
39
38
|
#
|
40
39
|
# Returns a Hash.
|
41
|
-
def
|
42
|
-
attributes.each_with_object({}) do |(attribute, value),
|
40
|
+
def current(comparison)
|
41
|
+
attributes.each_with_object({}) do |(attribute, value), final|
|
43
42
|
next unless comparison.key?(attribute)
|
44
|
-
|
45
|
-
|
46
|
-
diff[attribute] = value
|
43
|
+
final[attribute] = value
|
47
44
|
end
|
48
45
|
end
|
49
46
|
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Restforce
|
2
|
+
|
3
|
+
module DB
|
4
|
+
|
5
|
+
# Restforce::DB::Adapter defines the default data conversions between
|
6
|
+
# database and Salesforce formats. It translates Dates and Times to ISO-8601
|
7
|
+
# format for storage in Salesforce.
|
8
|
+
class Adapter
|
9
|
+
|
10
|
+
# Public: Convert the passed attribute hash to a format consumable by
|
11
|
+
# the ActiveRecord model. By default, performs no conversions.
|
12
|
+
#
|
13
|
+
# attributes - A Hash of attributes, with keys corresponding to a Mapping.
|
14
|
+
#
|
15
|
+
# Returns a Hash.
|
16
|
+
def to_database(attributes)
|
17
|
+
attributes.dup
|
18
|
+
end
|
19
|
+
|
20
|
+
# Public: Convert the passed attribute hash to a format consumable by
|
21
|
+
# Salesforce.
|
22
|
+
#
|
23
|
+
# attributes - A Hash of attributes, with keys corresponding to a Mapping.
|
24
|
+
#
|
25
|
+
# Returns a Hash.
|
26
|
+
def from_database(attributes)
|
27
|
+
attributes.each_with_object({}) do |(key, value), final|
|
28
|
+
value = value.utc if value.respond_to?(:utc)
|
29
|
+
value = value.iso8601 if value.respond_to?(:iso8601)
|
30
|
+
|
31
|
+
final[key] = value
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -4,40 +4,25 @@ module Restforce
|
|
4
4
|
|
5
5
|
# Restforce::DB::AttributeMap encapsulates the logic for converting between
|
6
6
|
# various representations of attribute hashes.
|
7
|
+
#
|
8
|
+
# For the purposes of our mappings, a "normalized" attribute Hash maps the
|
9
|
+
# Salesforce field names to Salesforce-compatible values. Value conversion
|
10
|
+
# into and out of the database occurs through a lightweight Adapter object.
|
7
11
|
class AttributeMap
|
8
12
|
|
9
|
-
# DefaultAdapter defines the default data conversions between database and
|
10
|
-
# Salesforce formats. It translates Dates and Times to ISO-8601 format for
|
11
|
-
# storage in Salesforce.
|
12
|
-
module DefaultAdapter
|
13
|
-
|
14
|
-
# :nodoc:
|
15
|
-
def self.to_database(value)
|
16
|
-
value
|
17
|
-
end
|
18
|
-
|
19
|
-
# :nodoc:
|
20
|
-
def self.to_salesforce(value)
|
21
|
-
value = value.respond_to?(:utc) ? value.utc : value
|
22
|
-
value.respond_to?(:iso8601) ? value.iso8601 : value
|
23
|
-
end
|
24
|
-
|
25
|
-
end
|
26
|
-
|
27
13
|
# Public: Initialize a Restforce::DB::AttributeMap.
|
28
14
|
#
|
29
15
|
# database_model - A Class compatible with ActiveRecord::Base.
|
30
16
|
# salesforce_model - A String name of an object type in Salesforce.
|
31
17
|
# fields - A Hash of mappings between database columns and
|
32
18
|
# fields in Salesforce.
|
33
|
-
#
|
34
|
-
#
|
35
|
-
|
36
|
-
def initialize(database_model, salesforce_model, fields = {}, conversions = {})
|
19
|
+
# adapter - An adapter object which should be used to convert
|
20
|
+
# between data formats.
|
21
|
+
def initialize(database_model, salesforce_model, fields = {}, adapter = Adapter.new)
|
37
22
|
@database_model = database_model
|
38
23
|
@salesforce_model = salesforce_model
|
39
24
|
@fields = fields
|
40
|
-
@
|
25
|
+
@adapter = adapter
|
41
26
|
|
42
27
|
@types = {
|
43
28
|
database_model => :database,
|
@@ -47,7 +32,7 @@ module Restforce
|
|
47
32
|
|
48
33
|
# Public: Build a normalized Hash of attributes from the appropriate set
|
49
34
|
# of mappings. The keys of the resulting mapping Hash will correspond to
|
50
|
-
# the
|
35
|
+
# the Salesforce field names.
|
51
36
|
#
|
52
37
|
# from_format - A String or Class reflecting the record type from which
|
53
38
|
# the attribute Hash is being compiled.
|
@@ -57,13 +42,18 @@ module Restforce
|
|
57
42
|
def attributes(from_format)
|
58
43
|
case @types[from_format]
|
59
44
|
when :salesforce
|
60
|
-
@fields.each_with_object({}) do |
|
61
|
-
values[
|
45
|
+
@fields.values.each_with_object({}) do |mapping, values|
|
46
|
+
values[mapping] = yield(mapping)
|
62
47
|
end
|
63
48
|
when :database
|
64
|
-
@fields.keys.each_with_object({}) do |attribute, values|
|
49
|
+
attributes = @fields.keys.each_with_object({}) do |attribute, values|
|
65
50
|
values[attribute] = yield(attribute)
|
66
51
|
end
|
52
|
+
attributes = @adapter.from_database(attributes)
|
53
|
+
|
54
|
+
@fields.each_with_object({}) do |(attribute, mapping), final|
|
55
|
+
final[mapping] = attributes[attribute]
|
56
|
+
end
|
67
57
|
else
|
68
58
|
raise ArgumentError
|
69
59
|
end
|
@@ -85,65 +75,22 @@ module Restforce
|
|
85
75
|
# some_key: "SomeField__c",
|
86
76
|
# )
|
87
77
|
#
|
88
|
-
# mapping.convert("
|
89
|
-
# # => {
|
78
|
+
# mapping.convert(MyClass, "Some_Field__c" => "some value")
|
79
|
+
# # => { some_key: "some value" }
|
90
80
|
#
|
91
|
-
# mapping.convert(
|
92
|
-
# # => {
|
81
|
+
# mapping.convert("Object__c", "Some_Field__c" => "some other value")
|
82
|
+
# # => { "Some_Field__c" => "some other value" }
|
93
83
|
#
|
94
84
|
# Returns a Hash.
|
95
85
|
def convert(to_format, attributes)
|
96
86
|
case @types[to_format]
|
97
87
|
when :database
|
98
|
-
attributes.
|
99
|
-
when :salesforce
|
100
|
-
@fields.each_with_object({}) do |(attribute, mapping), converted|
|
101
|
-
next unless attributes.key?(attribute)
|
102
|
-
value = adapter(attribute).to_salesforce(attributes[attribute])
|
103
|
-
converted[mapping] = value
|
104
|
-
end
|
105
|
-
else
|
106
|
-
raise ArgumentError
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
# Public: Convert a Hash of Salesforce attributes to a format compatible
|
111
|
-
# with a specific platform.
|
112
|
-
#
|
113
|
-
# to_format - A String or Class reflecting the record type for which the
|
114
|
-
# attribute Hash is being compiled.
|
115
|
-
# attributes - A Hash of attributes, with keys corresponding to the
|
116
|
-
# Salesforce attribute names.
|
117
|
-
#
|
118
|
-
# Examples
|
119
|
-
#
|
120
|
-
# map = AttributeMap.new(
|
121
|
-
# MyClass,
|
122
|
-
# "Object__c",
|
123
|
-
# some_key: "SomeField__c",
|
124
|
-
# )
|
125
|
-
#
|
126
|
-
# map.convert_from_salesforce(
|
127
|
-
# "Object__c",
|
128
|
-
# "Some_Field__c" => "some value",
|
129
|
-
# )
|
130
|
-
# # => { "Some_Field__c" => "some value" }
|
131
|
-
#
|
132
|
-
# map.convert_from_salesforce(
|
133
|
-
# MyClass,
|
134
|
-
# "Some_Field__c" => "some other value",
|
135
|
-
# )
|
136
|
-
# # => { some_key: "some other value" }
|
137
|
-
#
|
138
|
-
# Returns a Hash.
|
139
|
-
def convert_from_salesforce(to_format, attributes)
|
140
|
-
case @types[to_format]
|
141
|
-
when :database
|
142
|
-
@fields.each_with_object({}) do |(attribute, mapping), converted|
|
88
|
+
attributes = @fields.each_with_object({}) do |(attribute, mapping), converted|
|
143
89
|
next unless attributes.key?(mapping)
|
144
|
-
|
145
|
-
converted[attribute] = value
|
90
|
+
converted[attribute] = attributes[mapping]
|
146
91
|
end
|
92
|
+
|
93
|
+
@adapter.to_database(attributes)
|
147
94
|
when :salesforce
|
148
95
|
attributes.dup
|
149
96
|
else
|
@@ -151,15 +98,6 @@ module Restforce
|
|
151
98
|
end
|
152
99
|
end
|
153
100
|
|
154
|
-
# Internal: Get the data format adapter for the passed attribute. Defaults
|
155
|
-
# to DefaultAdapter if no adapter has been explicitly assigned for the
|
156
|
-
# attribute.
|
157
|
-
#
|
158
|
-
# Returns an Object.
|
159
|
-
def adapter(attribute)
|
160
|
-
@conversions[attribute] || DefaultAdapter
|
161
|
-
end
|
162
|
-
|
163
101
|
end
|
164
102
|
|
165
103
|
end
|
@@ -47,27 +47,27 @@ module Restforce
|
|
47
47
|
@accumulated_changes ||= Hash.new { |h, k| h[k] = {} }
|
48
48
|
end
|
49
49
|
|
50
|
-
# Internal: Append the passed
|
50
|
+
# Internal: Append the passed instance's attributes to its accumulated list
|
51
51
|
# of changesets.
|
52
52
|
#
|
53
|
-
#
|
53
|
+
# instance - A Restforce::DB::Instances::Base.
|
54
54
|
#
|
55
55
|
# Returns nothing.
|
56
|
-
def accumulate(
|
57
|
-
accumulated_changes[key_for(
|
58
|
-
|
59
|
-
|
56
|
+
def accumulate(instance)
|
57
|
+
accumulated_changes[key_for(instance)].store(
|
58
|
+
instance.last_update,
|
59
|
+
instance.attributes,
|
60
60
|
)
|
61
61
|
end
|
62
62
|
|
63
63
|
# Internal: Get a unique key with enough information to look up the passed
|
64
|
-
#
|
64
|
+
# instance in Salesforce.
|
65
65
|
#
|
66
|
-
#
|
66
|
+
# instance - A Restforce::DB::Instances::Base.
|
67
67
|
#
|
68
68
|
# Returns an Object.
|
69
|
-
def key_for(
|
70
|
-
[
|
69
|
+
def key_for(instance)
|
70
|
+
[instance.id, instance.mapping.salesforce_model]
|
71
71
|
end
|
72
72
|
|
73
73
|
end
|
data/lib/restforce/db/dsl.rb
CHANGED
@@ -97,17 +97,17 @@ module Restforce
|
|
97
97
|
# Public: Define a set of adapters which should be used to translate data
|
98
98
|
# between the database and Salesforce.
|
99
99
|
#
|
100
|
-
#
|
101
|
-
#
|
100
|
+
# adapter - An adapter object, which converts an attribute Hash between
|
101
|
+
# normalized and database-ready formats.
|
102
102
|
#
|
103
|
-
# Raises ArgumentError if
|
103
|
+
# Raises ArgumentError if the adapter object has an incomplete interface.
|
104
104
|
# Returns nothing.
|
105
|
-
def
|
106
|
-
unless
|
107
|
-
raise ArgumentError, "
|
105
|
+
def converts_with(adapter)
|
106
|
+
unless adapter.respond_to?(:to_database) && adapter.respond_to?(:from_database)
|
107
|
+
raise ArgumentError, "Your adapter must implement `to_database` and `from_database` methods"
|
108
108
|
end
|
109
109
|
|
110
|
-
@mapping.
|
110
|
+
@mapping.adapter = adapter
|
111
111
|
end
|
112
112
|
|
113
113
|
end
|
data/lib/restforce/db/mapping.rb
CHANGED
@@ -14,7 +14,6 @@ module Restforce
|
|
14
14
|
:attribute_map,
|
15
15
|
:attributes,
|
16
16
|
:convert,
|
17
|
-
:convert_from_salesforce,
|
18
17
|
)
|
19
18
|
|
20
19
|
attr_reader(
|
@@ -25,8 +24,8 @@ module Restforce
|
|
25
24
|
)
|
26
25
|
|
27
26
|
attr_accessor(
|
27
|
+
:adapter,
|
28
28
|
:fields,
|
29
|
-
:conversions,
|
30
29
|
:associations,
|
31
30
|
:conditions,
|
32
31
|
:strategy,
|
@@ -44,8 +43,8 @@ module Restforce
|
|
44
43
|
@database_record_type = RecordTypes::ActiveRecord.new(database_model, self)
|
45
44
|
@salesforce_record_type = RecordTypes::Salesforce.new(salesforce_model, self)
|
46
45
|
|
46
|
+
self.adapter = Adapter.new
|
47
47
|
self.fields = {}
|
48
|
-
self.conversions = {}
|
49
48
|
self.associations = []
|
50
49
|
self.conditions = []
|
51
50
|
self.strategy = strategy
|
@@ -109,7 +108,7 @@ module Restforce
|
|
109
108
|
#
|
110
109
|
# Returns a Restforce::DB::AttributeMap.
|
111
110
|
def attribute_map
|
112
|
-
@attribute_map ||= AttributeMap.new(database_model, salesforce_model, fields,
|
111
|
+
@attribute_map ||= AttributeMap.new(database_model, salesforce_model, fields, adapter)
|
113
112
|
end
|
114
113
|
|
115
114
|
end
|
@@ -49,8 +49,8 @@ module Restforce
|
|
49
49
|
#
|
50
50
|
# Returns nothing.
|
51
51
|
def update(instance, accumulator)
|
52
|
-
|
53
|
-
attributes = @mapping.
|
52
|
+
current_attributes = accumulator.current(instance.attributes)
|
53
|
+
attributes = @mapping.convert(instance.record_type, current_attributes)
|
54
54
|
|
55
55
|
return if attributes.empty?
|
56
56
|
instance.update!(attributes)
|
data/lib/restforce/db/version.rb
CHANGED
data/lib/restforce/db.rb
CHANGED
data/restforce-db.gemspec
CHANGED
@@ -28,7 +28,7 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_dependency "daemons"
|
29
29
|
spec.add_dependency "restforce"
|
30
30
|
|
31
|
-
spec.add_development_dependency "bundler"
|
31
|
+
spec.add_development_dependency "bundler"
|
32
32
|
spec.add_development_dependency "database_cleaner"
|
33
33
|
spec.add_development_dependency "minitest", "5.5.1"
|
34
34
|
spec.add_development_dependency "minitest-spec-expect"
|
@@ -65,22 +65,19 @@ describe Restforce::DB::Accumulator do
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
describe "#
|
68
|
+
describe "#current" do
|
69
69
|
let(:attributes) { { wocket: "Pocket", jertain: "Curtain", zelf: "Shelf" } }
|
70
70
|
|
71
71
|
before do
|
72
72
|
accumulator.store(Time.now, attributes)
|
73
73
|
end
|
74
74
|
|
75
|
-
it "returns the
|
76
|
-
expect(accumulator.
|
77
|
-
|
75
|
+
it "returns the current values for all attributes in the passed hash" do
|
76
|
+
expect(accumulator.current(wocket: "Locket")).to_equal(wocket: "Pocket")
|
77
|
+
expect(accumulator.current(jertain: "Curtain", zelf: "Belfrey")).to_equal(
|
78
|
+
jertain: "Curtain",
|
78
79
|
zelf: "Shelf",
|
79
80
|
)
|
80
81
|
end
|
81
|
-
|
82
|
-
it "ignores attributes not set in the Accumulator" do
|
83
|
-
expect(accumulator.diff(yottle: "Bottle")).to_be :empty?
|
84
|
-
end
|
85
82
|
end
|
86
83
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative "../../../test_helper"
|
2
|
+
|
3
|
+
describe Restforce::DB::Adapter do
|
4
|
+
|
5
|
+
configure!
|
6
|
+
|
7
|
+
let(:adapter) { Restforce::DB::Adapter.new }
|
8
|
+
let(:attributes) { { where: "Here", when: Time.now } }
|
9
|
+
|
10
|
+
describe "#to_database" do
|
11
|
+
let(:results) { adapter.to_database(attributes) }
|
12
|
+
|
13
|
+
it "returns the passed attributes, unchanged" do
|
14
|
+
expect(results).to_equal attributes
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#from_database" do
|
19
|
+
let(:results) { adapter.from_database(attributes) }
|
20
|
+
|
21
|
+
it "converts times to ISO-8601 timestamps" do
|
22
|
+
expect(results[:when]).to_equal attributes[:when].utc.iso8601
|
23
|
+
end
|
24
|
+
|
25
|
+
it "leaves other attributes unchanged" do
|
26
|
+
expect(results[:where]).to_equal attributes[:where]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -12,8 +12,8 @@ describe Restforce::DB::AttributeMap do
|
|
12
12
|
column_two: "SF_Field_Two__c",
|
13
13
|
}
|
14
14
|
end
|
15
|
-
let(:
|
16
|
-
let(:attribute_map) { Restforce::DB::AttributeMap.new(database_model, salesforce_model, fields,
|
15
|
+
let(:adapter) { Restforce::DB::Adapter.new }
|
16
|
+
let(:attribute_map) { Restforce::DB::AttributeMap.new(database_model, salesforce_model, fields, adapter) }
|
17
17
|
|
18
18
|
describe "#attributes" do
|
19
19
|
let(:mapping) do
|
@@ -28,7 +28,7 @@ describe Restforce::DB::AttributeMap do
|
|
28
28
|
attribute
|
29
29
|
end
|
30
30
|
|
31
|
-
expect(attributes.keys).to_equal(mapping.
|
31
|
+
expect(attributes.keys).to_equal(mapping.salesforce_fields)
|
32
32
|
expect(attributes.values).to_equal(mapping.database_fields)
|
33
33
|
end
|
34
34
|
|
@@ -38,82 +38,32 @@ describe Restforce::DB::AttributeMap do
|
|
38
38
|
attribute
|
39
39
|
end
|
40
40
|
|
41
|
-
expect(attributes.keys).to_equal(mapping.
|
41
|
+
expect(attributes.keys).to_equal(mapping.salesforce_fields)
|
42
42
|
expect(attributes.values).to_equal(mapping.salesforce_fields)
|
43
43
|
end
|
44
|
-
|
45
|
-
describe "when an adapter has been defined for an attribute" do
|
46
|
-
let(:conversions) { { column_one: boolean_adapter } }
|
47
|
-
|
48
|
-
it "uses the adapter to convert the value returned by the block" do
|
49
|
-
attributes = attribute_map.attributes(salesforce_model) { |_| "Yes" }
|
50
|
-
expect(attributes).to_equal(
|
51
|
-
column_one: true,
|
52
|
-
column_two: "Yes",
|
53
|
-
)
|
54
|
-
end
|
55
|
-
end
|
56
44
|
end
|
57
45
|
|
58
46
|
describe "#convert" do
|
59
|
-
let(:attributes) { {
|
47
|
+
let(:attributes) { { "SF_Field_One__c" => "some value" } }
|
60
48
|
|
61
49
|
it "converts an attribute Hash to a Salesforce-compatible form" do
|
62
|
-
expect(attribute_map.convert(salesforce_model, attributes)).to_equal(
|
63
|
-
fields[attributes.keys.first] => attributes.values.first,
|
64
|
-
)
|
65
|
-
end
|
66
|
-
|
67
|
-
it "performs no special conversion for database columns" do
|
68
|
-
expect(attribute_map.convert(database_model, attributes)).to_equal(attributes)
|
69
|
-
end
|
70
|
-
|
71
|
-
describe "when one of the attributes is a Date or Time" do
|
72
|
-
let(:timestamp) { Time.now }
|
73
|
-
let(:attributes) { { column_one: timestamp } }
|
74
|
-
|
75
|
-
it "converts the attribute to an ISO-8601 string for Salesforce" do
|
76
|
-
expect(attribute_map.convert(salesforce_model, attributes)).to_equal(
|
77
|
-
fields[attributes.keys.first] => timestamp.iso8601,
|
78
|
-
)
|
79
|
-
end
|
50
|
+
expect(attribute_map.convert(salesforce_model, attributes)).to_equal(attributes)
|
80
51
|
end
|
81
52
|
|
82
|
-
describe "when an adapter has been defined for an attribute" do
|
83
|
-
let(:conversions) { { column_one: boolean_adapter } }
|
84
|
-
|
85
|
-
it "uses the adapter to convert that attribute to a Salesforce-compatible form" do
|
86
|
-
expect(attribute_map.convert(salesforce_model, column_one: true)).to_equal(
|
87
|
-
"SF_Field_One__c" => "Yes",
|
88
|
-
)
|
89
|
-
expect(attribute_map.convert(salesforce_model, column_one: false)).to_equal(
|
90
|
-
"SF_Field_One__c" => "No",
|
91
|
-
)
|
92
|
-
end
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
describe "#convert_from_salesforce" do
|
97
|
-
let(:attributes) { { "SF_Field_One__c" => "some value" } }
|
98
|
-
|
99
53
|
it "converts an attribute Hash to a database-compatible form" do
|
100
|
-
expect(attribute_map.
|
54
|
+
expect(attribute_map.convert(database_model, attributes)).to_equal(
|
101
55
|
fields.key(attributes.keys.first) => attributes.values.first,
|
102
56
|
)
|
103
57
|
end
|
104
58
|
|
105
|
-
|
106
|
-
|
107
|
-
end
|
108
|
-
|
109
|
-
describe "when an adapter has been defined for an attribute" do
|
110
|
-
let(:conversions) { { column_one: boolean_adapter } }
|
59
|
+
describe "when an adapter has been specified" do
|
60
|
+
let(:adapter) { boolean_adapter }
|
111
61
|
|
112
|
-
it "uses the adapter to convert
|
113
|
-
expect(attribute_map.
|
62
|
+
it "uses the adapter to convert attributes to a database-compatible form" do
|
63
|
+
expect(attribute_map.convert(database_model, "SF_Field_One__c" => "Yes")).to_equal(
|
114
64
|
column_one: true,
|
115
65
|
)
|
116
|
-
expect(attribute_map.
|
66
|
+
expect(attribute_map.convert(database_model, "SF_Field_One__c" => "No")).to_equal(
|
117
67
|
column_one: false,
|
118
68
|
)
|
119
69
|
end
|
@@ -99,20 +99,19 @@ describe Restforce::DB::DSL do
|
|
99
99
|
end
|
100
100
|
end
|
101
101
|
|
102
|
-
describe "#
|
102
|
+
describe "#converts_with" do
|
103
103
|
let(:adapter) { boolean_adapter }
|
104
|
-
let(:conversions) { { some: adapter } }
|
105
104
|
|
106
105
|
it "sets the conversions for the created mapping" do
|
107
|
-
dsl.
|
108
|
-
expect(mapping.
|
106
|
+
dsl.converts_with adapter
|
107
|
+
expect(mapping.adapter).to_equal adapter
|
109
108
|
end
|
110
109
|
|
111
110
|
describe "when the adapter does not define the expected methods" do
|
112
111
|
let(:adapter) { Object.new }
|
113
112
|
|
114
113
|
it "raises an ArgumentError" do
|
115
|
-
expect(-> { dsl.
|
114
|
+
expect(-> { dsl.converts_with adapter }).to_raise ArgumentError
|
116
115
|
end
|
117
116
|
end
|
118
117
|
end
|
@@ -11,8 +11,8 @@ describe Restforce::DB::RecordTypes::ActiveRecord do
|
|
11
11
|
describe "#create!" do
|
12
12
|
let(:attributes) do
|
13
13
|
{
|
14
|
-
|
15
|
-
|
14
|
+
"Name" => "Some name",
|
15
|
+
"Example_Field__c" => "Some text",
|
16
16
|
}
|
17
17
|
end
|
18
18
|
let(:record) { nil }
|
@@ -32,8 +32,8 @@ describe Restforce::DB::RecordTypes::ActiveRecord do
|
|
32
32
|
|
33
33
|
it "creates a record in the database from the passed Salesforce record's attributes" do
|
34
34
|
expect(instance.salesforce_id).to_equal salesforce_id
|
35
|
-
expect(instance.name).to_equal attributes[
|
36
|
-
expect(instance.example).to_equal attributes[
|
35
|
+
expect(instance.name).to_equal attributes["Name"]
|
36
|
+
expect(instance.example).to_equal attributes["Example_Field__c"]
|
37
37
|
expect(instance.synchronized_at).to_not_be_nil
|
38
38
|
end
|
39
39
|
|
@@ -64,7 +64,7 @@ describe Restforce::DB::RecordTypes::ActiveRecord do
|
|
64
64
|
id,
|
65
65
|
Time.now,
|
66
66
|
Restforce::DB::Registry[User].first,
|
67
|
-
|
67
|
+
"Email" => "somebody@example.com",
|
68
68
|
)
|
69
69
|
end
|
70
70
|
end
|
@@ -10,20 +10,15 @@ describe Restforce::DB::Synchronizer do
|
|
10
10
|
describe "#run", vcr: { match_requests_on: [:method, VCR.request_matchers.uri_without_param(:q)] } do
|
11
11
|
let(:attributes) do
|
12
12
|
{
|
13
|
-
|
14
|
-
|
13
|
+
"Name" => "Custom object",
|
14
|
+
"Example_Field__c" => "Some sample text",
|
15
15
|
}
|
16
16
|
end
|
17
|
-
let(:salesforce_id)
|
18
|
-
Salesforce.create!(
|
19
|
-
salesforce_model,
|
20
|
-
mapping.convert(salesforce_model, attributes),
|
21
|
-
)
|
22
|
-
end
|
17
|
+
let(:salesforce_id) { Salesforce.create!(salesforce_model, attributes) }
|
23
18
|
let(:changes) { { [salesforce_id, salesforce_model] => accumulator } }
|
24
19
|
let(:new_attributes) do
|
25
20
|
{
|
26
|
-
"Name"
|
21
|
+
"Name" => "Some new name",
|
27
22
|
"Example_Field__c" => "New sample text",
|
28
23
|
}
|
29
24
|
end
|
data/test/support/utilities.rb
CHANGED
@@ -31,12 +31,16 @@ end
|
|
31
31
|
def boolean_adapter
|
32
32
|
Object.new.tap do |object|
|
33
33
|
|
34
|
-
def object.to_database(
|
35
|
-
|
34
|
+
def object.to_database(attributes)
|
35
|
+
attributes.each_with_object({}) do |(k, v), final|
|
36
|
+
final[k] = (v == "Yes")
|
37
|
+
end
|
36
38
|
end
|
37
39
|
|
38
|
-
def object.
|
39
|
-
|
40
|
+
def object.from_database(attributes)
|
41
|
+
attributes.each_with_object({}) do |(k, v), final|
|
42
|
+
final[k] = v ? "Yes" : "No"
|
43
|
+
end
|
40
44
|
end
|
41
45
|
|
42
46
|
end
|
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:
|
4
|
+
version: 2.0.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-05-
|
11
|
+
date: 2015-05-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -56,16 +56,16 @@ dependencies:
|
|
56
56
|
name: bundler
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: database_cleaner
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -200,6 +200,7 @@ files:
|
|
200
200
|
- Rakefile
|
201
201
|
- bin/console
|
202
202
|
- bin/setup
|
203
|
+
- circle.yml
|
203
204
|
- lib/file_daemon.rb
|
204
205
|
- lib/generators/restforce/install_generator.rb
|
205
206
|
- lib/generators/restforce/migration_generator.rb
|
@@ -208,6 +209,7 @@ files:
|
|
208
209
|
- lib/generators/templates/script
|
209
210
|
- lib/restforce/db.rb
|
210
211
|
- lib/restforce/db/accumulator.rb
|
212
|
+
- lib/restforce/db/adapter.rb
|
211
213
|
- lib/restforce/db/association_cache.rb
|
212
214
|
- lib/restforce/db/associations/base.rb
|
213
215
|
- lib/restforce/db/associations/belongs_to.rb
|
@@ -302,6 +304,7 @@ files:
|
|
302
304
|
- test/cassettes/Restforce_DB_Synchronizer/_run/given_a_Salesforce_record_with_an_associated_database_record/updates_the_salesforce_record.yml
|
303
305
|
- test/cassettes/Restforce_DB_Synchronizer/_run/given_a_Salesforce_record_with_no_associated_database_record/does_nothing_for_this_specific_mapping.yml
|
304
306
|
- test/lib/restforce/db/accumulator_test.rb
|
307
|
+
- test/lib/restforce/db/adapter_test.rb
|
305
308
|
- test/lib/restforce/db/association_cache_test.rb
|
306
309
|
- test/lib/restforce/db/associations/belongs_to_test.rb
|
307
310
|
- test/lib/restforce/db/associations/has_many_test.rb
|