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