lockstep_rails 0.3.22 → 0.3.28
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 +9 -0
- data/app/concepts/lockstep/api_record.rb +198 -168
- data/app/concepts/lockstep/exceptions.rb +4 -3
- data/app/concepts/lockstep/query.rb +6 -2
- data/app/models/lockstep/app_enrollment.rb +5 -0
- data/app/models/lockstep/report_ap_aging_header.rb +7 -0
- data/app/models/lockstep/report_ar_aging_header.rb +7 -0
- data/app/models/lockstep/report_cashflow.rb +7 -0
- data/app/models/lockstep/report_daily_payable_outstanding.rb +6 -0
- data/app/models/lockstep/report_daily_sales_outstanding.rb +6 -0
- data/app/models/lockstep/report_payable_coming_due.rb +6 -0
- data/app/models/lockstep/report_payable_summary.rb +7 -0
- data/app/models/lockstep/report_risk_rate.rb +7 -0
- data/app/models/lockstep/vendor_summary.rb +6 -0
- data/app/models/lockstep/webhook.rb +5 -0
- data/app/platform_api/schema/ap_aging_header_info.rb +59 -0
- data/app/platform_api/schema/daily_payable_outstanding_report.rb +22 -0
- data/app/platform_api/schema/invoice.rb +54 -53
- data/app/platform_api/schema/payable_coming_due_report.rb +25 -0
- data/app/platform_api/schema/payable_summary_report.rb +40 -0
- data/app/platform_api/schema/vendor_summary.rb +78 -0
- data/app/platform_api/schema/webhook.rb +11 -11
- data/lib/lockstep_rails/version.rb +1 -1
- metadata +18 -2
@@ -1,15 +1,16 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler/setup'
|
3
|
+
require 'active_model'
|
4
|
+
require 'erb'
|
5
|
+
require 'dry-types'
|
6
|
+
require 'json'
|
7
|
+
require 'active_support/hash_with_indifferent_access'
|
8
|
+
require 'lockstep/query'
|
9
|
+
require 'lockstep/query_methods'
|
10
|
+
require 'lockstep/error'
|
11
|
+
require 'lockstep/exceptions'
|
12
|
+
require 'lockstep/relation_array'
|
13
|
+
require 'pry'
|
13
14
|
|
14
15
|
module Lockstep
|
15
16
|
class ApiRecord
|
@@ -42,7 +43,7 @@ module Lockstep
|
|
42
43
|
# @params [Hash], [Boolean] a `Hash` of attributes and a `Boolean` that should be false only if the object already exists
|
43
44
|
# @return [Lockstep::ApiRecord] an object that subclasses `Parseresource::Base`
|
44
45
|
def initialize(attributes = {}, new = true)
|
45
|
-
#attributes = HashWithIndifferentAccess.new(attributes)
|
46
|
+
# attributes = HashWithIndifferentAccess.new(attributes)
|
46
47
|
|
47
48
|
if new
|
48
49
|
@unsaved_attributes = attributes
|
@@ -85,7 +86,7 @@ module Lockstep
|
|
85
86
|
fname = fname.to_sym
|
86
87
|
class_eval do
|
87
88
|
define_method(fname) do
|
88
|
-
val = get_attribute(
|
89
|
+
val = get_attribute(fname.to_s)
|
89
90
|
|
90
91
|
# If enum, substitute with the enum key
|
91
92
|
if val.present? && (enum = enum_config[fname]).present?
|
@@ -95,10 +96,10 @@ module Lockstep
|
|
95
96
|
val
|
96
97
|
end
|
97
98
|
end
|
98
|
-
unless
|
99
|
+
unless respond_to? "#{fname}="
|
99
100
|
class_eval do
|
100
101
|
define_method("#{fname}=") do |val|
|
101
|
-
set_attribute(
|
102
|
+
set_attribute(fname.to_s, val)
|
102
103
|
|
103
104
|
val
|
104
105
|
end
|
@@ -125,7 +126,7 @@ module Lockstep
|
|
125
126
|
def self.belongs_to(parent, config = {})
|
126
127
|
config = config.with_indifferent_access
|
127
128
|
class_name = config[:class_name]
|
128
|
-
raise "Class name cannot be empty in #{parent}: #{
|
129
|
+
raise "Class name cannot be empty in #{parent}: #{name}" if class_name.blank?
|
129
130
|
|
130
131
|
included = config[:included] || false
|
131
132
|
primary_key = config[:primary_key]
|
@@ -139,7 +140,7 @@ module Lockstep
|
|
139
140
|
belongs_to_relations[parent] = {
|
140
141
|
name: parent, class_name: class_name,
|
141
142
|
included: included, primary_key: primary_key, foreign_key: foreign_key,
|
142
|
-
loader: loader, polymorphic: polymorphic
|
143
|
+
loader: loader, polymorphic: polymorphic
|
143
144
|
}
|
144
145
|
|
145
146
|
# define_method("build_#{parent}") do |attributes_hash|
|
@@ -156,7 +157,7 @@ module Lockstep
|
|
156
157
|
def self.has_many(parent, config = {})
|
157
158
|
config = config.with_indifferent_access
|
158
159
|
class_name = config[:class_name]
|
159
|
-
raise "Class name cannot be empty in #{parent}: #{
|
160
|
+
raise "Class name cannot be empty in #{parent}: #{name}" if class_name.blank?
|
160
161
|
|
161
162
|
included = config[:included] || false
|
162
163
|
primary_key = config[:primary_key]
|
@@ -164,13 +165,13 @@ module Lockstep
|
|
164
165
|
polymorphic = config[:polymorphic]
|
165
166
|
loader = config[:loader]
|
166
167
|
|
167
|
-
primary_key ||=
|
168
|
-
foreign_key ||=
|
168
|
+
primary_key ||= id_ref
|
169
|
+
foreign_key ||= id_ref
|
169
170
|
field(parent)
|
170
171
|
has_many_relations[parent] = {
|
171
172
|
name: parent, class_name: class_name, included: included,
|
172
173
|
primary_key: primary_key, foreign_key: foreign_key, polymorphic: polymorphic,
|
173
|
-
loader: loader
|
174
|
+
loader: loader
|
174
175
|
}
|
175
176
|
end
|
176
177
|
|
@@ -182,46 +183,48 @@ module Lockstep
|
|
182
183
|
schema.belongs_to_relations.each do |relation, config|
|
183
184
|
params = {}
|
184
185
|
config.except(:name).each { |k, v| params[k.to_sym] = v }
|
185
|
-
|
186
|
+
belongs_to(relation, params)
|
186
187
|
end
|
187
188
|
|
188
189
|
schema.has_many_relations.each do |relation, config|
|
189
190
|
params = {}
|
190
191
|
config.except(:name).each { |k, v| params[k.to_sym] = v }
|
191
|
-
|
192
|
+
has_many(relation, params)
|
192
193
|
end
|
193
194
|
end
|
194
195
|
|
195
196
|
def to_pointer
|
196
197
|
klass_name = self.class.model_name.to_s
|
197
|
-
{
|
198
|
+
{ '__type' => 'Pointer', 'className' => klass_name.to_s, id_ref => id }
|
198
199
|
end
|
199
200
|
|
200
201
|
def self.to_date_object(date)
|
201
202
|
date = date.to_time if date.respond_to?(:to_time)
|
202
|
-
|
203
|
+
if date && (date.is_a?(Date) || date.is_a?(DateTime) || date.is_a?(Time))
|
204
|
+
date.getutc.iso8601(fraction_digits = 3)
|
205
|
+
end
|
203
206
|
end
|
204
207
|
|
205
208
|
# Creates setter methods for model fields
|
206
|
-
def create_setters!(k,
|
207
|
-
unless
|
209
|
+
def create_setters!(k, _v)
|
210
|
+
unless respond_to? "#{k}="
|
208
211
|
self.class.send(:define_method, "#{k}=") do |val|
|
209
|
-
set_attribute(
|
212
|
+
set_attribute(k.to_s, val)
|
210
213
|
|
211
214
|
val
|
212
215
|
end
|
213
216
|
end
|
214
217
|
end
|
215
218
|
|
216
|
-
def method_missing(method, *
|
217
|
-
raise StandardError
|
219
|
+
def method_missing(method, *_args)
|
220
|
+
raise StandardError, "#{method} has not been defined for #{self.class.name}"
|
218
221
|
# super
|
219
222
|
end
|
220
223
|
|
221
224
|
def self.method_missing(method_name, *args)
|
222
225
|
method_name = method_name.to_s
|
223
|
-
if method_name.start_with?(
|
224
|
-
attrib = method_name.gsub(/^find_by_/,
|
226
|
+
if method_name.start_with?('find_by_')
|
227
|
+
attrib = method_name.gsub(/^find_by_/, '')
|
225
228
|
finder_name = "find_all_by_#{attrib}"
|
226
229
|
|
227
230
|
define_singleton_method(finder_name) do |target_value|
|
@@ -229,8 +232,8 @@ module Lockstep
|
|
229
232
|
end
|
230
233
|
|
231
234
|
send(finder_name, args[0])
|
232
|
-
elsif method_name.start_with?(
|
233
|
-
attrib = method_name.gsub(/^find_all_by_/,
|
235
|
+
elsif method_name.start_with?('find_all_by_')
|
236
|
+
attrib = method_name.gsub(/^find_all_by_/, '')
|
234
237
|
finder_name = "find_all_by_#{attrib}"
|
235
238
|
|
236
239
|
define_singleton_method(finder_name) do |target_value|
|
@@ -244,10 +247,10 @@ module Lockstep
|
|
244
247
|
end
|
245
248
|
|
246
249
|
# Creates getter methods for model fields
|
247
|
-
def create_getters!(k,
|
248
|
-
unless
|
249
|
-
self.class.send(:define_method,
|
250
|
-
get_attribute(
|
250
|
+
def create_getters!(k, _v)
|
251
|
+
unless respond_to? k.to_s
|
252
|
+
self.class.send(:define_method, k.to_s) do
|
253
|
+
get_attribute(k.to_s)
|
251
254
|
end
|
252
255
|
end
|
253
256
|
end
|
@@ -283,19 +286,19 @@ module Lockstep
|
|
283
286
|
# end
|
284
287
|
# end
|
285
288
|
|
286
|
-
|
287
|
-
|
289
|
+
class << self
|
290
|
+
attr_writer :id_ref
|
288
291
|
end
|
289
292
|
|
290
293
|
def self.id_ref
|
291
|
-
raise StandardError
|
294
|
+
raise StandardError, "id_ref has not been defined for #{name}" if @id_ref.blank?
|
292
295
|
|
293
296
|
@id_ref
|
294
297
|
end
|
295
298
|
|
296
299
|
# Alias for id_ref. Used by polymorphic association
|
297
300
|
def self.primary_key
|
298
|
-
|
301
|
+
id_ref
|
299
302
|
end
|
300
303
|
|
301
304
|
def id_ref
|
@@ -308,8 +311,8 @@ module Lockstep
|
|
308
311
|
@model_name_uri
|
309
312
|
end
|
310
313
|
|
311
|
-
|
312
|
-
|
314
|
+
class << self
|
315
|
+
attr_reader :model_name_uri
|
313
316
|
end
|
314
317
|
|
315
318
|
def self.config
|
@@ -318,13 +321,16 @@ module Lockstep
|
|
318
321
|
|
319
322
|
# Gets the current class's Lockstep.io base_uri
|
320
323
|
def self.model_base_uri
|
321
|
-
|
322
|
-
|
324
|
+
if name.starts_with?('Schema::')
|
325
|
+
raise StandardError,
|
326
|
+
'Cannot establish connection for auto-generated Schema. Create a new model if you want to retrieve data from Lockstep Platform'
|
327
|
+
end
|
328
|
+
raise StandardError, "URL Path is not defined for #{name}" if model_name_uri.blank?
|
323
329
|
|
324
330
|
base_url = config[:base_url]
|
325
|
-
base_url +=
|
331
|
+
base_url += '/' unless base_url.ends_with?('/')
|
326
332
|
base_url += model_name_uri
|
327
|
-
base_url +=
|
333
|
+
base_url += '/' unless base_url.ends_with?('/')
|
328
334
|
base_url
|
329
335
|
end
|
330
336
|
|
@@ -339,19 +345,19 @@ module Lockstep
|
|
339
345
|
def self.resource
|
340
346
|
# load_settings
|
341
347
|
|
342
|
-
#refactor to settings['app_id'] etc
|
348
|
+
# refactor to settings['app_id'] etc
|
343
349
|
# app_id = @@settings['app_id']
|
344
350
|
# master_key = @@settings['master_key']
|
345
351
|
# RestClient::Resource.new(self.model_base_uri, app_id, master_key)
|
346
|
-
Lockstep::Client.new(
|
352
|
+
Lockstep::Client.new(model_base_uri)
|
347
353
|
end
|
348
354
|
|
349
|
-
|
350
|
-
|
355
|
+
class << self
|
356
|
+
attr_writer :query_path
|
351
357
|
end
|
352
358
|
|
353
359
|
def self.query_path
|
354
|
-
@query_path ||
|
360
|
+
@query_path || 'query'
|
355
361
|
end
|
356
362
|
|
357
363
|
# Batch requests
|
@@ -396,6 +402,7 @@ module Lockstep
|
|
396
402
|
def self.merge_all_attributes(objects, response)
|
397
403
|
objects.each_with_index do |item, index|
|
398
404
|
next unless response[index]
|
405
|
+
|
399
406
|
new_attributes = response[index].transform_keys { |key| key.underscore }
|
400
407
|
item.merge_attributes(new_attributes)
|
401
408
|
end
|
@@ -417,7 +424,7 @@ module Lockstep
|
|
417
424
|
# end
|
418
425
|
|
419
426
|
def self.bulk_import(new_objects, slice_size = 20)
|
420
|
-
return
|
427
|
+
return [] if new_objects.blank?
|
421
428
|
|
422
429
|
# Batch saves seem to fail if they're too big. We'll slice it up into multiple posts if they are.
|
423
430
|
new_objects.each_slice(slice_size) do |objects|
|
@@ -425,46 +432,41 @@ module Lockstep
|
|
425
432
|
batch_json = []
|
426
433
|
|
427
434
|
objects.each do |item|
|
428
|
-
|
435
|
+
unless item.new?
|
436
|
+
raise StandardError,
|
437
|
+
'Bulk Import cannot only create records at the moment. It cannot update records'
|
438
|
+
end
|
429
439
|
|
430
440
|
batch_json << item.attributes_for_saving.transform_keys { |key| key.camelize(:lower) }
|
431
441
|
end
|
432
442
|
|
433
|
-
resp =
|
434
|
-
# TODO attach errors if resp code is 400
|
435
|
-
|
436
|
-
if resp.code.to_s == "400"
|
443
|
+
resp = resource.post('', body: batch_json)
|
444
|
+
# TODO: attach errors if resp code is 400
|
445
|
+
if resp.code != '200'
|
437
446
|
# Error format in JSON
|
438
447
|
# "errors": {
|
439
448
|
# "[0].EmailAddress": [
|
440
449
|
# "The EmailAddress field is not a valid e-mail address."
|
441
450
|
# ]
|
442
451
|
# }
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
position = splits.first[1..(splits.first.size - 2)].to_i
|
450
|
-
messages.each do |message|
|
451
|
-
new_objects[position].errors.add attribute, ": #{message}"
|
452
|
-
end
|
452
|
+
if resp.code == '401'
|
453
|
+
raise Lockstep::Exceptions::UnauthorizedError, 'Unauthorized: Check your App ID & Master Key'
|
454
|
+
elsif resp.code == '400'
|
455
|
+
raise Lockstep::Exceptions::BadRequestError, JSON.parse(resp.body)
|
456
|
+
elsif resp.code == '404'
|
457
|
+
raise Lockstep::Exceptions::RecordNotFound, 'Resource not found in the Platfrom'
|
453
458
|
end
|
454
|
-
return false
|
455
|
-
elsif resp.code.to_s != "200"
|
456
|
-
return false
|
457
459
|
end
|
458
460
|
|
459
461
|
response = JSON.parse(resp.body)
|
460
|
-
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
|
465
|
-
|
462
|
+
next unless response && response.is_a?(Array) && response.length == objects.length
|
463
|
+
|
464
|
+
# return response.map { |item|
|
465
|
+
# Lockstep::Contact.new(item.transform_keys { |key| key.underscore }, false)
|
466
|
+
# }
|
467
|
+
merge_all_attributes(objects, response)
|
466
468
|
end
|
467
|
-
|
469
|
+
new_objects
|
468
470
|
end
|
469
471
|
|
470
472
|
# def self.load_settings
|
@@ -516,8 +518,10 @@ module Lockstep
|
|
516
518
|
# @return [Lockstep::ApiRecord] an object that subclasses Lockstep::ApiRecord.
|
517
519
|
def self.find(id)
|
518
520
|
raise Lockstep::Exceptions::RecordNotFound, "Couldn't find #{name} without an ID" if id.blank?
|
519
|
-
|
521
|
+
|
522
|
+
record = where(id_ref => id).first
|
520
523
|
raise Lockstep::Exceptions::RecordNotFound, "Couldn't find #{name} with id: #{id}" if record.blank?
|
524
|
+
|
521
525
|
record
|
522
526
|
end
|
523
527
|
|
@@ -525,13 +529,13 @@ module Lockstep
|
|
525
529
|
#
|
526
530
|
def self.find_by(*args)
|
527
531
|
raise Lockstep::Exceptions::RecordNotFound, "Couldn't find an object without arguments" if args.blank?
|
532
|
+
|
528
533
|
key, value = args.first.first
|
529
534
|
unless valid_attribute?(key, raise_exception: true)
|
530
|
-
raise StandardError
|
535
|
+
raise StandardError, "Attribute '#{key}' has not been defined for #{name}"
|
531
536
|
end
|
532
537
|
|
533
|
-
|
534
|
-
record
|
538
|
+
where(key => value).first
|
535
539
|
end
|
536
540
|
|
537
541
|
# Find a Lockstep::ApiRecord object by chaining #where method calls.
|
@@ -576,19 +580,17 @@ module Lockstep
|
|
576
580
|
# Valid only if the record is not an API record.
|
577
581
|
# Default scopes build queries using ApiRecord to avoid conflicts. In this case, the query results in an
|
578
582
|
# exception as the fields wouldn't have been defined in the ApiRecord
|
579
|
-
return true if
|
583
|
+
return true if name == 'Lockstep::ApiRecord'
|
580
584
|
|
581
585
|
attr = key.to_s
|
582
586
|
Lockstep::Query::PREDICATES.keys.each do |predicate|
|
583
587
|
if attr.end_with?(predicate)
|
584
|
-
attr = attr.gsub(predicate,
|
588
|
+
attr = attr.gsub(predicate, '')
|
585
589
|
break
|
586
590
|
end
|
587
591
|
end
|
588
592
|
valid = schema.has_key?(attr)
|
589
|
-
if raise_exception && !valid
|
590
|
-
raise StandardError.new("Attribute '#{attr}' has not been defined for #{self.name}")
|
591
|
-
end
|
593
|
+
raise StandardError, "Attribute '#{attr}' has not been defined for #{name}" if raise_exception && !valid
|
592
594
|
|
593
595
|
valid
|
594
596
|
end
|
@@ -613,19 +615,19 @@ module Lockstep
|
|
613
615
|
# create RESTful resource for the specific Parse object
|
614
616
|
# sends requests to [base_uri]/[classname]/[objectId]
|
615
617
|
def instance_resource
|
616
|
-
self.class.resource[
|
618
|
+
self.class.resource[id.to_s]
|
617
619
|
end
|
618
620
|
|
619
621
|
def pointerize(hash)
|
620
622
|
new_hash = {}
|
621
623
|
hash.each do |k, v|
|
622
|
-
if v.respond_to?(:to_pointer)
|
623
|
-
|
624
|
-
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
624
|
+
new_hash[k] = if v.respond_to?(:to_pointer)
|
625
|
+
v.to_pointer
|
626
|
+
elsif v.is_a?(Date) || v.is_a?(Time) || v.is_a?(DateTime)
|
627
|
+
self.class.to_date_object(v)
|
628
|
+
else
|
629
|
+
v
|
630
|
+
end
|
629
631
|
end
|
630
632
|
new_hash
|
631
633
|
end
|
@@ -642,13 +644,13 @@ module Lockstep
|
|
642
644
|
else
|
643
645
|
false
|
644
646
|
end
|
645
|
-
rescue
|
647
|
+
rescue StandardError
|
646
648
|
false
|
647
649
|
end
|
648
650
|
|
649
651
|
def create
|
650
652
|
attrs = attributes_for_saving.transform_keys { |key| key.camelize(:lower) }
|
651
|
-
resp =
|
653
|
+
resp = resource.post('', body: [attrs])
|
652
654
|
result = post_result(resp)
|
653
655
|
end
|
654
656
|
|
@@ -659,7 +661,7 @@ module Lockstep
|
|
659
661
|
# put_attrs = attributes_for_saving.to_json
|
660
662
|
|
661
663
|
attrs = attributes_for_saving.transform_keys { |key| key.camelize(:lower) }
|
662
|
-
resp =
|
664
|
+
resp = resource.patch(id, body: attrs)
|
663
665
|
result = post_result(resp)
|
664
666
|
end
|
665
667
|
|
@@ -681,40 +683,40 @@ module Lockstep
|
|
681
683
|
# the object nor the relations it contains. Make another request here.
|
682
684
|
# TODO: @@has_many_relations structure has been changed from array to hash, need to evaluate the impact here
|
683
685
|
if has_many_relations.keys.map { |relation| relation.to_s.to_sym }
|
684
|
-
#
|
686
|
+
# TODO: make this a little smarter by checking if there are any Pointer objects in the objects attributes.
|
685
687
|
# @attributes = self.class.to_s.constantize.where(:objectId => @attributes[self.id_ref]).first.attributes
|
686
|
-
@attributes = self.class.to_s.constantize.where(
|
688
|
+
@attributes = self.class.to_s.constantize.where(id_ref => @attributes[id_ref]).first.attributes
|
687
689
|
end
|
688
690
|
end
|
689
691
|
|
690
692
|
def post_result(resp)
|
691
|
-
if resp.code.to_s ==
|
693
|
+
if resp.code.to_s == '200' || resp.code.to_s == '201'
|
692
694
|
body = JSON.parse(resp.body)
|
693
695
|
# Create method always responds with an array, whereas update responds with the object
|
694
696
|
body = body.first if body.is_a?(Array)
|
695
697
|
|
696
698
|
merge_attributes(body)
|
697
699
|
|
698
|
-
|
699
|
-
elsif resp.code.to_s ==
|
700
|
+
true
|
701
|
+
elsif resp.code.to_s == '400'
|
700
702
|
error_response = JSON.parse(resp.body)
|
701
|
-
errors = error_response[
|
703
|
+
errors = error_response['errors']
|
702
704
|
errors.each do |key, messages|
|
703
|
-
attribute = key.split(
|
705
|
+
attribute = key.split('.').last&.underscore
|
704
706
|
messages.each do |message|
|
705
707
|
self.errors.add attribute, ": #{message}"
|
706
708
|
end
|
707
709
|
end
|
708
710
|
else
|
709
711
|
error_response = JSON.parse(resp.body)
|
710
|
-
if error_response[
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
712
|
+
pe = if error_response['error']
|
713
|
+
LockstepError.new(error_response['code'], error_response['error'])
|
714
|
+
else
|
715
|
+
LockstepError.new(resp.code.to_s)
|
716
|
+
end
|
715
717
|
self.errors.add(pe.code.to_s.to_sym, pe.msg)
|
716
|
-
|
717
|
-
|
718
|
+
error_instances << pe
|
719
|
+
false
|
718
720
|
end
|
719
721
|
end
|
720
722
|
|
@@ -724,9 +726,9 @@ module Lockstep
|
|
724
726
|
|
725
727
|
put_attrs = relations_for_saving(put_attrs)
|
726
728
|
|
727
|
-
put_attrs.delete(
|
728
|
-
put_attrs.delete(
|
729
|
-
put_attrs.delete(
|
729
|
+
put_attrs.delete(id_ref)
|
730
|
+
put_attrs.delete('created')
|
731
|
+
put_attrs.delete('modified')
|
730
732
|
put_attrs
|
731
733
|
end
|
732
734
|
|
@@ -734,25 +736,28 @@ module Lockstep
|
|
734
736
|
all_add_item_queries = {}
|
735
737
|
all_remove_item_queries = {}
|
736
738
|
@unsaved_attributes.each_pair do |key, value|
|
737
|
-
next
|
739
|
+
next unless value.is_a? Array
|
738
740
|
|
739
741
|
# Go through the array in unsaved and check if they are in array in attributes (saved stuff)
|
740
742
|
add_item_ops = []
|
741
743
|
@unsaved_attributes[key].each do |item|
|
742
744
|
found_item_in_saved = false
|
743
745
|
@attributes[key].each do |item_in_saved|
|
744
|
-
if !!(defined? item.attributes) && item.attributes[
|
746
|
+
if !!(defined? item.attributes) && item.attributes[id_ref] == item_in_saved.attributes[id_ref]
|
745
747
|
found_item_in_saved = true
|
746
748
|
end
|
747
749
|
end
|
748
750
|
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
|
751
|
+
next unless !found_item_in_saved && !!(defined? item.id)
|
752
|
+
|
753
|
+
# need to send additem operation to parse
|
754
|
+
put_attrs.delete(key) # arrays should not be sent along with REST to parse api
|
755
|
+
add_item_ops << { '__type' => 'Pointer', 'className' => item.class.to_s, id_ref => item.id }
|
756
|
+
end
|
757
|
+
unless add_item_ops.empty?
|
758
|
+
all_add_item_queries.merge!({ key => { '__op' => 'Add',
|
759
|
+
'objects' => add_item_ops } })
|
754
760
|
end
|
755
|
-
all_add_item_queries.merge!({ key => { "__op" => "Add", "objects" => add_item_ops } }) if !add_item_ops.empty?
|
756
761
|
|
757
762
|
# Go through saved and if it isn't in unsaved perform a removeitem operation
|
758
763
|
remove_item_ops = []
|
@@ -760,28 +765,31 @@ module Lockstep
|
|
760
765
|
@attributes[key].each do |item|
|
761
766
|
found_item_in_unsaved = false
|
762
767
|
@unsaved_attributes[key].each do |item_in_unsaved|
|
763
|
-
if !!(defined? item.attributes) && item.attributes[
|
768
|
+
if !!(defined? item.attributes) && item.attributes[id_ref] == item_in_unsaved.attributes[id_ref]
|
764
769
|
found_item_in_unsaved = true
|
765
770
|
end
|
766
771
|
end
|
767
772
|
|
768
773
|
if !found_item_in_unsaved && !!(defined? item.id)
|
769
774
|
# need to send removeitem operation to parse
|
770
|
-
remove_item_ops << {
|
775
|
+
remove_item_ops << { '__type' => 'Pointer', 'className' => item.class.to_s, id_ref => item.id }
|
771
776
|
end
|
772
777
|
end
|
773
778
|
end
|
774
|
-
|
779
|
+
unless remove_item_ops.empty?
|
780
|
+
all_remove_item_queries.merge!({ key => { '__op' => 'Remove',
|
781
|
+
'objects' => remove_item_ops } })
|
782
|
+
end
|
775
783
|
end
|
776
784
|
|
777
|
-
# TODO figure out a more elegant way to get this working. the remove_item merge overwrites the add.
|
785
|
+
# TODO: figure out a more elegant way to get this working. the remove_item merge overwrites the add.
|
778
786
|
# Use a seperate query to add objects to the relation.
|
779
|
-
#if !all_add_item_queries.empty?
|
787
|
+
# if !all_add_item_queries.empty?
|
780
788
|
# #result = self.instance_resource.put(all_add_item_queries.to_json, {:content_type => "application/json"}) do |resp, req, res, &block|
|
781
789
|
# # return puts(resp, req, res, false, &block)
|
782
790
|
# #end
|
783
791
|
# puts result
|
784
|
-
#end
|
792
|
+
# end
|
785
793
|
|
786
794
|
put_attrs.merge!(all_add_item_queries) unless all_add_item_queries.empty?
|
787
795
|
put_attrs.merge!(all_remove_item_queries) unless all_remove_item_queries.empty?
|
@@ -789,17 +797,17 @@ module Lockstep
|
|
789
797
|
end
|
790
798
|
|
791
799
|
def update_attributes(attributes = {})
|
792
|
-
|
800
|
+
update(attributes)
|
793
801
|
end
|
794
802
|
|
795
803
|
def update_attribute(key, value)
|
796
|
-
send(key.to_s +
|
804
|
+
send(key.to_s + '=', value)
|
797
805
|
update
|
798
806
|
end
|
799
807
|
|
800
808
|
def destroy
|
801
|
-
resp =
|
802
|
-
if resp.code.to_s ==
|
809
|
+
resp = resource.delete(id)
|
810
|
+
if resp.code.to_s == '200'
|
803
811
|
@attributes = {}
|
804
812
|
@unsaved_attributes = {}
|
805
813
|
return true
|
@@ -812,7 +820,7 @@ module Lockstep
|
|
812
820
|
|
813
821
|
fresh_object = self.class.find(id)
|
814
822
|
@attributes = {}
|
815
|
-
@attributes.update(fresh_object.instance_variable_get(
|
823
|
+
@attributes.update(fresh_object.instance_variable_get('@attributes'))
|
816
824
|
@unsaved_attributes = {}
|
817
825
|
|
818
826
|
self
|
@@ -845,27 +853,30 @@ module Lockstep
|
|
845
853
|
attrs = @unsaved_attributes[k.to_s] ? @unsaved_attributes : @attributes
|
846
854
|
case attrs[k]
|
847
855
|
when Hash
|
848
|
-
klass_name = attrs[k][
|
849
|
-
klass_name =
|
850
|
-
case attrs[k][
|
851
|
-
when
|
852
|
-
result = klass_name.to_s.constantize.find(attrs[k][
|
853
|
-
when
|
856
|
+
klass_name = attrs[k]['className']
|
857
|
+
klass_name = 'User' if klass_name == '_User'
|
858
|
+
case attrs[k]['__type']
|
859
|
+
when 'Pointer'
|
860
|
+
result = klass_name.to_s.constantize.find(attrs[k][id_ref])
|
861
|
+
when 'Object'
|
854
862
|
result = klass_name.to_s.constantize.new(attrs[k], false)
|
855
|
-
when
|
856
|
-
result = DateTime.parse(attrs[k][
|
857
|
-
when
|
858
|
-
result = attrs[k][
|
859
|
-
when
|
860
|
-
objects_related_to_self = klass_name.constantize.where(
|
863
|
+
when 'Date'
|
864
|
+
result = DateTime.parse(attrs[k]['iso']).in_time_zone
|
865
|
+
when 'File'
|
866
|
+
result = attrs[k]['url']
|
867
|
+
when 'Relation'
|
868
|
+
objects_related_to_self = klass_name.constantize.where('$relatedTo' => {
|
869
|
+
'object' => { '__type' => 'Pointer',
|
870
|
+
'className' => self.class.to_s, id_ref => id }, 'key' => k
|
871
|
+
}).all
|
861
872
|
attrs[k] = Lockstep::RelationArray.new self, objects_related_to_self, k, klass_name
|
862
873
|
@unsaved_attributes[k] = Lockstep::RelationArray.new self, objects_related_to_self, k, klass_name
|
863
874
|
result = @unsaved_attributes[k]
|
864
875
|
end
|
865
876
|
else
|
866
|
-
# TODO changed from @@has_many_relations to @@has_many_relations.keys as we have changed the has_many_relations
|
877
|
+
# TODO: changed from @@has_many_relations to @@has_many_relations.keys as we have changed the has_many_relations
|
867
878
|
# from array to hash to capture more data points. Not sure of the impact of this.
|
868
|
-
#relation will assign itself if an array, this will add to unsave_attributes
|
879
|
+
# relation will assign itself if an array, this will add to unsave_attributes
|
869
880
|
if has_many_relations.keys.index(k.to_s)
|
870
881
|
if attrs[k].nil?
|
871
882
|
# result = nil
|
@@ -883,7 +894,7 @@ module Lockstep
|
|
883
894
|
result = @unsaved_attributes[k]
|
884
895
|
end
|
885
896
|
else
|
886
|
-
result = attrs[
|
897
|
+
result = attrs[k.to_s]
|
887
898
|
end
|
888
899
|
end
|
889
900
|
result
|
@@ -938,24 +949,28 @@ module Lockstep
|
|
938
949
|
end
|
939
950
|
|
940
951
|
def primary_key
|
941
|
-
|
952
|
+
id_ref
|
942
953
|
end
|
943
954
|
|
944
955
|
# aliasing for idiomatic Ruby
|
945
956
|
def id
|
946
|
-
get_attribute(
|
957
|
+
get_attribute(id_ref)
|
958
|
+
rescue StandardError
|
959
|
+
nil
|
947
960
|
end
|
948
961
|
|
949
962
|
def objectId
|
950
|
-
get_attribute(
|
963
|
+
get_attribute(id_ref)
|
964
|
+
rescue StandardError
|
965
|
+
nil
|
951
966
|
end
|
952
967
|
|
953
968
|
def created_at
|
954
|
-
get_attribute(
|
969
|
+
get_attribute('created')
|
955
970
|
end
|
956
971
|
|
957
972
|
def updated_at
|
958
|
-
get_attribute(
|
973
|
+
get_attribute('modified')
|
959
974
|
end
|
960
975
|
|
961
976
|
def self.included(base)
|
@@ -965,10 +980,10 @@ module Lockstep
|
|
965
980
|
module ClassMethods
|
966
981
|
end
|
967
982
|
|
968
|
-
#if we are comparing objects, use id if they are both Lockstep::ApiRecord objects
|
969
|
-
def ==(
|
970
|
-
if
|
971
|
-
|
983
|
+
# if we are comparing objects, use id if they are both Lockstep::ApiRecord objects
|
984
|
+
def ==(other)
|
985
|
+
if other.class <= Lockstep::ApiRecord
|
986
|
+
id == other.id
|
972
987
|
else
|
973
988
|
super
|
974
989
|
end
|
@@ -989,12 +1004,14 @@ module Lockstep
|
|
989
1004
|
val = relation_config[:loader].call(self)
|
990
1005
|
else
|
991
1006
|
return val unless relation_config[:foreign_key].present? and relation_config[:primary_key].present?
|
1007
|
+
|
992
1008
|
relation_klass = relation_config[:class_name].constantize
|
993
1009
|
return val unless relation_klass.model_name_uri.present?
|
994
1010
|
|
995
|
-
query = { relation_config[:foreign_key] =>
|
1011
|
+
query = { relation_config[:foreign_key] => send(relation_config[:primary_key]) }
|
996
1012
|
if relation_config[:polymorphic]
|
997
|
-
polymorphic_config = Lockstep::RelationArray.has_many_polymorphic_attributes(self,
|
1013
|
+
polymorphic_config = Lockstep::RelationArray.has_many_polymorphic_attributes(self,
|
1014
|
+
relation_config[:polymorphic])
|
998
1015
|
query.merge!(polymorphic_config)
|
999
1016
|
end
|
1000
1017
|
related_objects = relation_klass.send(:where, query).execute
|
@@ -1005,7 +1022,8 @@ module Lockstep
|
|
1005
1022
|
if relation_config[:loader].present?
|
1006
1023
|
val = relation_config[:loader].call(self)
|
1007
1024
|
else
|
1008
|
-
val = relation_config[:class_name].constantize.send(:find_by,
|
1025
|
+
val = relation_config[:class_name].constantize.send(:find_by,
|
1026
|
+
relation_config[:primary_key] => send(relation_config[:foreign_key]))
|
1009
1027
|
end
|
1010
1028
|
end
|
1011
1029
|
|
@@ -1060,7 +1078,7 @@ module Lockstep
|
|
1060
1078
|
elsif values.is_a?(Hash)
|
1061
1079
|
value_map = values.with_indifferent_access
|
1062
1080
|
else
|
1063
|
-
raise StandardError
|
1081
|
+
raise StandardError, "Invalid values for enum #{attribute}"
|
1064
1082
|
end
|
1065
1083
|
|
1066
1084
|
# Convert values to string if the value is symbol
|
@@ -1089,9 +1107,21 @@ module Lockstep
|
|
1089
1107
|
value = get_attribute(attribute)
|
1090
1108
|
next if value.nil?
|
1091
1109
|
|
1092
|
-
unless values_map.values.include?(value)
|
1093
|
-
|
1094
|
-
|
1110
|
+
errors.add attribute, 'has an invalid value' unless values_map.values.include?(value)
|
1111
|
+
end
|
1112
|
+
end
|
1113
|
+
|
1114
|
+
def self.single_record!
|
1115
|
+
define_singleton_method :record do
|
1116
|
+
resp = resource.get('')
|
1117
|
+
|
1118
|
+
return [] if %w(404).include?(resp.code.to_s)
|
1119
|
+
# TODO handle non 200 response code. Throwing an exception for now
|
1120
|
+
raise StandardError.new("#{resp.code} error while fetching: #{resp.body}") unless %w(201 200).include?(resp.code.to_s)
|
1121
|
+
|
1122
|
+
result = JSON.parse(resp.body)
|
1123
|
+
r = result.transform_keys { |key| key.underscore }
|
1124
|
+
model_name.to_s.constantize.new(r, false)
|
1095
1125
|
end
|
1096
1126
|
end
|
1097
1127
|
|
@@ -1111,7 +1141,7 @@ module Lockstep
|
|
1111
1141
|
as_json(options).to_json
|
1112
1142
|
end
|
1113
1143
|
|
1114
|
-
def as_json(
|
1144
|
+
def as_json(_options = {})
|
1115
1145
|
@attributes.merge(@unsaved_attributes).as_json
|
1116
1146
|
end
|
1117
1147
|
end
|