alba 3.3.3 → 3.5.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 +4 -4
- data/.github/workflows/main.yml +4 -2
- data/CHANGELOG.md +16 -0
- data/Gemfile +12 -2
- data/README.md +56 -2
- data/benchmark/Gemfile +2 -0
- data/benchmark/README.md +21 -3
- data/benchmark/collection.rb +24 -2
- data/lib/alba/railtie.rb +5 -4
- data/lib/alba/resource.rb +5 -11
- data/lib/alba/version.rb +1 -1
- data/lib/alba.rb +73 -17
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ad9473eff6bbd8fb5f282d2ad7c9d84ad3f104b0a5e190999bd7788fcec2d8be
|
4
|
+
data.tar.gz: f778882ced7677d043694ba257df58bea4fb09fd108804fd4c0a28b7c722cc20
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 484f8725e3a71a7d7714f2d19e0c005c11fd995c33e59431bef48f1dac8336360fef3fdd7657f47984bded1753e366a85b4e8bd7101bd0e418c658a508788ccf
|
7
|
+
data.tar.gz: 0e7c53e58d3e9146b775b06d929b37e9860dba366114db7aca21e53628e78f946dd0ac13c7dc2788b0cc7fb10eb7211b9afc0c7ecf7c4bedd892786a07c04ea2
|
data/.github/workflows/main.yml
CHANGED
@@ -12,9 +12,11 @@ jobs:
|
|
12
12
|
fail-fast: false
|
13
13
|
matrix:
|
14
14
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
15
|
-
ruby: ['3.0', 3.1, 3.2, 3.3, head, jruby, truffleruby]
|
15
|
+
ruby: ['3.0', 3.1, 3.2, 3.3, 3.4, head, jruby, truffleruby]
|
16
16
|
gemfile: [all, without_active_support, without_oj]
|
17
17
|
exclude:
|
18
|
+
- os: windows-latest
|
19
|
+
ruby: 3.4
|
18
20
|
- os: windows-latest
|
19
21
|
ruby: jruby
|
20
22
|
- os: windows-latest
|
@@ -33,7 +35,7 @@ jobs:
|
|
33
35
|
run: |
|
34
36
|
bundle exec rake
|
35
37
|
- name: CodeCov
|
36
|
-
uses: codecov/codecov-action@
|
38
|
+
uses: codecov/codecov-action@v5
|
37
39
|
with:
|
38
40
|
files: ./coverage/coverage.xml
|
39
41
|
token: ${{ secrets.CODECOV_TOKEN }}
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [3.5.0] 2025-01-01
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- `render_serialized_json` now works with `root_key` and `meta` [#398](https://github.com/okuramasafumi/alba/pull/398)
|
14
|
+
|
15
|
+
### Improved
|
16
|
+
|
17
|
+
- Add transform_keys caching [#402](https://github.com/okuramasafumi/alba/pull/402)
|
18
|
+
|
19
|
+
## [3.4.0] 2024-12-01
|
20
|
+
|
21
|
+
### Added
|
22
|
+
|
23
|
+
- `Alba.serialize` now works with heterogeneous collection [#396](https://github.com/okuramasafumi/alba/pull/396)
|
24
|
+
|
9
25
|
## [3.3.3] 2024-11-09
|
10
26
|
|
11
27
|
### 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.
|
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.
|
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
|
@@ -23,6 +23,16 @@ gem 'simplecov-cobertura', require: false # For test coverage
|
|
23
23
|
# gem 'typeprof', require: false # For language server and typing
|
24
24
|
gem 'yard', require: false # For documentation
|
25
25
|
|
26
|
+
# FIXME: There is an upstream JRuby 9.4.9.0 issue with `psych` and the latest
|
27
|
+
# version of `jar-dependencies`. The issue will be resolved with the release of
|
28
|
+
# 9.4.10.0. Then, we can remove this `jar-dependencies` dependency lock.
|
29
|
+
#
|
30
|
+
# For more information, see: https://github.com/jruby/jruby/issues/8488
|
31
|
+
#
|
32
|
+
if defined?(JRUBY_VERSION) && Gem::Version.new(JRUBY_VERSION) < Gem::Version.new('9.4.10.0')
|
33
|
+
gem 'jar-dependencies', '< 0.5' # Fix
|
34
|
+
end
|
35
|
+
|
26
36
|
platforms :ruby do
|
27
37
|
gem 'oj', '~> 3.11', require: false # For backend
|
28
38
|
gem 'ruby-prof', require: false # For performance profiling
|
data/README.md
CHANGED
@@ -686,7 +686,7 @@ Alba.serialize(something)
|
|
686
686
|
# => Same as `FooResource.new(something).serialize` when `something` is an instance of `Foo`.
|
687
687
|
```
|
688
688
|
|
689
|
-
Although this might be useful sometimes, it's generally recommended to define a class for Resource.
|
689
|
+
Although this might be useful sometimes, it's generally recommended to define a class for Resource. Defining a class is often more readable and more maintainable, and inline definitions cannot levarage the benefit of YJIT (it's the slowest with the benchmark YJIT enabled).
|
690
690
|
|
691
691
|
#### Inline definition for multiple root keys
|
692
692
|
|
@@ -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`.
|
@@ -1102,7 +1156,7 @@ end
|
|
1102
1156
|
class UserResource
|
1103
1157
|
include Alba::Resource
|
1104
1158
|
|
1105
|
-
root_key!
|
1159
|
+
root_key! # This is required to add inferred root key, otherwise it has no root key
|
1106
1160
|
|
1107
1161
|
attributes :id
|
1108
1162
|
|
data/benchmark/Gemfile
CHANGED
data/benchmark/README.md
CHANGED
@@ -1,6 +1,24 @@
|
|
1
1
|
## Benchmark for json serializers
|
2
2
|
|
3
|
-
This directory contains a few different benchmark scripts.
|
3
|
+
This directory contains a few different benchmark scripts.
|
4
|
+
|
5
|
+
## How to run
|
6
|
+
|
7
|
+
```
|
8
|
+
bundle install
|
9
|
+
|
10
|
+
# with `Oj.optimize_rails`
|
11
|
+
bundle exec ruby collection.rb
|
12
|
+
|
13
|
+
# without `Oj.optimize_rails`
|
14
|
+
NO_OJ_OPTIMIZE_RAILS=1 bundle exec ruby collection.rb
|
15
|
+
|
16
|
+
# with `Oj.optimize_rails` and YJIT
|
17
|
+
YJIT=1 bundle exec ruby collection.rb
|
18
|
+
|
19
|
+
# with YJIT and without `Oj.optimize_rails`
|
20
|
+
YJIT=1 NO_OJ_OPTIMIZE_RAILS=1 bundle exec ruby collection.rb
|
21
|
+
```
|
4
22
|
|
5
23
|
## Result
|
6
24
|
|
@@ -65,7 +83,7 @@ Comparison:
|
|
65
83
|
ams: 14.2 i/s - 32.28x slower
|
66
84
|
```
|
67
85
|
|
68
|
-
`benchmark-ips` with `Oj.
|
86
|
+
`benchmark-ips` with `Oj.optimize_rails` and YJIT:
|
69
87
|
|
70
88
|
```
|
71
89
|
Comparison:
|
@@ -82,7 +100,7 @@ Comparison:
|
|
82
100
|
ams: 20.4 i/s - 33.10x slower
|
83
101
|
```
|
84
102
|
|
85
|
-
`benchmark-ips` with YJIT and without `Oj.
|
103
|
+
`benchmark-ips` with YJIT and without `Oj.optimize_rails`:
|
86
104
|
|
87
105
|
```
|
88
106
|
Comparison:
|
data/benchmark/collection.rb
CHANGED
@@ -7,6 +7,8 @@ require_relative 'prep'
|
|
7
7
|
|
8
8
|
require "alba"
|
9
9
|
|
10
|
+
Alba.inflector = :active_support
|
11
|
+
|
10
12
|
class AlbaCommentResource
|
11
13
|
include ::Alba::Resource
|
12
14
|
attributes :id, :body
|
@@ -21,6 +23,16 @@ class AlbaPostResource
|
|
21
23
|
many :comments, resource: AlbaCommentResource
|
22
24
|
end
|
23
25
|
|
26
|
+
class AlbaCommentWithTransformationResource < AlbaCommentResource
|
27
|
+
transform_keys :lower_camel
|
28
|
+
end
|
29
|
+
|
30
|
+
class AlbaPostWithTransformationResource < AlbaPostResource
|
31
|
+
many :comments, resource: AlbaCommentWithTransformationResource
|
32
|
+
|
33
|
+
transform_keys :lower_camel
|
34
|
+
end
|
35
|
+
|
24
36
|
# --- ActiveModelSerializer serializers ---
|
25
37
|
|
26
38
|
require "active_model_serializers"
|
@@ -133,7 +145,7 @@ class PostsRepresenter < Representable::Decorator
|
|
133
145
|
property :id
|
134
146
|
property :body
|
135
147
|
property :commenter_names
|
136
|
-
collection :comments
|
148
|
+
collection :comments, decorator: CommentRepresenter
|
137
149
|
end
|
138
150
|
|
139
151
|
def commenter_names
|
@@ -205,6 +217,7 @@ posts = Post.all.includes(:comments, :commenters)
|
|
205
217
|
# --- Store the serializers in procs ---
|
206
218
|
|
207
219
|
alba = Proc.new { AlbaPostResource.new(posts).serialize }
|
220
|
+
alba_with_transformation = Proc.new { AlbaPostWithTransformationResource.new(posts).serialize }
|
208
221
|
alba_inline = Proc.new do
|
209
222
|
Alba.serialize(posts) do
|
210
223
|
attributes :id, :body
|
@@ -222,7 +235,7 @@ fast_serializer = Proc.new { FastSerializerPostResource.new(posts).to_json }
|
|
222
235
|
jserializer = Proc.new { JserializerPostSerializer.new(posts, is_collection: true).to_json }
|
223
236
|
panko = proc { Panko::ArraySerializer.new(posts, each_serializer: PankoPostSerializer).to_json }
|
224
237
|
rails = Proc.new do
|
225
|
-
|
238
|
+
posts.to_json(include: {comments: {only: [:id, :body]}}, methods: [:commenter_names])
|
226
239
|
end
|
227
240
|
representable = Proc.new { PostsRepresenter.new(posts).to_json }
|
228
241
|
simple_ams = Proc.new { SimpleAMS::Renderer::Collection.new(posts, serializer: SimpleAMSPostSerializer).to_json }
|
@@ -254,6 +267,7 @@ end
|
|
254
267
|
|
255
268
|
benchmark_body = lambda do |x|
|
256
269
|
x.report(:alba, &alba)
|
270
|
+
x.report(:alba_with_transformation, &alba_with_transformation)
|
257
271
|
x.report(:alba_inline, &alba_inline)
|
258
272
|
x.report(:ams, &ams)
|
259
273
|
x.report(:blueprinter, &blueprinter)
|
@@ -273,3 +287,11 @@ Benchmark.ips(&benchmark_body)
|
|
273
287
|
|
274
288
|
require 'benchmark/memory'
|
275
289
|
Benchmark.memory(&benchmark_body)
|
290
|
+
|
291
|
+
# --- Show gem versions ---
|
292
|
+
|
293
|
+
puts "Gem versions:"
|
294
|
+
gems = %w[alba active_model_serializers blueprinter fast_serializer jserializer panko_serializer representable simple_ams turbostreamer]
|
295
|
+
Bundler.load.specs.each do |spec|
|
296
|
+
puts "#{spec.name}: #{spec.version}" if gems.include?(spec.name)
|
297
|
+
end
|
data/lib/alba/railtie.rb
CHANGED
@@ -7,13 +7,14 @@ 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, &block|
|
11
|
-
with.nil? ? Alba.resource_with(obj, &block) : with.new(obj)
|
10
|
+
define_method(:serialize) do |obj, with: nil, root_key: nil, meta: {}, &block|
|
11
|
+
resource = with.nil? ? Alba.resource_with(obj, &block) : with.new(obj)
|
12
|
+
resource.to_json(root_key: root_key, meta: meta)
|
12
13
|
end
|
13
14
|
|
14
|
-
define_method(:render_serialized_json) do |obj, with: nil, &block|
|
15
|
+
define_method(:render_serialized_json) do |obj, with: nil, root_key: nil, meta: {}, &block|
|
15
16
|
json = with.nil? ? Alba.resource_with(obj, &block) : with.new(obj)
|
16
|
-
render json: json
|
17
|
+
render json: json.to_json(root_key: root_key, meta: meta)
|
17
18
|
end
|
18
19
|
end
|
19
20
|
end
|
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
|
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
|
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
|
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
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
|
-
|
51
|
-
|
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
|
-
|
63
|
-
|
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
|
@@ -98,6 +117,7 @@ module Alba
|
|
98
117
|
# When it's a Class or a Module, it should have some methods, see {Alba::DefaultInflector}
|
99
118
|
def inflector=(inflector)
|
100
119
|
@inflector = inflector_from(inflector)
|
120
|
+
reset_transform_keys
|
101
121
|
end
|
102
122
|
|
103
123
|
# @param block [Block] resource body
|
@@ -125,11 +145,13 @@ module Alba
|
|
125
145
|
|
126
146
|
# Configure Alba to symbolize keys
|
127
147
|
def symbolize_keys!
|
148
|
+
reset_transform_keys unless @symbolize_keys
|
128
149
|
@symbolize_keys = true
|
129
150
|
end
|
130
151
|
|
131
152
|
# Configure Alba to stringify (not symbolize) keys
|
132
153
|
def stringify_keys!
|
154
|
+
reset_transform_keys if @symbolize_keys
|
133
155
|
@symbolize_keys = false
|
134
156
|
end
|
135
157
|
|
@@ -149,19 +171,22 @@ module Alba
|
|
149
171
|
# @param key [String] a target key
|
150
172
|
# @param transform_type [Symbol] a transform type, either one of `camel`, `lower_camel`, `dash` or `snake`
|
151
173
|
# @return [String]
|
152
|
-
def transform_key(key, transform_type:)
|
174
|
+
def transform_key(key, transform_type:) # rubocop:disable Metrics/MethodLength
|
153
175
|
raise Alba::Error, 'Inflector is nil. You must set inflector before transforming keys.' unless inflector
|
154
176
|
|
155
|
-
key
|
177
|
+
@_transformed_keys[transform_type][key] ||= begin
|
178
|
+
key = key.to_s
|
179
|
+
|
180
|
+
k = case transform_type
|
181
|
+
when :camel then inflector.camelize(key)
|
182
|
+
when :lower_camel then inflector.camelize_lower(key)
|
183
|
+
when :dash then inflector.dasherize(key)
|
184
|
+
when :snake then inflector.underscore(key)
|
185
|
+
else raise Alba::Error, "Unknown transform type: #{transform_type}"
|
186
|
+
end
|
156
187
|
|
157
|
-
|
158
|
-
|
159
|
-
when :lower_camel then inflector.camelize_lower(key)
|
160
|
-
when :dash then inflector.dasherize(key)
|
161
|
-
when :snake then inflector.underscore(key)
|
162
|
-
else raise Alba::Error, "Unknown transform type: #{transform_type}"
|
163
|
-
end
|
164
|
-
regularize_key(k)
|
188
|
+
regularize_key(k)
|
189
|
+
end
|
165
190
|
end
|
166
191
|
|
167
192
|
# Register types, used for both builtin and custom types
|
@@ -189,10 +214,15 @@ module Alba
|
|
189
214
|
@_on_error = :raise
|
190
215
|
@_on_nil = nil
|
191
216
|
@types = {}
|
217
|
+
reset_transform_keys
|
192
218
|
register_default_types
|
193
219
|
end
|
194
220
|
|
195
|
-
#
|
221
|
+
# Get a resource object from arguments
|
222
|
+
# If block is given, it creates a resource class with the block
|
223
|
+
# Otherwise, it infers resource class from the object's class name
|
224
|
+
#
|
225
|
+
# @ param object [Object] the object whose class name is used for inferring resource class
|
196
226
|
def resource_with(object, &block)
|
197
227
|
klass = block ? resource_class(&block) : infer_resource_class(object.class.name)
|
198
228
|
|
@@ -253,6 +283,24 @@ module Alba
|
|
253
283
|
end
|
254
284
|
end
|
255
285
|
|
286
|
+
def hashify_collection(collection, with, &block) # rubocop:disable Metrics/MethodLength
|
287
|
+
collection.map do |obj|
|
288
|
+
resource = if block
|
289
|
+
resource_class(&block)
|
290
|
+
else
|
291
|
+
case with
|
292
|
+
when Class then with
|
293
|
+
when :inference then infer_resource_class(obj.class.name)
|
294
|
+
when Proc then with.call(obj)
|
295
|
+
else raise ArgumentError, '`with` argument must be either :inference, Proc or Class'
|
296
|
+
end
|
297
|
+
end
|
298
|
+
raise Alba::Error if resource.nil?
|
299
|
+
|
300
|
+
resource.new(obj).to_h
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
256
304
|
def validate_inflector(inflector)
|
257
305
|
unless %i[camelize camelize_lower dasherize classify].all? { |m| inflector.respond_to?(m) }
|
258
306
|
raise Alba::Error, "Given inflector, #{inflector.inspect} is not valid. It must implement `camelize`, `camelize_lower`, `dasherize` and `classify`."
|
@@ -261,6 +309,10 @@ module Alba
|
|
261
309
|
inflector
|
262
310
|
end
|
263
311
|
|
312
|
+
def reset_transform_keys
|
313
|
+
@_transformed_keys = Hash.new { |h, k| h[k] = {} }
|
314
|
+
end
|
315
|
+
|
264
316
|
def register_default_types # rubocop:disable Metrics/AbcSize
|
265
317
|
[String, :String].each do |t|
|
266
318
|
register_type(t, check: ->(obj) { obj.is_a?(String) }, converter: lambda(&:to_s))
|
@@ -277,3 +329,7 @@ module Alba
|
|
277
329
|
|
278
330
|
reset!
|
279
331
|
end
|
332
|
+
|
333
|
+
# Monkey patch for TruffleRuby
|
334
|
+
# Delete this after 24.1.2 is released
|
335
|
+
File::SHARE_DELETE = 0 if defined?(Truffle)
|
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.
|
4
|
+
version: 3.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- OKURA Masafumi
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 2025-01-01 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: ostruct
|