alba 3.3.3 → 3.4.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: d3168959e9726c35e28faf0f027dd78010b4b0cf5ead0564b8fa8a7f32e5bf84
4
- data.tar.gz: b9cab46abe11f69ef40ef15d3cc673a279a6f1ab12ab8a8b908250a8462f65e8
3
+ metadata.gz: d1a7b2fac43a1ae40bc0598d6f989fb7b81108d00ca929d31327113590880b2f
4
+ data.tar.gz: d6bc3c22c9178eeaf8b3b7f3d3d92bc526edd18904fa736c0c25488250f89d02
5
5
  SHA512:
6
- metadata.gz: 8d5b3c0513e7f706d484fdc19c8423fc6261bd7b670eee46009b0cf15b9b47f8bdfc14041937a57f15e75c679ad3073fa84ecaf1bcc782ab0615fdebd141a6cc
7
- data.tar.gz: 017b40ee4d711a50bcd2c74719a0a4a27b4965058cfb8f37382765a84b7400fc78af754fb98b0d4aeb7ba5c935656b3d61687e9c0784a09a047269fbd2e7356f
6
+ metadata.gz: da62c8a7d30ab7c4c39032950c9e02cef238b8662f403843351116fb86e3a3010e2780a3b5efe958697fd698e7e703a130a292129f3efad4da253cc9e84570bd
7
+ data.tar.gz: 94a580ab65397d4f61a8534bfd22999d86bdd9e6cca0ec83b15d03bd2fdbdedc220bbbaf091b20c34e58c7dfe5c7b21c9f046a78b25639dcecf59b71b95199c0
@@ -33,7 +33,7 @@ jobs:
33
33
  run: |
34
34
  bundle exec rake
35
35
  - name: CodeCov
36
- uses: codecov/codecov-action@v4
36
+ uses: codecov/codecov-action@v5
37
37
  with:
38
38
  files: ./coverage/coverage.xml
39
39
  token: ${{ secrets.CODECOV_TOKEN }}
data/CHANGELOG.md CHANGED
@@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [3.4.0] 2024-12-01
10
+
11
+ ### Added
12
+
13
+ - `Alba.serialize` now works with heterogeneous collection [#396](https://github.com/okuramasafumi/alba/pull/396)
14
+
9
15
  ## [3.3.3] 2024-11-09
10
16
 
11
17
  ### Fixed
data/Gemfile CHANGED
@@ -11,11 +11,11 @@ gem 'ffaker', require: false # For testing
11
11
  gem 'minitest', '~> 5.14' # For test
12
12
  gem 'railties', require: false # For Rails integration testing
13
13
  gem 'rake', '~> 13.0' # For test and automation
14
- gem 'rubocop', '~> 1.68.0', require: false # For lint
14
+ gem 'rubocop', '~> 1.69.0', require: false # For lint
15
15
  gem 'rubocop-gem_dev', '>= 0.3.0', require: false # For lint
16
16
  gem 'rubocop-md', '~> 1.0', require: false # For lint
17
17
  gem 'rubocop-minitest', '~> 0.36.0', require: false # For lint
18
- gem 'rubocop-performance', '~> 1.22.1', require: false # For lint
18
+ gem 'rubocop-performance', '~> 1.23.0', require: false # For lint
19
19
  gem 'rubocop-rake', '~> 0.6.0', require: false # For lint
20
20
  gem 'simplecov', '~> 0.22.0', require: false # For test coverage
21
21
  gem 'simplecov-cobertura', require: false # For test coverage
data/README.md CHANGED
@@ -730,6 +730,60 @@ end
730
730
  # => JSON containing "foo" and "bar" as root keys
731
731
  ```
732
732
 
733
+ #### Inline definition with heterogeneous collection
734
+
735
+ Alba allows to serialize a heterogeneous collection with `Alba.serialize`.
736
+
737
+ ```ruby
738
+ Foo = Data.define(:id, :name)
739
+ Bar = Data.define(:id, :address)
740
+
741
+ class FooResource
742
+ include Alba::Resource
743
+
744
+ attributes :id, :name
745
+ end
746
+
747
+ class BarResource
748
+ include Alba::Resource
749
+
750
+ attributes :id, :address
751
+ end
752
+
753
+ class CustomFooResource
754
+ include Alba::Resource
755
+
756
+ attributes :id
757
+ end
758
+
759
+ foo1 = Foo.new(1, 'foo1')
760
+ foo2 = Foo.new(2, 'foo2')
761
+ bar1 = Bar.new(1, 'bar1')
762
+ bar2 = Bar.new(2, 'bar2')
763
+
764
+ # This works only when inflector is set
765
+ Alba.serialize([foo1, bar1, foo2, bar2], with: :inference)
766
+ # => '[{"id":1,"name":"foo1"},{"id":1,"address":"bar1"},{"id":2,"name":"foo2"},{"id":2,"address":"bar2"}]'
767
+
768
+ Alba.serialize(
769
+ [foo1, bar1, foo2, bar2],
770
+ # `with` option takes a lambda to return resource class
771
+ with: lambda do |obj|
772
+ case obj
773
+ when Foo
774
+ CustomFooResource
775
+ when Bar
776
+ BarResource
777
+ else
778
+ raise # Impossible in this case
779
+ end
780
+ end
781
+ )
782
+ # => '[{"id":1},{"id":1,"address":"bar1"},{"id":2},{"id":2,"address":"bar2"}]'
783
+ # Note `CustomFooResource` is used here
784
+
785
+ ```
786
+
733
787
  ### Serializable Hash
734
788
 
735
789
  Instead of serializing to JSON, you can also output a Hash by calling `serializable_hash` or the `to_h` alias. Note also that the `serialize` method is aliased as `to_json`.
data/lib/alba/resource.rb CHANGED
@@ -56,7 +56,7 @@ module Alba
56
56
  # Serialize object into JSON string
57
57
  #
58
58
  # @param root_key [Symbol, nil, true]
59
- # @param meta [Hash] metadata for this seialization
59
+ # @param meta [Hash] metadata for this serialization
60
60
  # @return [String] serialized JSON string
61
61
  def serialize(root_key: nil, meta: {})
62
62
  serialize_with(as_json(root_key: root_key, meta: meta))
@@ -78,11 +78,11 @@ module Alba
78
78
  serialize(root_key: root_key, meta: meta)
79
79
  end
80
80
 
81
- # Returns a Hash correspondng {#serialize}
81
+ # Returns a Hash corresponding {#serialize}
82
82
  #
83
83
  # @param _options [Hash] dummy parameter for Rails compatibility
84
84
  # @param root_key [Symbol, nil, true]
85
- # @param meta [Hash] metadata for this seialization
85
+ # @param meta [Hash] metadata for this serialization
86
86
  # @return [Hash]
87
87
  def as_json(_options = {}, root_key: nil, meta: {})
88
88
  key = root_key.nil? ? fetch_key : root_key
@@ -99,7 +99,7 @@ module Alba
99
99
  #
100
100
  # @return [Hash]
101
101
  def serializable_hash
102
- collection? ? serializable_hash_for_collection : converter.call(@object)
102
+ Alba.collection?(@object) ? serializable_hash_for_collection : converter.call(@object)
103
103
  end
104
104
  alias to_h serializable_hash
105
105
 
@@ -143,7 +143,7 @@ module Alba
143
143
 
144
144
  # @return [String]
145
145
  def fetch_key
146
- k = collection? ? _key_for_collection : _key
146
+ k = Alba.collection?(@object) ? _key_for_collection : _key
147
147
  transforming_root_key? ? transform_key(k) : k
148
148
  end
149
149
 
@@ -292,12 +292,6 @@ module Alba
292
292
  raise Alba::Error, "Unknown type for within option: #{@within.class}"
293
293
  end
294
294
  end
295
-
296
- # Detect if object is a collection or not.
297
- # When object is a Struct, it's Enumerable but not a collection
298
- def collection?
299
- @object.is_a?(Enumerable) && !@object.is_a?(Struct)
300
- end
301
295
  end
302
296
 
303
297
  # Class methods
data/lib/alba/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Alba
4
- VERSION = '3.3.3'
4
+ VERSION = '3.4.0'
5
5
  end
data/lib/alba.rb CHANGED
@@ -42,25 +42,44 @@ module Alba
42
42
  # Serialize the object with inline definitions
43
43
  #
44
44
  # @param object [Object] the object to be serialized
45
+ # @param with [:inference, Proc, Class<Alba::Resource>] determines how to get resource class for each object
45
46
  # @param root_key [Symbol, nil, true]
46
47
  # @param block [Block] resource block
47
48
  # @return [String] serialized JSON string
48
49
  # @raise [ArgumentError] if block is absent or `with` argument's type is wrong
49
- def serialize(object = nil, root_key: nil, &block)
50
- resource = resource_with(object, &block)
51
- resource.serialize(root_key: root_key)
50
+ def serialize(object = nil, with: :inference, root_key: nil, &block)
51
+ if collection?(object)
52
+ h = hashify_collection(object, with, &block)
53
+ Alba.encoder.call(h)
54
+ else
55
+ resource = resource_with(object, &block)
56
+ resource.serialize(root_key: root_key)
57
+ end
52
58
  end
53
59
 
54
60
  # Hashify the object with inline definitions
55
61
  #
56
62
  # @param object [Object] the object to be serialized
63
+ # @param with [:inference, Proc, Class<Alba::Resource>] determines how to get resource class for each object
57
64
  # @param root_key [Symbol, nil, true]
58
65
  # @param block [Block] resource block
59
66
  # @return [String] serialized JSON string
60
67
  # @raise [ArgumentError] if block is absent or `with` argument's type is wrong
61
- def hashify(object = nil, root_key: nil, &block)
62
- resource = resource_with(object, &block)
63
- resource.as_json(root_key: root_key)
68
+ def hashify(object = nil, with: :inference, root_key: nil, &block)
69
+ if collection?(object)
70
+ hashify_collection(object, with, &block)
71
+ else
72
+ resource = resource_with(object, &block)
73
+ resource.as_json(root_key: root_key)
74
+ end
75
+ end
76
+
77
+ # Detect if object is a collection or not.
78
+ # When object is a Struct, it's Enumerable but not a collection
79
+ #
80
+ # @api private
81
+ def collection?(object)
82
+ object.is_a?(Enumerable) && !object.is_a?(Struct)
64
83
  end
65
84
 
66
85
  # Enable inference for key and resource name
@@ -192,7 +211,11 @@ module Alba
192
211
  register_default_types
193
212
  end
194
213
 
195
- # This method could be part of public API, but for now it's private
214
+ # Get a resource object from arguments
215
+ # If block is given, it creates a resource class with the block
216
+ # Otherwise, it infers resource class from the object's class name
217
+ #
218
+ # @ param object [Object] the object whose class name is used for inferring resource class
196
219
  def resource_with(object, &block)
197
220
  klass = block ? resource_class(&block) : infer_resource_class(object.class.name)
198
221
 
@@ -253,6 +276,24 @@ module Alba
253
276
  end
254
277
  end
255
278
 
279
+ def hashify_collection(collection, with, &block) # rubocop:disable Metrics/MethodLength
280
+ collection.map do |obj|
281
+ resource = if block
282
+ resource_class(&block)
283
+ else
284
+ case with
285
+ when Class then with
286
+ when :inference then infer_resource_class(obj.class.name)
287
+ when Proc then with.call(obj)
288
+ else raise ArgumentError, '`with` argument must be either :inference, Proc or Class'
289
+ end
290
+ end
291
+ raise Alba::Error if resource.nil?
292
+
293
+ resource.new(obj).to_h
294
+ end
295
+ end
296
+
256
297
  def validate_inflector(inflector)
257
298
  unless %i[camelize camelize_lower dasherize classify].all? { |m| inflector.respond_to?(m) }
258
299
  raise Alba::Error, "Given inflector, #{inflector.inspect} is not valid. It must implement `camelize`, `camelize_lower`, `dasherize` and `classify`."
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alba
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.3.3
4
+ version: 3.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OKURA Masafumi
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2024-11-09 00:00:00.000000000 Z
10
+ date: 2024-12-01 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: ostruct