model-api 0.8.9 → 0.8.10
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/lib/model-api/api_context.rb +1 -1
- data/lib/model-api/utils.rb +74 -49
- data/model-api.gemspec +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f4236690ba6188e94302365bcb54af72c44d9895
|
4
|
+
data.tar.gz: 8feab210c4e863f0d031ec4a4ec65f7fad683fbd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6ad9de6daf542958341c2f86f50362a3b554bba9b74e10883a20c6905424f6b3e04b3c63660b48a178a626ce36baac70932f35b58c1533ceb91885346c2eaba6
|
7
|
+
data.tar.gz: 07725bfd70fd203020847b0c07cbd68fcdeb50db2917d999484c6a57d649388df07041e1432ad0cfb99ef86c844cc9853ea58573a353e09573fecfedd86faba0
|
@@ -126,7 +126,7 @@ module ModelApi
|
|
126
126
|
result_filters = {}
|
127
127
|
metadata.values.each do |attr_metadata|
|
128
128
|
collection = apply_filter_param(attr_metadata, collection,
|
129
|
-
opts.merge(attr_values: attr_values, result_filters: result_filters,
|
129
|
+
opts.merge(attr_values: attr_values, result_filters: result_filters, class_name: klass))
|
130
130
|
end
|
131
131
|
assoc_values.each do |assoc, assoc_filter_params|
|
132
132
|
ar_assoc = klass.reflect_on_association(assoc)
|
data/lib/model-api/utils.rb
CHANGED
@@ -277,9 +277,8 @@ module ModelApi
|
|
277
277
|
klass = parent_obj.class
|
278
278
|
assoc = klass.reflect_on_association(assoc) if assoc.is_a?(Symbol) || assoc.is_a?(String)
|
279
279
|
fail "Unrecognized association '#{assoc}' on class '#{klass.name}'" if assoc.nil?
|
280
|
-
|
281
|
-
model_metadata
|
282
|
-
do_resolve_assoc_obj(model_metadata, assoc, assoc_class, assoc_payload, parent_obj, opts)
|
280
|
+
model_metadata = model_metadata(assoc.class_name.constantize)
|
281
|
+
do_resolve_assoc_obj(model_metadata, assoc, assoc_payload, parent_obj, nil, opts)
|
283
282
|
end
|
284
283
|
|
285
284
|
def update_api_attr(obj, attr, value, opts = {})
|
@@ -296,9 +295,9 @@ module ModelApi
|
|
296
295
|
attr_metadata = opts[:attr_metadata]
|
297
296
|
assoc = attr_metadata[:association]
|
298
297
|
if assoc.macro == :has_many
|
299
|
-
|
300
|
-
elsif assoc.macro
|
301
|
-
|
298
|
+
update_one_to_many_assoc(obj, attr, value, opts)
|
299
|
+
elsif [:belongs_to, :has_one].include?(assoc.macro)
|
300
|
+
update_one_to_one_assoc(obj, attr, value, opts)
|
302
301
|
else
|
303
302
|
add_ignored_field(opts[:ignored_fields], attr, value, attr_metadata)
|
304
303
|
end
|
@@ -310,16 +309,20 @@ module ModelApi
|
|
310
309
|
end
|
311
310
|
end
|
312
311
|
|
313
|
-
def
|
314
|
-
return nil unless id_attributes.present?
|
312
|
+
def query_by_id_attrs(id_attributes, assoc, assoc_payload, opts = {})
|
313
|
+
return nil unless id_attributes.present? && opts[:api_context].present?
|
314
|
+
assoc_class = assoc.class_name.constantize
|
315
|
+
attr_metadata = filtered_attrs(assoc_class, opts[:operation] || :update, opts)
|
315
316
|
id_attributes.each do |id_attr_set|
|
316
317
|
query = nil
|
317
318
|
id_attr_set.each do |id_attr|
|
318
|
-
|
319
|
+
ext_attr = attr_metadata[id_attr].try(:[], :alias) || id_attr
|
320
|
+
unless ext_attr.present? && assoc_payload.include?(ext_attr.to_s)
|
319
321
|
query = nil
|
320
322
|
break
|
321
323
|
end
|
322
|
-
query = (query ||
|
324
|
+
query = (query || opts[:api_context].api_query(assoc_class, opts))
|
325
|
+
.where(id_attr => assoc_payload[ext_attr.to_s])
|
323
326
|
end
|
324
327
|
return query unless query.nil?
|
325
328
|
end
|
@@ -541,11 +544,10 @@ module ModelApi
|
|
541
544
|
true
|
542
545
|
end
|
543
546
|
|
544
|
-
def
|
547
|
+
def update_one_to_many_assoc(obj, attr, value, opts = {})
|
545
548
|
attr_metadata = opts[:attr_metadata]
|
546
549
|
assoc = attr_metadata[:association]
|
547
|
-
|
548
|
-
model_metadata = model_metadata(assoc_class)
|
550
|
+
model_metadata = model_metadata(assoc.class_name.constantize)
|
549
551
|
value_array = value.to_a rescue nil
|
550
552
|
unless value_array.is_a?(Array)
|
551
553
|
obj.errors.add(attr, 'must be supplied as an array of objects')
|
@@ -556,25 +558,37 @@ module ModelApi
|
|
556
558
|
assoc_objs = []
|
557
559
|
value_array.each_with_index do |assoc_payload, index|
|
558
560
|
opts[:ignored_fields].clear if opts.include?(:ignored_fields)
|
559
|
-
|
561
|
+
assoc_obj = update_one_to_many_assoc_obj(obj, assoc, assoc_payload,
|
560
562
|
opts.merge(model_metadata: model_metadata))
|
563
|
+
next if assoc_obj.nil?
|
564
|
+
assoc_objs << assoc_obj
|
561
565
|
if opts[:ignored_fields].present?
|
562
566
|
external_attr = ext_attr(attr, attr_metadata)
|
563
567
|
opts[:ignored_fields] << { "#{external_attr}[#{index}]" => opts[:ignored_fields] }
|
564
568
|
end
|
565
569
|
end
|
566
|
-
|
570
|
+
assoc_objs = assoc_objs.each do |assoc_obj|
|
571
|
+
if assoc_obj.new_record? || assoc_obj != existing_assoc_obj
|
572
|
+
set_api_attr(obj, attr, assoc_obj.attributes, opts.merge(setter: ->(v, _opts) {
|
573
|
+
obj.send(assoc.name).build(v)
|
574
|
+
}))
|
575
|
+
else
|
576
|
+
assoc_obj.save
|
577
|
+
assoc_obj
|
578
|
+
end
|
579
|
+
end
|
567
580
|
end
|
568
581
|
|
569
|
-
def
|
570
|
-
model_metadata = opts[:model_metadata] || model_metadata(
|
571
|
-
assoc_obj, assoc_oper, assoc_opts =
|
572
|
-
|
582
|
+
def update_one_to_many_assoc_obj(parent_obj, assoc, assoc_payload, opts = {})
|
583
|
+
model_metadata = opts[:model_metadata] || model_metadata(assoc.class_name.constantize)
|
584
|
+
assoc_obj, assoc_oper, assoc_opts = resolve_one_to_many_assoc_obj(model_metadata, assoc,
|
585
|
+
assoc_payload, parent_obj, opts)
|
586
|
+
return nil if assoc_obj.nil?
|
573
587
|
if (inverse_assoc = assoc.options[:inverse_of]).present? &&
|
574
588
|
assoc_obj.respond_to?("#{inverse_assoc}=")
|
575
589
|
assoc_obj.send("#{inverse_assoc}=", parent_obj)
|
576
590
|
elsif !parent_obj.new_record? && assoc_obj.respond_to?("#{assoc.foreign_key}=")
|
577
|
-
assoc_obj.send("#{assoc.foreign_key}=",
|
591
|
+
assoc_obj.send("#{assoc.foreign_key}=", parent_obj.id)
|
578
592
|
end
|
579
593
|
apply_updates(assoc_obj, assoc_payload, assoc_oper, assoc_opts)
|
580
594
|
invoke_callback(model_metadata[:after_initialize], assoc_obj,
|
@@ -582,36 +596,38 @@ module ModelApi
|
|
582
596
|
assoc_obj
|
583
597
|
end
|
584
598
|
|
585
|
-
def
|
599
|
+
def resolve_one_to_many_assoc_obj(model_metadata, assoc, assoc_payload,
|
586
600
|
parent_obj, opts = {})
|
587
|
-
assoc_obj = do_resolve_assoc_obj(model_metadata, assoc,
|
588
|
-
|
601
|
+
assoc_obj = do_resolve_assoc_obj(model_metadata, assoc, assoc_payload, parent_obj, nil,
|
602
|
+
opts.merge(auto_create: true))
|
603
|
+
return [nil, nil, nil] if assoc_obj.nil?
|
589
604
|
if assoc_obj.new_record?
|
590
605
|
assoc_oper = :create
|
591
606
|
opts[:create_opts] ||= opts.merge(api_attr_metadata: filtered_attrs(
|
592
|
-
|
607
|
+
assoc.class_name.constantize, :create, opts))
|
593
608
|
assoc_opts = opts[:create_opts]
|
594
609
|
else
|
595
610
|
assoc_oper = :update
|
596
611
|
opts[:update_opts] ||= opts.merge(api_attr_metadata: filtered_attrs(
|
597
|
-
|
612
|
+
assoc.class_name.constantize, :update, opts))
|
598
613
|
|
599
614
|
assoc_opts = opts[:update_opts]
|
600
615
|
end
|
601
616
|
[assoc_obj, assoc_oper, assoc_opts]
|
602
617
|
end
|
603
618
|
|
604
|
-
def
|
619
|
+
def update_one_to_one_assoc(parent_obj, attr, assoc_payload, opts = {})
|
605
620
|
unless assoc_payload.is_a?(Hash)
|
606
621
|
parent_obj.errors.add(attr, 'must be supplied as an object')
|
607
622
|
return
|
608
623
|
end
|
609
624
|
attr_metadata = opts[:attr_metadata]
|
610
625
|
assoc = attr_metadata[:association]
|
611
|
-
|
612
|
-
|
613
|
-
assoc_obj, assoc_oper, assoc_opts =
|
614
|
-
|
626
|
+
model_metadata = model_metadata(assoc.class_name.constantize)
|
627
|
+
existing_assoc_obj = parent_obj.send(assoc.name) rescue nil
|
628
|
+
assoc_obj, assoc_oper, assoc_opts = resolve_one_to_one_assoc_obj(model_metadata, assoc,
|
629
|
+
assoc_payload, parent_obj, existing_assoc_obj, opts)
|
630
|
+
return if assoc_obj.nil?
|
615
631
|
apply_updates(assoc_obj, assoc_payload, assoc_oper, assoc_opts)
|
616
632
|
invoke_callback(model_metadata[:after_initialize], assoc_obj,
|
617
633
|
opts.merge(operation: assoc_oper))
|
@@ -619,29 +635,36 @@ module ModelApi
|
|
619
635
|
external_attr = ext_attr(attr, attr_metadata)
|
620
636
|
opts[:ignored_fields] << { external_attr.to_s => assoc_opts[:ignored_fields] }
|
621
637
|
end
|
622
|
-
|
638
|
+
if assoc_obj.new_record? || assoc_obj != existing_assoc_obj
|
639
|
+
set_api_attr(parent_obj, attr, assoc_obj.attributes,
|
640
|
+
opts.merge(setter: "build_#{attr_metadata[:key] || attr}"))
|
641
|
+
else
|
642
|
+
assoc_obj.save
|
643
|
+
end
|
623
644
|
end
|
624
645
|
|
625
|
-
def
|
626
|
-
|
646
|
+
def resolve_one_to_one_assoc_obj(model_metadata, assoc, assoc_payload, parent_obj,
|
647
|
+
existing_assoc_obj, opts = {})
|
627
648
|
assoc_opts = opts[:ignored_fields].is_a?(Array) ? opts.merge(ignored_fields: []) : opts
|
628
|
-
assoc_obj = do_resolve_assoc_obj(model_metadata, assoc,
|
629
|
-
|
649
|
+
assoc_obj = do_resolve_assoc_obj(model_metadata, assoc, assoc_payload, parent_obj,
|
650
|
+
existing_assoc_obj, opts.merge(auto_create: true))
|
651
|
+
return [nil, nil, nil] if assoc_obj.nil?
|
630
652
|
assoc_oper = assoc_obj.new_record? ? :create : :update
|
631
653
|
assoc_opts = assoc_opts.merge(
|
632
|
-
api_attr_metadata: filtered_attrs(
|
654
|
+
api_attr_metadata: filtered_attrs(assoc.class_name.constantize, assoc_oper, opts))
|
633
655
|
return [assoc_obj, assoc_oper, assoc_opts]
|
634
656
|
end
|
635
657
|
|
636
|
-
def do_resolve_assoc_obj(model_metadata, assoc,
|
658
|
+
def do_resolve_assoc_obj(model_metadata, assoc, assoc_payload, parent_obj, existing_assoc_obj,
|
637
659
|
opts = {})
|
660
|
+
return nil unless assoc_payload.present?
|
638
661
|
if opts[:resolve].try(:respond_to?, :call)
|
639
662
|
assoc_obj = invoke_callback(opts[:resolve], assoc_payload, opts.merge(
|
640
663
|
parent: parent_obj, association: assoc, association_metadata: model_metadata))
|
641
|
-
|
642
|
-
assoc_obj =
|
643
|
-
assoc_obj = assoc_obj.
|
644
|
-
assoc_obj ||=
|
664
|
+
elsif (assoc_obj = existing_assoc_obj).blank?
|
665
|
+
assoc_obj = query_by_id_attrs(model_metadata[:id_attributes], assoc, assoc_payload, opts)
|
666
|
+
assoc_obj = (assoc_obj.count != 1 ? nil : assoc_obj.first) unless assoc_obj.nil?
|
667
|
+
assoc_obj ||= assoc.class_name.constantize.new if opts[:auto_create]
|
645
668
|
end
|
646
669
|
assoc_obj
|
647
670
|
end
|
@@ -650,14 +673,17 @@ module ModelApi
|
|
650
673
|
attr = attr.to_sym
|
651
674
|
attr_metadata = get_attr_metadata(obj, attr, opts)
|
652
675
|
internal_field = attr_metadata[:key] || attr
|
653
|
-
setter = attr_metadata[:setter] || "#{(internal_field)}="
|
654
|
-
|
676
|
+
setter = opts[:setter] || attr_metadata[:setter] || "#{(internal_field)}="
|
677
|
+
if setter.respond_to?(:call)
|
678
|
+
ModelApi::Utils.invoke_callback(setter, value, opts.merge(parent: obj, attribute: attr))
|
679
|
+
elsif !obj.respond_to?(setter)
|
655
680
|
Rails.logger.warn "Error encountered assigning API input for attribute \"#{attr}\" " \
|
656
681
|
'(setter not found): skipping.'
|
657
682
|
add_ignored_field(opts[:ignored_fields], attr, value, attr_metadata)
|
658
683
|
return
|
684
|
+
else
|
685
|
+
obj.send(setter, value)
|
659
686
|
end
|
660
|
-
obj.send(setter, value)
|
661
687
|
end
|
662
688
|
|
663
689
|
def handle_api_setter_exception(e, obj, attr_metadata, opts = {})
|
@@ -721,7 +747,6 @@ module ModelApi
|
|
721
747
|
attr_metadata = opts[:attr_metadata] || {}
|
722
748
|
processed_assoc_objects = {}
|
723
749
|
assoc = attr_metadata[:association]
|
724
|
-
assoc_class = assoc.class_name.constantize
|
725
750
|
external_attr = ext_attr(attr, attr_metadata)
|
726
751
|
attr_metadata_create = attr_metadata_update = nil
|
727
752
|
if assoc.macro == :has_many
|
@@ -730,11 +755,11 @@ module ModelApi
|
|
730
755
|
processed_assoc_objects[assoc_obj] = true
|
731
756
|
attr_prefix = "#{external_attr}[#{index}]."
|
732
757
|
if assoc_obj.new_record?
|
733
|
-
attr_metadata_create ||= filtered_attrs(
|
758
|
+
attr_metadata_create ||= filtered_attrs(assoc.class_name.constantize, :create, opts)
|
734
759
|
object_errors += extract_error_msgs(assoc_obj, opts.merge(
|
735
760
|
attr_prefix: attr_prefix, api_attr_metadata: attr_metadata_create))
|
736
761
|
else
|
737
|
-
attr_metadata_update ||= filtered_attrs(
|
762
|
+
attr_metadata_update ||= filtered_attrs(assoc.class_name.constantize, :update, opts)
|
738
763
|
object_errors += extract_error_msgs(assoc_obj, opts.merge(
|
739
764
|
attr_prefix: attr_prefix, api_attr_metadata: attr_metadata_update))
|
740
765
|
end
|
@@ -745,11 +770,11 @@ module ModelApi
|
|
745
770
|
processed_assoc_objects[assoc_obj] = true
|
746
771
|
attr_prefix = "#{external_attr}->"
|
747
772
|
if assoc_obj.new_record?
|
748
|
-
attr_metadata_create ||= filtered_attrs(
|
773
|
+
attr_metadata_create ||= filtered_attrs(assoc.class_name.constantize, :create, opts)
|
749
774
|
object_errors += extract_error_msgs(assoc_obj, opts.merge(
|
750
775
|
attr_prefix: attr_prefix, api_attr_metadata: attr_metadata_create))
|
751
776
|
else
|
752
|
-
attr_metadata_update ||= filtered_attrs(
|
777
|
+
attr_metadata_update ||= filtered_attrs(assoc.class_name.constantize, :update, opts)
|
753
778
|
object_errors += extract_error_msgs(assoc_obj, opts.merge(
|
754
779
|
attr_prefix: attr_prefix, api_attr_metadata: attr_metadata_update))
|
755
780
|
end
|
data/model-api.gemspec
CHANGED
@@ -3,7 +3,7 @@ $:.unshift lib unless $:.include?(lib)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = 'model-api'
|
6
|
-
s.version = '0.8.
|
6
|
+
s.version = '0.8.10'
|
7
7
|
s.summary = 'Create easy REST API\'s using metadata inside your ActiveRecord models'
|
8
8
|
s.description = 'Ruby gem allowing Ruby on Rails developers to create REST API’s using ' \
|
9
9
|
'metadata defined inside their ActiveRecord models.'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: model-api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.8.
|
4
|
+
version: 0.8.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matthew Mead
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|