jat 0.0.1 → 0.0.3

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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +7 -0
  3. data/README.md +21 -0
  4. data/jat.gemspec +37 -0
  5. data/lib/jat/attribute.rb +85 -0
  6. data/lib/jat/config.rb +38 -0
  7. data/lib/jat/plugins/_activerecord_preloads/_activerecord_preloads.rb +29 -0
  8. data/lib/jat/plugins/_activerecord_preloads/lib/preloader.rb +89 -0
  9. data/lib/jat/plugins/_json_api_activerecord/_json_api_activerecord.rb +22 -0
  10. data/lib/jat/plugins/_json_api_activerecord/lib/preloads.rb +84 -0
  11. data/lib/jat/plugins/_preloads/_preloads.rb +53 -0
  12. data/lib/jat/plugins/_preloads/lib/format_user_preloads.rb +52 -0
  13. data/lib/jat/plugins/_preloads/lib/preloads_with_path.rb +78 -0
  14. data/lib/jat/plugins/cache/cache.rb +39 -0
  15. data/lib/jat/plugins/camel_lower/camel_lower.rb +18 -0
  16. data/lib/jat/plugins/json_api/json_api.rb +207 -0
  17. data/lib/jat/plugins/json_api/lib/construct_traversal_map.rb +91 -0
  18. data/lib/jat/plugins/json_api/lib/map.rb +54 -0
  19. data/lib/jat/plugins/json_api/lib/params/fields/parse.rb +27 -0
  20. data/lib/jat/plugins/json_api/lib/params/fields/validate.rb +55 -0
  21. data/lib/jat/plugins/json_api/lib/params/fields.rb +23 -0
  22. data/lib/jat/plugins/json_api/lib/params/include/parse.rb +55 -0
  23. data/lib/jat/plugins/json_api/lib/params/include/validate.rb +29 -0
  24. data/lib/jat/plugins/json_api/lib/params/include.rb +49 -0
  25. data/lib/jat/plugins/json_api/lib/presenters/document_links_presenter.rb +48 -0
  26. data/lib/jat/plugins/json_api/lib/presenters/document_meta_presenter.rb +48 -0
  27. data/lib/jat/plugins/json_api/lib/presenters/jsonapi_presenter.rb +48 -0
  28. data/lib/jat/plugins/json_api/lib/presenters/links_presenter.rb +48 -0
  29. data/lib/jat/plugins/json_api/lib/presenters/meta_presenter.rb +48 -0
  30. data/lib/jat/plugins/json_api/lib/presenters/relationship_links_presenter.rb +53 -0
  31. data/lib/jat/plugins/json_api/lib/presenters/relationship_meta_presenter.rb +53 -0
  32. data/lib/jat/plugins/json_api/lib/response.rb +239 -0
  33. data/lib/jat/plugins/json_api/lib/traversal_map.rb +34 -0
  34. data/lib/jat/plugins/simple_api/lib/construct_traversal_map.rb +45 -0
  35. data/lib/jat/plugins/simple_api/lib/map.rb +29 -0
  36. data/lib/jat/plugins/simple_api/lib/params/parse.rb +68 -0
  37. data/lib/jat/plugins/simple_api/lib/response.rb +134 -0
  38. data/lib/jat/plugins/simple_api/simple_api.rb +65 -0
  39. data/lib/jat/plugins/to_str/to_str.rb +44 -0
  40. data/lib/jat/plugins.rb +39 -0
  41. data/lib/jat/presenter.rb +51 -0
  42. data/lib/jat/utils/enum_deep_dup.rb +29 -0
  43. data/lib/jat/utils/enum_deep_freeze.rb +19 -0
  44. data/lib/jat.rb +66 -144
  45. data/test/lib/jat/attribute_test.rb +142 -0
  46. data/test/lib/jat/config_test.rb +57 -0
  47. data/test/lib/jat/plugins/_activerecord_preloads/_activerecord_preloads_test.rb +40 -0
  48. data/test/lib/jat/plugins/_activerecord_preloads/lib/preloader_test.rb +98 -0
  49. data/test/lib/jat/plugins/_json_api_activerecord/_json_api_activerecord_test.rb +29 -0
  50. data/test/lib/jat/plugins/_json_api_activerecord/lib/preloads_test.rb +191 -0
  51. data/test/lib/jat/plugins/_preloads/_preloads_test.rb +68 -0
  52. data/test/lib/jat/plugins/_preloads/lib/format_user_preloads_test.rb +47 -0
  53. data/test/lib/jat/plugins/_preloads/lib/preloads_with_path_test.rb +33 -0
  54. data/test/lib/jat/plugins/cache/cache_test.rb +82 -0
  55. data/test/lib/jat/plugins/camel_lower/camel_lower_test.rb +78 -0
  56. data/test/lib/jat/plugins/json_api/json_api_test.rb +154 -0
  57. data/test/lib/jat/plugins/json_api/lib/construct_traversal_map_test.rb +119 -0
  58. data/test/lib/jat/plugins/json_api/lib/map_test.rb +117 -0
  59. data/test/lib/jat/plugins/json_api/lib/params/fields/parse_test.rb +24 -0
  60. data/test/lib/jat/plugins/json_api/lib/params/fields/validate_test.rb +47 -0
  61. data/test/lib/jat/plugins/json_api/lib/params/fields_test.rb +37 -0
  62. data/test/lib/jat/plugins/json_api/lib/params/include/parse_test.rb +46 -0
  63. data/test/lib/jat/plugins/json_api/lib/params/include/validate_test.rb +51 -0
  64. data/test/lib/jat/plugins/json_api/lib/params/include_test.rb +41 -0
  65. data/test/lib/jat/plugins/json_api/lib/presenters/document_links_presenter_test.rb +69 -0
  66. data/test/lib/jat/plugins/json_api/lib/presenters/document_meta_presenter_test.rb +69 -0
  67. data/test/lib/jat/plugins/json_api/lib/presenters/jsonapi_presenter_test.rb +69 -0
  68. data/test/lib/jat/plugins/json_api/lib/presenters/links_presenter_test.rb +69 -0
  69. data/test/lib/jat/plugins/json_api/lib/presenters/meta_presenter_test.rb +69 -0
  70. data/test/lib/jat/plugins/json_api/lib/presenters/relationship_links_presenter_test.rb +75 -0
  71. data/test/lib/jat/plugins/json_api/lib/presenters/relationship_meta_presenter_test.rb +75 -0
  72. data/test/lib/jat/plugins/json_api/lib/response_test.rb +489 -0
  73. data/test/lib/jat/plugins/json_api/lib/traversal_map_test.rb +58 -0
  74. data/test/lib/jat/plugins/simple_api/lib/construct_traversal_map_test.rb +100 -0
  75. data/test/lib/jat/plugins/simple_api/lib/map_test.rb +56 -0
  76. data/test/lib/jat/plugins/simple_api/lib/params/parse_test.rb +71 -0
  77. data/test/lib/jat/plugins/simple_api/lib/response_test.rb +342 -0
  78. data/test/lib/jat/plugins/simple_api/simple_api_test.rb +81 -0
  79. data/test/lib/jat/plugins/to_str/to_str_test.rb +52 -0
  80. data/test/lib/jat/presenter_test.rb +61 -0
  81. data/test/lib/jat/utils/enum_deep_dup_test.rb +31 -0
  82. data/test/lib/jat/utils/enum_deep_freeze_test.rb +28 -0
  83. data/test/lib/jat_test.rb +120 -0
  84. data/test/lib/plugin_test.rb +49 -0
  85. data/test/support/activerecord.rb +24 -0
  86. data/test/test_helper.rb +16 -0
  87. data/test/test_plugin.rb +59 -0
  88. metadata +240 -11
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Serializes to JSON-API format
4
+ class Jat
5
+ module Plugins
6
+ module SimpleApi
7
+ class Response
8
+ attr_reader :jat, :jat_class, :object, :context
9
+
10
+ def initialize(jat)
11
+ @jat = jat
12
+ @jat_class = jat.class
13
+ @object = jat.object
14
+ @context = jat.context
15
+ end
16
+
17
+ def response
18
+ # Add main response
19
+ result = many? ? many(object) : one(object)
20
+ result = {root_key => result} if root_key
21
+ result ||= {}
22
+
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
30
+ end
31
+
32
+ result
33
+ end
34
+
35
+ private
36
+
37
+ def metadata
38
+ result = context[:meta] || {}
39
+
40
+ config_meta = jat_class.config[:meta]
41
+ return result unless config_meta
42
+
43
+ config_meta.each_with_object(result) do |(key, value), res|
44
+ next if res.key?(key) # do not overwrite manually added meta
45
+
46
+ value = value.call(object, context) if value.respond_to?(:call)
47
+ res[key] = value unless value.nil?
48
+ end
49
+ end
50
+
51
+ def many(objects)
52
+ objects.map { |obj| one(obj) }
53
+ end
54
+
55
+ def one(obj)
56
+ ResponseData.new(jat_class, obj, context, jat.traversal_map).data
57
+ end
58
+
59
+ def many?
60
+ @is_many ||= begin
61
+ many = context[:many]
62
+ many.nil? ? object.is_a?(Enumerable) : many
63
+ end
64
+ end
65
+
66
+ # We can provide nil or false to remove root
67
+ def root_key
68
+ @root_key ||=
69
+ if context.key?(:root)
70
+ context[:root]
71
+ else
72
+ (many? ? jat_class.root_for_many : jat_class.root_for_one) || jat_class.root
73
+ end
74
+ end
75
+
76
+ def meta_key
77
+ context[:meta_key] || jat_class.meta_key
78
+ end
79
+ end
80
+
81
+ class ResponseData
82
+ attr_reader :jat_class, :object, :context, :map, :presenter
83
+
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
91
+
92
+ def data
93
+ return unless object
94
+
95
+ result = {}
96
+
97
+ map.each do |key, inner_keys|
98
+ attribute = jat_class.attributes.fetch(key)
99
+ value = presenter.public_send(attribute.original_name)
100
+
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
112
+
113
+ result
114
+ end
115
+
116
+ private
117
+
118
+ def response_data(attribute, value, map)
119
+ ResponseData.new(attribute.serializer.call, value, context, map).data
120
+ end
121
+
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
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "./lib/map"
4
+ require_relative "./lib/response"
5
+
6
+ # Serializes to JSON-API format
7
+ class Jat
8
+ module Plugins
9
+ module SimpleApi
10
+ DEFAULT_META_KEY = :meta
11
+
12
+ def self.after_load(jat_class, **opts)
13
+ jat_class.plugin(:_json_api_activerecord, **opts) if opts[:activerecord]
14
+ end
15
+
16
+ module InstanceMethods
17
+ def to_h
18
+ Response.new(self).response
19
+ end
20
+
21
+ def traversal_map
22
+ @traversal_map ||= Map.call(self)
23
+ end
24
+ end
25
+
26
+ module ClassMethods
27
+ def inherited(subclass)
28
+ subclass.root(@root) if defined?(@root)
29
+
30
+ super
31
+ end
32
+
33
+ def root(new_root = nil)
34
+ return (defined?(@root) && @root) unless new_root
35
+
36
+ new_root = new_root.to_sym
37
+ @root = new_root
38
+ end
39
+
40
+ def root_for_one(new_root_for_one = nil)
41
+ return (defined?(@root_for_one) && @root_for_one) unless new_root_for_one
42
+
43
+ new_root_for_one = new_root_for_one.to_sym
44
+ @root_for_one = new_root_for_one
45
+ end
46
+
47
+ def root_for_many(new_root_for_many = nil)
48
+ return (defined?(@root_for_many) && @root_for_many) unless new_root_for_many
49
+
50
+ new_root_for_many = new_root_for_many.to_sym
51
+ @root_for_many = new_root_for_many
52
+ end
53
+
54
+ def meta_key(new_meta_key = nil)
55
+ return ((defined?(@meta_key) && @meta_key) || DEFAULT_META_KEY) unless new_meta_key
56
+
57
+ new_meta_key = new_meta_key.to_sym
58
+ @meta_key = new_meta_key
59
+ end
60
+ end
61
+ end
62
+
63
+ register_plugin(:simple_api, SimpleApi)
64
+ end
65
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ module Plugins
5
+ module ToStr
6
+ def self.after_load(jat_class, **opts)
7
+ jat_class.config[:to_str] = opts[:to_str] || ->(data) { ToStrJSON.dump(data) }
8
+ end
9
+
10
+ module ClassMethods
11
+ def to_str(object, context = {})
12
+ new(object, context).to_str
13
+ end
14
+ end
15
+
16
+ module InstanceMethods
17
+ def to_str
18
+ config[:to_str].call(to_h)
19
+ end
20
+ end
21
+
22
+ class ToStrJSON
23
+ module ClassMethods
24
+ def dump(response)
25
+ json_adapter.dump(response)
26
+ end
27
+
28
+ private
29
+
30
+ def json_adapter
31
+ @json_adapter ||= begin
32
+ require "json"
33
+ ::JSON
34
+ end
35
+ end
36
+ end
37
+
38
+ extend ClassMethods
39
+ end
40
+ end
41
+
42
+ register_plugin(:to_str, ToStr)
43
+ end
44
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ module Plugins
5
+ @plugins = {}
6
+
7
+ # Register the given plugin with Jat, so that it can be loaded using
8
+ # `Jat.plugin` with a symbol. Should be used by plugin files. Example:
9
+ #
10
+ # Jat::Plugins.register_plugin(:plugin_name, PluginModule)
11
+ def self.register_plugin(name, mod)
12
+ @plugins[name] = mod
13
+ end
14
+
15
+ # If the registered plugin already exists, use it. Otherwise, require it
16
+ # and return it. This raises a LoadError if such a plugin doesn't exist,
17
+ # or a Jat::Error if it exists but it does not register itself
18
+ # correctly.
19
+ def self.load_plugin(name)
20
+ require "jat/plugins/#{name}/#{name}" unless @plugins.key?(name)
21
+
22
+ @plugins[name] || raise(Error, "plugin #{name} did not register itself correctly in Jat::Plugins")
23
+ end
24
+
25
+ # Delegate call to the plugin in a way that works across Ruby versions.
26
+ def self.before_load(plugin, jat_class, **opts)
27
+ return unless plugin.respond_to?(:before_load)
28
+
29
+ plugin.before_load(jat_class, **opts)
30
+ end
31
+
32
+ # Delegate call to the plugin in a way that works across Ruby versions.
33
+ def self.after_load(plugin, jat_class, **opts)
34
+ return unless plugin.respond_to?(:after_load)
35
+
36
+ plugin.after_load(jat_class, **opts)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ # Core class that encapsulates attributes presentation logic
5
+ class Presenter
6
+ module InstanceMethods
7
+ # Presented object
8
+ attr_reader :object
9
+
10
+ # Presented context
11
+ attr_reader :context
12
+
13
+ def initialize(object, context)
14
+ @object = object
15
+ @context = context
16
+ end
17
+ end
18
+
19
+ module ClassMethods
20
+ # Returns the Jat class that this presenter class is namespaced under.
21
+ attr_accessor :jat_class
22
+
23
+ # Since Presenter is anonymously subclassed when Jat is subclassed,
24
+ # and then assigned to a constant of the Jat subclass, make inspect
25
+ # reflect the likely name for the class.
26
+ def inspect
27
+ "#{jat_class.inspect}::Presenter"
28
+ end
29
+
30
+ def add_method(name, block)
31
+ # Warning-free method redefinition
32
+ remove_method(name) if method_defined?(name, false)
33
+ define_method(name, &block_without_args(block))
34
+ end
35
+
36
+ private
37
+
38
+ def block_without_args(block)
39
+ case block.parameters.count
40
+ when 0 then block
41
+ when 1 then -> { instance_exec(object, &block) }
42
+ when 2 then -> { instance_exec(object, context, &block) }
43
+ else raise Error, "Invalid block arguments count"
44
+ end
45
+ end
46
+ end
47
+
48
+ extend ClassMethods
49
+ include InstanceMethods
50
+ end
51
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ # Duplicates nested enumerable data
5
+ class EnumDeepDup
6
+ DUP = {
7
+ Hash => ->(data) { dup_hash_values(data) },
8
+ Array => ->(data) { dup_array_values(data) }
9
+ }.freeze
10
+
11
+ def self.call(data)
12
+ duplicate_data = data.dup
13
+ DUP.fetch(duplicate_data.class).call(duplicate_data)
14
+ duplicate_data
15
+ end
16
+
17
+ def self.dup_hash_values(duplicate_data)
18
+ duplicate_data.each do |key, value|
19
+ duplicate_data[key] = call(value) if value.is_a?(Enumerable)
20
+ end
21
+ end
22
+
23
+ def self.dup_array_values(duplicate_data)
24
+ duplicate_data.each_with_index do |value, index|
25
+ duplicate_data[index] = call(value) if value.is_a?(Enumerable)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Jat
4
+ # Freezes nested enumerable data
5
+ class EnumDeepFreeze
6
+ FREEZES = {
7
+ Hash => ->(data) { data.each_value(&FREEZE_ENUMS) },
8
+ Array => ->(data) { data.each(&FREEZE_ENUMS) }
9
+ }.freeze
10
+
11
+ FREEZE_ENUMS = ->(value) { call(value) if value.is_a?(Enumerable) }
12
+
13
+ def self.call(data)
14
+ data.freeze
15
+ FREEZES.fetch(data.class).call(data)
16
+ data
17
+ end
18
+ end
19
+ end
data/lib/jat.rb CHANGED
@@ -1,186 +1,108 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'jat/attributes'
4
- require 'jat/config'
5
- require 'jat/error'
6
- require 'jat/map'
7
- require 'jat/map/construct'
8
- require 'jat/opts'
9
- require 'jat/preloads'
10
- require 'jat/preload_handler'
11
- require 'jat/response'
12
- require 'jat/utils/preloads_to_hash'
3
+ require_relative "jat/attribute"
4
+ require_relative "jat/presenter"
5
+ require_relative "jat/config"
6
+ require_relative "jat/plugins"
13
7
 
14
8
  # Main namespace
15
9
  class Jat
10
+ # A generic exception used by Jat.
11
+ class Error < StandardError; end
12
+
13
+ @config = Config.new
14
+
16
15
  module ClassMethods
16
+ attr_reader :config
17
+
17
18
  def inherited(subclass)
18
- subclass.extend DSLClassMethods
19
- subclass.include DSLInstanceMethods
20
- copy_to(subclass)
19
+ # Initialize config
20
+ config_class = Class.new(self::Config)
21
+ config_class.jat_class = subclass
22
+ subclass.const_set(:Config, config_class)
23
+ subclass.instance_variable_set(:@config, subclass::Config.new(config.opts))
24
+
25
+ # Initialize attribute class
26
+ attribute_class = Class.new(self::Attribute)
27
+ attribute_class.jat_class = subclass
28
+ subclass.const_set(:Attribute, attribute_class)
29
+
30
+ # Initialize object presenter
31
+ presenter_class = Class.new(self::Presenter)
32
+ presenter_class.jat_class = subclass
33
+ subclass.const_set(:Presenter, presenter_class)
34
+
35
+ # Assign same attributes
36
+ attributes.each_value do |attribute|
37
+ params = attribute.params
38
+ subclass.attribute(params[:name], **params[:opts], &params[:block])
39
+ end
21
40
 
22
41
  super
23
42
  end
24
43
 
25
- def config
26
- @config ||= Config.new(self)
27
- end
44
+ def plugin(plugin, **opts)
45
+ if !plugin.is_a?(Symbol) && !plugin.is_a?(Module)
46
+ raise Error, "Plugin class must be a Symbol or a Module, #{plugin.inspect} given"
47
+ end
28
48
 
29
- def config=(config)
30
- @config = config
31
- end
49
+ plugin = Plugins.load_plugin(plugin) if plugin.is_a?(Symbol)
32
50
 
33
- def attributes
34
- @attributes ||= Attributes.new
35
- end
51
+ Plugins.before_load(plugin, self, **opts)
36
52
 
37
- # Used to validate provided params (fields, include)
38
- def full_map
39
- @full_map ||= Map::Construct.new(self, :all).to_h
40
- end
53
+ include(plugin::InstanceMethods) if defined?(plugin::InstanceMethods)
54
+ extend(plugin::ClassMethods) if defined?(plugin::ClassMethods)
41
55
 
42
- def exposed_map
43
- @exposed_map ||= Map::Construct.new(self, :exposed).to_h
44
- end
56
+ self::Attribute.include plugin::AttributeMethods if defined?(plugin::AttributeMethods)
57
+ self::Attribute.extend plugin::AttributeClassMethods if defined?(plugin::AttributeClassMethods)
45
58
 
46
- def clear
47
- @full_map = nil
48
- @exposed_map = nil
49
- end
59
+ self::Config.include plugin::ConfigMethods if defined?(plugin::ConfigMethods)
60
+ self::Config.extend plugin::ConfigClassMethods if defined?(plugin::ConfigClassMethods)
50
61
 
51
- def refresh
52
- attributes.refresh
53
- clear
54
- end
62
+ self::Presenter.include plugin::PresenterMethods if defined?(plugin::PresenterMethods)
63
+ self::Presenter.extend plugin::PresenterClassMethods if defined?(plugin::PresenterClassMethods)
55
64
 
56
- def copy_to(subclass)
57
- subclass.type(@type) if defined?(@type)
58
- config.copy_to(subclass)
59
- attributes.copy_to(subclass)
65
+ Plugins.after_load(plugin, self, **opts)
66
+ plugin
60
67
  end
61
- end
62
68
 
63
- module DSLClassMethods
64
69
  def call
65
70
  self
66
71
  end
67
72
 
68
- def to_h(object, context = {})
69
- new.to_h(object, context)
70
- end
71
-
72
- def to_str(object, context = {})
73
- new.to_str(object, context)
74
- end
75
-
76
- def type(new_type = nil)
77
- return @type || raise(Error, "#{self} has no defined type") unless new_type
78
-
79
- new_type = new_type.to_sym
80
- define_method(:type) { new_type }
81
- @type = new_type
73
+ def to_h(object, context = nil)
74
+ new(object, context || {}).to_h
82
75
  end
83
76
 
84
- def id(key: nil, &block)
85
- raise Error, "Key and block can't be provided together" if key && block
86
- raise Error, 'Key or block must be provided' if !key && !block
87
-
88
- block ||= proc { |obj| obj.public_send(key) }
89
- define_method(:id, &block)
77
+ def attributes
78
+ @attributes ||= {}
90
79
  end
91
80
 
92
81
  def attribute(name, **opts, &block)
93
- add_attribute(name: name, opts: opts, block: block)
94
- end
95
-
96
- def relationship(name, serializer:, **opts, &block)
97
- opts[:serializer] = serializer
98
- add_attribute(name: name, opts: opts, block: block)
99
- end
100
-
101
- private
102
-
103
- def add_attribute(params)
104
- opts = Opts.new(self, params)
105
-
106
- Attribute.new(opts).tap do |attribute|
107
- attributes << attribute
108
- add_method(attribute)
109
- clear
110
- end
111
- end
112
-
113
- def add_method(attribute)
114
- block = attribute.block
115
- return unless block
116
-
117
- name = attribute.original_name
118
- # Warning-free method redefinition
119
- remove_method(name) if method_defined?(name)
120
- define_method(name, &block)
82
+ new_attr = self::Attribute.new(name: name, opts: opts, block: block)
83
+ attributes[new_attr.name] = new_attr
84
+ self::Presenter.add_method(new_attr.original_name, new_attr.block)
85
+ new_attr
121
86
  end
122
87
  end
123
88
 
124
- # :reek:ModuleInitialize
125
- module DSLInstanceMethods
126
- attr_reader :_context
127
-
128
- def initialize(context = {}, full_map = nil)
129
- @_context = context.dup
130
- @_full_map = full_map
131
- end
132
-
133
- def to_h(object, context = {})
134
- _reinitialize(context)
135
-
136
- Response.new(self, object).to_h
137
- end
138
-
139
- def to_str(object, context = {})
140
- _reinitialize(context)
141
- Response.new(self, object).to_str
142
- end
89
+ module InstanceMethods
90
+ attr_reader :object, :context
143
91
 
144
- def id(object)
145
- object.id
92
+ def initialize(object, context)
93
+ @object = object
94
+ @context = context
146
95
  end
147
96
 
148
- def _preloads
149
- Preloads.new(_full_map).for(self.class)
97
+ def to_h
98
+ raise Error, "Method #to_h must be implemented by plugin"
150
99
  end
151
100
 
152
- def _copy_to(nested_serializer)
153
- nested_serializer.().new(_context, _full_map)
154
- end
155
-
156
- def _full_map
157
- @_full_map ||= begin
158
- params = _context[:params]
159
- fields = params && (params[:fields] || params['fields'])
160
- includes = params && (params[:include] || params['include'])
161
- Map.(self.class, fields, includes)
162
- end
163
- end
164
-
165
- def _map
166
- @_map ||= _full_map.fetch(type)
167
- end
168
-
169
- private
170
-
171
- def _reinitialize(context)
172
- new_params = context[:params]
173
- old_params = _context[:params]
174
-
175
- # maps depend on params, so we should clear them when params changed
176
- if new_params != old_params
177
- @_full_map = nil
178
- @_map = nil
179
- end
180
-
181
- @_context = _context.merge!(context)
101
+ def config
102
+ self.class.config
182
103
  end
183
104
  end
184
105
 
185
106
  extend ClassMethods
107
+ include InstanceMethods
186
108
  end