lockstep_rails 0.3.22 → 0.3.28
Sign up to get free protection for your applications and to get access to all the features.
- 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
|