jat 0.0.1 → 0.0.3

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