jat 0.0.3 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. data/lib/jat/attribute.rb +26 -5
  3. data/lib/jat/config.rb +4 -7
  4. data/lib/jat/plugins/activerecord/activerecord.rb +23 -0
  5. data/lib/jat/plugins/base/base_activerecord_preloads/base_activerecord_preloads.rb +38 -0
  6. data/lib/jat/plugins/{_activerecord_preloads → base/base_activerecord_preloads}/lib/preloader.rb +22 -16
  7. data/lib/jat/plugins/base/base_lower_camel_case/base_lower_camel_case.rb +36 -0
  8. data/lib/jat/plugins/{_preloads/_preloads.rb → base/base_preloads/base_preloads.rb} +14 -4
  9. data/lib/jat/plugins/{_preloads → base/base_preloads}/lib/format_user_preloads.rb +1 -1
  10. data/lib/jat/plugins/{_preloads → base/base_preloads}/lib/preloads_with_path.rb +1 -1
  11. data/lib/jat/plugins/cache/cache.rb +14 -6
  12. data/lib/jat/plugins/json_api/json_api.rb +150 -106
  13. data/lib/jat/plugins/json_api/lib/fields_param_parser.rb +40 -0
  14. data/lib/jat/plugins/json_api/lib/include_param_parser.rb +84 -0
  15. data/lib/jat/plugins/json_api/lib/map.rb +92 -27
  16. data/lib/jat/plugins/json_api/lib/params/fields/validate.rb +8 -5
  17. data/lib/jat/plugins/json_api/lib/response.rb +81 -197
  18. data/lib/jat/plugins/json_api/lib/response_piece.rb +175 -0
  19. data/lib/jat/plugins/json_api/plugins/json_api_activerecord/json_api_activerecord.rb +23 -0
  20. data/lib/jat/plugins/json_api/plugins/json_api_lower_camel_case/json_api_lower_camel_case.rb +34 -0
  21. data/lib/jat/plugins/json_api/plugins/json_api_maps_cache/json_api_maps_cache.rb +58 -0
  22. data/lib/jat/plugins/json_api/plugins/json_api_preloads/json_api_preloads.rb +38 -0
  23. data/lib/jat/plugins/{_json_api_activerecord → json_api/plugins/json_api_preloads}/lib/preloads.rb +17 -25
  24. data/lib/jat/plugins/json_api/plugins/json_api_validate_params/json_api_validate_params.rb +61 -0
  25. data/lib/jat/plugins/json_api/plugins/json_api_validate_params/lib/params_error.rb +6 -0
  26. data/lib/jat/plugins/json_api/plugins/json_api_validate_params/lib/validate_fields_param.rb +59 -0
  27. data/lib/jat/plugins/json_api/plugins/json_api_validate_params/lib/validate_include_param.rb +33 -0
  28. data/lib/jat/plugins/lower_camel_case/lower_camel_case.rb +23 -0
  29. data/lib/jat/plugins/maps_cache/maps_cache.rb +23 -0
  30. data/lib/jat/plugins/preloads/preloads.rb +23 -0
  31. data/lib/jat/plugins/simple_api/lib/fields_param_parser.rb +97 -0
  32. data/lib/jat/plugins/simple_api/lib/map.rb +80 -10
  33. data/lib/jat/plugins/simple_api/lib/response.rb +75 -90
  34. data/lib/jat/plugins/simple_api/lib/response_piece.rb +80 -0
  35. data/lib/jat/plugins/simple_api/plugins/simple_api_activerecord/simple_api_activerecord.rb +23 -0
  36. data/lib/jat/plugins/simple_api/plugins/simple_api_lower_camel_case/simple_api_lower_camel_case.rb +34 -0
  37. data/lib/jat/plugins/simple_api/plugins/simple_api_maps_cache/simple_api_maps_cache.rb +50 -0
  38. data/lib/jat/plugins/simple_api/plugins/simple_api_preloads/lib/preloads.rb +55 -0
  39. data/lib/jat/plugins/simple_api/plugins/simple_api_preloads/simple_api_preloads.rb +38 -0
  40. data/lib/jat/plugins/simple_api/plugins/simple_api_validate_params/lib/fields_error.rb +6 -0
  41. data/lib/jat/plugins/simple_api/plugins/simple_api_validate_params/lib/validate_fields_param.rb +45 -0
  42. data/lib/jat/plugins/simple_api/plugins/simple_api_validate_params/simple_api_validate_params.rb +49 -0
  43. data/lib/jat/plugins/simple_api/simple_api.rb +87 -27
  44. data/lib/jat/plugins/to_str/to_str.rb +15 -5
  45. data/lib/jat/plugins/types/types.rb +54 -0
  46. data/lib/jat/plugins/validate_params/validate_params.rb +23 -0
  47. data/lib/jat/plugins.rb +42 -30
  48. data/lib/jat.rb +40 -35
  49. data/test/lib/jat/attribute_test.rb +22 -4
  50. data/test/lib/jat/plugins/activerecord/activerecord_test.rb +27 -0
  51. data/test/lib/jat/plugins/base_activerecord_preloads/base_activerecord_preloads_test.rb +59 -0
  52. data/test/lib/jat/plugins/{_activerecord_preloads → base_activerecord_preloads}/lib/preloader_test.rb +11 -25
  53. data/test/lib/jat/plugins/base_lower_camel_case/base_lower_camel_case_test.rb +26 -0
  54. data/test/lib/jat/plugins/{_preloads/_preloads_test.rb → base_preloads/base_preloads_test.rb} +2 -2
  55. data/test/lib/jat/plugins/{_preloads → base_preloads}/lib/format_user_preloads_test.rb +3 -3
  56. data/test/lib/jat/plugins/{_preloads → base_preloads}/lib/preloads_with_path_test.rb +2 -2
  57. data/test/lib/jat/plugins/cache/cache_test.rb +11 -11
  58. data/test/lib/jat/plugins/json_api/json_api_test.rb +57 -49
  59. data/test/lib/jat/plugins/json_api/lib/fields_param_parser_test.rb +46 -0
  60. data/test/lib/jat/plugins/json_api/lib/include_param_parser_test.rb +57 -0
  61. data/test/lib/jat/plugins/json_api/lib/map_test.rb +171 -92
  62. data/test/lib/jat/plugins/json_api/lib/response_piece_test.rb +13 -0
  63. data/test/lib/jat/plugins/json_api/lib/response_test.rb +38 -32
  64. data/test/lib/jat/plugins/json_api_activerecord/json_api_activerecord_test.rb +24 -0
  65. data/test/lib/jat/plugins/json_api_lower_camel_case/json_api_lower_camel_case_test.rb +85 -0
  66. data/test/lib/jat/plugins/json_api_maps_cache/json_api_maps_cache_test.rb +107 -0
  67. data/test/lib/jat/plugins/json_api_preloads/json_api_preloads_test.rb +51 -0
  68. data/test/lib/jat/plugins/{_json_api_activerecord → json_api_preloads}/lib/preloads_test.rb +19 -13
  69. data/test/lib/jat/plugins/json_api_validate_params/json_api_validate_params_test.rb +108 -0
  70. data/test/lib/jat/plugins/lower_camel_case/lower_camel_case_test.rb +27 -0
  71. data/test/lib/jat/plugins/maps_cache/maps_cache_test.rb +27 -0
  72. data/test/lib/jat/plugins/preloads/preloads_test.rb +27 -0
  73. data/test/lib/jat/plugins/simple_api/lib/fields_param_parser_test.rb +87 -0
  74. data/test/lib/jat/plugins/simple_api/lib/map_test.rb +118 -33
  75. data/test/lib/jat/plugins/simple_api/lib/response_piece_test.rb +13 -0
  76. data/test/lib/jat/plugins/simple_api/lib/response_test.rb +90 -76
  77. data/test/lib/jat/plugins/simple_api/simple_api_test.rb +118 -25
  78. data/test/lib/jat/plugins/simple_api_activerecord/simple_api_activerecord_test.rb +24 -0
  79. data/test/lib/jat/plugins/simple_api_lower_camel_case/simple_api_lower_camel_case_test.rb +54 -0
  80. data/test/lib/jat/plugins/simple_api_maps_cache/simple_api_maps_cache_test.rb +95 -0
  81. data/test/lib/jat/plugins/simple_api_preloads/lib/preloads_test.rb +140 -0
  82. data/test/lib/jat/plugins/simple_api_preloads/simple_api_preloads_test.rb +51 -0
  83. data/test/lib/jat/plugins/simple_api_validate_params/simple_api_validate_params_test.rb +101 -0
  84. data/test/lib/jat/plugins/to_str/to_str_test.rb +3 -3
  85. data/test/lib/jat/plugins/types/types_test.rb +84 -0
  86. data/test/lib/jat/plugins/validate_params/validate_params_test.rb +27 -0
  87. data/test/lib/jat/plugins_test.rb +88 -0
  88. data/test/lib/jat_test.rb +47 -24
  89. data/test/lib/plugin_test.rb +3 -3
  90. data/test/test_helper.rb +7 -6
  91. data/test/test_plugin.rb +9 -12
  92. metadata +107 -88
  93. data/CHANGELOG.md +0 -7
  94. data/README.md +0 -21
  95. data/jat.gemspec +0 -37
  96. data/lib/jat/plugins/_activerecord_preloads/_activerecord_preloads.rb +0 -29
  97. data/lib/jat/plugins/_json_api_activerecord/_json_api_activerecord.rb +0 -22
  98. data/lib/jat/plugins/camel_lower/camel_lower.rb +0 -18
  99. data/lib/jat/plugins/json_api/lib/construct_traversal_map.rb +0 -91
  100. data/lib/jat/plugins/json_api/lib/presenters/document_links_presenter.rb +0 -48
  101. data/lib/jat/plugins/json_api/lib/presenters/document_meta_presenter.rb +0 -48
  102. data/lib/jat/plugins/json_api/lib/presenters/jsonapi_presenter.rb +0 -48
  103. data/lib/jat/plugins/json_api/lib/presenters/links_presenter.rb +0 -48
  104. data/lib/jat/plugins/json_api/lib/presenters/meta_presenter.rb +0 -48
  105. data/lib/jat/plugins/json_api/lib/presenters/relationship_links_presenter.rb +0 -53
  106. data/lib/jat/plugins/json_api/lib/presenters/relationship_meta_presenter.rb +0 -53
  107. data/lib/jat/plugins/json_api/lib/traversal_map.rb +0 -34
  108. data/lib/jat/plugins/simple_api/lib/construct_traversal_map.rb +0 -45
  109. data/lib/jat/plugins/simple_api/lib/params/parse.rb +0 -68
  110. data/lib/jat/presenter.rb +0 -51
  111. data/test/lib/jat/plugins/_activerecord_preloads/_activerecord_preloads_test.rb +0 -40
  112. data/test/lib/jat/plugins/_json_api_activerecord/_json_api_activerecord_test.rb +0 -29
  113. data/test/lib/jat/plugins/camel_lower/camel_lower_test.rb +0 -78
  114. data/test/lib/jat/plugins/json_api/lib/construct_traversal_map_test.rb +0 -119
  115. data/test/lib/jat/plugins/json_api/lib/params/fields/parse_test.rb +0 -24
  116. data/test/lib/jat/plugins/json_api/lib/params/fields/validate_test.rb +0 -47
  117. data/test/lib/jat/plugins/json_api/lib/params/fields_test.rb +0 -37
  118. data/test/lib/jat/plugins/json_api/lib/params/include/parse_test.rb +0 -46
  119. data/test/lib/jat/plugins/json_api/lib/params/include/validate_test.rb +0 -51
  120. data/test/lib/jat/plugins/json_api/lib/params/include_test.rb +0 -41
  121. data/test/lib/jat/plugins/json_api/lib/presenters/document_links_presenter_test.rb +0 -69
  122. data/test/lib/jat/plugins/json_api/lib/presenters/document_meta_presenter_test.rb +0 -69
  123. data/test/lib/jat/plugins/json_api/lib/presenters/jsonapi_presenter_test.rb +0 -69
  124. data/test/lib/jat/plugins/json_api/lib/presenters/links_presenter_test.rb +0 -69
  125. data/test/lib/jat/plugins/json_api/lib/presenters/meta_presenter_test.rb +0 -69
  126. data/test/lib/jat/plugins/json_api/lib/presenters/relationship_links_presenter_test.rb +0 -75
  127. data/test/lib/jat/plugins/json_api/lib/presenters/relationship_meta_presenter_test.rb +0 -75
  128. data/test/lib/jat/plugins/json_api/lib/traversal_map_test.rb +0 -58
  129. data/test/lib/jat/plugins/simple_api/lib/construct_traversal_map_test.rb +0 -100
  130. data/test/lib/jat/plugins/simple_api/lib/params/parse_test.rb +0 -71
  131. data/test/lib/jat/presenter_test.rb +0 -61
@@ -5,129 +5,114 @@ class Jat
5
5
  module Plugins
6
6
  module SimpleApi
7
7
  class Response
8
- attr_reader :jat, :jat_class, :object, :context
8
+ module ClassMethods
9
+ # Returns the Jat class that this Response class is namespaced under.
10
+ attr_accessor :jat_class
11
+
12
+ # Since Response is anonymously subclassed when Jat is subclassed,
13
+ # and then assigned to a constant of the Jat subclass, make inspect
14
+ # reflect the likely name for the class.
15
+ def inspect
16
+ "#{jat_class.inspect}::Response"
17
+ end
9
18
 
10
- def initialize(jat)
11
- @jat = jat
12
- @jat_class = jat.class
13
- @object = jat.object
14
- @context = jat.context
19
+ def call(object, context)
20
+ new(object, context).to_h
21
+ end
15
22
  end
16
23
 
17
- def response
18
- # Add main response
19
- result = many? ? many(object) : one(object)
20
- result = {root_key => result} if root_key
21
- result ||= {}
24
+ module InstanceMethods
25
+ attr_reader :object, :context, :jat_class
22
26
 
23
- # Add metadata to response
24
- # We can add metadata to empty response, or to top-level namespace
25
- # We should not mix metadata with object attributes
26
- metadata.tap do |meta|
27
- next if meta.empty?
28
- raise Error, "Response must have a root key to add metadata" if !result.empty? && !root_key
29
- result[meta_key] = meta
27
+ def initialize(object, context)
28
+ @object = object
29
+ @context = context
30
+ @jat_class = self.class.jat_class
30
31
  end
31
32
 
32
- result
33
- end
33
+ def to_h
34
+ # Add main response
35
+ is_many = many?
36
+ root = root_key(is_many)
34
37
 
35
- private
38
+ response = is_many ? many(object) : one(object)
39
+ response = {root => response} if root
40
+ response ||= {}
36
41
 
37
- def metadata
38
- result = context[:meta] || {}
42
+ add_metadata(response, root)
39
43
 
40
- config_meta = jat_class.config[:meta]
41
- return result unless config_meta
44
+ response
45
+ end
42
46
 
43
- config_meta.each_with_object(result) do |(key, value), res|
44
- next if res.key?(key) # do not overwrite manually added meta
47
+ private
45
48
 
46
- value = value.call(object, context) if value.respond_to?(:call)
47
- res[key] = value unless value.nil?
49
+ def many(objects)
50
+ objects.map { |obj| one(obj) }
48
51
  end
49
- end
50
-
51
- def many(objects)
52
- objects.map { |obj| one(obj) }
53
- end
54
52
 
55
- def one(obj)
56
- ResponseData.new(jat_class, obj, context, jat.traversal_map).data
57
- end
53
+ def one(obj)
54
+ map = jat_class.map(context)
55
+ jat_class::ResponsePiece.to_h(obj, context, map)
56
+ end
58
57
 
59
- def many?
60
- @is_many ||= begin
58
+ def many?
61
59
  many = context[:many]
62
60
  many.nil? ? object.is_a?(Enumerable) : many
63
61
  end
64
- end
65
62
 
66
- # We can provide nil or false to remove root
67
- def root_key
68
- @root_key ||=
63
+ # We can provide nil or false to remove root
64
+ def root_key(is_many)
69
65
  if context.key?(:root)
70
- context[:root]
66
+ root = context[:root]
67
+ root ? root.to_sym : root
71
68
  else
72
- (many? ? jat_class.root_for_many : jat_class.root_for_one) || jat_class.root
69
+ config = jat_class.config
70
+ is_many ? config[:root_many] : config[:root_one]
73
71
  end
74
- end
72
+ end
75
73
 
76
- def meta_key
77
- context[:meta_key] || jat_class.meta_key
78
- end
79
- end
74
+ # Add metadata to response
75
+ # We can add metadata whether to empty response or to top-level namespace
76
+ # We should not mix metadata with object attributes
77
+ def add_metadata(response, root)
78
+ meta = metadata
79
+ return if meta.empty?
80
80
 
81
- class ResponseData
82
- attr_reader :jat_class, :object, :context, :map, :presenter
81
+ raise Error, "Response must have a root key to add metadata" if !response.empty? && !root
82
+ response[meta_key] = meta
83
+ end
83
84
 
84
- def initialize(jat_class, object, context, map)
85
- @jat_class = jat_class
86
- @object = object
87
- @context = context
88
- @map = map
89
- @presenter = jat_class::Presenter.new(object, context)
90
- end
85
+ def meta_key
86
+ context[:meta_key]&.to_sym || jat_class.config[:meta_key]
87
+ end
91
88
 
92
- def data
93
- return unless object
89
+ def metadata
90
+ data = context_metadata
94
91
 
95
- result = {}
92
+ meta = jat_class.added_meta
93
+ return data if meta.empty?
96
94
 
97
- map.each do |key, inner_keys|
98
- attribute = jat_class.attributes.fetch(key)
99
- value = presenter.public_send(attribute.original_name)
95
+ meta.each do |name, attribute|
96
+ next if data.key?(name)
100
97
 
101
- result[key] =
102
- if attribute.relation?
103
- if many?(attribute, value)
104
- value.map { |obj| response_data(attribute, obj, inner_keys) }
105
- else
106
- response_data(attribute, value, inner_keys)
107
- end
108
- else
109
- value
110
- end
111
- end
98
+ value = attribute.value(object, context)
112
99
 
113
- result
114
- end
100
+ unless value.nil?
101
+ data = data.dup if data.equal?(FROZEN_EMPTY_HASH)
102
+ data[name] = value
103
+ end
104
+ end
115
105
 
116
- private
106
+ data
107
+ end
117
108
 
118
- def response_data(attribute, value, map)
119
- ResponseData.new(attribute.serializer.call, value, context, map).data
109
+ def context_metadata
110
+ context[:meta]&.transform_keys(&:to_sym) || FROZEN_EMPTY_HASH
111
+ end
120
112
  end
121
113
 
122
- def many?(attribute, object)
123
- is_many = attribute.many?
124
-
125
- # handle boolean
126
- return is_many if (is_many == true) || (is_many == false)
127
-
128
- # handle nil
129
- object.is_a?(Enumerable)
130
- end
114
+ extend ClassMethods
115
+ include InstanceMethods
131
116
  end
132
117
  end
133
118
  end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Serializes to JSON-API format
4
+ class Jat
5
+ module Plugins
6
+ module SimpleApi
7
+ class ResponsePiece
8
+ module ClassMethods
9
+ # Returns the Jat class that this ResponsePiece class is namespaced under.
10
+ attr_accessor :jat_class
11
+
12
+ # Since ResponsePiece is anonymously subclassed when Jat is subclassed,
13
+ # and then assigned to a constant of the Jat subclass, make inspect
14
+ # reflect the likely name for the class.
15
+ def inspect
16
+ "#{jat_class.inspect}::ResponsePiece"
17
+ end
18
+
19
+ def to_h(object, context, map)
20
+ new(object, context).piece(map)
21
+ end
22
+ end
23
+
24
+ module InstanceMethods
25
+ attr_reader :jat_class, :object, :context
26
+
27
+ def initialize(object, context)
28
+ @object = object
29
+ @context = context
30
+ @jat_class = self.class.jat_class
31
+ end
32
+
33
+ def piece(map)
34
+ return unless object
35
+
36
+ result = {}
37
+
38
+ map.each do |key, inner_map|
39
+ attribute = jat_class.attributes.fetch(key)
40
+ value = attribute.value(object, context)
41
+
42
+ result[key] =
43
+ if attribute.relation?
44
+ if many?(attribute, value)
45
+ value.map { |obj| inner_piece(attribute, obj, inner_map) }
46
+ else
47
+ inner_piece(attribute, value, inner_map)
48
+ end
49
+ else
50
+ value
51
+ end
52
+ end
53
+
54
+ result
55
+ end
56
+
57
+ private
58
+
59
+ def inner_piece(attribute, value, inner_map)
60
+ serializer = attribute.serializer.call
61
+ serializer::ResponsePiece.to_h(value, context, inner_map)
62
+ end
63
+
64
+ def many?(attribute, nested_object)
65
+ is_many = attribute.many?
66
+
67
+ # handle boolean
68
+ return is_many if (is_many == true) || (is_many == false)
69
+
70
+ # handle nil
71
+ nested_object.is_a?(Enumerable)
72
+ end
73
+ end
74
+
75
+ extend ClassMethods
76
+ include InstanceMethods
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ module Plugins
5
+ module SimpleApiActiverecord
6
+ def self.plugin_name
7
+ :simple_api_activerecord
8
+ end
9
+
10
+ def self.before_load(jat_class, **_opts)
11
+ return if jat_class.plugin_used?(:simple_api)
12
+ raise Error, "Please load :simple_api plugin first"
13
+ end
14
+
15
+ def self.load(jat_class, **opts)
16
+ jat_class.plugin :simple_api_preloads, **opts
17
+ jat_class.plugin :base_activerecord_preloads, **opts
18
+ end
19
+ end
20
+
21
+ register_plugin(SimpleApiActiverecord.plugin_name, SimpleApiActiverecord)
22
+ end
23
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ module Plugins
5
+ module SimpleApiLowerCamelCase
6
+ def self.plugin_name
7
+ :simple_api_lower_camel_case
8
+ end
9
+
10
+ def self.before_load(jat_class, **_opts)
11
+ raise Error, "Please load :simple_api plugin first" unless jat_class.plugin_used?(:simple_api)
12
+
13
+ jat_class.plugin :base_lower_camel_case
14
+ end
15
+
16
+ def self.load(jat_class, **_opts)
17
+ jat_class::Response.include(ResponseInstanceMethods)
18
+ end
19
+
20
+ module ResponseInstanceMethods
21
+ private
22
+
23
+ def context_metadata
24
+ metadata = super
25
+ return metadata if metadata.empty?
26
+
27
+ metadata.transform_keys! { |key| Jat::LowerCamelCaseTransformation.call(key) }
28
+ end
29
+ end
30
+ end
31
+
32
+ register_plugin(SimpleApiLowerCamelCase.plugin_name, SimpleApiLowerCamelCase)
33
+ end
34
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ module Plugins
5
+ module SimpleApiMapsCache
6
+ def self.plugin_name
7
+ :simple_api_maps_cache
8
+ end
9
+
10
+ def self.before_load(jat_class, **_opts)
11
+ return if jat_class.plugin_used?(:simple_api)
12
+
13
+ raise Error, "Please load :simple_api plugin first"
14
+ end
15
+
16
+ def self.load(jat_class, **_opts)
17
+ jat_class::Map.extend(MapsCacheClassMethods)
18
+ end
19
+
20
+ def self.after_load(jat_class, **opts)
21
+ jat_class.config[:cached_maps_count] = opts[:cached_maps_count] || 100
22
+ end
23
+
24
+ module MapsCacheClassMethods
25
+ # Caches up to `:cached_maps_count` maps for each serializer.
26
+ # Removes earliest value if new value exceeds limit.
27
+ def maps_cache
28
+ @maps_cache ||= Hash.new do |cache, cache_key|
29
+ cache.shift if cache.length >= jat_class.config[:cached_maps_count] # protect from memory leak
30
+ cache[cache_key] = EnumDeepFreeze.call(yield)
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def construct_map(exposed, fields)
37
+ cache = maps_cache { super }
38
+ cache_key = cache_key(exposed, fields)
39
+ cache[cache_key]
40
+ end
41
+
42
+ def cache_key(exposed, fields)
43
+ "exposed:#{exposed}:fields:#{fields}:"
44
+ end
45
+ end
46
+ end
47
+
48
+ register_plugin(SimpleApiMapsCache.plugin_name, SimpleApiMapsCache)
49
+ end
50
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ module Plugins
5
+ module SimpleApiPreloads
6
+ class Preloads
7
+ module ClassMethods
8
+ def call(jat)
9
+ result = {}
10
+ append_many(result, jat.class, jat.map)
11
+ result
12
+ end
13
+
14
+ private
15
+
16
+ def append_many(result, jat_class, keys)
17
+ keys.each do |key, inner_keys|
18
+ attribute = jat_class.attributes.fetch(key)
19
+ preloads = attribute.preloads
20
+ next unless preloads
21
+
22
+ append_one(result, jat_class, preloads)
23
+ next if inner_keys.empty?
24
+
25
+ path = attribute.preloads_path
26
+ nested_result = nested(result, path)
27
+ nested_serializer = attribute.serializer.call
28
+
29
+ append_many(nested_result, nested_serializer, inner_keys)
30
+ end
31
+ end
32
+
33
+ def append_one(result, jat_class, preloads)
34
+ return if preloads.empty?
35
+
36
+ preloads = EnumDeepDup.call(preloads)
37
+ merge(result, preloads)
38
+ end
39
+
40
+ def merge(result, preloads)
41
+ result.merge!(preloads) do |_key, value_one, value_two|
42
+ merge(value_one, value_two)
43
+ end
44
+ end
45
+
46
+ def nested(result, path)
47
+ !path || path.empty? ? result : result.dig(*path)
48
+ end
49
+ end
50
+
51
+ extend ClassMethods
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./lib/preloads"
4
+
5
+ class Jat
6
+ module Plugins
7
+ module SimpleApiPreloads
8
+ def self.plugin_name
9
+ :simple_api_preloads
10
+ end
11
+
12
+ def self.before_load(jat_class, **opts)
13
+ raise Error, "Please load :simple_api plugin first" unless jat_class.plugin_used?(:simple_api)
14
+
15
+ jat_class.plugin :base_preloads, **opts
16
+ end
17
+
18
+ def self.load(jat_class, **_opts)
19
+ jat_class.extend(ClassMethods)
20
+ jat_class.include(InstanceMethods)
21
+ end
22
+
23
+ module ClassMethods
24
+ def preloads(context = {})
25
+ new(context).preloads
26
+ end
27
+ end
28
+
29
+ module InstanceMethods
30
+ def preloads
31
+ @preloads ||= Preloads.call(self)
32
+ end
33
+ end
34
+ end
35
+
36
+ register_plugin(SimpleApiPreloads.plugin_name, SimpleApiPreloads)
37
+ end
38
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ class SimpleApiFieldsError < Error
5
+ end
6
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ module Plugins
5
+ module SimpleApiValidateParams
6
+ class ValidateFieldsParam
7
+ class << self
8
+ def call(jat_class, fields, prev_names = [])
9
+ fields.each do |name, nested_fields|
10
+ attribute = jat_class.attributes[name]
11
+
12
+ raise_error(name, prev_names) unless attribute
13
+ next if nested_fields.empty?
14
+
15
+ raise_nested_error(name, prev_names, nested_fields) unless attribute.relation?
16
+ nested_serializer = attribute.serializer.call
17
+ call(nested_serializer, nested_fields, prev_names + [name])
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def raise_error(name, prev_names)
24
+ field_name = field_name(name, prev_names)
25
+
26
+ raise SimpleApiFieldsError, "Field #{field_name} not exists"
27
+ end
28
+
29
+ def raise_nested_error(name, prev_names, nested_fields)
30
+ field_name = field_name(name, prev_names)
31
+ first_nested = nested_fields.keys.first
32
+
33
+ raise SimpleApiFieldsError, "Field #{field_name} is not a relationship to add '#{first_nested}' attribute"
34
+ end
35
+
36
+ def field_name(name, prev_names)
37
+ res = "'#{name}'"
38
+ res += " ('#{prev_names.join(".")}.#{name}')" if prev_names.any?
39
+ res
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./lib/fields_error"
4
+ require_relative "./lib/validate_fields_param"
5
+
6
+ class Jat
7
+ module Plugins
8
+ module SimpleApiValidateParams
9
+ def self.plugin_name
10
+ :simple_api_validate_params
11
+ end
12
+
13
+ def self.before_load(jat_class, **_opts)
14
+ return if jat_class.plugin_used?(:simple_api)
15
+ raise Error, "Please load :simple_api plugin first"
16
+ end
17
+
18
+ def self.load(jat_class, **_opts)
19
+ jat_class.extend(ClassMethods)
20
+ jat_class.include(InstanceMethods)
21
+
22
+ jat_class::FieldsParamParser.include(FieldsParamParserMethods)
23
+ end
24
+
25
+ module InstanceMethods
26
+ def validate
27
+ @validate ||= self.class.validate(context)
28
+ end
29
+ end
30
+
31
+ module ClassMethods
32
+ def validate(context)
33
+ # Generate map for current context.
34
+ # Params are valid if no errors were raised
35
+ map(context)
36
+ true
37
+ end
38
+ end
39
+
40
+ module FieldsParamParserMethods
41
+ def parse
42
+ super.tap { |result| ValidateFieldsParam.call(self.class.jat_class, result) }
43
+ end
44
+ end
45
+ end
46
+
47
+ register_plugin(SimpleApiValidateParams.plugin_name, SimpleApiValidateParams)
48
+ end
49
+ end