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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +6 -3
  3. data/README.md +119 -23
  4. data/ledger_sync.gemspec +1 -0
  5. data/lib/ledger_sync.rb +1 -1
  6. data/lib/ledger_sync/adaptors/ledger_serializer.rb +2 -10
  7. data/lib/ledger_sync/adaptors/mixins/infer_ledger_serializer_mixin.rb +19 -5
  8. data/lib/ledger_sync/adaptors/mixins/infer_validation_contract_mixin.rb +27 -0
  9. data/lib/ledger_sync/adaptors/netsuite/config.rb +0 -1
  10. data/lib/ledger_sync/adaptors/netsuite/operation.rb +0 -6
  11. data/lib/ledger_sync/adaptors/operation.rb +48 -87
  12. data/lib/ledger_sync/adaptors/quickbooks_online/bill/ledger_serializer.rb +3 -0
  13. data/lib/ledger_sync/adaptors/quickbooks_online/bill/operations/create.rb +1 -0
  14. data/lib/ledger_sync/adaptors/quickbooks_online/bill/operations/find.rb +1 -0
  15. data/lib/ledger_sync/adaptors/quickbooks_online/bill/operations/update.rb +1 -0
  16. data/lib/ledger_sync/adaptors/quickbooks_online/bill_line_item/ledger_serializer.rb +3 -0
  17. data/lib/ledger_sync/adaptors/quickbooks_online/dashboard_url_helper.rb +2 -0
  18. data/lib/ledger_sync/adaptors/quickbooks_online/deposit_line_item/ledger_serializer.rb +3 -0
  19. data/lib/ledger_sync/adaptors/quickbooks_online/expense/ledger_serializer.rb +3 -0
  20. data/lib/ledger_sync/adaptors/quickbooks_online/expense/operations/create.rb +1 -0
  21. data/lib/ledger_sync/adaptors/quickbooks_online/expense/operations/find.rb +1 -0
  22. data/lib/ledger_sync/adaptors/quickbooks_online/expense/operations/update.rb +1 -0
  23. data/lib/ledger_sync/adaptors/quickbooks_online/expense_line_item/ledger_serializer.rb +3 -0
  24. data/lib/ledger_sync/adaptors/quickbooks_online/journal_entry_line_item/ledger_serializer.rb +6 -0
  25. data/lib/ledger_sync/adaptors/quickbooks_online/operation.rb +2 -2
  26. data/lib/ledger_sync/adaptors/quickbooks_online/operation/create.rb +1 -1
  27. data/lib/ledger_sync/adaptors/quickbooks_online/operation/find.rb +1 -1
  28. data/lib/ledger_sync/adaptors/quickbooks_online/operation/full_update.rb +1 -1
  29. data/lib/ledger_sync/adaptors/quickbooks_online/operation/sparse_update.rb +1 -1
  30. data/lib/ledger_sync/adaptors/stripe/config.rb +0 -1
  31. data/lib/ledger_sync/adaptors/test/customer/ledger_serializer.rb +12 -0
  32. data/lib/ledger_sync/adaptors/test/ledger_serializer.rb +0 -52
  33. data/lib/ledger_sync/core_ext/resonad.rb +0 -1
  34. data/lib/ledger_sync/error.rb +33 -0
  35. data/lib/ledger_sync/resources/bill.rb +2 -0
  36. data/lib/ledger_sync/resources/bill_line_item.rb +2 -0
  37. data/lib/ledger_sync/resources/deposit_line_item.rb +3 -0
  38. data/lib/ledger_sync/resources/expense.rb +2 -0
  39. data/lib/ledger_sync/resources/expense_line_item.rb +2 -0
  40. data/lib/ledger_sync/resources/journal_entry_line_item.rb +4 -0
  41. data/lib/ledger_sync/version.rb +1 -1
  42. data/qa/netsuite/customer.rb +1 -1
  43. data/qa/netsuite/vendor.rb +1 -1
  44. metadata +17 -37
  45. data/lib/ledger_sync/adaptors/test/account/operations/create.rb +0 -57
  46. data/lib/ledger_sync/adaptors/test/account/operations/find.rb +0 -42
  47. data/lib/ledger_sync/adaptors/test/account/operations/invalid.rb +0 -26
  48. data/lib/ledger_sync/adaptors/test/account/operations/update.rb +0 -56
  49. data/lib/ledger_sync/adaptors/test/account/operations/valid.rb +0 -32
  50. data/lib/ledger_sync/adaptors/test/account/searcher.rb +0 -40
  51. data/lib/ledger_sync/adaptors/test/adaptor.rb +0 -47
  52. data/lib/ledger_sync/adaptors/test/config.rb +0 -6
  53. data/lib/ledger_sync/adaptors/test/customer/operations/create.rb +0 -52
  54. data/lib/ledger_sync/adaptors/test/customer/operations/find.rb +0 -38
  55. data/lib/ledger_sync/adaptors/test/customer/operations/invalid.rb +0 -22
  56. data/lib/ledger_sync/adaptors/test/customer/operations/update.rb +0 -22
  57. data/lib/ledger_sync/adaptors/test/customer/operations/valid.rb +0 -24
  58. data/lib/ledger_sync/adaptors/test/customer/searcher.rb +0 -40
  59. data/lib/ledger_sync/adaptors/test/error/adaptor_error/operations/throttle_error.rb +0 -28
  60. data/lib/ledger_sync/adaptors/test/expense/operations/create.rb +0 -29
  61. data/lib/ledger_sync/adaptors/test/expense/operations/find.rb +0 -29
  62. data/lib/ledger_sync/adaptors/test/expense/operations/update.rb +0 -27
  63. data/lib/ledger_sync/adaptors/test/operation/create.rb +0 -27
  64. data/lib/ledger_sync/adaptors/test/operation/find.rb +0 -29
  65. data/lib/ledger_sync/adaptors/test/operation/update.rb +0 -34
  66. data/lib/ledger_sync/adaptors/test/payment/operations/create.rb +0 -51
  67. data/lib/ledger_sync/adaptors/test/payment/operations/find.rb +0 -44
  68. data/lib/ledger_sync/adaptors/test/payment/operations/update.rb +0 -28
  69. data/lib/ledger_sync/adaptors/test/transfer/operations/create.rb +0 -24
  70. data/lib/ledger_sync/adaptors/test/transfer/operations/find.rb +0 -24
  71. data/lib/ledger_sync/adaptors/test/transfer/operations/update.rb +0 -24
  72. data/lib/ledger_sync/adaptors/test/vendor/operations/create.rb +0 -56
  73. data/lib/ledger_sync/adaptors/test/vendor/operations/find.rb +0 -43
  74. data/lib/ledger_sync/adaptors/test/vendor/operations/invalid.rb +0 -27
  75. data/lib/ledger_sync/adaptors/test/vendor/operations/update.rb +0 -27
  76. data/lib/ledger_sync/adaptors/test/vendor/operations/valid.rb +0 -33
  77. data/lib/ledger_sync/adaptors/test/vendor/searcher.rb +0 -41
  78. data/lib/ledger_sync/util/debug.rb +0 -16
  79. data/qa/netsuite_test.rb +0 -42
  80. data/qa/test.rb +0 -67
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 05667fe1a37dc8edfcb33121d2eb5f08ad076b8b635ecaaf33809b663c830f68
4
- data.tar.gz: 3b3a6c68af8ecf2ce04a8f789854fb1002b3d90d5e747e71d398aa1b1b8b4c9f
3
+ metadata.gz: 6c159bdb17d877ede52d532d64ca436bf590fd64d06f96f3302003bbade4a5e9
4
+ data.tar.gz: 4146c094eb32435710901c88442ebab06e51bfb8b32144604c159a02f5287e33
5
5
  SHA512:
6
- metadata.gz: 14cdebe2e5342838d44202fbe9b11c28e09a6e18ccbb4827145d0b098833c1549c9e922bd8760555708358058df74ff5574a46b91aa67d48f347a99d2e5a0129
7
- data.tar.gz: 8a48a4251273a4aa7a2cca65cc1eaed38c87d385caa2aeb1c3eaf63c76f0d67bf5af3099dcb16a1c4df82517e79c3bd35c8b41584a1a5197a584bce5c951ca5f
6
+ metadata.gz: 87e52ac4566ad46fd64a32a67e4ca2221b5c19638bd6fd0b2347a43b9934e4c6e2952a84efc83ee414e05c07e384b2e8e9f72a785066619b7c8a8a9780939f10
7
+ data.tar.gz: f887774835cbfaa38bfcf9a6a7d4b69760961875e86bb6096fdc36e8bdf19bdcdc11a24777e9277c818c734eaf85f1d3eadbd1ef90bcffd483c99ae681ae52a2
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ledger_sync (1.3.2)
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.2)
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.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
- #### Resources
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.
@@ -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')
@@ -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(ensure_inferred_resource_class: true, resource:)
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 inferred_ledger_serializer(resource:)
9
- inferred_ledger_serializer_class.new(
10
- resource: resource
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
- "#{inferred_resource_class.resource_module_str}::LedgerSerializer"
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
@@ -2,5 +2,4 @@
2
2
 
3
3
  LedgerSync.register_adaptor(:netsuite, module_string: 'NetSuite') do |config|
4
4
  config.name = 'NetSuite REST'
5
- config.rate_limiting_wait_in_seconds = 60
6
5
  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
- adaptor
38
- after_operations
39
- before_operations
40
- operations
41
- resource
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
- raise "#{resource.class.name} is not a valid resource type. Expected #{self.class.resource_klass.name}" unless resource.is_a?(self.class.resource_klass)
66
-
67
- @adaptor = adaptor
68
- @after_operations = []
69
- @before_operations = []
70
- @operations = []
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
- @root_operation = nil
75
- end
50
+ @validation_contract = keywords.fetch(:validation_contract, nil)
76
51
 
77
- def add_after_operation(operation)
78
- @operations << operation
79
- @after_operations << operation
80
- end
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
- rescue LedgerSync::Error => e
98
- failure(e)
99
- rescue StandardError => e
100
- parsed_error = adaptor.parse_operation_error(error: e, operation: self)
101
- raise e unless parsed_error
102
-
103
- failure(parsed_error)
104
- ensure
105
- @performed = true
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
- self.class.inferred_ledger_serializer(resource: resource)
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: self.class::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)