jat 0.0.1 → 0.0.3
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/CHANGELOG.md +7 -0
- data/README.md +21 -0
- data/jat.gemspec +37 -0
- data/lib/jat/attribute.rb +85 -0
- data/lib/jat/config.rb +38 -0
- data/lib/jat/plugins/_activerecord_preloads/_activerecord_preloads.rb +29 -0
- data/lib/jat/plugins/_activerecord_preloads/lib/preloader.rb +89 -0
- data/lib/jat/plugins/_json_api_activerecord/_json_api_activerecord.rb +22 -0
- data/lib/jat/plugins/_json_api_activerecord/lib/preloads.rb +84 -0
- data/lib/jat/plugins/_preloads/_preloads.rb +53 -0
- data/lib/jat/plugins/_preloads/lib/format_user_preloads.rb +52 -0
- data/lib/jat/plugins/_preloads/lib/preloads_with_path.rb +78 -0
- data/lib/jat/plugins/cache/cache.rb +39 -0
- data/lib/jat/plugins/camel_lower/camel_lower.rb +18 -0
- data/lib/jat/plugins/json_api/json_api.rb +207 -0
- data/lib/jat/plugins/json_api/lib/construct_traversal_map.rb +91 -0
- data/lib/jat/plugins/json_api/lib/map.rb +54 -0
- data/lib/jat/plugins/json_api/lib/params/fields/parse.rb +27 -0
- data/lib/jat/plugins/json_api/lib/params/fields/validate.rb +55 -0
- data/lib/jat/plugins/json_api/lib/params/fields.rb +23 -0
- data/lib/jat/plugins/json_api/lib/params/include/parse.rb +55 -0
- data/lib/jat/plugins/json_api/lib/params/include/validate.rb +29 -0
- data/lib/jat/plugins/json_api/lib/params/include.rb +49 -0
- data/lib/jat/plugins/json_api/lib/presenters/document_links_presenter.rb +48 -0
- data/lib/jat/plugins/json_api/lib/presenters/document_meta_presenter.rb +48 -0
- data/lib/jat/plugins/json_api/lib/presenters/jsonapi_presenter.rb +48 -0
- data/lib/jat/plugins/json_api/lib/presenters/links_presenter.rb +48 -0
- data/lib/jat/plugins/json_api/lib/presenters/meta_presenter.rb +48 -0
- data/lib/jat/plugins/json_api/lib/presenters/relationship_links_presenter.rb +53 -0
- data/lib/jat/plugins/json_api/lib/presenters/relationship_meta_presenter.rb +53 -0
- data/lib/jat/plugins/json_api/lib/response.rb +239 -0
- data/lib/jat/plugins/json_api/lib/traversal_map.rb +34 -0
- data/lib/jat/plugins/simple_api/lib/construct_traversal_map.rb +45 -0
- data/lib/jat/plugins/simple_api/lib/map.rb +29 -0
- data/lib/jat/plugins/simple_api/lib/params/parse.rb +68 -0
- data/lib/jat/plugins/simple_api/lib/response.rb +134 -0
- data/lib/jat/plugins/simple_api/simple_api.rb +65 -0
- data/lib/jat/plugins/to_str/to_str.rb +44 -0
- data/lib/jat/plugins.rb +39 -0
- data/lib/jat/presenter.rb +51 -0
- data/lib/jat/utils/enum_deep_dup.rb +29 -0
- data/lib/jat/utils/enum_deep_freeze.rb +19 -0
- data/lib/jat.rb +66 -144
- data/test/lib/jat/attribute_test.rb +142 -0
- data/test/lib/jat/config_test.rb +57 -0
- data/test/lib/jat/plugins/_activerecord_preloads/_activerecord_preloads_test.rb +40 -0
- data/test/lib/jat/plugins/_activerecord_preloads/lib/preloader_test.rb +98 -0
- data/test/lib/jat/plugins/_json_api_activerecord/_json_api_activerecord_test.rb +29 -0
- data/test/lib/jat/plugins/_json_api_activerecord/lib/preloads_test.rb +191 -0
- data/test/lib/jat/plugins/_preloads/_preloads_test.rb +68 -0
- data/test/lib/jat/plugins/_preloads/lib/format_user_preloads_test.rb +47 -0
- data/test/lib/jat/plugins/_preloads/lib/preloads_with_path_test.rb +33 -0
- data/test/lib/jat/plugins/cache/cache_test.rb +82 -0
- data/test/lib/jat/plugins/camel_lower/camel_lower_test.rb +78 -0
- data/test/lib/jat/plugins/json_api/json_api_test.rb +154 -0
- data/test/lib/jat/plugins/json_api/lib/construct_traversal_map_test.rb +119 -0
- data/test/lib/jat/plugins/json_api/lib/map_test.rb +117 -0
- data/test/lib/jat/plugins/json_api/lib/params/fields/parse_test.rb +24 -0
- data/test/lib/jat/plugins/json_api/lib/params/fields/validate_test.rb +47 -0
- data/test/lib/jat/plugins/json_api/lib/params/fields_test.rb +37 -0
- data/test/lib/jat/plugins/json_api/lib/params/include/parse_test.rb +46 -0
- data/test/lib/jat/plugins/json_api/lib/params/include/validate_test.rb +51 -0
- data/test/lib/jat/plugins/json_api/lib/params/include_test.rb +41 -0
- data/test/lib/jat/plugins/json_api/lib/presenters/document_links_presenter_test.rb +69 -0
- data/test/lib/jat/plugins/json_api/lib/presenters/document_meta_presenter_test.rb +69 -0
- data/test/lib/jat/plugins/json_api/lib/presenters/jsonapi_presenter_test.rb +69 -0
- data/test/lib/jat/plugins/json_api/lib/presenters/links_presenter_test.rb +69 -0
- data/test/lib/jat/plugins/json_api/lib/presenters/meta_presenter_test.rb +69 -0
- data/test/lib/jat/plugins/json_api/lib/presenters/relationship_links_presenter_test.rb +75 -0
- data/test/lib/jat/plugins/json_api/lib/presenters/relationship_meta_presenter_test.rb +75 -0
- data/test/lib/jat/plugins/json_api/lib/response_test.rb +489 -0
- data/test/lib/jat/plugins/json_api/lib/traversal_map_test.rb +58 -0
- data/test/lib/jat/plugins/simple_api/lib/construct_traversal_map_test.rb +100 -0
- data/test/lib/jat/plugins/simple_api/lib/map_test.rb +56 -0
- data/test/lib/jat/plugins/simple_api/lib/params/parse_test.rb +71 -0
- data/test/lib/jat/plugins/simple_api/lib/response_test.rb +342 -0
- data/test/lib/jat/plugins/simple_api/simple_api_test.rb +81 -0
- data/test/lib/jat/plugins/to_str/to_str_test.rb +52 -0
- data/test/lib/jat/presenter_test.rb +61 -0
- data/test/lib/jat/utils/enum_deep_dup_test.rb +31 -0
- data/test/lib/jat/utils/enum_deep_freeze_test.rb +28 -0
- data/test/lib/jat_test.rb +120 -0
- data/test/lib/plugin_test.rb +49 -0
- data/test/support/activerecord.rb +24 -0
- data/test/test_helper.rb +16 -0
- data/test/test_plugin.rb +59 -0
- metadata +240 -11
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Jat
|
|
4
|
+
module Presenters
|
|
5
|
+
class JsonapiPresenter
|
|
6
|
+
module InstanceMethods
|
|
7
|
+
# Presented object
|
|
8
|
+
attr_reader :object
|
|
9
|
+
|
|
10
|
+
# Presented context
|
|
11
|
+
attr_reader :context
|
|
12
|
+
|
|
13
|
+
def initialize(object, context)
|
|
14
|
+
@object = object
|
|
15
|
+
@context = context
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module ClassMethods
|
|
20
|
+
attr_accessor :jat_class
|
|
21
|
+
|
|
22
|
+
def inspect
|
|
23
|
+
"#{jat_class.inspect}::Presenters::JsonapiPresenter"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def add_method(name, block)
|
|
27
|
+
# Warning-free method redefinition
|
|
28
|
+
remove_method(name) if method_defined?(name, false)
|
|
29
|
+
define_method(name, &block_without_args(block))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def block_without_args(block)
|
|
35
|
+
case block.parameters.count
|
|
36
|
+
when 0 then block
|
|
37
|
+
when 1 then -> { instance_exec(object, &block) }
|
|
38
|
+
when 2 then -> { instance_exec(object, context, &block) }
|
|
39
|
+
else raise Error, "Invalid block arguments count"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
extend ClassMethods
|
|
45
|
+
include InstanceMethods
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Jat
|
|
4
|
+
module Presenters
|
|
5
|
+
class LinksPresenter
|
|
6
|
+
module InstanceMethods
|
|
7
|
+
# Presented object
|
|
8
|
+
attr_reader :object
|
|
9
|
+
|
|
10
|
+
# Presented context
|
|
11
|
+
attr_reader :context
|
|
12
|
+
|
|
13
|
+
def initialize(object, context)
|
|
14
|
+
@object = object
|
|
15
|
+
@context = context
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module ClassMethods
|
|
20
|
+
attr_accessor :jat_class
|
|
21
|
+
|
|
22
|
+
def inspect
|
|
23
|
+
"#{jat_class.inspect}::Presenters::LinksPresenter"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def add_method(name, block)
|
|
27
|
+
# Warning-free method redefinition
|
|
28
|
+
remove_method(name) if method_defined?(name, false)
|
|
29
|
+
define_method(name, &block_without_args(block))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def block_without_args(block)
|
|
35
|
+
case block.parameters.count
|
|
36
|
+
when 0 then block
|
|
37
|
+
when 1 then -> { instance_exec(object, &block) }
|
|
38
|
+
when 2 then -> { instance_exec(object, context, &block) }
|
|
39
|
+
else raise Error, "Invalid block arguments count"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
extend ClassMethods
|
|
45
|
+
include InstanceMethods
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Jat
|
|
4
|
+
module Presenters
|
|
5
|
+
class MetaPresenter
|
|
6
|
+
module InstanceMethods
|
|
7
|
+
# Presented object
|
|
8
|
+
attr_reader :object
|
|
9
|
+
|
|
10
|
+
# Presented context
|
|
11
|
+
attr_reader :context
|
|
12
|
+
|
|
13
|
+
def initialize(object, context)
|
|
14
|
+
@object = object
|
|
15
|
+
@context = context
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
module ClassMethods
|
|
20
|
+
attr_accessor :jat_class
|
|
21
|
+
|
|
22
|
+
def inspect
|
|
23
|
+
"#{jat_class.inspect}::Presenters::MetaPresenter"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def add_method(name, block)
|
|
27
|
+
# Warning-free method redefinition
|
|
28
|
+
remove_method(name) if method_defined?(name, false)
|
|
29
|
+
define_method(name, &block_without_args(block))
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def block_without_args(block)
|
|
35
|
+
case block.parameters.count
|
|
36
|
+
when 0 then block
|
|
37
|
+
when 1 then -> { instance_exec(object, &block) }
|
|
38
|
+
when 2 then -> { instance_exec(object, context, &block) }
|
|
39
|
+
else raise Error, "Invalid block arguments count"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
extend ClassMethods
|
|
45
|
+
include InstanceMethods
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Jat
|
|
4
|
+
module Presenters
|
|
5
|
+
class RelationshipLinksPresenter
|
|
6
|
+
module InstanceMethods
|
|
7
|
+
# Relationship parent object
|
|
8
|
+
attr_reader :parent_object
|
|
9
|
+
|
|
10
|
+
# Presented relationship
|
|
11
|
+
attr_reader :object
|
|
12
|
+
|
|
13
|
+
# Presented context
|
|
14
|
+
attr_reader :context
|
|
15
|
+
|
|
16
|
+
def initialize(parent_object, object, context)
|
|
17
|
+
@parent_object = parent_object
|
|
18
|
+
@object = object
|
|
19
|
+
@context = context
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module ClassMethods
|
|
24
|
+
attr_accessor :jat_class
|
|
25
|
+
|
|
26
|
+
def inspect
|
|
27
|
+
"#{jat_class.inspect}::Presenters::RelationshipLinksPresenter"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def add_method(name, block)
|
|
31
|
+
# Warning-free method redefinition
|
|
32
|
+
remove_method(name) if method_defined?(name, false)
|
|
33
|
+
define_method(name, &block_without_args(block))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def block_without_args(block)
|
|
39
|
+
case block.parameters.count
|
|
40
|
+
when 0 then block
|
|
41
|
+
when 1 then -> { instance_exec(parent_object, &block) }
|
|
42
|
+
when 2 then -> { instance_exec(parent_object, object, &block) }
|
|
43
|
+
when 3 then -> { instance_exec(parent_object, object, context, &block) }
|
|
44
|
+
else raise Error, "Invalid block arguments count"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
extend ClassMethods
|
|
50
|
+
include InstanceMethods
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Jat
|
|
4
|
+
module Presenters
|
|
5
|
+
class RelationshipMetaPresenter
|
|
6
|
+
module InstanceMethods
|
|
7
|
+
# Relationship parent object
|
|
8
|
+
attr_reader :parent_object
|
|
9
|
+
|
|
10
|
+
# Presented relationship
|
|
11
|
+
attr_reader :object
|
|
12
|
+
|
|
13
|
+
# Presented context
|
|
14
|
+
attr_reader :context
|
|
15
|
+
|
|
16
|
+
def initialize(parent_object, object, context)
|
|
17
|
+
@parent_object = parent_object
|
|
18
|
+
@object = object
|
|
19
|
+
@context = context
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
module ClassMethods
|
|
24
|
+
attr_accessor :jat_class
|
|
25
|
+
|
|
26
|
+
def inspect
|
|
27
|
+
"#{jat_class.inspect}::Presenters::RelationshipMetaPresenter"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def add_method(name, block)
|
|
31
|
+
# Warning-free method redefinition
|
|
32
|
+
remove_method(name) if method_defined?(name, false)
|
|
33
|
+
define_method(name, &block_without_args(block))
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def block_without_args(block)
|
|
39
|
+
case block.parameters.count
|
|
40
|
+
when 0 then block
|
|
41
|
+
when 1 then -> { instance_exec(parent_object, &block) }
|
|
42
|
+
when 2 then -> { instance_exec(parent_object, object, &block) }
|
|
43
|
+
when 3 then -> { instance_exec(parent_object, object, context, &block) }
|
|
44
|
+
else raise Error, "Invalid block arguments count"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
extend ClassMethods
|
|
50
|
+
include InstanceMethods
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Serializes to JSON-API format
|
|
4
|
+
class Jat
|
|
5
|
+
module Plugins
|
|
6
|
+
module JsonApi
|
|
7
|
+
class Response
|
|
8
|
+
attr_reader :jat, :jat_class, :object, :context
|
|
9
|
+
|
|
10
|
+
def initialize(jat)
|
|
11
|
+
@jat = jat
|
|
12
|
+
@jat_class = jat.class
|
|
13
|
+
@object = jat.object
|
|
14
|
+
@context = jat.context
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def response
|
|
18
|
+
data, includes = data_with_includes
|
|
19
|
+
meta = document_meta
|
|
20
|
+
links = document_links
|
|
21
|
+
jsonapi = jsonapi_data
|
|
22
|
+
|
|
23
|
+
result = {}
|
|
24
|
+
result[:links] = links if links.any?
|
|
25
|
+
result[:data] = data if data
|
|
26
|
+
result[:included] = includes.values if includes.any?
|
|
27
|
+
result[:meta] = meta if meta.any?
|
|
28
|
+
result[:jsonapi] = jsonapi if jsonapi.any?
|
|
29
|
+
result
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def data_with_includes
|
|
35
|
+
includes = {}
|
|
36
|
+
data = many?(object, context) ? many(object, includes) : one(object, includes)
|
|
37
|
+
[data, includes]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def many(objs, includes)
|
|
41
|
+
objs.map { |obj| one(obj, includes) }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def one(obj, includes)
|
|
45
|
+
ResponseData.new(jat_class, obj, context, full_map, includes).data
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def many?(data, context)
|
|
49
|
+
many = context[:many]
|
|
50
|
+
many.nil? ? data.is_a?(Enumerable) : many
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def full_map
|
|
54
|
+
@full_map ||= jat.traversal_map.current
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def jsonapi_data
|
|
58
|
+
data = context[:jsonapi]&.transform_keys(&:to_sym) || {}
|
|
59
|
+
jsonapi_data = jat_class.jsonapi_data
|
|
60
|
+
return data if jsonapi_data.empty?
|
|
61
|
+
|
|
62
|
+
presenter = jat_class::JsonapiPresenter.new(object, context)
|
|
63
|
+
jsonapi_data.each_key do |key|
|
|
64
|
+
data[key] = presenter.public_send(key) unless data.key?(key)
|
|
65
|
+
end
|
|
66
|
+
data.compact!
|
|
67
|
+
data
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def document_links
|
|
71
|
+
data = context[:links]&.transform_keys(&:to_sym) || {}
|
|
72
|
+
document_links = jat_class.document_links
|
|
73
|
+
return data if document_links.empty?
|
|
74
|
+
|
|
75
|
+
presenter = jat_class::DocumentLinksPresenter.new(object, context)
|
|
76
|
+
document_links.each_key do |key|
|
|
77
|
+
data[key] = presenter.public_send(key) unless data.key?(key)
|
|
78
|
+
end
|
|
79
|
+
data.compact!
|
|
80
|
+
data
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def document_meta
|
|
84
|
+
data = context[:meta]&.transform_keys(&:to_sym) || {}
|
|
85
|
+
document_meta = jat_class.added_document_meta
|
|
86
|
+
return data if document_meta.empty?
|
|
87
|
+
|
|
88
|
+
presenter = jat_class::DocumentMetaPresenter.new(object, context)
|
|
89
|
+
document_meta.each_key do |key|
|
|
90
|
+
data[key] = presenter.public_send(key) unless data.key?(key)
|
|
91
|
+
end
|
|
92
|
+
data.compact!
|
|
93
|
+
data
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
class ResponseData
|
|
98
|
+
attr_reader :jat_class, :object, :context, :full_map, :map, :includes, :presenter
|
|
99
|
+
|
|
100
|
+
def initialize(jat_class, object, context, full_map, includes)
|
|
101
|
+
@jat_class = jat_class
|
|
102
|
+
@object = object
|
|
103
|
+
@context = context
|
|
104
|
+
@includes = includes
|
|
105
|
+
@full_map = full_map
|
|
106
|
+
@map = full_map.fetch(jat_class.type)
|
|
107
|
+
@presenter = jat_class::Presenter.new(object, context)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def data
|
|
111
|
+
return unless object
|
|
112
|
+
|
|
113
|
+
attributes = get_attributes
|
|
114
|
+
relationships = get_relationships
|
|
115
|
+
links = get_links
|
|
116
|
+
meta = get_meta
|
|
117
|
+
|
|
118
|
+
result = uid
|
|
119
|
+
result[:attributes] = attributes if attributes
|
|
120
|
+
result[:relationships] = relationships if relationships
|
|
121
|
+
result[:links] = links if links.any?
|
|
122
|
+
result[:meta] = meta if meta.any?
|
|
123
|
+
result
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def uid
|
|
127
|
+
{type: jat_class.type, id: presenter.id}
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
private
|
|
131
|
+
|
|
132
|
+
def get_attributes
|
|
133
|
+
attributes_names = map[:attributes]
|
|
134
|
+
return if attributes_names.empty?
|
|
135
|
+
|
|
136
|
+
attributes_names.each_with_object({}) do |name, attrs|
|
|
137
|
+
attribute = jat_class.attributes[name]
|
|
138
|
+
attrs[name] = presenter.public_send(attribute.original_name)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
def get_relationships
|
|
143
|
+
relationships_names = map[:relationships]
|
|
144
|
+
return if relationships_names.empty?
|
|
145
|
+
|
|
146
|
+
relationships_names.each_with_object({}) do |name, rels|
|
|
147
|
+
rel_attribute = jat_class.attributes[name]
|
|
148
|
+
rel_object = presenter.public_send(rel_attribute.original_name)
|
|
149
|
+
|
|
150
|
+
rel_serializer = rel_attribute.serializer.call
|
|
151
|
+
rel_links = get_relationship_links(rel_serializer, rel_object)
|
|
152
|
+
rel_meta = get_relationship_meta(rel_serializer, rel_object)
|
|
153
|
+
rel_data =
|
|
154
|
+
if many?(rel_attribute, rel_object)
|
|
155
|
+
many_relationships_data(rel_serializer, rel_object)
|
|
156
|
+
else
|
|
157
|
+
one_relationship_data(rel_serializer, rel_object)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
result = {}
|
|
161
|
+
result[:data] = rel_data
|
|
162
|
+
result[:links] = rel_links if rel_links.any?
|
|
163
|
+
result[:meta] = rel_meta if rel_meta.any?
|
|
164
|
+
rels[name] = result
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def many_relationships_data(rel_serializer, rel_objects)
|
|
169
|
+
return [] if rel_objects.empty?
|
|
170
|
+
|
|
171
|
+
rel_objects.map { |rel_object| add_relationship_data(rel_serializer, rel_object) }
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def one_relationship_data(rel_serializer, rel_object)
|
|
175
|
+
return unless rel_object
|
|
176
|
+
|
|
177
|
+
add_relationship_data(rel_serializer, rel_object)
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def add_relationship_data(rel_serializer, rel_object)
|
|
181
|
+
rel_response_data = self.class.new(rel_serializer, rel_object, context, full_map, includes)
|
|
182
|
+
rel_uid = rel_response_data.uid
|
|
183
|
+
includes[rel_uid] ||= rel_response_data.data
|
|
184
|
+
rel_uid
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def many?(attribute, object)
|
|
188
|
+
is_many = attribute.many?
|
|
189
|
+
|
|
190
|
+
# handle boolean
|
|
191
|
+
return is_many if (is_many == true) || (is_many == false)
|
|
192
|
+
|
|
193
|
+
# handle nil
|
|
194
|
+
object.is_a?(Enumerable)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
def get_links
|
|
198
|
+
links = jat_class.object_links
|
|
199
|
+
return links if links.empty?
|
|
200
|
+
|
|
201
|
+
presenter = jat_class::LinksPresenter.new(object, context)
|
|
202
|
+
result = links.each_key.each_with_object({}) { |key, data| data[key] = presenter.public_send(key) }
|
|
203
|
+
result.compact!
|
|
204
|
+
result
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def get_meta
|
|
208
|
+
meta = jat_class.added_object_meta
|
|
209
|
+
return meta if meta.empty?
|
|
210
|
+
|
|
211
|
+
presenter = jat_class::MetaPresenter.new(object, context)
|
|
212
|
+
result = meta.each_key.each_with_object({}) { |key, data| data[key] = presenter.public_send(key) }
|
|
213
|
+
result.compact!
|
|
214
|
+
result
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def get_relationship_links(rel_serializer, rel_object)
|
|
218
|
+
links = rel_serializer.relationship_links
|
|
219
|
+
return links if links.empty?
|
|
220
|
+
|
|
221
|
+
presenter = rel_serializer::RelationshipLinksPresenter.new(object, rel_object, context)
|
|
222
|
+
result = links.each_key.each_with_object({}) { |key, data| data[key] = presenter.public_send(key) }
|
|
223
|
+
result.compact!
|
|
224
|
+
result
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def get_relationship_meta(rel_serializer, rel_object)
|
|
228
|
+
meta = rel_serializer.added_relationship_meta
|
|
229
|
+
return meta if meta.empty?
|
|
230
|
+
|
|
231
|
+
presenter = rel_serializer::RelationshipMetaPresenter.new(object, rel_object, context)
|
|
232
|
+
result = meta.each_key.each_with_object({}) { |key, data| data[key] = presenter.public_send(key) }
|
|
233
|
+
result.compact!
|
|
234
|
+
result
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "./construct_traversal_map"
|
|
4
|
+
require_relative "./map"
|
|
5
|
+
|
|
6
|
+
class Jat
|
|
7
|
+
module Plugins
|
|
8
|
+
module JsonApi
|
|
9
|
+
class TraversalMap
|
|
10
|
+
module InstanceMethods
|
|
11
|
+
attr_reader :jat
|
|
12
|
+
|
|
13
|
+
def initialize(jat)
|
|
14
|
+
@jat = jat
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def full
|
|
18
|
+
@full ||= ConstructTraversalMap.new(jat.class, :all).to_h
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def exposed
|
|
22
|
+
@exposed ||= ConstructTraversalMap.new(jat.class, :exposed).to_h
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def current
|
|
26
|
+
@current ||= Map.call(jat)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
include InstanceMethods
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Combines all serializer and its relations exposed fields into one hash.
|
|
4
|
+
class Jat
|
|
5
|
+
module Plugins
|
|
6
|
+
module SimpleApi
|
|
7
|
+
class ConstructTraversalMap
|
|
8
|
+
attr_reader :jat_class, :exposed, :manually_exposed
|
|
9
|
+
|
|
10
|
+
def initialize(jat_class, exposed, manually_exposed: nil)
|
|
11
|
+
@jat_class = jat_class
|
|
12
|
+
@exposed = exposed.to_sym
|
|
13
|
+
@manually_exposed = manually_exposed || {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def to_h
|
|
17
|
+
jat_class.attributes.each_with_object({}) do |(name, attribute), result|
|
|
18
|
+
next unless expose?(attribute)
|
|
19
|
+
|
|
20
|
+
result[name] =
|
|
21
|
+
if attribute.relation?
|
|
22
|
+
self.class.new(attribute.serializer.call, exposed, manually_exposed: manually_exposed[name]).to_h
|
|
23
|
+
else
|
|
24
|
+
{}
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def expose?(attribute)
|
|
32
|
+
case exposed
|
|
33
|
+
when :all then true
|
|
34
|
+
when :none then manually_exposed?(attribute)
|
|
35
|
+
else attribute.exposed? || manually_exposed?(attribute)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def manually_exposed?(attribute)
|
|
40
|
+
manually_exposed.include?(attribute.name)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "./params/parse"
|
|
4
|
+
require_relative "./construct_traversal_map"
|
|
5
|
+
|
|
6
|
+
class Jat
|
|
7
|
+
module Plugins
|
|
8
|
+
module SimpleApi
|
|
9
|
+
class Map
|
|
10
|
+
class << self
|
|
11
|
+
# Returns structure like
|
|
12
|
+
# {
|
|
13
|
+
# key1 => { key11 => {}, key12 => { ... } },
|
|
14
|
+
# key2 => { key21 => {}, key22 => { ... } },
|
|
15
|
+
# }
|
|
16
|
+
def call(jat)
|
|
17
|
+
params = jat.context[:params]
|
|
18
|
+
fields = params && (params[:fields] || params["fields"])
|
|
19
|
+
exposed = jat.context[:exposed] || :default
|
|
20
|
+
|
|
21
|
+
manually_exposed = Params::Parse.new(fields).parse
|
|
22
|
+
|
|
23
|
+
ConstructTraversalMap.new(jat.class, exposed, manually_exposed: manually_exposed).to_h
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Jat
|
|
4
|
+
module Plugins
|
|
5
|
+
module SimpleApi
|
|
6
|
+
module Params
|
|
7
|
+
class Parse
|
|
8
|
+
module InstanceMethods
|
|
9
|
+
COMMA = ","
|
|
10
|
+
OPEN_BRACKET = "("
|
|
11
|
+
CLOSE_BRACKET = ")"
|
|
12
|
+
|
|
13
|
+
def initialize(fields)
|
|
14
|
+
@fields = fields
|
|
15
|
+
@map = []
|
|
16
|
+
@res = {}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# user => { user: {} }
|
|
20
|
+
# user(id) => { user: { id: true } }
|
|
21
|
+
# user(id, name) => { user: { id: true, name: true } }
|
|
22
|
+
# user, comments => { user: {}, comments: {} }
|
|
23
|
+
# user(comments(text)) => { user: {}, comments: {} }
|
|
24
|
+
def parse
|
|
25
|
+
return {} unless fields
|
|
26
|
+
|
|
27
|
+
current_attr = nil
|
|
28
|
+
|
|
29
|
+
fields.each_char do |char|
|
|
30
|
+
case char
|
|
31
|
+
when COMMA
|
|
32
|
+
add_attribute(current_attr)
|
|
33
|
+
current_attr = nil
|
|
34
|
+
when CLOSE_BRACKET
|
|
35
|
+
add_attribute(current_attr)
|
|
36
|
+
map.pop
|
|
37
|
+
current_attr = nil
|
|
38
|
+
when OPEN_BRACKET
|
|
39
|
+
add_attribute(current_attr)
|
|
40
|
+
map << current_attr.to_sym if current_attr
|
|
41
|
+
current_attr = nil
|
|
42
|
+
else
|
|
43
|
+
current_attr = current_attr ? current_attr.insert(-1, char) : char
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
add_attribute(current_attr)
|
|
47
|
+
|
|
48
|
+
res
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
attr_reader :fields, :map, :res
|
|
54
|
+
|
|
55
|
+
def add_attribute(current_attr)
|
|
56
|
+
return unless current_attr
|
|
57
|
+
|
|
58
|
+
current_resource = map.empty? ? res : res.dig(*map)
|
|
59
|
+
current_resource[current_attr.to_sym] = {}
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
include InstanceMethods
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|