ledger_sync 1.3.2 → 1.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +6 -3
- data/README.md +119 -23
- data/ledger_sync.gemspec +1 -0
- data/lib/ledger_sync.rb +1 -1
- data/lib/ledger_sync/adaptors/ledger_serializer.rb +2 -10
- data/lib/ledger_sync/adaptors/mixins/infer_ledger_serializer_mixin.rb +19 -5
- data/lib/ledger_sync/adaptors/mixins/infer_validation_contract_mixin.rb +27 -0
- data/lib/ledger_sync/adaptors/netsuite/config.rb +0 -1
- data/lib/ledger_sync/adaptors/netsuite/operation.rb +0 -6
- data/lib/ledger_sync/adaptors/operation.rb +48 -87
- data/lib/ledger_sync/adaptors/quickbooks_online/bill/ledger_serializer.rb +3 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/bill/operations/create.rb +1 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/bill/operations/find.rb +1 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/bill/operations/update.rb +1 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/bill_line_item/ledger_serializer.rb +3 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/dashboard_url_helper.rb +2 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/deposit_line_item/ledger_serializer.rb +3 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/expense/ledger_serializer.rb +3 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/expense/operations/create.rb +1 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/expense/operations/find.rb +1 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/expense/operations/update.rb +1 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/expense_line_item/ledger_serializer.rb +3 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/journal_entry_line_item/ledger_serializer.rb +6 -0
- data/lib/ledger_sync/adaptors/quickbooks_online/operation.rb +2 -2
- data/lib/ledger_sync/adaptors/quickbooks_online/operation/create.rb +1 -1
- data/lib/ledger_sync/adaptors/quickbooks_online/operation/find.rb +1 -1
- data/lib/ledger_sync/adaptors/quickbooks_online/operation/full_update.rb +1 -1
- data/lib/ledger_sync/adaptors/quickbooks_online/operation/sparse_update.rb +1 -1
- data/lib/ledger_sync/adaptors/stripe/config.rb +0 -1
- data/lib/ledger_sync/adaptors/test/customer/ledger_serializer.rb +12 -0
- data/lib/ledger_sync/adaptors/test/ledger_serializer.rb +0 -52
- data/lib/ledger_sync/core_ext/resonad.rb +0 -1
- data/lib/ledger_sync/error.rb +33 -0
- data/lib/ledger_sync/resources/bill.rb +2 -0
- data/lib/ledger_sync/resources/bill_line_item.rb +2 -0
- data/lib/ledger_sync/resources/deposit_line_item.rb +3 -0
- data/lib/ledger_sync/resources/expense.rb +2 -0
- data/lib/ledger_sync/resources/expense_line_item.rb +2 -0
- data/lib/ledger_sync/resources/journal_entry_line_item.rb +4 -0
- data/lib/ledger_sync/version.rb +1 -1
- data/qa/netsuite/customer.rb +1 -1
- data/qa/netsuite/vendor.rb +1 -1
- metadata +17 -37
- data/lib/ledger_sync/adaptors/test/account/operations/create.rb +0 -57
- data/lib/ledger_sync/adaptors/test/account/operations/find.rb +0 -42
- data/lib/ledger_sync/adaptors/test/account/operations/invalid.rb +0 -26
- data/lib/ledger_sync/adaptors/test/account/operations/update.rb +0 -56
- data/lib/ledger_sync/adaptors/test/account/operations/valid.rb +0 -32
- data/lib/ledger_sync/adaptors/test/account/searcher.rb +0 -40
- data/lib/ledger_sync/adaptors/test/adaptor.rb +0 -47
- data/lib/ledger_sync/adaptors/test/config.rb +0 -6
- data/lib/ledger_sync/adaptors/test/customer/operations/create.rb +0 -52
- data/lib/ledger_sync/adaptors/test/customer/operations/find.rb +0 -38
- data/lib/ledger_sync/adaptors/test/customer/operations/invalid.rb +0 -22
- data/lib/ledger_sync/adaptors/test/customer/operations/update.rb +0 -22
- data/lib/ledger_sync/adaptors/test/customer/operations/valid.rb +0 -24
- data/lib/ledger_sync/adaptors/test/customer/searcher.rb +0 -40
- data/lib/ledger_sync/adaptors/test/error/adaptor_error/operations/throttle_error.rb +0 -28
- data/lib/ledger_sync/adaptors/test/expense/operations/create.rb +0 -29
- data/lib/ledger_sync/adaptors/test/expense/operations/find.rb +0 -29
- data/lib/ledger_sync/adaptors/test/expense/operations/update.rb +0 -27
- data/lib/ledger_sync/adaptors/test/operation/create.rb +0 -27
- data/lib/ledger_sync/adaptors/test/operation/find.rb +0 -29
- data/lib/ledger_sync/adaptors/test/operation/update.rb +0 -34
- data/lib/ledger_sync/adaptors/test/payment/operations/create.rb +0 -51
- data/lib/ledger_sync/adaptors/test/payment/operations/find.rb +0 -44
- data/lib/ledger_sync/adaptors/test/payment/operations/update.rb +0 -28
- data/lib/ledger_sync/adaptors/test/transfer/operations/create.rb +0 -24
- data/lib/ledger_sync/adaptors/test/transfer/operations/find.rb +0 -24
- data/lib/ledger_sync/adaptors/test/transfer/operations/update.rb +0 -24
- data/lib/ledger_sync/adaptors/test/vendor/operations/create.rb +0 -56
- data/lib/ledger_sync/adaptors/test/vendor/operations/find.rb +0 -43
- data/lib/ledger_sync/adaptors/test/vendor/operations/invalid.rb +0 -27
- data/lib/ledger_sync/adaptors/test/vendor/operations/update.rb +0 -27
- data/lib/ledger_sync/adaptors/test/vendor/operations/valid.rb +0 -33
- data/lib/ledger_sync/adaptors/test/vendor/searcher.rb +0 -41
- data/lib/ledger_sync/util/debug.rb +0 -16
- data/qa/netsuite_test.rb +0 -42
- data/qa/test.rb +0 -67
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c159bdb17d877ede52d532d64ca436bf590fd64d06f96f3302003bbade4a5e9
|
4
|
+
data.tar.gz: 4146c094eb32435710901c88442ebab06e51bfb8b32144604c159a02f5287e33
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 87e52ac4566ad46fd64a32a67e4ca2221b5c19638bd6fd0b2347a43b9934e4c6e2952a84efc83ee414e05c07e384b2e8e9f72a785066619b7c8a8a9780939f10
|
7
|
+
data.tar.gz: f887774835cbfaa38bfcf9a6a7d4b69760961875e86bb6096fdc36e8bdf19bdcdc11a24777e9277c818c734eaf85f1d3eadbd1ef90bcffd483c99ae681ae52a2
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ledger_sync (1.3.
|
4
|
+
ledger_sync (1.3.3)
|
5
5
|
activemodel
|
6
6
|
colorize
|
7
7
|
coveralls
|
@@ -15,6 +15,7 @@ PATH
|
|
15
15
|
netsuite
|
16
16
|
nokogiri
|
17
17
|
oauth2
|
18
|
+
pd_ruby
|
18
19
|
resonad
|
19
20
|
simply_serializable (>= 1.3.0)
|
20
21
|
stripe
|
@@ -121,7 +122,7 @@ GEM
|
|
121
122
|
nokogiri (1.10.7)
|
122
123
|
mini_portile2 (~> 2.4.0)
|
123
124
|
nori (2.6.0)
|
124
|
-
oauth2 (1.4.
|
125
|
+
oauth2 (1.4.3)
|
125
126
|
faraday (>= 0.8, < 2.0)
|
126
127
|
jwt (>= 1.0, < 3.0)
|
127
128
|
multi_json (~> 1.3)
|
@@ -130,8 +131,10 @@ GEM
|
|
130
131
|
parallel (1.17.0)
|
131
132
|
parser (2.6.4.1)
|
132
133
|
ast (~> 2.4.0)
|
134
|
+
pd_ruby (0.2.0)
|
135
|
+
colorize
|
133
136
|
public_suffix (4.0.1)
|
134
|
-
rack (2.1.
|
137
|
+
rack (2.1.2)
|
135
138
|
rainbow (3.0.0)
|
136
139
|
rake (10.5.0)
|
137
140
|
resonad (1.2.0)
|
data/README.md
CHANGED
@@ -79,14 +79,12 @@ end
|
|
79
79
|
|
80
80
|
## How it Works
|
81
81
|
|
82
|
-
### The Library Structure
|
83
|
-
|
84
82
|
This library consists of two important layers:
|
85
83
|
|
86
84
|
1. Resources
|
87
85
|
2. Adaptors
|
88
86
|
|
89
|
-
|
87
|
+
### Resources
|
90
88
|
|
91
89
|
Resources are named ruby objects (e.g. `Customer`, `Payment`, etc.) with strict attributes (e.g. `name`, `amount`, etc.). They are a layer between your application and an adaptor. They can be validated using an adaptor. You can create and use the resources, and an adaptor will update resources as needed based on the intention and outcome of that operation.
|
92
90
|
|
@@ -96,11 +94,59 @@ Resources have defined attributes. Attributes are explicitly defined. An error
|
|
96
94
|
|
97
95
|
A subset of these `attributes` may be a `reference`, which is simply a special type of attribute that references another resource. You can retrieve the references of a resource by calling `LedgerSync::Customer.references`.
|
98
96
|
|
97
|
+
#### Custom Attributes
|
98
|
+
|
99
|
+
Some ledgers (e.g. NetSuite) allow for custom attributes (or "fields"), which can vary by account and user. To allow for custom attributes, you can create new resources, ledger serializers (see below), and validation contracts (see below). Assuming your ledger supports the string attribute `foo` for customers, you could do the following:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
class CustomCustomer < LedgerSync::Customer
|
103
|
+
attribute :foo, type: LedgerSync::Type::String
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
107
|
+
Depending on your use of LedgerSync, you may need to define resources dynamically with different custom attributes. You could do so using something like the following:
|
108
|
+
|
109
|
+
```ruby
|
110
|
+
custom_attributes_for_customers = [
|
111
|
+
[
|
112
|
+
[:foo, LedgerSync::Type::String]
|
113
|
+
],
|
114
|
+
[
|
115
|
+
[:foo, LedgerSync::Type::Integer],
|
116
|
+
[:bar, LedgerSync::Type::Boolean]
|
117
|
+
]
|
118
|
+
]
|
119
|
+
|
120
|
+
custom_customer_classes = custom_attributes_for_customers.map do |attributes|
|
121
|
+
klass = Class.new(LedgerSync::Customer)
|
122
|
+
attributes.each do |name, type|
|
123
|
+
klass.attribute name, type: type
|
124
|
+
end
|
125
|
+
klass
|
126
|
+
end
|
127
|
+
|
128
|
+
klass1, klass2 = custom_customer_classes
|
129
|
+
|
130
|
+
# First Custom Customer Class
|
131
|
+
klass1.resource_attributes.include?(:foo) # => true
|
132
|
+
klass1.resource_attributes[:foo].type # => #<LedgerSync::Type::String:0x00007fe04e9529b0 @precision=nil, @scale=nil, @limit=nil>
|
133
|
+
klass1.resource_attributes.include?(:bar) # => false
|
134
|
+
|
135
|
+
# Second Custom Customer Class
|
136
|
+
klass2.resource_attributes.include?(:foo) # => true
|
137
|
+
klass2.resource_attributes[:foo].type # => #<LedgerSync::Type::Integer:0x00007fe04e2c7898 @precision=nil, @scale=nil, @limit=nil, @range=-2147483648...2147483648>
|
138
|
+
klass2.resource_attributes.include?(:bar) # => true
|
139
|
+
klass2.resource_attributes[:bar].type # => #<LedgerSync::Type::Boolean:0x00007fe04e2e4f10 @precision=nil, @scale=nil, @limit=nil>
|
140
|
+
```
|
141
|
+
|
142
|
+
You can now use these custom resources in operations that require custom attributes.
|
143
|
+
|
99
144
|
### Adaptors
|
100
145
|
|
101
146
|
Adaptors are ledger-specific ruby objects that contain all the logic to authenticate to a ledger, perform ledger-specific operations, and validate resources based on the requirements of the ledger. Adaptors contain a number of useful objects:
|
102
147
|
|
103
148
|
- adaptor
|
149
|
+
- serializers
|
104
150
|
- operations
|
105
151
|
- searchers
|
106
152
|
|
@@ -108,6 +154,45 @@ Adaptors are ledger-specific ruby objects that contain all the logic to authenti
|
|
108
154
|
|
109
155
|
The adaptor handles authentication and requests to the ledger. Each adaptors initializer will vary based on the needs of that ledger.
|
110
156
|
|
157
|
+
#### Serializers
|
158
|
+
|
159
|
+
Operations depend on `LedgerSync::Adaptor::LedgerSerializer`s to serialize and deserialize objects. Most resources have a default serializer per adaptor which may acts as both a serializer and deserializer. You can provide custom serializers, which is necessary when working with custom attributes. For example, given the following:
|
160
|
+
- `custom_resource` that is a `LedgerSync::Customer` (see above)
|
161
|
+
- the attribute `foo` is used in both the request and response bodies
|
162
|
+
- using the `NetSuite` adaptor
|
163
|
+
|
164
|
+
you could implement custom serializers using the following code:
|
165
|
+
|
166
|
+
```ruby
|
167
|
+
class CustomSerializer < LedgerSync::Adaptors::NetSuite::Customer::LedgerSerializer
|
168
|
+
attribute ledger_attribute: :foo,
|
169
|
+
resource_attribute: :foo,
|
170
|
+
deserialize: true, # optional, default: true
|
171
|
+
serialize: true # optional, default: true
|
172
|
+
end
|
173
|
+
|
174
|
+
# Serializing
|
175
|
+
custom_resource = CustomCustomer.new(foo: 'asdf') # See above under Resources -> Custom Attributes
|
176
|
+
serializer = CustomSerializer.new(resource: custom_resource)
|
177
|
+
serializer.to_ledger_hash # => {"foo"=>"asdf"}
|
178
|
+
|
179
|
+
# Deserializing
|
180
|
+
deserialized_resource = serializer.deserialize(hash: { foo: 'qwerty' })
|
181
|
+
deserialized_resource.foo # => 'qwerty'
|
182
|
+
custom_resource.foo # => 'asdf'
|
183
|
+
|
184
|
+
op = LedgerSync::Adaptors::NetSuite::Customer::Operations::Create.new(
|
185
|
+
adaptor: adaptor,
|
186
|
+
ledger_deserializer_class: CustomSerializer, # You must specify, though you could use a separate class
|
187
|
+
ledger_serializer_class: CustomSerializer,
|
188
|
+
resource: custom_resource
|
189
|
+
)
|
190
|
+
```
|
191
|
+
|
192
|
+
Note that in the above example, we extend an existing customer serializer in the NetSuite adaptor. In most cases, serializers have the following inheritance pattern: `LedgerSync::Adaptors::[ADAPTOR]::[RESOURCE]::LedgerSerializer < LedgerSync::Adaptors::[ADAPTOR]::LedgerSerializer < LedgerSync::Adaptors::LedgerSerializer`
|
193
|
+
|
194
|
+
So in this example, it would be `LedgerSync::Adaptors::NetSuite::Customer::LedgerSerializer < LedgerSync::Adaptors::NetSuite::LedgerSerializer < LedgerSync::Adaptors::LedgerSerializer`. The more specific the serializer, the more helper methods are available that are adaptor and/or resource specific.
|
195
|
+
|
111
196
|
#### Operation
|
112
197
|
|
113
198
|
Each adaptor defines operations that can be performed on specific resources (e.g. `Customer::Operations::Update`, `Payment::Operations::Create`). The operation defines two key things:
|
@@ -117,6 +202,37 @@ Each adaptor defines operations that can be performed on specific resources (e.g
|
|
117
202
|
|
118
203
|
Note: Adaptors may support different operations for each resource type.
|
119
204
|
|
205
|
+
##### Contracts
|
206
|
+
|
207
|
+
Contracts are dry-validation schemas, which determine if an operation can be performed. You can create custom schemas and pass them to operations. Assuming you have an `operation_class` variable and `foo` is an attribute of a `custom_resource` (see above) that is required to be a string, you can implement it with the following:
|
208
|
+
|
209
|
+
```ruby
|
210
|
+
class CustomContract < LedgerSync::Adaptors::Contract
|
211
|
+
params do
|
212
|
+
required(:foo).filled(:string)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# A valid case
|
217
|
+
custom_resource = CustomResource.new(foo: 'asdf')
|
218
|
+
op = operation_class.new(
|
219
|
+
adaptor: adaptor,
|
220
|
+
resource: resource,
|
221
|
+
validation_contract: CustomContract
|
222
|
+
)
|
223
|
+
op.valid? # => true
|
224
|
+
|
225
|
+
# An invalid case
|
226
|
+
custom_resource = CustomResource.new(foo: nil)
|
227
|
+
operation_class.new(
|
228
|
+
adaptor: adaptor,
|
229
|
+
resource: resource,
|
230
|
+
validation_contract: CustomContract
|
231
|
+
)
|
232
|
+
op.valid? # => false
|
233
|
+
|
234
|
+
```
|
235
|
+
|
120
236
|
#### Searcher
|
121
237
|
|
122
238
|
Searchers are used to search objects in the ledger. A searcher takes an `adaptor`, `query` string and optional `pagination` hash. For example, to search customer's by name:
|
@@ -330,26 +446,6 @@ The serialization of any object follows the same structure. There is a `:root`
|
|
330
446
|
}
|
331
447
|
```
|
332
448
|
|
333
|
-
## Test Adaptor
|
334
|
-
|
335
|
-
LedgerSync offers a test adaptor `LedgerSync::Adaptors::Test::Adaptor` that you can easily use and stub without requiring API requests. For example:
|
336
|
-
|
337
|
-
```ruby
|
338
|
-
|
339
|
-
operation = LedgerSync::Adaptors::Test::Customer::Operations::Create.new(
|
340
|
-
adaptor: LedgerSync::Adaptors::Test::Adaptor.new,
|
341
|
-
resource: LedgerSync::Customer.new(name: 'Test Customer')
|
342
|
-
)
|
343
|
-
|
344
|
-
expect(operation).to be_valid
|
345
|
-
|
346
|
-
result = operation.perform
|
347
|
-
expect(result).to be_a(LedgerSync::OperationResult::Success)
|
348
|
-
expect(result).to be_success
|
349
|
-
|
350
|
-
expect { operation.perform }.to raise_error(PerformedOperationError)
|
351
|
-
```
|
352
|
-
|
353
449
|
## Development
|
354
450
|
|
355
451
|
After checking out the repo, run `bin/setup` to install dependencies.
|
data/ledger_sync.gemspec
CHANGED
@@ -50,6 +50,7 @@ Gem::Specification.new do |spec|
|
|
50
50
|
spec.add_runtime_dependency('netsuite')
|
51
51
|
spec.add_runtime_dependency('nokogiri', '>= 0')
|
52
52
|
spec.add_runtime_dependency('oauth2', '>= 0')
|
53
|
+
spec.add_runtime_dependency('pd_ruby', '>= 0')
|
53
54
|
spec.add_runtime_dependency('resonad', '>= 0')
|
54
55
|
spec.add_runtime_dependency('simply_serializable', '>= 1.3.0')
|
55
56
|
spec.add_runtime_dependency('stripe')
|
data/lib/ledger_sync.rb
CHANGED
@@ -14,6 +14,7 @@ require 'stripe'
|
|
14
14
|
require 'netsuite'
|
15
15
|
require 'oauth2'
|
16
16
|
require 'tempfile'
|
17
|
+
require 'pd_ruby'
|
17
18
|
|
18
19
|
# Dotenv
|
19
20
|
require 'dotenv'
|
@@ -34,7 +35,6 @@ Gem.find_files('ledger_sync/error/**/*.rb').each { |path| require path }
|
|
34
35
|
|
35
36
|
# Support Classes
|
36
37
|
require 'ledger_sync/util/resonad'
|
37
|
-
require 'ledger_sync/util/debug'
|
38
38
|
require 'ledger_sync/util/signer'
|
39
39
|
require 'ledger_sync/util/hash_helpers'
|
40
40
|
require 'ledger_sync/util/resources_builder'
|
@@ -12,9 +12,8 @@ module LedgerSync
|
|
12
12
|
|
13
13
|
attr_reader :resource
|
14
14
|
|
15
|
-
def initialize(
|
16
|
-
@resource = resource
|
17
|
-
ensure_inferred_resource_class! if ensure_inferred_resource_class
|
15
|
+
def initialize(**keywords)
|
16
|
+
@resource = keywords.fetch(:resource)
|
18
17
|
end
|
19
18
|
|
20
19
|
def attribute_value_from_ledger(hash:, ledger_serializer_attribute:, resource:)
|
@@ -139,13 +138,6 @@ module LedgerSync
|
|
139
138
|
type: type
|
140
139
|
)
|
141
140
|
end
|
142
|
-
|
143
|
-
private
|
144
|
-
|
145
|
-
def ensure_inferred_resource_class!
|
146
|
-
inferred_resource_class = self.class.inferred_resource_class
|
147
|
-
raise "Resource must be a #{inferred_resource_class.name}. Given #{resource.class}" unless resource.is_a?(inferred_resource_class)
|
148
|
-
end
|
149
141
|
end
|
150
142
|
end
|
151
143
|
end
|
@@ -5,19 +5,33 @@ module LedgerSync
|
|
5
5
|
module Mixins
|
6
6
|
module InferLedgerSerializerMixin
|
7
7
|
module ClassMethods
|
8
|
-
def
|
9
|
-
|
10
|
-
|
11
|
-
|
8
|
+
def inferred_ledger_deserializer_class
|
9
|
+
@inferred_ledger_deserializer_class ||= begin
|
10
|
+
inferred_adaptor_class.base_module.const_get(
|
11
|
+
inferred_ledger_deserializer_class_name
|
12
|
+
)
|
13
|
+
rescue NameError
|
14
|
+
inferred_adaptor_class.base_module.const_get(
|
15
|
+
inferred_ledger_serializer_class_name
|
16
|
+
)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def inferred_ledger_deserializer_class_name
|
21
|
+
@inferred_ledger_deserializer_class_name ||= "#{inferred_resource_class.resource_module_str}::LedgerDeserializer"
|
12
22
|
end
|
13
23
|
|
14
24
|
def inferred_ledger_serializer_class
|
15
25
|
@inferred_ledger_serializer_class ||= begin
|
16
26
|
inferred_adaptor_class.base_module.const_get(
|
17
|
-
|
27
|
+
inferred_ledger_serializer_class_name
|
18
28
|
)
|
19
29
|
end
|
20
30
|
end
|
31
|
+
|
32
|
+
def inferred_ledger_serializer_class_name
|
33
|
+
@inferred_ledger_serializer_class_name ||= "#{inferred_resource_class.resource_module_str}::LedgerSerializer"
|
34
|
+
end
|
21
35
|
end
|
22
36
|
|
23
37
|
def self.included(base)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LedgerSync
|
4
|
+
module Adaptors
|
5
|
+
module Mixins
|
6
|
+
module InferValidationContractMixin
|
7
|
+
module ClassMethods
|
8
|
+
def inferred_validation_contract_class
|
9
|
+
@inferred_validation_contract_class ||= begin
|
10
|
+
const_get(
|
11
|
+
inferred_validation_contract_class_name
|
12
|
+
)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def inferred_validation_contract_class_name
|
17
|
+
@inferred_validation_contract_class_name ||= "#{name}::Contract"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.included(base)
|
22
|
+
base.extend ClassMethods
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -11,12 +11,6 @@ module LedgerSync
|
|
11
11
|
end
|
12
12
|
|
13
13
|
module InstanceMethods
|
14
|
-
def ledger_deserializer
|
15
|
-
@ledger_deserializer ||= begin
|
16
|
-
modules = self.class.name.split('::Operations::').first
|
17
|
-
Object.const_get("#{modules}::LedgerDeserializer").new(resource: resource)
|
18
|
-
end
|
19
|
-
end
|
20
14
|
end
|
21
15
|
end
|
22
16
|
end
|
@@ -3,8 +3,6 @@
|
|
3
3
|
module LedgerSync
|
4
4
|
module Adaptors
|
5
5
|
module Operation
|
6
|
-
TYPES = %i[create delete find update].freeze
|
7
|
-
|
8
6
|
module Mixin
|
9
7
|
module ClassMethods
|
10
8
|
def adaptor_klass
|
@@ -14,95 +12,63 @@ module LedgerSync
|
|
14
12
|
def operations_module
|
15
13
|
@operations_module ||= Object.const_get(name.split('::Operations::').first + '::Operations')
|
16
14
|
end
|
17
|
-
|
18
|
-
def resource_klass
|
19
|
-
@resource_klass ||= LedgerSync.const_get(
|
20
|
-
name
|
21
|
-
.split("#{adaptor_klass.config.base_module.name}::")
|
22
|
-
.last
|
23
|
-
.split('::Operations')
|
24
|
-
.first
|
25
|
-
)
|
26
|
-
end
|
27
15
|
end
|
28
16
|
|
29
17
|
def self.included(base)
|
30
18
|
base.include SimplySerializable::Mixin
|
31
19
|
base.include Fingerprintable::Mixin
|
20
|
+
base.include Error::HelpersMixin
|
32
21
|
base.include Adaptors::Mixins::InferLedgerSerializerMixin
|
22
|
+
base.include Adaptors::Mixins::InferValidationContractMixin
|
33
23
|
base.extend ClassMethods
|
34
24
|
|
35
25
|
base.class_eval do
|
36
26
|
serialize only: %i[
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
root_operation
|
43
|
-
result
|
44
|
-
response
|
45
|
-
original
|
46
|
-
]
|
27
|
+
adaptor
|
28
|
+
resource
|
29
|
+
result
|
30
|
+
response
|
31
|
+
]
|
47
32
|
end
|
48
33
|
end
|
49
34
|
|
50
35
|
attr_reader :adaptor,
|
51
|
-
:after_operations,
|
52
|
-
:before_operations,
|
53
|
-
:operations,
|
54
36
|
:resource,
|
55
37
|
:resource_before_perform,
|
56
|
-
:root_operation,
|
57
38
|
:result,
|
58
|
-
:response
|
59
|
-
:original
|
60
|
-
|
61
|
-
def initialize(adaptor:, resource:)
|
62
|
-
raise 'Missing adaptor' if adaptor.nil?
|
63
|
-
raise 'Missing resource' if resource.nil?
|
39
|
+
:response
|
64
40
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
@
|
69
|
-
@
|
70
|
-
@
|
71
|
-
@resource = resource
|
41
|
+
def initialize(
|
42
|
+
**keywords
|
43
|
+
)
|
44
|
+
@adaptor = keywords.fetch(:adaptor)
|
45
|
+
@ledger_deserializer_class = keywords.fetch(:ledger_deserializer_class, nil)
|
46
|
+
@ledger_serializer_class = keywords.fetch(:ledger_serializer_class, nil)
|
47
|
+
@resource = keywords.fetch(:resource)
|
72
48
|
@resource_before_perform = resource.dup
|
73
49
|
@result = nil
|
74
|
-
@
|
75
|
-
end
|
50
|
+
@validation_contract = keywords.fetch(:validation_contract, nil)
|
76
51
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
def add_before_operation(operation)
|
83
|
-
@operations << operation
|
84
|
-
@before_operations << operation
|
85
|
-
end
|
86
|
-
|
87
|
-
def add_root_operation(operation)
|
88
|
-
@operations << operation
|
89
|
-
@root_operation = operation
|
52
|
+
self.class.raise_if_unexpected_class(expected: self.class.inferred_resource_class, given: @resource.class)
|
53
|
+
self.class.raise_if_unexpected_class(expected: LedgerSync::Adaptors::LedgerSerializer, given: ledger_deserializer_class) unless @ledger_deserializer_class.nil?
|
54
|
+
self.class.raise_if_unexpected_class(expected: LedgerSync::Adaptors::LedgerSerializer, given: ledger_serializer_class) unless @ledger_serializer_class.nil?
|
55
|
+
self.class.raise_if_unexpected_class(expected: LedgerSync::Adaptors::Contract, given: validation_contract) unless @validation_contract.nil?
|
90
56
|
end
|
91
57
|
|
92
58
|
def perform
|
93
59
|
failure(LedgerSync::Error::OperationError::PerformedOperationError.new(operation: self)) if @performed
|
94
60
|
|
95
|
-
begin
|
61
|
+
@result = begin
|
96
62
|
operate
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
63
|
+
rescue LedgerSync::Error => e
|
64
|
+
failure(e)
|
65
|
+
rescue StandardError => e
|
66
|
+
parsed_error = adaptor.parse_operation_error(error: e, operation: self)
|
67
|
+
raise e unless parsed_error
|
68
|
+
|
69
|
+
failure(parsed_error)
|
70
|
+
ensure
|
71
|
+
@performed = true
|
106
72
|
end
|
107
73
|
end
|
108
74
|
|
@@ -110,8 +76,20 @@ module LedgerSync
|
|
110
76
|
@performed == true
|
111
77
|
end
|
112
78
|
|
79
|
+
def ledger_deserializer
|
80
|
+
ledger_deserializer_class.new(resource: resource)
|
81
|
+
end
|
82
|
+
|
83
|
+
def ledger_deserializer_class
|
84
|
+
@ledger_deserializer_class ||= self.class.inferred_ledger_deserializer_class
|
85
|
+
end
|
86
|
+
|
113
87
|
def ledger_serializer
|
114
|
-
|
88
|
+
ledger_serializer_class.new(resource: resource)
|
89
|
+
end
|
90
|
+
|
91
|
+
def ledger_serializer_class
|
92
|
+
@ledger_serializer_class ||= self.class.inferred_ledger_serializer_class
|
115
93
|
end
|
116
94
|
|
117
95
|
# Results
|
@@ -149,14 +127,16 @@ module LedgerSync
|
|
149
127
|
end
|
150
128
|
|
151
129
|
def validate
|
152
|
-
raise "#{self.class.name}::Contract must be defined to validate." unless self.class.const_defined?('Contract')
|
153
|
-
|
154
130
|
Util::Validator.new(
|
155
|
-
contract:
|
131
|
+
contract: validation_contract,
|
156
132
|
data: validation_data
|
157
133
|
).validate
|
158
134
|
end
|
159
135
|
|
136
|
+
def validation_contract
|
137
|
+
@validation_contract ||= self.class.inferred_validation_contract_class
|
138
|
+
end
|
139
|
+
|
160
140
|
def validation_data
|
161
141
|
serializer = resource.serializer(
|
162
142
|
do_not_serialize_if_class_is: Resource::PRIMITIVES
|
@@ -177,14 +157,6 @@ module LedgerSync
|
|
177
157
|
true
|
178
158
|
end
|
179
159
|
|
180
|
-
# Type Methods
|
181
|
-
|
182
|
-
TYPES.each do |type|
|
183
|
-
define_method "#{type.to_s.downcase}?" do
|
184
|
-
false
|
185
|
-
end
|
186
|
-
end
|
187
|
-
|
188
160
|
private
|
189
161
|
|
190
162
|
def operate
|
@@ -192,17 +164,6 @@ module LedgerSync
|
|
192
164
|
end
|
193
165
|
end
|
194
166
|
|
195
|
-
TYPES.each do |type|
|
196
|
-
klass = Class.new do
|
197
|
-
include Operation::Mixin
|
198
|
-
|
199
|
-
define_method("#{type.to_s.downcase}?") do
|
200
|
-
true
|
201
|
-
end
|
202
|
-
end
|
203
|
-
Operation.const_set(LedgerSync::Util::StringHelpers.camelcase(type.to_s), klass)
|
204
|
-
end
|
205
|
-
|
206
167
|
def self.klass_from(adaptor:, method:, object:)
|
207
168
|
adaptor.base_module.const_get(
|
208
169
|
LedgerSync::Util::StringHelpers.camelcase(object)
|