alba 3.9.1 → 3.11.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: ce994ee53f2a5f9291096f897469bd131c5f925560a0cd13690247d1278b9310
4
- data.tar.gz: cbcc9e5e031fc52c20560fa0d8f06465728e5825a08a36d50d6d26f5f1ca4a3a
3
+ metadata.gz: 18b9be1d51ec5b7bed2d31fc59904a57c8dc7f4e24eb1a7cb6364a42ec8a27ec
4
+ data.tar.gz: 4112d0ace0fda10a9e6aee754e2b056b6488924fddbd5f12040652a2fcbbda75
5
5
  SHA512:
6
- metadata.gz: 1ac432a009738c6e6d69e3d9db07895ca67653a33f7c226194a1851978076dbde05de1da6c767e5b582f5e6e0cded2347fb2bcda858a147d4911cc7b55b06617
7
- data.tar.gz: f317a32584cc90e2b49043e4e0c5610351ba710866cd29336a43e151fb0b42061d82e8e39837ed900c6994f7c8d65214cc31845e315a84cb0fd10904c8b09b89
6
+ metadata.gz: 7bee8581bad84469254b1282b3c81bdaf493be4f2c5ee1b31747b16f2908f4e2639d783ac9fa7e6f345b361117512f0ce4b208a81a2f000e05455d3bc875b08b
7
+ data.tar.gz: 6939052cd6b8ea09f23df209874c858aadc032193a0ab5729bd04d33bbf7d24e3223cc642494a0095ca4a8f7a4b182fdd4cbd4e9781c1d4689e205953c2ca2e7
data/CHANGELOG.md CHANGED
@@ -6,6 +6,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## 3.11.0 2026-07-25
10
+
11
+ ### Added
12
+
13
+ - Rails: `serialize` and `render_serialized_json` now takes `params` [#509](https://github.com/okuramasafumi/alba/pull/509)
14
+ - Add `Alba.non_collection_types` for declaring Enumerable classes that should not be treated as collections [#532](https://github.com/okuramasafumi/alba/pull/532)
15
+ - Add default superclass configuration [#547](https://github.com/okuramasafumi/alba/pull/547)
16
+
17
+ ### Fixed
18
+
19
+ - Trait redefining base-level attribute causes wrong result with multiple traits [#498](https://github.com/okuramasafumi/alba/issues/498) and [#502](https://github.com/okuramasafumi/alba/pull/502)
20
+ - Thank you for reporting this issue, @yukihiro-numata
21
+ - Pass params to resource in serialize when with is given [#524](https://github.com/okuramasafumi/alba/pull/524)
22
+ - Thank you, @JohnnyKei
23
+ - `Alba.serialize(single, with: SomeResource)` crash
24
+ - helpers are now available in NestedAttribute [#503](https://github.com/okuramasafumi/alba/pull/503)
25
+
26
+ ### Changed
27
+
28
+ - More strict types
29
+
30
+ ## 3.10.0 2025-11-11
31
+
32
+ ### Added
33
+
34
+ - Support type validation and coercion for a single attribute method [#470](https://github.com/okuramasafumi/alba/pull/470)
35
+ - Thank you [@denblackstache](https://github.com/denblackstache) for requesting the feature
36
+ - Fix hash object resource methods [#475](https://github.com/okuramasafumi/alba/pull/475)
37
+ - Thank you, [@jkmcrg](https://github.com/jkmcrg)
38
+
9
39
  ## 3.9.1 2025-09-27
10
40
 
11
41
  ### Added
@@ -189,7 +219,7 @@ This change is supposed to be released as 3.8.0 with the change in 3.7.4, but I
189
219
 
190
220
  ## [2.4.1] 2023-08-02
191
221
 
192
- #### Fixed
222
+ ### Fixed
193
223
 
194
224
  - Fix the bug of resource name inference for classes whose name end with "Serializer" [No PR](https://github.com/okuramasafumi/alba/commit/1695af4351981725231fd071aaef5b2e4174fb26)
195
225
 
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/alba.svg)](https://badge.fury.io/rb/alba)
4
4
  [![CI](https://github.com/okuramasafumi/alba/actions/workflows/main.yml/badge.svg)](https://github.com/okuramasafumi/alba/actions/workflows/main.yml)
5
5
  [![codecov](https://codecov.io/gh/okuramasafumi/alba/branch/main/graph/badge.svg?token=3D3HEZ5OXT)](https://codecov.io/gh/okuramasafumi/alba)
6
- [![Maintainability](https://qlty.sh/gh/okuramasafumi/projects/alba/maintainability.svg)](https://qlty.sh/gh/okuramasafumi/projects/alba)
6
+ [![Codacy Badge](https://app.codacy.com/project/badge/Grade/acf6a96689c349ecbb806db07fa6948a)](https://app.codacy.com/gh/okuramasafumi/alba/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
7
7
  ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/okuramasafumi/alba)
8
8
  ![GitHub](https://img.shields.io/github/license/okuramasafumi/alba)
9
9
  [![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md)
@@ -56,6 +56,12 @@ UserResource.new(user).serialize
56
56
 
57
57
  Seems useful? Continue reading!
58
58
 
59
+ Pro tip: You can find more examples in separate Ruby files under "examples" directory.
60
+
61
+ ### Coming from ActiveModelSerializers(AMS)?
62
+
63
+ If you are using ActiveModelSerializers and interested in Alba, we have an automatic converter! [alba_migration](https://github.com/ShoheiMitani/alba_migration) gem from [ShoheiMitani](https://github.com/ShoheiMitani) "provides a command-line tool to convert AMS serializer classes to Alba resource classes, making your migration process easier and less error-prone."
64
+
59
65
  ## Discussions
60
66
 
61
67
  Alba uses [GitHub Discussions](https://github.com/okuramasafumi/alba/discussions) to openly discuss the project.
@@ -100,6 +106,7 @@ While Alba's core is simple, it provides additional features when you need them.
100
106
  - Well tested, the test coverage is 99%
101
107
  - Well maintained, getting frequent update and new releases (see [version history](https://rubygems.org/gems/alba/versions))
102
108
 
109
+
103
110
  ## Comparison with other serializers
104
111
 
105
112
  Alba aims to provide a well-balanced combination of simplicity, performance, and features. Here's how it compares to other popular Ruby JSON serializers:
@@ -138,7 +145,7 @@ Or install it yourself as:
138
145
 
139
146
  ## Supported Ruby versions
140
147
 
141
- Alba supports CRuby 3.0 and higher and latest JRuby and TruffleRuby.
148
+ Alba works with CRuby 3.0 and higher and latest JRuby and TruffleRuby. However, Ruby 3.0 is not actively tested, so use it at your own risk.
142
149
 
143
150
  ## Documentation
144
151
 
@@ -228,6 +235,20 @@ else
228
235
  end
229
236
  ```
230
237
 
238
+ #### Default superclass configuration
239
+
240
+ You can set the default superclass for resource classes created for Nested Attributes and Traits.
241
+
242
+ ```ruby
243
+ Alba.default_superclass = MyBaseSerializer
244
+ ```
245
+
246
+ Note that in some environments like Rails applications, the class object is not loaded in the initializer phase. You can also set `String`.
247
+
248
+ ```ruby
249
+ Alba.default_superclass = 'MyBaseSerializer'
250
+ ```
251
+
231
252
  ### Naming
232
253
 
233
254
  Alba tries to infer resource name from class name like the following.
@@ -349,7 +370,7 @@ end
349
370
  class FooResource
350
371
  include Alba::Resource
351
372
 
352
- attributes :bar
373
+ attributes :format
353
374
 
354
375
  # Here, `format` method is available
355
376
  end
@@ -1921,16 +1942,18 @@ Don't forget calling `super` in this way.
1921
1942
 
1922
1943
  ### Treating specific classes as non-collection
1923
1944
 
1924
- Sometimes we need to serialize an object that's `Enumerable` but not a collection. By default, Alba treats `Hash`, `Range` and `Struct` as non-collection object, but if we want to add some classes to this list, we can override `Alba.collection?` method like following:
1945
+ Sometimes we need to serialize an object that's `Enumerable` but not a collection. By default, Alba treats `Hash`, `Range` and `Struct` as non-collection objects. You can add additional classes to this list via `Alba.non_collection_types`:
1925
1946
 
1926
1947
  ```ruby
1927
- Alba.singleton_class.prepend(
1928
- Module.new do
1929
- def collection?(object)
1930
- super && !object.is_a?(SomeClass)
1931
- end
1932
- end
1933
- )
1948
+ Alba.non_collection_types << Stripe::StripeObject
1949
+ Alba.non_collection_types << MyEnumerableEntity
1950
+ ```
1951
+
1952
+ You can inspect the current list of excluded types (including the defaults) at any time:
1953
+
1954
+ ```ruby
1955
+ Alba.non_collection_types
1956
+ # => [Struct, Range, Hash, Stripe::StripeObject, MyEnumerableEntity]
1934
1957
  ```
1935
1958
 
1936
1959
  ### Adding indexes to `many` association
@@ -101,7 +101,7 @@ module Alba
101
101
 
102
102
  def assign_resource(nesting, key_transformation, block, helper)
103
103
  @resource = if block
104
- charged_resource_class(helper, key_transformation, block)
104
+ Alba.resource_class(helper: helper, key_transformation: key_transformation, &block)
105
105
  elsif Alba.inflector
106
106
  Alba.infer_resource_class(@name, nesting: nesting)
107
107
  else
@@ -109,14 +109,6 @@ module Alba
109
109
  end
110
110
  end
111
111
 
112
- def charged_resource_class(helper, key_transformation, block)
113
- klass = Alba.resource_class
114
- klass.helper(helper) if helper
115
- klass.transform_keys(key_transformation)
116
- klass.class_eval(&block)
117
- klass
118
- end
119
-
120
112
  def to_h_with_each_resource(object, within, params)
121
113
  object.map do |item|
122
114
  @resource.call(item).new(item, within: within, params: params, with_traits: @with_traits).to_h
@@ -7,9 +7,11 @@ module Alba
7
7
  # Setter for key_transformation, used when it's changed after class definition
8
8
  attr_writer :key_transformation
9
9
 
10
+ # @param klass [Class<Alba::Resource>] the parent for this nested attribute
10
11
  # @param key_transformation [Symbol] determines how to transform keys
11
12
  # @param block [Proc] class body
12
- def initialize(key_transformation: :none, &block)
13
+ def initialize(klass:, key_transformation: :none, &block)
14
+ @klass = klass
13
15
  @key_transformation = key_transformation
14
16
  @block = block
15
17
  end
@@ -17,13 +19,13 @@ module Alba
17
19
  # @param object [Object] the object being serialized
18
20
  # @param params [Hash] params Hash inherited from Resource
19
21
  # @param within [Object, nil, false, true] determines what associations to be serialized. If not set, it serializes all associations.
20
- # @param select [Method] select method object from its origin
21
22
  # @return [Hash] hash serialized from running the class body in the object
22
- def value(object:, params:, within:, select: nil)
23
- resource_class = Alba.resource_class
23
+ def value(object:, params:, within:)
24
+ resource_class = Class.new(@klass)
25
+ resource_class.instance_variable_set(:@_attributes, {}) # reset
24
26
  resource_class.transform_keys(@key_transformation)
25
27
  resource_class.class_eval(&@block)
26
- resource_class.new(object, params: params, within: within, select: select).serializable_hash
28
+ resource_class.new(object, params: params, within: within).serializable_hash
27
29
  end
28
30
  end
29
31
  end
data/lib/alba/railtie.rb CHANGED
@@ -7,13 +7,13 @@ module Alba
7
7
  Alba.inflector = :active_support
8
8
 
9
9
  ActiveSupport.on_load(:action_controller) do
10
- define_method(:serialize) do |obj, with: nil, root_key: nil, meta: {}, &block|
11
- resource = with.nil? ? Alba.resource_for(obj, &block) : with.new(obj)
10
+ define_method(:serialize) do |obj, params: {}, with: nil, root_key: nil, meta: {}, &block|
11
+ resource = with.nil? ? Alba.resource_for(obj, params: params, &block) : with.new(obj, params: params)
12
12
  resource.to_json(root_key: root_key, meta: meta)
13
13
  end
14
14
 
15
- define_method(:render_serialized_json) do |obj, with: nil, root_key: nil, meta: {}, &block|
16
- json = with.nil? ? Alba.resource_for(obj, &block) : with.new(obj)
15
+ define_method(:render_serialized_json) do |obj, params: {}, with: nil, root_key: nil, meta: {}, &block|
16
+ json = with.nil? ? Alba.resource_for(obj, params: params, &block) : with.new(obj, params: params)
17
17
  render json: json.to_json(root_key: root_key, meta: meta)
18
18
  end
19
19
  end
data/lib/alba/resource.rb CHANGED
@@ -20,6 +20,9 @@ module Alba
20
20
  WITHIN_DEFAULT = Object.new.freeze
21
21
  private_constant :WITHIN_DEFAULT
22
22
 
23
+ EMPTY_HASH = {}.freeze
24
+ private_constant :EMPTY_HASH
25
+
23
26
  # `setup` method is meta-programmatically defined here for performance.
24
27
  # @api private
25
28
  def self.included(base) # rubocop:disable Metrics/MethodLength
@@ -48,15 +51,13 @@ module Alba
48
51
  # @param within [Alba::WITHIN_DEFAULT, Hash, Array, nil, false, true]
49
52
  # determines what associations to be serialized. If not set, it serializes all associations.
50
53
  # @param with_traits [Symbol, Array<Symbol>, nil] specified traits
51
- # @param select [Method] select method object used with `nested_attribute` and `trait`
52
- def initialize(object, params: {}, within: WITHIN_DEFAULT, with_traits: nil, select: nil)
54
+ # @param select [Method] DEPRECATED noop
55
+ def initialize(object, params: EMPTY_HASH, within: WITHIN_DEFAULT, with_traits: nil, select: nil)
53
56
  @object = object
54
57
  @params = params
55
58
  @within = within
56
59
  @with_traits = with_traits
57
- # select override to share the same method with `trait` and `nested_attribute`
58
- # Trait and NestedAttribute generates anonymous class so it checks if it's anonymous class to prevent accidental overriding
59
- self.class.define_method(:select, &select) if select && self.class.name.nil?
60
+ warn '`select` keyword for Alba::Resource is deprecated', category: :deprecated, uplevel: 1 if select
60
61
  _setup
61
62
  end
62
63
 
@@ -65,7 +66,7 @@ module Alba
65
66
  # @param root_key [Symbol, nil]
66
67
  # @param meta [Hash] metadata for this serialization
67
68
  # @return [String] serialized JSON string
68
- def serialize(root_key: nil, meta: {})
69
+ def serialize(root_key: nil, meta: EMPTY_HASH)
69
70
  serialize_with(as_json(root_key: root_key, meta: meta))
70
71
  end
71
72
 
@@ -74,7 +75,7 @@ module Alba
74
75
  #
75
76
  # @see #serialize
76
77
  # @see https://github.com/rails/rails/blob/7-0-stable/actionpack/lib/action_controller/metal/renderers.rb#L156
77
- def to_json(options = {}, root_key: nil, meta: {})
78
+ def to_json(options = EMPTY_HASH, root_key: nil, meta: EMPTY_HASH)
78
79
  confusing_keys = [:only, :except]
79
80
  confusing_options = options.keys.select { |k| confusing_keys.include?(k.to_sym) }
80
81
  unless confusing_options.empty?
@@ -92,7 +93,7 @@ module Alba
92
93
  # @param root_key [Symbol, nil]
93
94
  # @param meta [Hash] metadata for this serialization
94
95
  # @return [Hash]
95
- def as_json(_options = {}, root_key: nil, meta: {})
96
+ def as_json(_options = EMPTY_HASH, root_key: nil, meta: EMPTY_HASH)
96
97
  key = root_key.nil? ? fetch_key : root_key
97
98
  key = Alba.regularize_key(key)
98
99
  if key && !key.empty?
@@ -119,9 +120,10 @@ module Alba
119
120
  Array(@with_traits).each_with_object({}) do |trait, hash|
120
121
  body = @_traits.fetch(trait) { raise Alba::Error, "Trait not found: #{trait}" }
121
122
  resource_class = Class.new(self.class)
123
+ resource_class.instance_variable_set(:@_attributes, {})
122
124
  resource_class.class_eval(&body)
123
125
  resource_class.transform_keys(@_transform_type) unless @_transform_type == :none
124
- hash.merge!(resource_class.new(obj, params: params, within: @within, select: method(:select)).serializable_hash)
126
+ hash.merge!(resource_class.new(obj, params: params, within: @within).serializable_hash)
125
127
  end
126
128
  end
127
129
 
@@ -290,8 +292,8 @@ module Alba
290
292
  when Symbol then fetch_attribute_from_object_and_resource(obj, attribute)
291
293
  when Proc then instance_exec(obj, &attribute)
292
294
  when Alba::Association then yield_if_within(attribute.name.to_sym) { |within| attribute.to_h(obj, params: params, within: within) }
293
- when TypedAttribute then attribute.value { |attr| fetch_attribute(obj, key, attr) }
294
- when NestedAttribute then attribute.value(object: obj, params: params, within: @within, select: method(:select))
295
+ when TypedAttribute then attribute.value(object: obj) { |attr| fetch_attribute(obj, key, attr) }
296
+ when NestedAttribute then attribute.value(object: obj, params: params, within: @within)
295
297
  when ConditionalAttribute then attribute.with_passing_condition(resource: self, object: obj) { |attr| fetch_attribute(obj, key, attr) }
296
298
  # :nocov:
297
299
  else raise ::Alba::Error, "Unsupported type of attribute: #{attribute.class}"
@@ -308,7 +310,7 @@ module Alba
308
310
  return obj.fetch(attribute) if obj.is_a?(Hash)
309
311
 
310
312
  obj.__send__(attribute)
311
- rescue NoMethodError
313
+ rescue NoMethodError, KeyError
312
314
  __send__(attribute, obj)
313
315
  end
314
316
 
@@ -320,6 +322,8 @@ module Alba
320
322
  else
321
323
  obj.__send__(attribute)
322
324
  end
325
+ rescue KeyError
326
+ __send__(attribute, obj)
323
327
  end
324
328
 
325
329
  def nil_handler
@@ -399,11 +403,11 @@ module Alba
399
403
  end
400
404
  private :assign_attributes
401
405
 
402
- def assign_attributes_with_types(attrs_with_types, if_value)
406
+ def assign_attributes_with_types(attrs_with_types, if_value, &block)
403
407
  attrs_with_types.each do |attr_name, type_and_converter|
404
408
  attr_name = attr_name.to_sym
405
409
  type, type_converter = type_and_converter
406
- typed_attr = TypedAttribute.new(name: attr_name, type: type, converter: type_converter)
410
+ typed_attr = TypedAttribute.new(name: attr_name, type: type, converter: type_converter, &block)
407
411
  attr = if_value ? ConditionalAttribute.new(body: typed_attr, condition: if_value) : typed_attr
408
412
  @_attributes[attr_name] = attr
409
413
  end
@@ -413,15 +417,21 @@ module Alba
413
417
  # Set an attribute with the given block
414
418
  #
415
419
  # @param name [String, Symbol] key name
416
- # @param options [Hash<Symbol, Proc>]
417
- # @option options [Proc] if a condition to decide if this attribute should be serialized
420
+ # @param if [Proc] condition to decide if it should serialize these attributes
418
421
  # @param block [Block] the block called during serialization
419
422
  # @raise [ArgumentError] if block is absent
420
423
  # @return [void]
421
- def attribute(name, **options, &block)
424
+ def attribute(name = nil, if: nil, **name_with_type, &block)
425
+ if_value = binding.local_variable_get(:if)
422
426
  raise ArgumentError, 'No block given in attribute method' unless block
427
+ raise ArgumentError, 'You must specify either name or name with type' if name.nil? && name_with_type.empty?
423
428
 
424
- @_attributes[name.to_sym] = options[:if] ? ConditionalAttribute.new(body: block, condition: options[:if]) : block
429
+ if name.nil?
430
+ assign_attributes_with_types(name_with_type, if_value, &block)
431
+ else # Symbol
432
+ attr = if_value ? ConditionalAttribute.new(body: block, condition: if_value) : block
433
+ @_attributes[name.to_sym] = attr
434
+ end
425
435
  end
426
436
 
427
437
  # Set association
@@ -441,7 +451,7 @@ module Alba
441
451
  # @param block [Block]
442
452
  # @return [void]
443
453
  # @see Alba::Association#initialize
444
- def association(name, condition = nil, resource: nil, serializer: nil, source: nil, key: nil, with_traits: nil, params: {}, **options, &block)
454
+ def association(name, condition = nil, resource: nil, serializer: nil, source: nil, key: nil, with_traits: nil, params: EMPTY_HASH, **options, &block)
445
455
  resource ||= serializer
446
456
  transformation = @_key_transformation_cascade ? @_transform_type : :none
447
457
  assoc = Association.new(
@@ -477,7 +487,7 @@ module Alba
477
487
  raise ArgumentError, 'No block given in attribute method' unless block
478
488
 
479
489
  key_transformation = @_key_transformation_cascade ? @_transform_type : :none
480
- attribute = NestedAttribute.new(key_transformation: key_transformation, &block)
490
+ attribute = NestedAttribute.new(klass: self, key_transformation: key_transformation, &block)
481
491
  @_attributes[name.to_sym] = options[:if] ? ConditionalAttribute.new(body: attribute, condition: options[:if]) : attribute
482
492
  end
483
493
  alias nested nested_attribute
@@ -622,7 +632,8 @@ module Alba
622
632
  # @return [void]
623
633
  def helper(mod = @_helper || Module.new, &block)
624
634
  mod.module_eval(&block) if block
625
- extend mod
635
+ include(mod)
636
+ extend(mod)
626
637
 
627
638
  @_helper = mod
628
639
  end
@@ -9,7 +9,7 @@ module Alba
9
9
  # @param name [Symbol, String]
10
10
  # @param type [Symbol, Class]
11
11
  # @param converter [Proc, true, false, nil]
12
- def initialize(name:, type:, converter:)
12
+ def initialize(name:, type:, converter:, &block)
13
13
  @name = name
14
14
  t = Alba.find_type(type)
15
15
  @type = case converter
@@ -18,11 +18,12 @@ module Alba
18
18
  else
19
19
  t.dup.tap { _1.auto_convert_with(converter) }
20
20
  end
21
+ @block = block
21
22
  end
22
23
 
23
24
  # @return [String, Integer, Boolean] type-checked or type-converted object
24
- def value
25
- v = yield(@name)
25
+ def value(object: nil)
26
+ v = @block ? @block.call(object) : yield(@name)
26
27
  result = @type.check(v)
27
28
  result ? v : @type.convert(v)
28
29
  rescue TypeError
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.9.1'
4
+ VERSION = '3.11.0'
5
5
  end
data/lib/alba.rb CHANGED
@@ -16,6 +16,16 @@ module Alba
16
16
  # Getter for inflector, a module responsible for inflecting strings
17
17
  attr_reader :inflector
18
18
 
19
+ # @return [Array<Class>] classes that include Enumerable but should not be treated as collections
20
+ attr_reader :non_collection_types
21
+
22
+ # Set the default superclass for resource classes created with {.resource_class}
23
+ #
24
+ # @param value [Class, String, Symbol] the default superclass
25
+ # @example
26
+ # Alba.default_superclass = '::MyApp::BaseResource'
27
+ attr_writer :default_superclass
28
+
19
29
  # Set the backend, which actually serializes object into JSON
20
30
  #
21
31
  # @param backend [#to_sym, nil] the name of the backend
@@ -54,7 +64,7 @@ module Alba
54
64
  h = hashify_collection(object, with, root_key, &block)
55
65
  Alba.encoder.call(h)
56
66
  else
57
- resource = resource_for(object, &block)
67
+ resource = resource_for(object, with: with, &block)
58
68
  resource.serialize(root_key: root_key)
59
69
  end
60
70
  end
@@ -73,17 +83,18 @@ module Alba
73
83
  if collection?(object)
74
84
  hashify_collection(object, with, root_key, &block)
75
85
  else
76
- resource = resource_for(object, &block)
86
+ resource = resource_for(object, with: with, &block)
77
87
  resource.as_json(root_key: root_key)
78
88
  end
79
89
  end
80
90
 
81
91
  # Detect if object is a collection or not.
82
- # When object is a Struct or a Range, it's Enumerable but not a collection
92
+ # Types in {.non_collection_types} (default: Struct, Range, Hash) are
93
+ # considered non-collection even if they include Enumerable.
83
94
  #
84
95
  # @api private
85
96
  def collection?(object)
86
- object.is_a?(Enumerable) && !object.is_a?(Struct) && !object.is_a?(Range) && !object.is_a?(Hash)
97
+ object.is_a?(Enumerable) && @non_collection_types.none? { |type| object.is_a?(type) }
87
98
  end
88
99
 
89
100
  # Enable inference for key and resource name
@@ -124,11 +135,15 @@ module Alba
124
135
  reset_transform_keys
125
136
  end
126
137
 
138
+ # @param helper [Module] helper module to include
139
+ # @param key_transformation [Symbol] key transformation type
127
140
  # @param block [Block] resource body
128
141
  # @return [Class<Alba::Resource>] resource class
129
- def resource_class(&block)
130
- klass = Class.new
142
+ def resource_class(helper: nil, key_transformation: :none, &block)
143
+ klass = Class.new(resolved_default_superclass)
131
144
  klass.include(Alba::Resource)
145
+ klass.helper(helper) if helper
146
+ klass.transform_keys(key_transformation)
132
147
  klass.class_eval(&block) if block
133
148
  klass
134
149
  end
@@ -219,6 +234,8 @@ module Alba
219
234
  @_on_error = :raise
220
235
  @_on_nil = nil
221
236
  @types = {}
237
+ @non_collection_types = [Struct, Range, Hash]
238
+ @default_superclass = ::Object
222
239
  reset_transform_keys
223
240
  register_default_types
224
241
  end
@@ -234,6 +251,7 @@ module Alba
234
251
  # Otherwise, it behaves depending on `with` argument
235
252
  #
236
253
  # @param object [Object] the object whose class name is used for inferring resource class
254
+ # @param params [Hash] user-given Hash for arbitrary data
237
255
  # @param with [:inference, Proc, Class<Alba::Resource>] determines how to get resource class for `object`
238
256
  # When it's `:inference`, it infers resource class from `object`'s class name
239
257
  # When it's a Proc, it calls the Proc with `object` as an argument
@@ -241,13 +259,13 @@ module Alba
241
259
  # Otherwise, it raises an ArgumentError
242
260
  # @return [Alba::Resource] resource class with `object` as its target object
243
261
  # @raise [ArgumentError] if `with` argument is not one of `:inference`, Proc or Class
244
- def resource_for(object, with: :inference, &block)
245
- _resource_for(object, with: with, &block)
262
+ def resource_for(object, params: {}, with: :inference, &block)
263
+ _resource_for(object, params: params, with: with, &block)
246
264
  end
247
265
 
248
266
  private
249
267
 
250
- def _resource_for(object, with: :inference, &block) # rubocop:disable Metrics/MethodLength
268
+ def _resource_for(object, params: {}, with: :inference, &block) # rubocop:disable Metrics/MethodLength
251
269
  klass = if block
252
270
  resource_class(&block)
253
271
  else
@@ -259,7 +277,7 @@ module Alba
259
277
  end
260
278
  end
261
279
 
262
- klass.new(object)
280
+ klass.new(object, params: params)
263
281
  end
264
282
 
265
283
  def inflector_from(name_or_module)
@@ -336,6 +354,10 @@ module Alba
336
354
  @_transformed_keys = Hash.new { |h, k| h[k] = {} }
337
355
  end
338
356
 
357
+ def resolved_default_superclass
358
+ @default_superclass.is_a?(Class) ? @default_superclass : Object.const_get(@default_superclass.to_s)
359
+ end
360
+
339
361
  def register_default_types # rubocop:disable Metrics/AbcSize
340
362
  [String, :String].each do |t|
341
363
  register_type(t, check: ->(obj) { obj.is_a?(String) }, converter: lambda(&:to_s))
@@ -345,7 +367,7 @@ module Alba
345
367
  end
346
368
  register_type(:Boolean, check: ->(obj) { [true, false].include?(obj) }, converter: ->(obj) { !!obj })
347
369
  [String, Integer].each do |t|
348
- register_type(:"ArrayOf#{t}", check: ->(d) { d.is_a?(Array) && d.all? { _1.is_a?(t) } })
370
+ register_type(:"ArrayOf#{t}", check: ->(d) { d.is_a?(Array) && d.all?(t) })
349
371
  end
350
372
  end
351
373
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: alba
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.9.1
4
+ version: 3.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OKURA Masafumi
@@ -9,8 +9,8 @@ bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies: []
12
- description: Alba is the fastest JSON serializer for Ruby. It focuses on performance,
13
- flexibility and usability.
12
+ description: Alba is a JSON serializer for Ruby, JRuby and TruffleRuby. It focuses
13
+ on performance, flexibility and usability.
14
14
  email:
15
15
  - masafumi.o1988@gmail.com
16
16
  executables: []
@@ -59,5 +59,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
59
59
  requirements: []
60
60
  rubygems_version: 3.6.7
61
61
  specification_version: 4
62
- summary: Alba is the fastest JSON serializer for Ruby.
62
+ summary: Alba is a JSON serializer for Ruby, JRuby and TruffleRuby.
63
63
  test_files: []