transmutation 0.2.3 → 0.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93679644e2df5418371d258fb0ede26d51bc9e29c4f835990ce5fa52f38dface
4
- data.tar.gz: 64c956a59e385145d1870e949eadbe078db170e950ef711f1c4e46e735a8462c
3
+ metadata.gz: a1184809d63cb5abaad091e433a5e150579f5ca634a5522c57b7dd49c3465a3c
4
+ data.tar.gz: a0e2bddde883737a82369d14cd4bc75235ea1dee366c7a0234a262e29930a74c
5
5
  SHA512:
6
- metadata.gz: 704c1832843d5d5d19a8ec26c672ed9d5f7cae9716f789f6622d0f3bf38aefb97d7ca9a7290ef954e7dc47824f1173e4c823b52272f4f3363b53e14c61f212c1
7
- data.tar.gz: 3eaaf2c39d9c3e6f3ac448ce529932751b23edd43675e4eb04623e985e69ef53f20d95e5862d0e97d543919f690d3f8eff96ccc90d83e7ff43002c0ffe24c0ae
6
+ metadata.gz: ee23262517dba8436c182542b02bd3e1b15df0225b84eae7dedb106c4d155cf6dd78db791e9a98d44c57654dcf87d9aa8110e87cde6bface0943952199f8e2a5
7
+ data.tar.gz: 8b23bfbd305587fde6d6fef4a714840a73e4415d27902c3169dd06561f141b127944d64f13029a37f9aaf10d88bdab5b1059554ae132fc4584c899f6ecb3148e
data/.rubocop.yml CHANGED
@@ -16,4 +16,8 @@ Style/StringLiteralsInInterpolation:
16
16
  Layout/LineLength:
17
17
  Max: 120
18
18
 
19
+ Metrics/ParameterLists:
20
+ Enabled: true
21
+ CountKeywordArgs: false
22
+
19
23
  require: rubocop-rspec
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- transmutation (0.2.3)
4
+ transmutation (0.3.0)
5
5
  zeitwerk (~> 2.6.15)
6
6
 
7
7
  GEM
@@ -12,9 +12,9 @@ module Transmutation
12
12
  # Bubbles up the namespace until we find a matching serializer.
13
13
  #
14
14
  # @see Transmutation::Serialization#lookup_serializer
15
+ # @note This never bubbles up the object's namespace, only the caller's namespace.
15
16
  #
16
- # Example:
17
- #
17
+ # @example
18
18
  # namespace: Api::V1::Admin::Detailed
19
19
  # serializer: Chat::User
20
20
  #
@@ -25,11 +25,7 @@ module Transmutation
25
25
  # - Api::V1::Chat::UserSerializer
26
26
  # - Api::Chat::UserSerializer
27
27
  # - Chat::UserSerializer
28
- #
29
- # Note: This never bubbles up the object's namespace, only the caller's namespace.
30
28
  def serializer_for(object, serializer: nil)
31
- return Transmutation::CollectionSerializer if object.respond_to?(:map)
32
-
33
29
  serializer_name = serializer_name_for(object, serializer: serializer)
34
30
 
35
31
  return constantize_serializer!(Object, serializer_name, object: object) if serializer_name.start_with?("::")
@@ -38,7 +34,7 @@ module Transmutation
38
34
  return potential_namespace.const_get(serializer_name) if potential_namespace.const_defined?(serializer_name)
39
35
  end
40
36
 
41
- raise SerializerNotFound.new(@object, namespace: serializer_namespace, name: serializer_name)
37
+ raise SerializerNotFound.new(object, namespace: serializer_namespace, name: serializer_name)
42
38
  end
43
39
 
44
40
  # Returns the highest specificity serializer name for the given object.
@@ -47,8 +43,6 @@ module Transmutation
47
43
  #
48
44
  # @return [String] The serializer name.
49
45
  def serializer_name_for(object, serializer: nil)
50
- return "::Transmutation::CollectionSerializer" if object.respond_to?(:map)
51
-
52
46
  "#{serializer&.delete_suffix("Serializer") || object.class.name}Serializer"
53
47
  end
54
48
 
@@ -3,11 +3,11 @@
3
3
  module Transmutation
4
4
  module Serialization
5
5
  module Rendering
6
- def render(json: nil, serialize: true, **args)
6
+ def render(json: nil, serialize: true, namespace: nil, serializer: nil, max_depth: 1, **args)
7
7
  return super(**args) unless json
8
- return super(json: json, **args) unless serialize
8
+ return super(**args, json: json) unless serialize
9
9
 
10
- super(**args, json: serialize(json))
10
+ super(**args, json: serialize(json, namespace: namespace, serializer: serializer, max_depth: max_depth))
11
11
  end
12
12
  end
13
13
  end
@@ -8,10 +8,18 @@ module Transmutation
8
8
  # @param object [Object] The object to serialize.
9
9
  # @param namespace [String, Symbol, Module] The namespace to lookup the serializer in.
10
10
  # @param serializer [String, Symbol, Class] The serializer to use.
11
+ # @param max_depth [Integer] The maximum depth of nested associations to serialize.
11
12
  #
12
13
  # @return [Transmutation::Serializer] The serialized object. This will respond to `#as_json` and `#to_json`.
13
- def serialize(object, namespace: nil, serializer: nil)
14
- lookup_serializer(object, namespace: namespace, serializer: serializer).new(object)
14
+ def serialize(object, namespace: nil, serializer: nil, depth: 0, max_depth: 1)
15
+ if object.respond_to?(:map)
16
+ return object.map do |item|
17
+ serialize(item, namespace: namespace, serializer: serializer, depth: depth, max_depth: max_depth)
18
+ end
19
+ end
20
+
21
+ lookup_serializer(object, namespace: namespace, serializer: serializer)
22
+ .new(object, depth: depth, max_depth: max_depth)
15
23
  end
16
24
 
17
25
  # Lookup the serializer for the given object.
@@ -29,7 +37,7 @@ module Transmutation
29
37
  end
30
38
 
31
39
  private_class_method def self.included(base)
32
- base.include(Rendering) if base.respond_to?(:render)
40
+ base.include(Rendering) if base.method_defined?(:render)
33
41
  end
34
42
  end
35
43
  end
@@ -10,12 +10,20 @@ module Transmutation
10
10
  # attribute :full_name do
11
11
  # "#{object.first_name} #{object.last_name}".strip
12
12
  # end
13
+ #
14
+ # belongs_to :organization
15
+ #
16
+ # has_many :posts
13
17
  # end
14
18
  class Serializer
15
19
  extend ClassAttributes
16
20
 
17
- def initialize(object)
21
+ include Transmutation::Serialization
22
+
23
+ def initialize(object, depth: 0, max_depth: 1)
18
24
  @object = object
25
+ @depth = depth
26
+ @max_depth = max_depth
19
27
  end
20
28
 
21
29
  def to_json(options = {})
@@ -24,39 +32,84 @@ module Transmutation
24
32
 
25
33
  def as_json(_options = {})
26
34
  attributes_config.each_with_object({}) do |(attr_name, attr_options), hash|
27
- hash[attr_name.to_s] = attr_options[:block] ? instance_exec(&attr_options[:block]) : object.send(attr_name)
35
+ if attr_options[:association]
36
+ hash[attr_name.to_s] = instance_exec(&attr_options[:block]) if @depth + 1 <= @max_depth
37
+ else
38
+ hash[attr_name.to_s] = attr_options[:block] ? instance_exec(&attr_options[:block]) : object.send(attr_name)
39
+ end
28
40
  end
29
41
  end
30
42
 
31
- # Define an attribute to be serialized
32
- #
33
- # @param attribute_name [Symbol] The name of the attribute to serialize
34
- # @param block [Proc] The block to call to get the value of the attribute.
35
- # The block is called in the context of the serializer instance.
36
- #
37
- # @example
38
- # class UserSerializer < Transmutation::Serializer
39
- # attribute :first_name
40
- #
41
- # attribute :full_name do
42
- # "#{object.first_name} #{object.last_name}".strip
43
- # end
44
- # end
45
- def self.attribute(attribute_name, &block)
46
- attributes_config[attribute_name] = { block: block }
47
- end
43
+ class << self
44
+ # Define an attribute to be serialized
45
+ #
46
+ # @param attribute_name [Symbol] The name of the attribute to serialize
47
+ # @param block [Proc] The block to call to get the value of the attribute
48
+ # - The block is called in the context of the serializer instance
49
+ #
50
+ # @example
51
+ # class UserSerializer < Transmutation::Serializer
52
+ # attribute :first_name
53
+ #
54
+ # attribute :full_name do
55
+ # "#{object.first_name} #{object.last_name}".strip
56
+ # end
57
+ # end
58
+ def attribute(attribute_name, &block)
59
+ attributes_config[attribute_name] = { block: block }
60
+ end
61
+
62
+ # Define an association to be serialized
63
+ #
64
+ # @note By default, the serializer for the association is looked up in the same namespace as the serializer
65
+ #
66
+ # @param association_name [Symbol] The name of the association to serialize
67
+ # @param namespace [String, Symbol, Module] The namespace to lookup the association's serializer in
68
+ # @param serializer [String, Symbol, Class] The serializer to use for the association's serialization
69
+ #
70
+ # @example
71
+ # class UserSerializer < Transmutation::Serializer
72
+ # association :posts
73
+ # association :comments, namespace: "Nested", serializer: "User::CommentSerializer"
74
+ # end
75
+ def association(association_name, namespace: nil, serializer: nil)
76
+ block = lambda do
77
+ serialize(object.send(association_name), namespace: namespace, serializer: serializer, depth: @depth + 1)
78
+ end
79
+
80
+ attributes_config[association_name] = { block: block, association: true }
81
+ end
82
+
83
+ alias belongs_to association
84
+ alias has_one association
85
+ alias has_many association
86
+
87
+ # Shorthand for defining multiple attributes
88
+ #
89
+ # @param attribute_names [Array<Symbol>] The names of the attributes to serialize
90
+ #
91
+ # @example
92
+ # class UserSerializer < Transmutation::Serializer
93
+ # attributes :first_name, :last_name
94
+ # end
95
+ def attributes(*attribute_names)
96
+ attribute_names.each do |attribute_name|
97
+ attribute(attribute_name)
98
+ end
99
+ end
48
100
 
49
- # Shorthand for defining multiple attributes
50
- #
51
- # @param attribute_names [Array<Symbol>] The names of the attributes to serialize
52
- #
53
- # @example
54
- # class UserSerializer < Transmutation::Serializer
55
- # attributes :first_name, :last_name
56
- # end
57
- def self.attributes(*attribute_names)
58
- attribute_names.each do |attr_name|
59
- attribute(attr_name)
101
+ # Shorthand for defining multiple associations
102
+ #
103
+ # @param association_names [Array<Symbol>] The names of the associations to serialize
104
+ #
105
+ # @example
106
+ # class UserSerializer < Transmutation::Serializer
107
+ # associations :posts, :comments
108
+ # end
109
+ def associations(*association_names)
110
+ association_names.each do |association_name|
111
+ association(association_name)
112
+ end
60
113
  end
61
114
  end
62
115
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Transmutation
4
- VERSION = "0.2.3"
4
+ VERSION = "0.3.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: transmutation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - nitemaeric
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2024-06-05 00:00:00.000000000 Z
12
+ date: 2024-06-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: zeitwerk
@@ -46,7 +46,6 @@ files:
46
46
  - Rakefile
47
47
  - lib/transmutation.rb
48
48
  - lib/transmutation/class_attributes.rb
49
- - lib/transmutation/collection_serializer.rb
50
49
  - lib/transmutation/serialization.rb
51
50
  - lib/transmutation/serialization/lookup.rb
52
51
  - lib/transmutation/serialization/lookup/serializer_not_found.rb
@@ -1,31 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Transmutation
4
- # Out-of-the-box collection serializer.
5
- #
6
- # This serializer will be used to serialize all collections of objects.
7
- #
8
- # @example Basic usage
9
- # Transmutation::CollectionSerializer.new([object, object]).to_json
10
- class CollectionSerializer
11
- include Transmutation::Serialization
12
-
13
- def initialize(objects, namespace: nil, serializer: nil)
14
- @objects = objects
15
- @namespace = namespace
16
- @serializer = serializer
17
- end
18
-
19
- def as_json(options = {})
20
- objects.map { |item| serialize(item, namespace: namespace, serializer: serializer).as_json(options) }
21
- end
22
-
23
- def to_json(options = {})
24
- as_json(options).to_json
25
- end
26
-
27
- private
28
-
29
- attr_reader :objects, :namespace, :serializer
30
- end
31
- end