jat 0.0.1 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
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