jat 0.0.6 → 0.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/lib/jat/attribute.rb +5 -18
  3. data/lib/jat/config.rb +5 -20
  4. data/lib/jat/plugins/{common/_activerecord_preloads/_activerecord_preloads.rb → base/base_activerecord_preloads/base_activerecord_preloads.rb} +3 -3
  5. data/lib/jat/plugins/{common/_activerecord_preloads → base/base_activerecord_preloads}/lib/preloader.rb +3 -1
  6. data/lib/jat/plugins/{common/_lower_camel_case/_lower_camel_case.rb → base/base_lower_camel_case/base_lower_camel_case.rb} +3 -3
  7. data/lib/jat/plugins/{common/_preloads/_preloads.rb → base/base_preloads/base_preloads.rb} +3 -3
  8. data/lib/jat/plugins/{common/_preloads → base/base_preloads}/lib/format_user_preloads.rb +1 -1
  9. data/lib/jat/plugins/{common/_preloads → base/base_preloads}/lib/preloads_with_path.rb +1 -1
  10. data/lib/jat/plugins/json_api/lib/fields_param_parser.rb +1 -10
  11. data/lib/jat/plugins/json_api/lib/include_param_parser.rb +1 -10
  12. data/lib/jat/plugins/json_api/lib/map.rb +5 -15
  13. data/lib/jat/plugins/json_api/lib/response.rb +2 -15
  14. data/lib/jat/plugins/json_api/lib/response_piece.rb +10 -19
  15. data/lib/jat/plugins/json_api/plugins/json_api_activerecord/json_api_activerecord.rb +1 -1
  16. data/lib/jat/plugins/json_api/plugins/json_api_lower_camel_case/json_api_lower_camel_case.rb +1 -1
  17. data/lib/jat/plugins/json_api/plugins/json_api_preloads/json_api_preloads.rb +1 -1
  18. data/lib/jat/plugins/json_api/plugins/json_api_validate_params/lib/validate_include_param.rb +1 -3
  19. data/lib/jat/plugins/presenter/presenter.rb +79 -0
  20. data/lib/jat/plugins/simple_api/lib/fields_param_parser.rb +21 -46
  21. data/lib/jat/plugins/simple_api/lib/map.rb +5 -15
  22. data/lib/jat/plugins/simple_api/lib/response.rb +2 -15
  23. data/lib/jat/plugins/simple_api/lib/response_piece.rb +2 -15
  24. data/lib/jat/plugins/simple_api/plugins/simple_api_activerecord/simple_api_activerecord.rb +1 -1
  25. data/lib/jat/plugins/simple_api/plugins/simple_api_lower_camel_case/simple_api_lower_camel_case.rb +1 -1
  26. data/lib/jat/plugins/simple_api/plugins/simple_api_preloads/simple_api_preloads.rb +1 -1
  27. data/lib/jat/plugins/simple_api/plugins/simple_api_validate_params/simple_api_validate_params.rb +1 -1
  28. data/lib/jat/plugins/simple_api/simple_api.rb +1 -1
  29. data/lib/jat/plugins/types/types.rb +55 -0
  30. data/lib/jat/plugins.rb +38 -25
  31. data/lib/jat/utils/jat_class.rb +21 -0
  32. data/lib/jat.rb +9 -6
  33. data/test/lib/jat/attribute_test.rb +8 -0
  34. data/test/lib/jat/plugins/activerecord/activerecord_test.rb +27 -0
  35. data/test/lib/jat/plugins/{_activerecord_preloads/_activerecord_preloads_test.rb → base_activerecord_preloads/base_activerecord_preloads_test.rb} +3 -3
  36. data/test/lib/jat/plugins/{_activerecord_preloads → base_activerecord_preloads}/lib/preloader_test.rb +2 -2
  37. data/test/lib/jat/plugins/{_camel_lower/_camel_lower_test.rb → base_lower_camel_case/base_lower_camel_case_test.rb} +2 -2
  38. data/test/lib/jat/plugins/{_preloads/_preloads_test.rb → base_preloads/base_preloads_test.rb} +2 -2
  39. data/test/lib/jat/plugins/{_preloads → base_preloads}/lib/format_user_preloads_test.rb +3 -3
  40. data/test/lib/jat/plugins/{_preloads → base_preloads}/lib/preloads_with_path_test.rb +2 -2
  41. data/test/lib/jat/plugins/json_api/json_api_test.rb +1 -1
  42. data/test/lib/jat/plugins/json_api/lib/fields_param_parser_test.rb +20 -12
  43. data/test/lib/jat/plugins/json_api/lib/include_param_parser_test.rb +25 -11
  44. data/test/lib/jat/plugins/json_api/lib/map_test.rb +132 -124
  45. data/test/lib/jat/plugins/json_api/lib/response_piece_test.rb +13 -0
  46. data/test/lib/jat/plugins/json_api/lib/response_test.rb +6 -1
  47. data/test/lib/jat/plugins/json_api_activerecord/json_api_activerecord_test.rb +1 -1
  48. data/test/lib/jat/plugins/{json_api_camel_lower/json_api_camel_lower_test.rb → json_api_lower_camel_case/json_api_lower_camel_case_test.rb} +6 -0
  49. data/test/lib/jat/plugins/json_api_preloads/json_api_preloads_test.rb +16 -2
  50. data/test/lib/jat/plugins/json_api_preloads/lib/preloads_test.rb +0 -3
  51. data/test/lib/jat/plugins/json_api_validate_params/json_api_validate_params_test.rb +30 -6
  52. data/test/lib/jat/plugins/lower_camel_case/lower_camel_case_test.rb +27 -0
  53. data/test/lib/jat/plugins/maps_cache/maps_cache_test.rb +27 -0
  54. data/test/lib/jat/plugins/preloads/preloads_test.rb +27 -0
  55. data/test/lib/jat/plugins/presenter/presenter_test.rb +215 -0
  56. data/test/lib/jat/plugins/simple_api/lib/fields_param_parser_test.rb +57 -43
  57. data/test/lib/jat/plugins/simple_api/lib/map_test.rb +90 -82
  58. data/test/lib/jat/plugins/simple_api/lib/response_piece_test.rb +13 -0
  59. data/test/lib/jat/plugins/simple_api/lib/response_test.rb +17 -11
  60. data/test/lib/jat/plugins/simple_api/simple_api_test.rb +36 -1
  61. data/test/lib/jat/plugins/simple_api_activerecord/simple_api_activerecord_test.rb +1 -1
  62. data/test/lib/jat/plugins/{simple_api_camel_lower/simple_api_camel_lower_test.rb → simple_api_lower_camel_case/simple_api_lower_camel_case_test.rb} +7 -1
  63. data/test/lib/jat/plugins/simple_api_preloads/lib/preloads_test.rb +1 -6
  64. data/test/lib/jat/plugins/simple_api_preloads/simple_api_preloads_test.rb +16 -2
  65. data/test/lib/jat/plugins/simple_api_validate_params/simple_api_validate_params_test.rb +12 -0
  66. data/test/lib/jat/plugins/types/types_test.rb +83 -0
  67. data/test/lib/jat/plugins/validate_params/validate_params_test.rb +27 -0
  68. data/test/lib/jat/plugins_test.rb +89 -0
  69. data/test/test_helper.rb +14 -4
  70. metadata +72 -67
  71. data/test/lib/plugin_test.rb +0 -49
  72. data/test/test_plugin.rb +0 -56
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 18b7274ab643b613d2356e6f4704683d0ee17f6789fc3c01ba5c6435b09c0c84
4
- data.tar.gz: 197d92680d9bbe8610eca370516fc2b7a36bb7a6dfadf0a2fc75ce5e3959af90
3
+ metadata.gz: d7b5ab9fe35df564d6282bd57f3008293bbc38e72c7dbf0ade8ef27734aafb10
4
+ data.tar.gz: c2bd6717c5555e862bdaafab2bdd16f510d7535be1922e0327253fee7d8cb248
5
5
  SHA512:
6
- metadata.gz: f887b6f910f4677439e5842e4882eedb349e2bc6865e987b9dcec565dca83e20e13b2cf31eed117a332b1ae044c3194445f4241ba65c053c2b7adbba4ceb316d
7
- data.tar.gz: 9562212be2cacf78f9e6e166ca25d7eddfd995d15d16470b1b39cdd0e0cec33d8ffe14b912b4331d51079a6b16b76dd45f4cb2b9d7f74b3ebbab7dbcca202cee
6
+ metadata.gz: 2979f947fc8b2052632549e92990ef0f9039c582915cd405d97954f2a7aa6d7c6bf88627aea7e708f8417107a89e2ea089dfd3f0005425147cca297eb138e3f0
7
+ data.tar.gz: f90de936bc75392e315c4e8c5e8bf3e191aacaca84dd7b0a3dd924d93976e753bccf31425cc9b8588c406dbde0a0690cf0f13788d1e7ffb5871dfdcb553152a0
data/lib/jat/attribute.rb CHANGED
@@ -9,6 +9,8 @@ class Jat
9
9
  attr_reader :params, :opts
10
10
 
11
11
  def initialize(name:, opts: {}, block: nil)
12
+ check_block_valid(block)
13
+
12
14
  @opts = EnumDeepDup.call(opts)
13
15
  @params = EnumDeepFreeze.call(name: name, opts: @opts, block: block)
14
16
  end
@@ -61,7 +63,7 @@ class Jat
61
63
  def block
62
64
  return @block if instance_variable_defined?(:@block)
63
65
 
64
- current_block = params.fetch(:block).tap { |bl| check_block_valid(bl) if bl }
66
+ current_block = params.fetch(:block)
65
67
  current_block ||= keyword_block
66
68
 
67
69
  @block = current_block
@@ -79,29 +81,14 @@ class Jat
79
81
  end
80
82
 
81
83
  def check_block_valid(block)
82
- raise Error, "Block must be a Proc (not lambda)" if block.lambda?
84
+ return unless block
83
85
 
84
86
  params = block.parameters
85
87
  raise Error, "Block can have 0-2 parameters" if params.count > 2
86
-
87
- valid_params_types = params.all? { |param| param[0] == :opt }
88
- raise Error, "Block parameters must be optional and no keyword parameters" unless valid_params_types
89
- end
90
- end
91
-
92
- module ClassMethods
93
- # Returns the Jat class that this Attribute class is namespaced under.
94
- attr_accessor :jat_class
95
-
96
- # Since Attribute is anonymously subclassed when Jat is subclassed,
97
- # and then assigned to a constant of the Jat subclass, make inspect
98
- # reflect the likely name for the class.
99
- def inspect
100
- "#{jat_class.inspect}::Attribute"
101
88
  end
102
89
  end
103
90
 
104
- extend ClassMethods
91
+ extend Jat::JatClass
105
92
  include InstanceMethods
106
93
  end
107
94
  end
data/lib/jat/config.rb CHANGED
@@ -1,38 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "forwardable"
3
4
  require_relative "utils/enum_deep_dup"
4
5
 
5
6
  class Jat
6
7
  class Config
7
8
  module InstanceMethods
9
+ extend Forwardable
10
+
8
11
  attr_reader :opts
9
12
 
10
13
  def initialize(opts = {})
11
14
  @opts = EnumDeepDup.call(opts)
12
15
  end
13
16
 
14
- def []=(key, value)
15
- opts[key] = value
16
- end
17
-
18
- def [](key)
19
- opts[key]
20
- end
21
- end
22
-
23
- module ClassMethods
24
- # Returns the Jat class that this config class is namespaced under.
25
- attr_accessor :jat_class
26
-
27
- # Since Config is anonymously subclassed when Jat is subclassed,
28
- # and then assigned to a constant of the Jat subclass, make inspect
29
- # reflect the likely name for the class.
30
- def inspect
31
- "#{jat_class.inspect}::Config"
32
- end
17
+ def_delegators :@opts, :[], :[]=, :fetch
33
18
  end
34
19
 
35
20
  include InstanceMethods
36
- extend ClassMethods
21
+ extend Jat::JatClass
37
22
  end
38
23
  end
@@ -4,9 +4,9 @@ require_relative "./lib/preloader"
4
4
 
5
5
  class Jat
6
6
  module Plugins
7
- module ActiverecordPreloads
7
+ module BaseActiverecordPreloads
8
8
  def self.plugin_name
9
- :_activerecord_preloads
9
+ :base_activerecord_preloads
10
10
  end
11
11
 
12
12
  def self.load(jat_class, **_opts)
@@ -33,6 +33,6 @@ class Jat
33
33
  end
34
34
  end
35
35
 
36
- register_plugin(ActiverecordPreloads.plugin_name, ActiverecordPreloads)
36
+ register_plugin(BaseActiverecordPreloads.plugin_name, BaseActiverecordPreloads)
37
37
  end
38
38
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  class Jat
4
4
  module Plugins
5
- module ActiverecordPreloads
5
+ module BaseActiverecordPreloads
6
6
  class Preloader
7
7
  module ClassMethods
8
8
  def preload(object, preloads)
@@ -21,6 +21,7 @@ class Jat
21
21
  end
22
22
 
23
23
  class Loader
24
+ # :nocov: We can check only one version of activerecord
24
25
  def self.call(records, associations)
25
26
  if ActiveRecord::VERSION::MAJOR >= 7
26
27
  ActiveRecord::Associations::Preloader.new(records: records, associations: associations).call
@@ -28,6 +29,7 @@ class Jat
28
29
  ActiveRecord::Associations::Preloader.new.preload(records, associations)
29
30
  end
30
31
  end
32
+ # :nocov:
31
33
  end
32
34
 
33
35
  class ActiverecordObject
@@ -2,9 +2,9 @@
2
2
 
3
3
  class Jat
4
4
  module Plugins
5
- module LowerCamelCase
5
+ module BaseLowerCamelCase
6
6
  def self.plugin_name
7
- :_lower_camel_case
7
+ :base_lower_camel_case
8
8
  end
9
9
 
10
10
  def self.load(jat_class, **_opts)
@@ -18,7 +18,7 @@ class Jat
18
18
  end
19
19
  end
20
20
 
21
- register_plugin(LowerCamelCase.plugin_name, LowerCamelCase)
21
+ register_plugin(BaseLowerCamelCase.plugin_name, BaseLowerCamelCase)
22
22
  end
23
23
 
24
24
  class LowerCamelCaseTransformation
@@ -6,9 +6,9 @@ require_relative "./lib/preloads_with_path"
6
6
  # This plugin adds attribute methods #preloads, #preloads_path
7
7
  class Jat
8
8
  module Plugins
9
- module Preloads
9
+ module BasePreloads
10
10
  def self.plugin_name
11
- :_preloads
11
+ :base_preloads
12
12
  end
13
13
 
14
14
  def self.load(jat_class, **_opts)
@@ -58,6 +58,6 @@ class Jat
58
58
  end
59
59
  end
60
60
 
61
- register_plugin(Preloads.plugin_name, Preloads)
61
+ register_plugin(BasePreloads.plugin_name, BasePreloads)
62
62
  end
63
63
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  class Jat
4
4
  module Plugins
5
- module Preloads
5
+ module BasePreloads
6
6
  class FormatUserPreloads
7
7
  METHODS = {
8
8
  Array => :array_to_hash,
@@ -2,7 +2,7 @@
2
2
 
3
3
  class Jat
4
4
  module Plugins
5
- module Preloads
5
+ module BasePreloads
6
6
  class PreloadsWithPath
7
7
  module ClassMethods
8
8
  BANG = "!"
@@ -7,16 +7,6 @@ class Jat
7
7
  module ClassMethods
8
8
  COMMA = ","
9
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
10
  def parse(fields)
21
11
  return FROZEN_EMPTY_HASH unless fields
22
12
 
@@ -33,6 +23,7 @@ class Jat
33
23
  end
34
24
  end
35
25
 
26
+ extend Jat::JatClass
36
27
  extend ClassMethods
37
28
  end
38
29
  end
@@ -8,16 +8,6 @@ class Jat
8
8
  COMMA = ","
9
9
  DOT = "."
10
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
11
  def parse(includes_string_param)
22
12
  return {} unless includes_string_param
23
13
 
@@ -77,6 +67,7 @@ class Jat
77
67
  end
78
68
  end
79
69
 
70
+ extend Jat::JatClass
80
71
  extend ClassMethods
81
72
  end
82
73
  end
@@ -5,16 +5,6 @@ class Jat
5
5
  module JsonApi
6
6
  class Map
7
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
-
18
8
  # Returns structure like
19
9
  # {
20
10
  # type1 => {
@@ -103,14 +93,14 @@ class Jat
103
93
  end
104
94
 
105
95
  def attribute_exposed?(attribute)
106
- case exposed
107
- when :all then true
108
- when :none then false
109
- else attribute.exposed?
110
- end
96
+ return true if exposed == :all
97
+ return false if exposed == :none
98
+
99
+ attribute.exposed?
111
100
  end
112
101
  end
113
102
 
103
+ extend Jat::JatClass
114
104
  extend ClassMethods
115
105
  include InstanceMethods
116
106
  end
@@ -6,16 +6,6 @@ class Jat
6
6
  module JsonApi
7
7
  class Response
8
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
18
-
19
9
  def call(object, context)
20
10
  new(object, context).to_h
21
11
  end
@@ -87,7 +77,7 @@ class Jat
87
77
  attributes.each do |name, attribute|
88
78
  next if data.key?(name)
89
79
 
90
- value = attribute_value(attribute)
80
+ value = attribute.value(object, context)
91
81
 
92
82
  unless value.nil?
93
83
  data = data.dup if data.equal?(FROZEN_EMPTY_HASH)
@@ -98,10 +88,6 @@ class Jat
98
88
  data
99
89
  end
100
90
 
101
- def attribute_value(attribute)
102
- attribute.block.call(object, context)
103
- end
104
-
105
91
  def context_jsonapi
106
92
  context_attr_transform(:jsonapi)
107
93
  end
@@ -119,6 +105,7 @@ class Jat
119
105
  end
120
106
  end
121
107
 
108
+ extend Jat::JatClass
122
109
  extend ClassMethods
123
110
  include InstanceMethods
124
111
  end
@@ -5,16 +5,6 @@ class Jat
5
5
  module JsonApi
6
6
  class ResponsePiece
7
7
  module ClassMethods
8
- # Returns the Jat class that this ResponsePiece class is namespaced under.
9
- attr_accessor :jat_class
10
-
11
- # Since ResponsePiece 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}::ResponsePiece"
16
- end
17
-
18
8
  def call(object, context, map, includes)
19
9
  new(object, context, map, includes).to_h
20
10
  end
@@ -60,7 +50,7 @@ class Jat
60
50
 
61
51
  attributes_names.each_with_object({}) do |name, attrs|
62
52
  attribute = jat_class.attributes[name]
63
- attrs[name] = attribute.block.call(object, context)
53
+ attrs[name] = attribute.value(object, context)
64
54
  end
65
55
  end
66
56
 
@@ -70,7 +60,7 @@ class Jat
70
60
 
71
61
  relationships_names.each_with_object({}) do |name, rels|
72
62
  rel_attribute = jat_class.attributes[name]
73
- rel_object = rel_attribute.block.call(object, context)
63
+ rel_object = rel_attribute.value(object, context)
74
64
 
75
65
  rel_serializer = rel_attribute.serializer.call
76
66
  rel_links = get_relationship_links(rel_serializer, rel_object)
@@ -123,37 +113,37 @@ class Jat
123
113
  def get_links
124
114
  jat_class
125
115
  .object_links
126
- .transform_values { |attr| attr.block.call(object, context) }
116
+ .transform_values { |attr| attr.value(object, context) }
127
117
  .tap(&:compact!)
128
118
  end
129
119
 
130
120
  def get_meta
131
121
  jat_class
132
122
  .added_object_meta
133
- .transform_values { |attr| attr.block.call(object, context) }
123
+ .transform_values { |attr| attr.value(object, context) }
134
124
  .tap(&:compact!)
135
125
  end
136
126
 
137
127
  def get_relationship_links(rel_serializer, rel_object)
138
128
  links = rel_serializer.relationship_links
139
- return FROZEN_EMPTY_HASH unless links
129
+ return FROZEN_EMPTY_HASH if links.empty?
140
130
 
141
131
  context[:parent_object] = object
142
132
 
143
133
  links
144
- .transform_values { |attr| attr.block.call(rel_object, context) }
134
+ .transform_values { |attr| attr.value(rel_object, context) }
145
135
  .tap(&:compact!)
146
136
  .tap { context.delete(:parent_object) }
147
137
  end
148
138
 
149
139
  def get_relationship_meta(rel_serializer, rel_object)
150
140
  meta = rel_serializer.added_relationship_meta
151
- return FROZEN_EMPTY_HASH unless meta
141
+ return FROZEN_EMPTY_HASH if meta.empty?
152
142
 
153
143
  context[:parent_object] = object
154
144
 
155
145
  meta
156
- .transform_values { |attr| attr.block.call(rel_object, context) }
146
+ .transform_values { |attr| attr.value(rel_object, context) }
157
147
  .tap(&:compact!)
158
148
  .tap { context.delete(:parent_object) }
159
149
  end
@@ -163,10 +153,11 @@ class Jat
163
153
  end
164
154
 
165
155
  def id
166
- @id ||= jat_class.get_id.block.call(object, context)
156
+ @id ||= jat_class.get_id.value(object, context)
167
157
  end
168
158
  end
169
159
 
160
+ extend Jat::JatClass
170
161
  extend ClassMethods
171
162
  include InstanceMethods
172
163
  end
@@ -14,7 +14,7 @@ class Jat
14
14
 
15
15
  def self.load(jat_class, **opts)
16
16
  jat_class.plugin :json_api_preloads, **opts
17
- jat_class.plugin :_activerecord_preloads, **opts
17
+ jat_class.plugin :base_activerecord_preloads, **opts
18
18
  end
19
19
  end
20
20
 
@@ -10,7 +10,7 @@ class Jat
10
10
  def self.before_load(jat_class, **_opts)
11
11
  raise Error, "Please load :json_api plugin first" unless jat_class.plugin_used?(:json_api)
12
12
 
13
- jat_class.plugin :_lower_camel_case
13
+ jat_class.plugin :base_lower_camel_case
14
14
  end
15
15
 
16
16
  def self.load(jat_class, **_opts)
@@ -12,7 +12,7 @@ class Jat
12
12
  def self.before_load(jat_class, **opts)
13
13
  raise Error, "Please load :json_api plugin first" unless jat_class.plugin_used?(:json_api)
14
14
 
15
- jat_class.plugin :_preloads, **opts
15
+ jat_class.plugin :base_preloads, **opts
16
16
  end
17
17
 
18
18
  def self.load(jat_class, **_opts)
@@ -22,9 +22,7 @@ class Jat
22
22
  allowed_relationships = jat_class.attributes.each_value.select(&:relation?).map!(&:name)
23
23
  allowed_relationships = "'#{allowed_relationships.join("', '")}'"
24
24
 
25
- raise JsonApiParamsError,
26
- "Type '#{type}' has no included '#{name}' relationship. " \
27
- "Existing relationships are: #{allowed_relationships}"
25
+ raise JsonApiParamsError, "Type '#{type}' has no included '#{name}' relationship. Existing relationships are: #{allowed_relationships}"
28
26
  end
29
27
  end
30
28
  end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "delegate"
4
+ require "forwardable"
5
+
6
+ class Jat
7
+ module Plugins
8
+ module Presenter
9
+ def self.plugin_name
10
+ :presenter
11
+ end
12
+
13
+ def self.before_load(jat_class, **opts)
14
+ if jat_class.plugin_used?(:json_api)
15
+ jat_class.plugin :json_api_preloads, **opts
16
+ elsif jat_class.plugin_used?(:simple_api)
17
+ jat_class.plugin :simple_api_preloads, **opts
18
+ else
19
+ raise Error, "Please load :json_api or :simple_api plugin first"
20
+ end
21
+ end
22
+
23
+ def self.load(jat_class, **_opts)
24
+ jat_class.extend(ClassMethods)
25
+ jat_class::ResponsePiece.include(ResponsePieceInstanceMethods)
26
+ end
27
+
28
+ def self.after_load(jat_class, **_opts)
29
+ presenter_class = Class.new(Presenter)
30
+ presenter_class.jat_class = jat_class
31
+ jat_class.const_set(:Presenter, presenter_class)
32
+ end
33
+
34
+ class Presenter < SimpleDelegator
35
+ module InstanceMethods
36
+ # Delegates all missing methods to current object.
37
+ # We create real methods after first missing method.
38
+ # rubocop:disable Style/MissingRespondToMissing (base SimpleDelegator class has this method)
39
+ def method_missing(name, *args, &block)
40
+ super(name, *args, &block).tap do
41
+ self.class.def_delegator :__getobj__, name
42
+ end
43
+ end
44
+ # rubocop:enable Style/MissingRespondToMissing
45
+ end
46
+
47
+ extend Forwardable
48
+ extend Jat::JatClass
49
+ include InstanceMethods
50
+ end
51
+
52
+ module ClassMethods
53
+ def inherited(subclass)
54
+ presenter_class = Class.new(self::Presenter)
55
+ presenter_class.jat_class = subclass
56
+ subclass.const_set(:Presenter, presenter_class)
57
+
58
+ super
59
+ end
60
+
61
+ def attribute(name, **opts, &block)
62
+ # Define attr_accessor in presenter automatically
63
+ super.tap do |attribute|
64
+ self::Presenter.def_delegator(:__getobj__, attribute.key) unless attribute.params[:block]
65
+ end
66
+ end
67
+ end
68
+
69
+ module ResponsePieceInstanceMethods
70
+ def initialize(*)
71
+ super
72
+ @object = jat_class::Presenter.new(object)
73
+ end
74
+ end
75
+ end
76
+
77
+ register_plugin(Presenter.plugin_name, Presenter)
78
+ end
79
+ end
@@ -5,20 +5,10 @@ class Jat
5
5
  module SimpleApi
6
6
  class FieldsParamParser
7
7
  module ClassMethods
8
- # Returns the Jat class that this FieldsParamParser class is namespaced under.
9
- attr_accessor :jat_class
10
-
11
- # Since FieldsParamParser 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}::FieldsParamParser"
16
- end
17
-
18
8
  def parse(fields)
19
9
  return FROZEN_EMPTY_HASH unless fields
20
10
 
21
- new(fields).parse
11
+ new.parse(fields)
22
12
  end
23
13
  end
24
14
 
@@ -27,68 +17,53 @@ class Jat
27
17
  OPEN_BRACKET = "("
28
18
  CLOSE_BRACKET = ")"
29
19
 
30
- def initialize(fields)
31
- @fields = fields
32
- end
33
-
34
20
  # user => { user: {} }
35
21
  # user(id) => { user: { id: {} } }
36
22
  # user(id,name) => { user: { id: {}, name: {} } }
37
23
  # user,comments => { user: {}, comments: {} }
38
24
  # user(comments(text)) => { user: { comments: { text: {} } } }
39
- def parse
40
- current_attr = nil
25
+ def parse(fields)
26
+ res = {}
27
+ attribute = +""
28
+ path_stack = nil
41
29
 
42
30
  fields.each_char do |char|
43
31
  case char
44
32
  when COMMA
45
- next unless current_attr
46
-
47
- add_attribute(current_attr)
48
- current_attr = nil
33
+ add_attribute(res, path_stack, attribute, FROZEN_EMPTY_HASH)
49
34
  when CLOSE_BRACKET
50
- if current_attr
51
- add_attribute(current_attr)
52
- current_attr = nil
53
- end
54
-
55
- route.pop
35
+ add_attribute(res, path_stack, attribute, FROZEN_EMPTY_HASH)
36
+ path_stack&.pop
56
37
  when OPEN_BRACKET
57
- next unless current_attr
58
-
59
- attribute_name = add_attribute(current_attr, {})
60
- route << attribute_name
61
- current_attr = nil
38
+ name = add_attribute(res, path_stack, attribute, {})
39
+ (path_stack ||= []).push(name) if name
62
40
  else
63
- current_attr = current_attr ? current_attr.insert(-1, char) : char
41
+ attribute.insert(-1, char)
64
42
  end
65
43
  end
66
44
 
67
- add_attribute(current_attr) if current_attr
45
+ add_attribute(res, path_stack, attribute, FROZEN_EMPTY_HASH)
68
46
 
69
47
  res
70
48
  end
71
49
 
72
50
  private
73
51
 
74
- attr_reader :fields
52
+ def add_attribute(res, path_stack, attribute, nested_attributes = FROZEN_EMPTY_HASH)
53
+ attribute.strip!
54
+ return if attribute.empty?
75
55
 
76
- def add_attribute(current_attr, nested_attrs = FROZEN_EMPTY_HASH)
77
- current_resource = route.empty? ? res : res.dig(*route)
78
- attribute_name = current_attr.strip.to_sym
79
- current_resource[attribute_name] = nested_attrs
80
- attribute_name
81
- end
56
+ name = attribute.to_sym
57
+ attribute.clear
82
58
 
83
- def res
84
- @res ||= {}
85
- end
59
+ current_attrs = !path_stack || path_stack.empty? ? res : res.dig(*path_stack)
60
+ current_attrs[name] = nested_attributes
86
61
 
87
- def route
88
- @route ||= []
62
+ name
89
63
  end
90
64
  end
91
65
 
66
+ extend Jat::JatClass
92
67
  extend ClassMethods
93
68
  include InstanceMethods
94
69
  end