restforce-db 1.1.0 → 1.2.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 +21 -0
- data/lib/restforce/db/attribute_map.rb +45 -18
- data/lib/restforce/db/dsl.rb +16 -0
- data/lib/restforce/db/mapping.rb +3 -1
- data/lib/restforce/db/version.rb +1 -1
- data/test/lib/restforce/db/attribute_map_test.rb +40 -2
- data/test/lib/restforce/db/dsl_test.rb +18 -0
- data/test/support/utilities.rb +15 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: df2303b0a1b785e92719f690b7d5ea5f6ec9ad09
|
4
|
+
data.tar.gz: 1add9035c044ff8022562d547c1cab48c6c35299
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e7de9ccdec2e054a494cb31d1caf00636e0893de35dd474aaa11862b42b2028be1fa8c50174a600594fa61e23284f7f0510946340c8f19f4bfbcb02d2a9a9c44
|
7
|
+
data.tar.gz: dbae2200380469cd1ef662a9b05f3324309f37e6ac0a6615bb41cedb94b7e99b68e080207728eccdb45c0ec3245f4c8093196ecee698bc9523a6714b768264fc
|
data/README.md
CHANGED
@@ -42,14 +42,31 @@ class Restaurant < ActiveRecord::Base
|
|
42
42
|
has_one :chef, inverse_of: :restaurant
|
43
43
|
has_many :dishes, inverse_of: :restaurant
|
44
44
|
|
45
|
+
module StyleAdapter
|
46
|
+
|
47
|
+
def self.to_salesforce(value)
|
48
|
+
"#{value} in Salesforce"
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.to_database(value)
|
52
|
+
value.chomp(" in Salesforce")
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
45
57
|
sync_with("Restaurant__c", :always) do
|
46
58
|
where "StarRating__c > 4"
|
47
59
|
has_many :dishes, through: "Restaurant__c"
|
48
60
|
belongs_to :chef, through: %w(Chef__c Cuisine__c)
|
61
|
+
|
49
62
|
maps(
|
50
63
|
name: "Name",
|
51
64
|
style: "Style__c",
|
52
65
|
)
|
66
|
+
|
67
|
+
converts(
|
68
|
+
style: StyleAdapter,
|
69
|
+
)
|
53
70
|
end
|
54
71
|
|
55
72
|
end
|
@@ -118,6 +135,10 @@ Individual conditions supplied to `where` will be appended together with `AND` c
|
|
118
135
|
|
119
136
|
`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.
|
120
137
|
|
138
|
+
#### Field Conversions
|
139
|
+
|
140
|
+
`converts` defines a set of value adapters. It takes a Hash as an argument; the keys should line up with the ActiveRecord attribute names defined in your `maps` clause, while the values should be the corresponding adapter objects. The only requirement for an adapter is that it respond to the methods `#to_database` and `#to_salesforce`.
|
141
|
+
|
121
142
|
#### Associations
|
122
143
|
|
123
144
|
Associations in `Restforce::DB` can be a little tricky, as they depend on your ActiveRecord association mappings, but are independent of those mappings, and can even (as seen above) seem to conflict with them.
|
@@ -6,16 +6,38 @@ module Restforce
|
|
6
6
|
# various representations of attribute hashes.
|
7
7
|
class AttributeMap
|
8
8
|
|
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
|
+
|
9
27
|
# Public: Initialize a Restforce::DB::AttributeMap.
|
10
28
|
#
|
11
29
|
# database_model - A Class compatible with ActiveRecord::Base.
|
12
30
|
# salesforce_model - A String name of an object type in Salesforce.
|
13
31
|
# fields - A Hash of mappings between database columns and
|
14
32
|
# fields in Salesforce.
|
15
|
-
|
33
|
+
# conversions - A Hash of mappings between database columns and the
|
34
|
+
# corresponding adapter objects which should be used to
|
35
|
+
# convert between data formats.
|
36
|
+
def initialize(database_model, salesforce_model, fields = {}, conversions = {})
|
16
37
|
@database_model = database_model
|
17
38
|
@salesforce_model = salesforce_model
|
18
39
|
@fields = fields
|
40
|
+
@conversions = conversions
|
19
41
|
|
20
42
|
@types = {
|
21
43
|
database_model => :database,
|
@@ -33,19 +55,17 @@ module Restforce
|
|
33
55
|
# Yields a series of attribute names.
|
34
56
|
# Returns a Hash.
|
35
57
|
def attributes(from_format)
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
when :database
|
41
|
-
# Generate a mapping of database column names to record attributes.
|
42
|
-
@fields.keys.zip(@fields.keys)
|
43
|
-
else
|
44
|
-
raise ArgumentError
|
58
|
+
case @types[from_format]
|
59
|
+
when :salesforce
|
60
|
+
@fields.each_with_object({}) do |(attribute, mapping), values|
|
61
|
+
values[attribute] = adapter(attribute).to_database(yield(mapping))
|
45
62
|
end
|
46
|
-
|
47
|
-
|
48
|
-
|
63
|
+
when :database
|
64
|
+
@fields.keys.each_with_object({}) do |attribute, values|
|
65
|
+
values[attribute] = yield(attribute)
|
66
|
+
end
|
67
|
+
else
|
68
|
+
raise ArgumentError
|
49
69
|
end
|
50
70
|
end
|
51
71
|
|
@@ -79,10 +99,7 @@ module Restforce
|
|
79
99
|
when :salesforce
|
80
100
|
@fields.each_with_object({}) do |(attribute, mapping), converted|
|
81
101
|
next unless attributes.key?(attribute)
|
82
|
-
value = attributes[attribute]
|
83
|
-
value = value.respond_to?(:utc) ? value.utc : value
|
84
|
-
value = value.respond_to?(:iso8601) ? value.iso8601 : value
|
85
|
-
|
102
|
+
value = adapter(attribute).to_salesforce(attributes[attribute])
|
86
103
|
converted[mapping] = value
|
87
104
|
end
|
88
105
|
else
|
@@ -124,7 +141,8 @@ module Restforce
|
|
124
141
|
when :database
|
125
142
|
@fields.each_with_object({}) do |(attribute, mapping), converted|
|
126
143
|
next unless attributes.key?(mapping)
|
127
|
-
|
144
|
+
value = adapter(attribute).to_database(attributes[mapping])
|
145
|
+
converted[attribute] = value
|
128
146
|
end
|
129
147
|
when :salesforce
|
130
148
|
attributes.dup
|
@@ -133,6 +151,15 @@ module Restforce
|
|
133
151
|
end
|
134
152
|
end
|
135
153
|
|
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
|
+
|
136
163
|
end
|
137
164
|
|
138
165
|
end
|
data/lib/restforce/db/dsl.rb
CHANGED
@@ -85,6 +85,22 @@ module Restforce
|
|
85
85
|
@mapping.fields = fields
|
86
86
|
end
|
87
87
|
|
88
|
+
# Public: Define a set of adapters which should be used to translate data
|
89
|
+
# between the database and Salesforce.
|
90
|
+
#
|
91
|
+
# fields - A Hash, with keys corresponding to attributes of the database
|
92
|
+
# record, and adapter objects as values.
|
93
|
+
#
|
94
|
+
# Raises ArgumentError if any adapter object has an incomplete interface.
|
95
|
+
# Returns nothing.
|
96
|
+
def converts(conversions)
|
97
|
+
unless conversions.values.all? { |c| c.respond_to?(:to_database) && c.respond_to?(:to_salesforce) }
|
98
|
+
raise ArgumentError, "All adapters must implement `to_database` and `to_salesforce` methods"
|
99
|
+
end
|
100
|
+
|
101
|
+
@mapping.conversions = conversions
|
102
|
+
end
|
103
|
+
|
88
104
|
end
|
89
105
|
|
90
106
|
end
|
data/lib/restforce/db/mapping.rb
CHANGED
@@ -26,6 +26,7 @@ module Restforce
|
|
26
26
|
|
27
27
|
attr_accessor(
|
28
28
|
:fields,
|
29
|
+
:conversions,
|
29
30
|
:associations,
|
30
31
|
:conditions,
|
31
32
|
:strategy,
|
@@ -44,6 +45,7 @@ module Restforce
|
|
44
45
|
@salesforce_record_type = RecordTypes::Salesforce.new(salesforce_model, self)
|
45
46
|
|
46
47
|
self.fields = {}
|
48
|
+
self.conversions = {}
|
47
49
|
self.associations = []
|
48
50
|
self.conditions = []
|
49
51
|
self.strategy = strategy
|
@@ -91,7 +93,7 @@ module Restforce
|
|
91
93
|
#
|
92
94
|
# Returns a Restforce::DB::AttributeMap.
|
93
95
|
def attribute_map
|
94
|
-
@attribute_map ||= AttributeMap.new(database_model, salesforce_model, fields)
|
96
|
+
@attribute_map ||= AttributeMap.new(database_model, salesforce_model, fields, conversions)
|
95
97
|
end
|
96
98
|
|
97
99
|
end
|
data/lib/restforce/db/version.rb
CHANGED
@@ -12,8 +12,8 @@ describe Restforce::DB::AttributeMap do
|
|
12
12
|
column_two: "SF_Field_Two__c",
|
13
13
|
}
|
14
14
|
end
|
15
|
-
|
16
|
-
let(:attribute_map) { Restforce::DB::AttributeMap.new(database_model, salesforce_model, fields) }
|
15
|
+
let(:conversions) { {} }
|
16
|
+
let(:attribute_map) { Restforce::DB::AttributeMap.new(database_model, salesforce_model, fields, conversions) }
|
17
17
|
|
18
18
|
describe "#attributes" do
|
19
19
|
let(:mapping) do
|
@@ -41,6 +41,18 @@ describe Restforce::DB::AttributeMap do
|
|
41
41
|
expect(attributes.keys).to_equal(mapping.database_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
|
44
56
|
end
|
45
57
|
|
46
58
|
describe "#convert" do
|
@@ -66,6 +78,19 @@ describe Restforce::DB::AttributeMap do
|
|
66
78
|
)
|
67
79
|
end
|
68
80
|
end
|
81
|
+
|
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
|
69
94
|
end
|
70
95
|
|
71
96
|
describe "#convert_from_salesforce" do
|
@@ -80,6 +105,19 @@ describe Restforce::DB::AttributeMap do
|
|
80
105
|
it "performs no special conversion for Salesforce fields" do
|
81
106
|
expect(attribute_map.convert_from_salesforce(salesforce_model, attributes)).to_equal(attributes)
|
82
107
|
end
|
108
|
+
|
109
|
+
describe "when an adapter has been defined for an attribute" do
|
110
|
+
let(:conversions) { { column_one: boolean_adapter } }
|
111
|
+
|
112
|
+
it "uses the adapter to convert that attribute to a database-compatible form" do
|
113
|
+
expect(attribute_map.convert_from_salesforce(database_model, "SF_Field_One__c" => "Yes")).to_equal(
|
114
|
+
column_one: true,
|
115
|
+
)
|
116
|
+
expect(attribute_map.convert_from_salesforce(database_model, "SF_Field_One__c" => "No")).to_equal(
|
117
|
+
column_one: false,
|
118
|
+
)
|
119
|
+
end
|
120
|
+
end
|
83
121
|
end
|
84
122
|
|
85
123
|
end
|
@@ -96,4 +96,22 @@ describe Restforce::DB::DSL do
|
|
96
96
|
expect(mapping.fields).to_equal fields
|
97
97
|
end
|
98
98
|
end
|
99
|
+
|
100
|
+
describe "#converts" do
|
101
|
+
let(:adapter) { boolean_adapter }
|
102
|
+
let(:conversions) { { some: adapter } }
|
103
|
+
|
104
|
+
it "sets the conversions for the created mapping" do
|
105
|
+
dsl.converts conversions
|
106
|
+
expect(mapping.conversions).to_equal conversions
|
107
|
+
end
|
108
|
+
|
109
|
+
describe "when the adapter does not define the expected methods" do
|
110
|
+
let(:adapter) { Object.new }
|
111
|
+
|
112
|
+
it "raises an ArgumentError" do
|
113
|
+
expect(-> { dsl.converts conversions }).to_raise ArgumentError
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
99
117
|
end
|
data/test/support/utilities.rb
CHANGED
@@ -26,3 +26,18 @@ def mappings!
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
29
|
+
|
30
|
+
# :nodoc:
|
31
|
+
def boolean_adapter
|
32
|
+
Object.new.tap do |object|
|
33
|
+
|
34
|
+
def object.to_database(value)
|
35
|
+
value == "Yes"
|
36
|
+
end
|
37
|
+
|
38
|
+
def object.to_salesforce(value)
|
39
|
+
value ? "Yes" : "No"
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
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: 1.
|
4
|
+
version: 1.2.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-
|
11
|
+
date: 2015-04-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|