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.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +21 -0
  4. data/jat.gemspec +37 -0
  5. data/lib/jat/attribute.rb +85 -0
  6. data/lib/jat/config.rb +38 -0
  7. data/lib/jat/plugins/_activerecord_preloads/_activerecord_preloads.rb +29 -0
  8. data/lib/jat/plugins/_activerecord_preloads/lib/preloader.rb +89 -0
  9. data/lib/jat/plugins/_json_api_activerecord/_json_api_activerecord.rb +22 -0
  10. data/lib/jat/plugins/_json_api_activerecord/lib/preloads.rb +84 -0
  11. data/lib/jat/plugins/_preloads/_preloads.rb +53 -0
  12. data/lib/jat/plugins/_preloads/lib/format_user_preloads.rb +52 -0
  13. data/lib/jat/plugins/_preloads/lib/preloads_with_path.rb +78 -0
  14. data/lib/jat/plugins/cache/cache.rb +39 -0
  15. data/lib/jat/plugins/camel_lower/camel_lower.rb +18 -0
  16. data/lib/jat/plugins/json_api/json_api.rb +207 -0
  17. data/lib/jat/plugins/json_api/lib/construct_traversal_map.rb +91 -0
  18. data/lib/jat/plugins/json_api/lib/map.rb +54 -0
  19. data/lib/jat/plugins/json_api/lib/params/fields/parse.rb +27 -0
  20. data/lib/jat/plugins/json_api/lib/params/fields/validate.rb +55 -0
  21. data/lib/jat/plugins/json_api/lib/params/fields.rb +23 -0
  22. data/lib/jat/plugins/json_api/lib/params/include/parse.rb +55 -0
  23. data/lib/jat/plugins/json_api/lib/params/include/validate.rb +29 -0
  24. data/lib/jat/plugins/json_api/lib/params/include.rb +49 -0
  25. data/lib/jat/plugins/json_api/lib/presenters/document_links_presenter.rb +48 -0
  26. data/lib/jat/plugins/json_api/lib/presenters/document_meta_presenter.rb +48 -0
  27. data/lib/jat/plugins/json_api/lib/presenters/jsonapi_presenter.rb +48 -0
  28. data/lib/jat/plugins/json_api/lib/presenters/links_presenter.rb +48 -0
  29. data/lib/jat/plugins/json_api/lib/presenters/meta_presenter.rb +48 -0
  30. data/lib/jat/plugins/json_api/lib/presenters/relationship_links_presenter.rb +53 -0
  31. data/lib/jat/plugins/json_api/lib/presenters/relationship_meta_presenter.rb +53 -0
  32. data/lib/jat/plugins/json_api/lib/response.rb +239 -0
  33. data/lib/jat/plugins/json_api/lib/traversal_map.rb +34 -0
  34. data/lib/jat/plugins/simple_api/lib/construct_traversal_map.rb +45 -0
  35. data/lib/jat/plugins/simple_api/lib/map.rb +29 -0
  36. data/lib/jat/plugins/simple_api/lib/params/parse.rb +68 -0
  37. data/lib/jat/plugins/simple_api/lib/response.rb +134 -0
  38. data/lib/jat/plugins/simple_api/simple_api.rb +65 -0
  39. data/lib/jat/plugins/to_str/to_str.rb +44 -0
  40. data/lib/jat/plugins.rb +39 -0
  41. data/lib/jat/presenter.rb +51 -0
  42. data/lib/jat/utils/enum_deep_dup.rb +29 -0
  43. data/lib/jat/utils/enum_deep_freeze.rb +19 -0
  44. data/lib/jat.rb +66 -144
  45. data/test/lib/jat/attribute_test.rb +142 -0
  46. data/test/lib/jat/config_test.rb +57 -0
  47. data/test/lib/jat/plugins/_activerecord_preloads/_activerecord_preloads_test.rb +40 -0
  48. data/test/lib/jat/plugins/_activerecord_preloads/lib/preloader_test.rb +98 -0
  49. data/test/lib/jat/plugins/_json_api_activerecord/_json_api_activerecord_test.rb +29 -0
  50. data/test/lib/jat/plugins/_json_api_activerecord/lib/preloads_test.rb +191 -0
  51. data/test/lib/jat/plugins/_preloads/_preloads_test.rb +68 -0
  52. data/test/lib/jat/plugins/_preloads/lib/format_user_preloads_test.rb +47 -0
  53. data/test/lib/jat/plugins/_preloads/lib/preloads_with_path_test.rb +33 -0
  54. data/test/lib/jat/plugins/cache/cache_test.rb +82 -0
  55. data/test/lib/jat/plugins/camel_lower/camel_lower_test.rb +78 -0
  56. data/test/lib/jat/plugins/json_api/json_api_test.rb +154 -0
  57. data/test/lib/jat/plugins/json_api/lib/construct_traversal_map_test.rb +119 -0
  58. data/test/lib/jat/plugins/json_api/lib/map_test.rb +117 -0
  59. data/test/lib/jat/plugins/json_api/lib/params/fields/parse_test.rb +24 -0
  60. data/test/lib/jat/plugins/json_api/lib/params/fields/validate_test.rb +47 -0
  61. data/test/lib/jat/plugins/json_api/lib/params/fields_test.rb +37 -0
  62. data/test/lib/jat/plugins/json_api/lib/params/include/parse_test.rb +46 -0
  63. data/test/lib/jat/plugins/json_api/lib/params/include/validate_test.rb +51 -0
  64. data/test/lib/jat/plugins/json_api/lib/params/include_test.rb +41 -0
  65. data/test/lib/jat/plugins/json_api/lib/presenters/document_links_presenter_test.rb +69 -0
  66. data/test/lib/jat/plugins/json_api/lib/presenters/document_meta_presenter_test.rb +69 -0
  67. data/test/lib/jat/plugins/json_api/lib/presenters/jsonapi_presenter_test.rb +69 -0
  68. data/test/lib/jat/plugins/json_api/lib/presenters/links_presenter_test.rb +69 -0
  69. data/test/lib/jat/plugins/json_api/lib/presenters/meta_presenter_test.rb +69 -0
  70. data/test/lib/jat/plugins/json_api/lib/presenters/relationship_links_presenter_test.rb +75 -0
  71. data/test/lib/jat/plugins/json_api/lib/presenters/relationship_meta_presenter_test.rb +75 -0
  72. data/test/lib/jat/plugins/json_api/lib/response_test.rb +489 -0
  73. data/test/lib/jat/plugins/json_api/lib/traversal_map_test.rb +58 -0
  74. data/test/lib/jat/plugins/simple_api/lib/construct_traversal_map_test.rb +100 -0
  75. data/test/lib/jat/plugins/simple_api/lib/map_test.rb +56 -0
  76. data/test/lib/jat/plugins/simple_api/lib/params/parse_test.rb +71 -0
  77. data/test/lib/jat/plugins/simple_api/lib/response_test.rb +342 -0
  78. data/test/lib/jat/plugins/simple_api/simple_api_test.rb +81 -0
  79. data/test/lib/jat/plugins/to_str/to_str_test.rb +52 -0
  80. data/test/lib/jat/presenter_test.rb +61 -0
  81. data/test/lib/jat/utils/enum_deep_dup_test.rb +31 -0
  82. data/test/lib/jat/utils/enum_deep_freeze_test.rb +28 -0
  83. data/test/lib/jat_test.rb +120 -0
  84. data/test/lib/plugin_test.rb +49 -0
  85. data/test/support/activerecord.rb +24 -0
  86. data/test/test_helper.rb +16 -0
  87. data/test/test_plugin.rb +59 -0
  88. 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