transmutation 0.2.3 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 93679644e2df5418371d258fb0ede26d51bc9e29c4f835990ce5fa52f38dface
4
- data.tar.gz: 64c956a59e385145d1870e949eadbe078db170e950ef711f1c4e46e735a8462c
3
+ metadata.gz: 3f47031b9ba7f42582eb94bf4656a767ab0d80571cac2dbab539586ee1026717
4
+ data.tar.gz: bf8324685ee7b78dce729d5d9d3862f252516d3601078029936ed16cfc193a17
5
5
  SHA512:
6
- metadata.gz: 704c1832843d5d5d19a8ec26c672ed9d5f7cae9716f789f6622d0f3bf38aefb97d7ca9a7290ef954e7dc47824f1173e4c823b52272f4f3363b53e14c61f212c1
7
- data.tar.gz: 3eaaf2c39d9c3e6f3ac448ce529932751b23edd43675e4eb04623e985e69ef53f20d95e5862d0e97d543919f690d3f8eff96ccc90d83e7ff43002c0ffe24c0ae
6
+ metadata.gz: 4cd441a3d1f40bfea4dd9090eb2ed58aad496166f986a1e2d086ef564302153c1381bfd4bf5868bea20cff125447aab012c3fc04488d545f4e7b436721b9edb1
7
+ data.tar.gz: 3931c97882ba5f3bd972f9cb540904f1ef71296dc8fda76c7d79770ec760c54c8f4490557a3ea5e1f6315177099e63b762d4cfe7f24bad5e6fd80d1906c9f608
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.1)
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
 
@@ -58,13 +52,15 @@ module Transmutation
58
52
  @potential_namespaces ||= begin
59
53
  namespace_parts = serializer_namespace.split("::")
60
54
 
61
- namespace_parts.filter_map.with_index do |part, index|
55
+ namespaces = namespace_parts.filter_map.with_index do |part, index|
62
56
  namespace = [*namespace_parts[...index], part].join("::")
63
57
 
64
58
  next if namespace.empty?
65
59
 
66
60
  Object.const_get(namespace) if Object.const_defined?(namespace)
67
- end.reverse
61
+ end
62
+
63
+ [*namespaces.reverse, Object]
68
64
  end
69
65
  end
70
66
 
@@ -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.1"
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.1
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-10 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