jat 0.0.3 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/lib/jat/attribute.rb +36 -4
  3. data/lib/jat/plugins/_activerecord_preloads/_activerecord_preloads.rb +7 -3
  4. data/lib/jat/plugins/_activerecord_preloads/lib/preloader.rb +7 -13
  5. data/lib/jat/plugins/_lower_camel_case/_lower_camel_case.rb +32 -0
  6. data/lib/jat/plugins/_preloads/_preloads.rb +8 -2
  7. data/lib/jat/plugins/cache/cache.rb +9 -5
  8. data/lib/jat/plugins/json_api/json_api.rb +145 -105
  9. data/lib/jat/plugins/json_api/lib/fields_param_parser.rb +40 -0
  10. data/lib/jat/plugins/json_api/lib/include_param_parser.rb +84 -0
  11. data/lib/jat/plugins/json_api/lib/map.rb +92 -27
  12. data/lib/jat/plugins/json_api/lib/params/fields/validate.rb +8 -5
  13. data/lib/jat/plugins/json_api/lib/response.rb +84 -196
  14. data/lib/jat/plugins/json_api/lib/response_piece.rb +166 -0
  15. data/lib/jat/plugins/json_api_activerecord/json_api_activerecord.rb +31 -0
  16. data/lib/jat/plugins/{_json_api_activerecord → json_api_activerecord}/lib/preloads.rb +16 -24
  17. data/lib/jat/plugins/json_api_lower_camel_case/json_api_lower_camel_case.rb +30 -0
  18. data/lib/jat/plugins/json_api_maps_cache/json_api_maps_cache.rb +54 -0
  19. data/lib/jat/plugins/json_api_validate_params/json_api_validate_params.rb +57 -0
  20. data/lib/jat/plugins/json_api_validate_params/lib/params_error.rb +6 -0
  21. data/lib/jat/plugins/json_api_validate_params/lib/validate_fields_param.rb +59 -0
  22. data/lib/jat/plugins/json_api_validate_params/lib/validate_include_param.rb +33 -0
  23. data/lib/jat/plugins/simple_api/lib/fields_param_parser.rb +97 -0
  24. data/lib/jat/plugins/simple_api/lib/map.rb +80 -10
  25. data/lib/jat/plugins/simple_api/lib/response.rb +78 -89
  26. data/lib/jat/plugins/simple_api/lib/response_piece.rb +84 -0
  27. data/lib/jat/plugins/simple_api/simple_api.rb +83 -24
  28. data/lib/jat/plugins/simple_api_activerecord/lib/preloads.rb +55 -0
  29. data/lib/jat/plugins/simple_api_activerecord/simple_api_activerecord.rb +31 -0
  30. data/lib/jat/plugins/simple_api_lower_camel_case/simple_api_lower_camel_case.rb +30 -0
  31. data/lib/jat/plugins/simple_api_maps_cache/simple_api_maps_cache.rb +48 -0
  32. data/lib/jat/plugins/simple_api_validate_params/lib/fields_error.rb +6 -0
  33. data/lib/jat/plugins/simple_api_validate_params/lib/validate_fields_param.rb +45 -0
  34. data/lib/jat/plugins/simple_api_validate_params/simple_api_validate_params.rb +45 -0
  35. data/lib/jat/plugins/to_str/to_str.rb +10 -4
  36. data/lib/jat/plugins.rb +3 -16
  37. data/lib/jat.rb +28 -30
  38. data/test/lib/jat/attribute_test.rb +15 -5
  39. data/test/lib/jat/plugins/_activerecord_preloads/_activerecord_preloads_test.rb +34 -15
  40. data/test/lib/jat/plugins/_activerecord_preloads/lib/preloader_test.rb +10 -24
  41. data/test/lib/jat/plugins/_camel_lower/_camel_lower_test.rb +26 -0
  42. data/test/lib/jat/plugins/_preloads/lib/format_user_preloads_test.rb +1 -1
  43. data/test/lib/jat/plugins/_preloads/lib/preloads_with_path_test.rb +1 -1
  44. data/test/lib/jat/plugins/cache/cache_test.rb +11 -11
  45. data/test/lib/jat/plugins/json_api/json_api_test.rb +63 -47
  46. data/test/lib/jat/plugins/json_api/lib/{params/fields_test.rb → fields_param_parser_test.rb} +7 -6
  47. data/test/lib/jat/plugins/json_api/lib/{params/include_test.rb → include_param_parser_test.rb} +4 -4
  48. data/test/lib/jat/plugins/json_api/lib/map_test.rb +150 -79
  49. data/test/lib/jat/plugins/json_api/lib/response_test.rb +32 -32
  50. data/test/lib/jat/plugins/{_json_api_activerecord/_json_api_activerecord_test.rb → json_api_activerecord/json_api_activerecord_test.rb} +14 -5
  51. data/test/lib/jat/plugins/{_json_api_activerecord → json_api_activerecord}/lib/preloads_test.rb +11 -10
  52. data/test/lib/jat/plugins/json_api_camel_lower/json_api_camel_lower_test.rb +79 -0
  53. data/test/lib/jat/plugins/json_api_maps_cache/json_api_maps_cache_test.rb +107 -0
  54. data/test/lib/jat/plugins/json_api_validate_params/json_api_validate_params_test.rb +84 -0
  55. data/test/lib/jat/plugins/simple_api/lib/{params/parse_test.rb → fields_param_parser_test.rb} +10 -4
  56. data/test/lib/jat/plugins/simple_api/lib/map_test.rb +111 -34
  57. data/test/lib/jat/plugins/simple_api/lib/response_test.rb +80 -74
  58. data/test/lib/jat/plugins/simple_api/simple_api_test.rb +91 -25
  59. data/test/lib/jat/plugins/simple_api_activerecord/lib/preloads_test.rb +135 -0
  60. data/test/lib/jat/plugins/simple_api_activerecord/simple_api_activerecord_test.rb +38 -0
  61. data/test/lib/jat/plugins/simple_api_camel_lower/simple_api_camel_lower_test.rb +48 -0
  62. data/test/lib/jat/plugins/simple_api_maps_cache/simple_api_maps_cache_test.rb +95 -0
  63. data/test/lib/jat/plugins/simple_api_validate_params/simple_api_validate_params_test.rb +89 -0
  64. data/test/lib/jat/plugins/to_str/to_str_test.rb +3 -3
  65. data/test/lib/jat_test.rb +47 -24
  66. data/test/lib/plugin_test.rb +3 -3
  67. data/test/test_helper.rb +0 -3
  68. data/test/test_plugin.rb +9 -12
  69. metadata +60 -71
  70. data/CHANGELOG.md +0 -7
  71. data/README.md +0 -21
  72. data/jat.gemspec +0 -37
  73. data/lib/jat/plugins/_json_api_activerecord/_json_api_activerecord.rb +0 -22
  74. data/lib/jat/plugins/camel_lower/camel_lower.rb +0 -18
  75. data/lib/jat/plugins/json_api/lib/construct_traversal_map.rb +0 -91
  76. data/lib/jat/plugins/json_api/lib/presenters/document_links_presenter.rb +0 -48
  77. data/lib/jat/plugins/json_api/lib/presenters/document_meta_presenter.rb +0 -48
  78. data/lib/jat/plugins/json_api/lib/presenters/jsonapi_presenter.rb +0 -48
  79. data/lib/jat/plugins/json_api/lib/presenters/links_presenter.rb +0 -48
  80. data/lib/jat/plugins/json_api/lib/presenters/meta_presenter.rb +0 -48
  81. data/lib/jat/plugins/json_api/lib/presenters/relationship_links_presenter.rb +0 -53
  82. data/lib/jat/plugins/json_api/lib/presenters/relationship_meta_presenter.rb +0 -53
  83. data/lib/jat/plugins/json_api/lib/traversal_map.rb +0 -34
  84. data/lib/jat/plugins/simple_api/lib/construct_traversal_map.rb +0 -45
  85. data/lib/jat/plugins/simple_api/lib/params/parse.rb +0 -68
  86. data/lib/jat/presenter.rb +0 -51
  87. data/test/lib/jat/plugins/camel_lower/camel_lower_test.rb +0 -78
  88. data/test/lib/jat/plugins/json_api/lib/construct_traversal_map_test.rb +0 -119
  89. data/test/lib/jat/plugins/json_api/lib/params/fields/parse_test.rb +0 -24
  90. data/test/lib/jat/plugins/json_api/lib/params/fields/validate_test.rb +0 -47
  91. data/test/lib/jat/plugins/json_api/lib/params/include/parse_test.rb +0 -46
  92. data/test/lib/jat/plugins/json_api/lib/params/include/validate_test.rb +0 -51
  93. data/test/lib/jat/plugins/json_api/lib/presenters/document_links_presenter_test.rb +0 -69
  94. data/test/lib/jat/plugins/json_api/lib/presenters/document_meta_presenter_test.rb +0 -69
  95. data/test/lib/jat/plugins/json_api/lib/presenters/jsonapi_presenter_test.rb +0 -69
  96. data/test/lib/jat/plugins/json_api/lib/presenters/links_presenter_test.rb +0 -69
  97. data/test/lib/jat/plugins/json_api/lib/presenters/meta_presenter_test.rb +0 -69
  98. data/test/lib/jat/plugins/json_api/lib/presenters/relationship_links_presenter_test.rb +0 -75
  99. data/test/lib/jat/plugins/json_api/lib/presenters/relationship_meta_presenter_test.rb +0 -75
  100. data/test/lib/jat/plugins/json_api/lib/traversal_map_test.rb +0 -58
  101. data/test/lib/jat/plugins/simple_api/lib/construct_traversal_map_test.rb +0 -100
  102. data/test/lib/jat/presenter_test.rb +0 -61
@@ -1,28 +1,98 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "./params/parse"
4
- require_relative "./construct_traversal_map"
5
-
6
3
  class Jat
7
4
  module Plugins
8
5
  module SimpleApi
9
6
  class Map
10
- class << self
7
+ module ClassMethods
8
+ # Returns the Jat class that this Map class is namespaced under.
9
+ attr_accessor :jat_class
10
+
11
+ # Since Map is anonymously subclassed when Jat is subclassed,
12
+ # and then assigned to a constant of the Jat subclass, make inspect
13
+ # reflect the likely name for the class.
14
+ def inspect
15
+ "#{jat_class.inspect}::Map"
16
+ end
17
+
11
18
  # Returns structure like
12
19
  # {
13
20
  # key1 => { key11 => {}, key12 => { ... } },
14
21
  # key2 => { key21 => {}, key22 => { ... } },
15
22
  # }
16
- def call(jat)
17
- params = jat.context[:params]
18
- fields = params && (params[:fields] || params["fields"])
19
- exposed = jat.context[:exposed] || :default
23
+ def call(context)
24
+ exposed = context[:exposed]&.to_sym || :default
25
+ fields = context[:fields]
26
+
27
+ construct_map(exposed, fields)
28
+ end
29
+
30
+ private
31
+
32
+ def construct_map(exposed, fields)
33
+ fields = jat_class::FieldsParamParser.parse(fields) if fields
34
+ new(exposed, fields).to_h
35
+ end
36
+ end
37
+
38
+ module InstanceMethods
39
+ attr_reader :exposed, :fields
40
+
41
+ EXPOSED_TYPES = {all: :all, default: :default, none: :none}.freeze
42
+
43
+ def initialize(exposed, fields)
44
+ @exposed = EXPOSED_TYPES.fetch(exposed)
45
+ @fields = fields
46
+ end
47
+
48
+ def to_h
49
+ map_for(self.class.jat_class, fields)
50
+ end
51
+
52
+ def map_for(serializer, fields, stack = [])
53
+ serializer.attributes.each_with_object({}) do |name_attr, result|
54
+ name = name_attr[0]
55
+ attribute = name_attr[1]
56
+ next unless expose?(attribute, fields)
20
57
 
21
- manually_exposed = Params::Parse.new(fields).parse
58
+ raise Error, recursive_error_message(stack, name) if stack.any?(name_attr)
59
+ stack << name_attr
22
60
 
23
- ConstructTraversalMap.new(jat.class, exposed, manually_exposed: manually_exposed).to_h
61
+ result[name] =
62
+ if attribute.relation?
63
+ nested_serializer = attribute.serializer.call
64
+ nested_fields = fields&.[](name)
65
+ map_for(nested_serializer, nested_fields, stack)
66
+ else
67
+ FROZEN_EMPTY_HASH
68
+ end
69
+
70
+ stack.pop
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ def expose?(attribute, fields)
77
+ case exposed
78
+ when :all then true
79
+ when :none then manually_exposed?(attribute, fields)
80
+ else attribute.exposed? || manually_exposed?(attribute, fields)
81
+ end
82
+ end
83
+
84
+ def manually_exposed?(attribute, fields)
85
+ fields&.include?(attribute.name)
86
+ end
87
+
88
+ def recursive_error_message(stack, name)
89
+ recursion = (stack.map!(&:first) << name).join(" -> ")
90
+ "Recursive serialization: #{recursion}"
24
91
  end
25
92
  end
93
+
94
+ extend ClassMethods
95
+ include InstanceMethods
26
96
  end
27
97
  end
28
98
  end
@@ -5,129 +5,118 @@ 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
88
+
89
+ def metadata
90
+ data = context_metadata
91
91
 
92
- def data
93
- return unless object
92
+ meta = jat_class.added_meta
93
+ return data if meta.empty?
94
94
 
95
- result = {}
95
+ meta.each do |name, attribute|
96
+ next if data.key?(name)
96
97
 
97
- map.each do |key, inner_keys|
98
- attribute = jat_class.attributes.fetch(key)
99
- value = presenter.public_send(attribute.original_name)
98
+ value = attribute_value(attribute)
100
99
 
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
100
+ unless value.nil?
101
+ data = data.dup if data.equal?(FROZEN_EMPTY_HASH)
102
+ data[name] = value
110
103
  end
111
- end
104
+ end
112
105
 
113
- result
114
- end
106
+ data
107
+ end
115
108
 
116
- private
109
+ def context_metadata
110
+ context[:meta]&.transform_keys(&:to_sym) || FROZEN_EMPTY_HASH
111
+ end
117
112
 
118
- def response_data(attribute, value, map)
119
- ResponseData.new(attribute.serializer.call, value, context, map).data
113
+ def attribute_value(attribute)
114
+ attribute.block.call(object, context)
115
+ end
120
116
  end
121
117
 
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
118
+ extend ClassMethods
119
+ include InstanceMethods
131
120
  end
132
121
  end
133
122
  end
@@ -0,0 +1,84 @@
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(attribute)
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 attribute_value(attribute)
60
+ attribute.block.call(object, context)
61
+ end
62
+
63
+ def inner_piece(attribute, value, inner_map)
64
+ serializer = attribute.serializer.call
65
+ serializer::ResponsePiece.to_h(value, context, inner_map)
66
+ end
67
+
68
+ def many?(attribute, nested_object)
69
+ is_many = attribute.many?
70
+
71
+ # handle boolean
72
+ return is_many if (is_many == true) || (is_many == false)
73
+
74
+ # handle nil
75
+ nested_object.is_a?(Enumerable)
76
+ end
77
+ end
78
+
79
+ extend ClassMethods
80
+ include InstanceMethods
81
+ end
82
+ end
83
+ end
84
+ end
@@ -1,61 +1,120 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "./lib/fields_param_parser"
3
4
  require_relative "./lib/map"
4
5
  require_relative "./lib/response"
6
+ require_relative "./lib/response_piece"
5
7
 
6
- # Serializes to JSON-API format
7
8
  class Jat
8
9
  module Plugins
9
10
  module SimpleApi
10
11
  DEFAULT_META_KEY = :meta
11
12
 
13
+ def self.before_load(jat_class, **_opts)
14
+ response_plugin = jat_class.config[:response_plugin_loaded]
15
+ return unless response_plugin
16
+
17
+ raise Error, "Response plugin `#{response_plugin}` was already loaded before"
18
+ end
19
+
20
+ def self.load(jat_class, **_opts)
21
+ jat_class.include(InstanceMethods)
22
+ jat_class.extend(ClassMethods)
23
+ end
24
+
12
25
  def self.after_load(jat_class, **opts)
13
- jat_class.plugin(:_json_api_activerecord, **opts) if opts[:activerecord]
26
+ jat_class.config[:response_plugin_loaded] = :simple_api
27
+ jat_class.plugin(:simple_api_activerecord, **opts) if opts[:activerecord]
28
+
29
+ jat_class.meta_key(DEFAULT_META_KEY)
30
+
31
+ fields_parser_class = Class.new(FieldsParamParser)
32
+ fields_parser_class.jat_class = jat_class
33
+ jat_class.const_set(:FieldsParamParser, fields_parser_class)
34
+
35
+ map_class = Class.new(Map)
36
+ map_class.jat_class = jat_class
37
+ jat_class.const_set(:Map, map_class)
38
+
39
+ response_class = Class.new(Response)
40
+ response_class.jat_class = jat_class
41
+ jat_class.const_set(:Response, response_class)
42
+
43
+ response_piece_class = Class.new(ResponsePiece)
44
+ response_piece_class.jat_class = jat_class
45
+ jat_class.const_set(:ResponsePiece, response_piece_class)
14
46
  end
15
47
 
16
48
  module InstanceMethods
17
- def to_h
18
- Response.new(self).response
49
+ def to_h(object)
50
+ self.class::Response.call(object, context)
19
51
  end
20
52
 
21
- def traversal_map
22
- @traversal_map ||= Map.call(self)
53
+ def map
54
+ @map ||= self.class.map(context)
23
55
  end
24
56
  end
25
57
 
26
58
  module ClassMethods
27
59
  def inherited(subclass)
28
- subclass.root(@root) if defined?(@root)
29
-
30
60
  super
61
+
62
+ fields_parser_class = Class.new(self::FieldsParamParser)
63
+ fields_parser_class.jat_class = subclass
64
+ subclass.const_set(:FieldsParamParser, fields_parser_class)
65
+
66
+ map_class = Class.new(self::Map)
67
+ map_class.jat_class = subclass
68
+ subclass.const_set(:Map, map_class)
69
+
70
+ response_class = Class.new(self::Response)
71
+ response_class.jat_class = subclass
72
+ subclass.const_set(:Response, response_class)
73
+
74
+ response_piece_class = Class.new(self::ResponsePiece)
75
+ response_piece_class.jat_class = subclass
76
+ subclass.const_set(:ResponsePiece, response_piece_class)
77
+
78
+ # Assign same meta
79
+ added_meta.each_value do |attribute|
80
+ params = attribute.params
81
+ subclass.attribute(params[:name], **params[:opts], &params[:block])
82
+ end
31
83
  end
32
84
 
33
- def root(new_root = nil)
34
- return (defined?(@root) && @root) unless new_root
85
+ def root(default = nil, one: nil, many: nil)
86
+ root_one = one || default
87
+ root_many = many || default
88
+
89
+ config[:root_one] = root_one ? root_one.to_sym : nil
90
+ config[:root_many] = root_many ? root_many.to_sym : nil
35
91
 
36
- new_root = new_root.to_sym
37
- @root = new_root
92
+ {root_one: root_one, root_many: root_many}
38
93
  end
39
94
 
40
- def root_for_one(new_root_for_one = nil)
41
- return (defined?(@root_for_one) && @root_for_one) unless new_root_for_one
95
+ def meta_key(new_meta_key)
96
+ config[:meta_key] = new_meta_key.to_sym
97
+ end
42
98
 
43
- new_root_for_one = new_root_for_one.to_sym
44
- @root_for_one = new_root_for_one
99
+ def added_meta
100
+ @added_meta ||= {}
45
101
  end
46
102
 
47
- def root_for_many(new_root_for_many = nil)
48
- return (defined?(@root_for_many) && @root_for_many) unless new_root_for_many
103
+ def meta(name, **opts, &block)
104
+ new_attr = self::Attribute.new(name: name, opts: opts, block: block)
105
+ added_meta[new_attr.name] = new_attr
106
+ end
49
107
 
50
- new_root_for_many = new_root_for_many.to_sym
51
- @root_for_many = new_root_for_many
108
+ def map(context)
109
+ self::Map.call(context)
52
110
  end
53
111
 
54
- def meta_key(new_meta_key = nil)
55
- return ((defined?(@meta_key) && @meta_key) || DEFAULT_META_KEY) unless new_meta_key
112
+ def map_full
113
+ @map_full ||= self::Map.call(exposed: :all)
114
+ end
56
115
 
57
- new_meta_key = new_meta_key.to_sym
58
- @meta_key = new_meta_key
116
+ def map_exposed
117
+ @map_exposed ||= self::Map.call(exposed: :default)
59
118
  end
60
119
  end
61
120
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ module Plugins
5
+ module SimpleApiActiverecord
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,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./lib/preloads"
4
+
5
+ class Jat
6
+ module Plugins
7
+ module SimpleApiActiverecord
8
+ def self.before_load(jat_class, **_opts)
9
+ return if jat_class.plugin_used?(:simple_api)
10
+ raise Error, "Please load :simple_api plugin first"
11
+ end
12
+
13
+ def self.load(jat_class, **_opts)
14
+ jat_class.include(InstanceMethods)
15
+ end
16
+
17
+ def self.after_load(jat_class, **opts)
18
+ jat_class.plugin :_preloads, **opts
19
+ jat_class.plugin :_activerecord_preloads, **opts
20
+ end
21
+
22
+ module InstanceMethods
23
+ def preloads
24
+ @preloads ||= Preloads.call(self)
25
+ end
26
+ end
27
+ end
28
+
29
+ register_plugin(:simple_api_activerecord, SimpleApiActiverecord)
30
+ end
31
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ module Plugins
5
+ module SimpleApiLowerCamelCase
6
+ def self.before_load(jat_class, **_opts)
7
+ raise Error, "Please load :simple_api plugin first" unless jat_class.plugin_used?(:simple_api)
8
+
9
+ jat_class.plugin :_lower_camel_case
10
+ end
11
+
12
+ def self.load(jat_class, **_opts)
13
+ jat_class::Response.include(ResponseInstanceMethods)
14
+ end
15
+
16
+ module ResponseInstanceMethods
17
+ private
18
+
19
+ def context_metadata
20
+ metadata = super
21
+ return metadata if metadata.empty?
22
+
23
+ metadata.transform_keys! { |key| Jat::LowerCamelCaseTransformation.call(key) }
24
+ end
25
+ end
26
+ end
27
+
28
+ register_plugin(:simple_api_lower_camel_case, SimpleApiLowerCamelCase)
29
+ end
30
+ end