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
@@ -1,125 +1,163 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "./lib/fields_param_parser"
4
+ require_relative "./lib/include_param_parser"
5
+ require_relative "./lib/map"
3
6
  require_relative "./lib/response"
4
- require_relative "./lib/traversal_map"
5
- require_relative "./lib/presenters/document_links_presenter"
6
- require_relative "./lib/presenters/document_meta_presenter"
7
- require_relative "./lib/presenters/jsonapi_presenter"
8
- require_relative "./lib/presenters/links_presenter"
9
- require_relative "./lib/presenters/meta_presenter"
10
- require_relative "./lib/presenters/relationship_links_presenter"
11
- require_relative "./lib/presenters/relationship_meta_presenter"
7
+ require_relative "./lib/response_piece"
12
8
 
13
9
  # Serializes to JSON-API format
14
10
  class Jat
15
11
  module Plugins
16
12
  module JsonApi
17
- def self.after_load(jat_class, **opts)
18
- jat_class.plugin(:_json_api_activerecord, **opts) if opts[:activerecord]
13
+ def self.plugin_name
14
+ :json_api
15
+ end
16
+
17
+ def self.before_load(jat_class, **_opts)
18
+ response_plugin = jat_class.config[:response_plugin_loaded]
19
+ return unless response_plugin
20
+
21
+ raise Error, "Response plugin `#{response_plugin}` was already loaded before"
22
+ end
23
+
24
+ def self.load(jat_class, **_opts)
25
+ jat_class.include(InstanceMethods)
26
+ jat_class.extend(ClassMethods)
27
+ end
19
28
 
20
- jat_class.const_set(:JsonapiPresenter, Class.new(Jat::Presenters::JsonapiPresenter))
21
- jat_class::JsonapiPresenter.jat_class = jat_class
29
+ def self.after_load(jat_class, **opts)
30
+ jat_class.config[:response_plugin_loaded] = plugin_name
22
31
 
23
- jat_class.const_set(:LinksPresenter, Class.new(Jat::Presenters::LinksPresenter))
24
- jat_class::LinksPresenter.jat_class = jat_class
32
+ fields_parser_class = Class.new(FieldsParamParser)
33
+ fields_parser_class.jat_class = jat_class
34
+ jat_class.const_set(:FieldsParamParser, fields_parser_class)
25
35
 
26
- jat_class.const_set(:DocumentLinksPresenter, Class.new(Jat::Presenters::DocumentLinksPresenter))
27
- jat_class::DocumentLinksPresenter.jat_class = jat_class
36
+ includes_parser_class = Class.new(IncludeParamParser)
37
+ includes_parser_class.jat_class = jat_class
38
+ jat_class.const_set(:IncludeParamParser, includes_parser_class)
28
39
 
29
- jat_class.const_set(:RelationshipLinksPresenter, Class.new(Jat::Presenters::RelationshipLinksPresenter))
30
- jat_class::RelationshipLinksPresenter.jat_class = jat_class
40
+ map_class = Class.new(Map)
41
+ map_class.jat_class = jat_class
42
+ jat_class.const_set(:Map, map_class)
31
43
 
32
- jat_class.const_set(:MetaPresenter, Class.new(Jat::Presenters::MetaPresenter))
33
- jat_class::MetaPresenter.jat_class = jat_class
44
+ response_class = Class.new(Response)
45
+ response_class.jat_class = jat_class
46
+ jat_class.const_set(:Response, response_class)
34
47
 
35
- jat_class.const_set(:DocumentMetaPresenter, Class.new(Jat::Presenters::DocumentMetaPresenter))
36
- jat_class::DocumentMetaPresenter.jat_class = jat_class
48
+ response_piece_class = Class.new(ResponsePiece)
49
+ response_piece_class.jat_class = jat_class
50
+ jat_class.const_set(:ResponsePiece, response_piece_class)
37
51
 
38
- jat_class.const_set(:RelationshipMetaPresenter, Class.new(Jat::Presenters::RelationshipMetaPresenter))
39
- jat_class::RelationshipMetaPresenter.jat_class = jat_class
52
+ jat_class.id
40
53
  end
41
54
 
42
55
  module InstanceMethods
43
- def to_h
44
- Response.new(self).response
56
+ def to_h(object)
57
+ self.class::Response.call(object, context)
45
58
  end
46
59
 
47
- def traversal_map
48
- @traversal_map ||= TraversalMap.new(self)
60
+ def map
61
+ @map ||= self.class.map(context)
49
62
  end
50
63
  end
51
64
 
52
65
  module ClassMethods
53
66
  def inherited(subclass)
67
+ super
68
+
69
+ fields_parser_class = Class.new(self::FieldsParamParser)
70
+ fields_parser_class.jat_class = subclass
71
+ subclass.const_set(:FieldsParamParser, fields_parser_class)
72
+
73
+ includes_parser_class = Class.new(self::IncludeParamParser)
74
+ includes_parser_class.jat_class = subclass
75
+ subclass.const_set(:IncludeParamParser, includes_parser_class)
76
+
77
+ map_class = Class.new(self::Map)
78
+ map_class.jat_class = subclass
79
+ subclass.const_set(:Map, map_class)
80
+
81
+ response_class = Class.new(self::Response)
82
+ response_class.jat_class = subclass
83
+ subclass.const_set(:Response, response_class)
84
+
85
+ response_piece_class = Class.new(self::ResponsePiece)
86
+ response_piece_class.jat_class = subclass
87
+ subclass.const_set(:ResponsePiece, response_piece_class)
88
+
54
89
  subclass.type(@type) if defined?(@type)
90
+ subclass.id(&get_id.params[:block])
55
91
 
56
- # Initialize JsonApi data Presenters
57
- jsonapi_data_presenter_class = Class.new(self::Presenters::JsonapiPresenter)
58
- jsonapi_data_presenter_class.jat_class = subclass
59
- subclass.const_set(:JsonapiPresenter, jsonapi_data_presenter_class)
60
- jsonapi_data.each { |key, block| subclass.jsonapi(key, &block) }
61
-
62
- # Initialize Links Presenters
63
- links_presenter_class = Class.new(self::Presenters::LinksPresenter)
64
- links_presenter_class.jat_class = subclass
65
- subclass.const_set(:LinksPresenter, links_presenter_class)
66
- object_links.each { |key, block| subclass.object_link(key, &block) }
67
-
68
- # Initialize DocumentLinks Presenters
69
- document_links_presenter_class = Class.new(self::Presenters::DocumentLinksPresenter)
70
- document_links_presenter_class.jat_class = subclass
71
- subclass.const_set(:DocumentLinksPresenter, document_links_presenter_class)
72
- document_links.each { |key, block| subclass.document_link(key, &block) }
73
-
74
- # Initialize RelationshipLinks Presenters
75
- relationship_links_presenter_class = Class.new(self::Presenters::RelationshipLinksPresenter)
76
- relationship_links_presenter_class.jat_class = subclass
77
- subclass.const_set(:RelationshipLinksPresenter, relationship_links_presenter_class)
78
- relationship_links.each { |key, block| subclass.relationship_link(key, &block) }
79
-
80
- # Initialize Meta Presenters
81
- meta_presenter_class = Class.new(self::Presenters::MetaPresenter)
82
- meta_presenter_class.jat_class = subclass
83
- subclass.const_set(:MetaPresenter, meta_presenter_class)
84
- added_object_meta.each { |key, block| subclass.object_meta(key, &block) }
85
-
86
- # Initialize DocumentMeta Presenters
87
- document_meta_presenter_class = Class.new(self::Presenters::DocumentMetaPresenter)
88
- document_meta_presenter_class.jat_class = subclass
89
- subclass.const_set(:DocumentMetaPresenter, document_meta_presenter_class)
90
- added_document_meta.each { |key, block| subclass.document_meta(key, &block) }
91
-
92
- # Initialize RelationshipMeta Presenters
93
- relationship_meta_presenter_class = Class.new(self::Presenters::RelationshipMetaPresenter)
94
- relationship_meta_presenter_class.jat_class = subclass
95
- subclass.const_set(:RelationshipMetaPresenter, relationship_meta_presenter_class)
96
- added_relationship_meta.each { |key, block| subclass.relationship_meta(key, &block) }
92
+ # Assign same jsonapi_data
93
+ jsonapi_data.each_value do |attribute|
94
+ params = attribute.params
95
+ subclass.jsonapi(params[:name], **params[:opts], &params[:block])
96
+ end
97
97
 
98
- super
98
+ # Assign same object_links
99
+ object_links.each_value do |attribute|
100
+ params = attribute.params
101
+ subclass.object_link(params[:name], **params[:opts], &params[:block])
102
+ end
103
+
104
+ # Assign same document_links
105
+ document_links.each_value do |attribute|
106
+ params = attribute.params
107
+ subclass.document_link(params[:name], **params[:opts], &params[:block])
108
+ end
109
+
110
+ # Assign same relationship_links
111
+ relationship_links.each_value do |attribute|
112
+ params = attribute.params
113
+ subclass.relationship_link(params[:name], **params[:opts], &params[:block])
114
+ end
115
+
116
+ # Assign same added_object_meta
117
+ added_object_meta.each_value do |attribute|
118
+ params = attribute.params
119
+ subclass.object_meta(params[:name], **params[:opts], &params[:block])
120
+ end
121
+
122
+ # Assign same added_document_meta
123
+ added_document_meta.each_value do |attribute|
124
+ params = attribute.params
125
+ subclass.document_meta(params[:name], **params[:opts], &params[:block])
126
+ end
127
+
128
+ # Assign same added_relationship_meta
129
+ added_relationship_meta.each_value do |attribute|
130
+ params = attribute.params
131
+ subclass.relationship_meta(params[:name], **params[:opts], &params[:block])
132
+ end
99
133
  end
100
134
 
101
- def type(new_type = nil)
102
- return (defined?(@type) && @type) || raise(Error, "#{self} has no defined type") unless new_type
135
+ def get_type
136
+ (defined?(@type) && @type) || raise(Error, "#{self} has no defined type")
137
+ end
103
138
 
104
- new_type = new_type.to_sym
105
- @type = new_type
139
+ def type(new_type)
140
+ @type = new_type.to_sym
106
141
  end
107
142
 
108
- def relationship(name, serializer:, **opts, &block)
109
- attribute(name, serializer: serializer, **opts, &block)
143
+ def get_id
144
+ @id
145
+ end
146
+
147
+ def id(**opts, &block)
148
+ @id = self::Attribute.new(name: :id, opts: opts, block: block)
110
149
  end
111
150
 
112
151
  # JSON API block values
113
152
  #
114
153
  # https://jsonapi.org/format/#document-jsonapi-object
115
- def jsonapi_data(value = nil)
154
+ def jsonapi_data(_value = nil)
116
155
  @jsonapi_data ||= {}
117
156
  end
118
157
 
119
- def jsonapi(key, &block)
120
- jsonapi_data[key.to_sym] = block
121
- self::JsonapiPresenter.add_method(key, block)
122
- block
158
+ def jsonapi(name, **opts, &block)
159
+ new_attr = self::Attribute.new(name: name, opts: opts, block: block)
160
+ jsonapi_data[new_attr.name] = new_attr
123
161
  end
124
162
 
125
163
  # Links related to the resource
@@ -129,10 +167,9 @@ class Jat
129
167
  @object_links ||= {}
130
168
  end
131
169
 
132
- def object_link(key, &block)
133
- object_links[key.to_sym] = block
134
- self::LinksPresenter.add_method(key, block)
135
- block
170
+ def object_link(name, **opts, &block)
171
+ new_attr = self::Attribute.new(name: name, opts: opts, block: block)
172
+ object_links[new_attr.name] = new_attr
136
173
  end
137
174
 
138
175
  # Top-level document links
@@ -142,10 +179,9 @@ class Jat
142
179
  @document_links ||= {}
143
180
  end
144
181
 
145
- def document_link(key, &block)
146
- document_links[key.to_sym] = block
147
- self::DocumentLinksPresenter.add_method(key, block)
148
- block
182
+ def document_link(name, **opts, &block)
183
+ new_attr = self::Attribute.new(name: name, opts: opts, block: block)
184
+ document_links[new_attr.name] = new_attr
149
185
  end
150
186
 
151
187
  # Relationship links
@@ -155,10 +191,9 @@ class Jat
155
191
  @relationship_links ||= {}
156
192
  end
157
193
 
158
- def relationship_link(key, &block)
159
- relationship_links[key.to_sym] = block
160
- self::RelationshipLinksPresenter.add_method(key, block)
161
- block
194
+ def relationship_link(name, **opts, &block)
195
+ new_attr = self::Attribute.new(name: name, opts: opts, block: block)
196
+ relationship_links[new_attr.name] = new_attr
162
197
  end
163
198
 
164
199
  # Object meta
@@ -168,10 +203,9 @@ class Jat
168
203
  @added_object_meta ||= {}
169
204
  end
170
205
 
171
- def object_meta(key, &block)
172
- added_object_meta[key.to_sym] = block
173
- self::MetaPresenter.add_method(key, block)
174
- block
206
+ def object_meta(name, **opts, &block)
207
+ new_attr = self::Attribute.new(name: name, opts: opts, block: block)
208
+ added_object_meta[new_attr.name] = new_attr
175
209
  end
176
210
 
177
211
  # Top-level document meta
@@ -181,10 +215,9 @@ class Jat
181
215
  @added_document_meta ||= {}
182
216
  end
183
217
 
184
- def document_meta(key, &block)
185
- added_document_meta[key.to_sym] = block
186
- self::DocumentMetaPresenter.add_method(key, block)
187
- block
218
+ def document_meta(name, **opts, &block)
219
+ new_attr = self::Attribute.new(name: name, opts: opts, block: block)
220
+ added_document_meta[new_attr.name] = new_attr
188
221
  end
189
222
 
190
223
  # Relationship meta
@@ -194,14 +227,25 @@ class Jat
194
227
  @added_relationship_meta ||= {}
195
228
  end
196
229
 
197
- def relationship_meta(key, &block)
198
- added_relationship_meta[key.to_sym] = block
199
- self::RelationshipMetaPresenter.add_method(key, block)
200
- block
230
+ def relationship_meta(name, **opts, &block)
231
+ new_attr = self::Attribute.new(name: name, opts: opts, block: block)
232
+ added_relationship_meta[new_attr.name] = new_attr
233
+ end
234
+
235
+ def map(context)
236
+ self::Map.call(context)
237
+ end
238
+
239
+ def map_full
240
+ @map_full ||= self::Map.call(exposed: :all)
241
+ end
242
+
243
+ def map_exposed
244
+ @map_exposed ||= self::Map.call(exposed: :default)
201
245
  end
202
246
  end
203
247
  end
204
248
 
205
- register_plugin(:json_api, JsonApi)
249
+ register_plugin(JsonApi.plugin_name, JsonApi)
206
250
  end
207
251
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ module Plugins
5
+ module JsonApi
6
+ class FieldsParamParser
7
+ module ClassMethods
8
+ COMMA = ","
9
+
10
+ # Returns the Jat class that this FieldsParamParser class is namespaced under.
11
+ attr_accessor :jat_class
12
+
13
+ # Since FieldsParamParser is anonymously subclassed when Jat is subclassed,
14
+ # and then assigned to a constant of the Jat subclass, make inspect
15
+ # reflect the likely name for the class.
16
+ def inspect
17
+ "#{jat_class.inspect}::FieldsParamParser"
18
+ end
19
+
20
+ def parse(fields)
21
+ return FROZEN_EMPTY_HASH unless fields
22
+
23
+ parse_to_nested_hash(fields)
24
+ end
25
+
26
+ private
27
+
28
+ def parse_to_nested_hash(fields)
29
+ fields.each_with_object({}) do |(type, attrs_string), obj|
30
+ attrs = attrs_string.split(COMMA).map!(&:to_sym)
31
+ obj[type.to_sym] = attrs
32
+ end
33
+ end
34
+ end
35
+
36
+ extend ClassMethods
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ module Plugins
5
+ module JsonApi
6
+ class IncludeParamParser
7
+ module ClassMethods
8
+ COMMA = ","
9
+ DOT = "."
10
+
11
+ # Returns the Jat class that this IncludeParamParser class is namespaced under.
12
+ attr_accessor :jat_class
13
+
14
+ # Since IncludeParamParser is anonymously subclassed when Jat is subclassed,
15
+ # and then assigned to a constant of the Jat subclass, make inspect
16
+ # reflect the likely name for the class.
17
+ def inspect
18
+ "#{jat_class.inspect}::IncludeParamParser"
19
+ end
20
+
21
+ def parse(includes_string_param)
22
+ return {} unless includes_string_param
23
+
24
+ includes_hash = parse_to_nested_hash(includes_string_param)
25
+ typed_includes(jat_class, includes_hash, {})
26
+ end
27
+
28
+ private
29
+
30
+ def parse_to_nested_hash(includes_string_param)
31
+ includes_string_param.split(COMMA).each_with_object({}) do |part, obj|
32
+ includes = parse_part(part)
33
+ deep_merge!(obj, includes)
34
+ end
35
+ end
36
+
37
+ def typed_includes(jat_class, includes, result)
38
+ includes.each do |included_attr_name, nested_includes|
39
+ add_typed_include(result, jat_class, included_attr_name)
40
+
41
+ nested_serializer = jat_class.attributes.fetch(included_attr_name).serializer.call
42
+ typed_includes(nested_serializer, nested_includes, result)
43
+ end
44
+
45
+ result
46
+ end
47
+
48
+ def add_typed_include(result, serializer, included_attr_name)
49
+ type = serializer.get_type
50
+
51
+ includes = result[type] || []
52
+ includes |= [included_attr_name]
53
+
54
+ result[type] = includes
55
+ end
56
+
57
+ def parse_part(part)
58
+ val = {}
59
+
60
+ part.split(DOT).reverse_each do |inc|
61
+ val = {inc.to_sym => val}
62
+ end
63
+
64
+ val
65
+ end
66
+
67
+ def deep_merge!(this_hash, other_hash)
68
+ this_hash.merge!(other_hash) do |_key, this_val, other_val|
69
+ deep_merge(this_val, other_val)
70
+ end
71
+ end
72
+
73
+ def deep_merge(this_hash, other_hash)
74
+ this_hash.merge(other_hash) do |_key, this_val, other_val|
75
+ deep_merge(this_val, other_val)
76
+ end
77
+ end
78
+ end
79
+
80
+ extend ClassMethods
81
+ end
82
+ end
83
+ end
84
+ end
@@ -1,53 +1,118 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "./params/fields"
4
- require_relative "./params/include"
5
- require_relative "./construct_traversal_map"
6
-
7
3
  class Jat
8
4
  module Plugins
9
5
  module JsonApi
10
6
  class Map
11
- 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
+
12
18
  # Returns structure like
13
19
  # {
14
- # type => {
20
+ # type1 => {
15
21
  # attributes: [attr1, attr2, ...],
16
22
  # relationships: [rel1, rel2, ...]
17
- # }
23
+ # },
24
+ # type2 => { ... }
18
25
  # }
19
- def call(jat)
20
- params = jat.context[:params]
21
- fields = params && (params[:fields] || params["fields"])
22
- includes = params && (params[:include] || params["include"])
26
+ def call(context)
27
+ exposed = context[:exposed]&.to_sym || :default
28
+ fields = context[:fields]
29
+ includes = context[:include]
30
+
31
+ construct_map(exposed, fields, includes)
32
+ end
33
+
34
+ private
35
+
36
+ def construct_map(exposed, fields, includes)
37
+ fields = jat_class::FieldsParamParser.parse(fields) if fields
38
+ includes = jat_class::IncludeParamParser.parse(includes) if includes
39
+
40
+ new(exposed, fields, includes).to_h
41
+ end
42
+ end
43
+
44
+ module InstanceMethods
45
+ attr_reader :jat_class, :exposed, :includes, :fields
23
46
 
24
- default_attrs = jat.traversal_map.exposed
25
- includes_attrs = requested_includes_fields(jat, includes)
26
- fields_attrs = requested_fields(jat, fields)
47
+ EXPOSED_TYPES = {all: :all, default: :default, none: :none}.freeze
27
48
 
28
- default_attrs.merge!(includes_attrs).merge!(fields_attrs)
49
+ def initialize(exposed, fields, includes)
50
+ @jat_class = self.class.jat_class
51
+ @exposed = EXPOSED_TYPES.fetch(exposed)
52
+ @fields = fields
53
+ @includes = includes
54
+ end
55
+
56
+ def to_h
57
+ map = {}
58
+ append_map(map, jat_class)
59
+ map
29
60
  end
30
61
 
31
62
  private
32
63
 
33
- def requested_includes_fields(jat, includes)
34
- return {} unless includes
64
+ def append_map(map, jat_class)
65
+ type = jat_class.get_type
66
+ return map if map.key?(type)
67
+
68
+ type_map = {serializer: jat_class}
69
+ map[type] = type_map
70
+
71
+ fill_type_map(map, type_map, type, jat_class)
72
+
73
+ type_map[:attributes] ||= FROZEN_EMPTY_ARRAY
74
+ type_map[:relationships] ||= FROZEN_EMPTY_ARRAY
75
+ end
76
+
77
+ def fill_type_map(map, type_map, type, jat_class)
78
+ jat_class.attributes.each_value do |attribute|
79
+ next unless expose?(type, attribute)
35
80
 
36
- include_types = Params::Include.call(jat, includes)
37
- ConstructTraversalMap
38
- .new(jat.class, :exposed, manually_exposed: include_types)
39
- .to_h
81
+ fill_attr(map, type_map, attribute)
82
+ end
40
83
  end
41
84
 
42
- def requested_fields(jat, fields)
43
- return {} unless fields
85
+ def fill_attr(map, type_map, attribute)
86
+ name = attribute.name
44
87
 
45
- fields_types = Params::Fields.call(jat, fields)
46
- ConstructTraversalMap
47
- .new(jat.class, :manual, manually_exposed: fields_types)
48
- .to_h
88
+ if attribute.relation?
89
+ (type_map[:relationships] ||= []) << name
90
+ append_map(map, attribute.serializer.call)
91
+ else
92
+ (type_map[:attributes] ||= []) << name
93
+ end
94
+ end
95
+
96
+ def expose?(type, attribute)
97
+ attribute_name = attribute.name
98
+ fields_attribute_names = fields && fields[type]
99
+ return fields_attribute_names.include?(attribute_name) if fields_attribute_names
100
+
101
+ includes_attribute_names = includes && includes[type]
102
+ includes_attribute_names&.include?(attribute_name) || attribute_exposed?(attribute)
103
+ end
104
+
105
+ def attribute_exposed?(attribute)
106
+ case exposed
107
+ when :all then true
108
+ when :none then false
109
+ else attribute.exposed?
110
+ end
49
111
  end
50
112
  end
113
+
114
+ extend ClassMethods
115
+ include InstanceMethods
51
116
  end
52
117
  end
53
118
  end
@@ -8,17 +8,20 @@ class Jat
8
8
  class Validate
9
9
  class << self
10
10
  def call(jat, fields)
11
+ full_map = jat.maps.full
12
+
11
13
  fields.each do |type, attributes_names|
12
- new(jat, type).validate(attributes_names)
14
+ new(jat, type, full_map).validate(attributes_names)
13
15
  end
14
16
  end
15
17
  end
16
18
 
17
- attr_reader :jat, :type
19
+ attr_reader :jat, :type, :full_map
18
20
 
19
- def initialize(jat, type)
21
+ def initialize(jat, type, full_map)
20
22
  @jat = jat
21
23
  @type = type
24
+ @full_map = full_map
22
25
  end
23
26
 
24
27
  def validate(attributes_names)
@@ -29,7 +32,7 @@ class Jat
29
32
  private
30
33
 
31
34
  def check_fields_type
32
- return if jat.traversal_map.full.key?(type)
35
+ return if full_map.key?(type)
33
36
 
34
37
  raise Error, "#{jat.class} and its children have no requested type `#{type}`"
35
38
  end
@@ -41,7 +44,7 @@ class Jat
41
44
  end
42
45
 
43
46
  def check_attribute_name(attribute_name)
44
- type_data = jat.traversal_map.full.fetch(type)
47
+ type_data = full_map.fetch(type)
45
48
  type_serializer = type_data.fetch(:serializer)
46
49
  return if type_serializer.attributes.key?(attribute_name)
47
50