alba 2.4.2 → 3.0.1
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/codeql-analysis.yml +1 -1
- data/.github/workflows/lint.yml +17 -0
- data/.github/workflows/main.yml +2 -2
- data/.github/workflows/perf.yml +2 -2
- data/.rubocop.yml +16 -2
- data/CHANGELOG.md +28 -2
- data/README.md +53 -14
- data/alba.gemspec +1 -1
- data/lib/alba/association.rb +5 -3
- data/lib/alba/conditional_attribute.rb +4 -0
- data/lib/alba/resource.rb +34 -31
- data/lib/alba/type.rb +43 -0
- data/lib/alba/typed_attribute.rb +11 -38
- data/lib/alba/version.rb +1 -1
- data/lib/alba.rb +30 -3
- metadata +6 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a659a06525b2938d8115b64712344d9dc173ad86a3c6c2b392c283eccd5048fd
|
|
4
|
+
data.tar.gz: 9a9fa379c582928259580396277472438ce1afe566088214b08cccddd5836bae
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6985bbc1306bc15d7173cab450df98aa8382dcaf9384975558b0a590e2f056c1d3fa16e53684033baabbeabc202fce975487afa34170691f174eafcca43043f7
|
|
7
|
+
data.tar.gz: 16eab414db7324830348eff012f269a670879138110c0595341101649acc7cf6432a9d916d44b65b885d91923d1387e6a19ca4094654f82ef60b152d94778d1d
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: Lint
|
|
2
|
+
|
|
3
|
+
on: [push,pull_request]
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
build:
|
|
7
|
+
runs-on: ubuntu-latest
|
|
8
|
+
steps:
|
|
9
|
+
- uses: actions/checkout@v4
|
|
10
|
+
- name: Set up Ruby
|
|
11
|
+
uses: ruby/setup-ruby@v1
|
|
12
|
+
with:
|
|
13
|
+
ruby-version: 3.0 # The lowest version Alba supports
|
|
14
|
+
bundler-cache: true
|
|
15
|
+
- name: Run RuboCop
|
|
16
|
+
run: |
|
|
17
|
+
bundle exec rubocop
|
data/.github/workflows/main.yml
CHANGED
|
@@ -8,7 +8,7 @@ jobs:
|
|
|
8
8
|
fail-fast: false
|
|
9
9
|
matrix:
|
|
10
10
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
|
11
|
-
ruby: [
|
|
11
|
+
ruby: ['3.0', 3.1, 3.2, head, jruby, truffleruby]
|
|
12
12
|
gemfile: [all, without_active_support, without_oj]
|
|
13
13
|
exclude:
|
|
14
14
|
- os: windows-latest
|
|
@@ -19,7 +19,7 @@ jobs:
|
|
|
19
19
|
env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
|
|
20
20
|
BUNDLE_GEMFILE: ${{ (matrix.gemfile == 'without_active_support' && 'gemfiles/without_active_support.gemfile') || (matrix.gemfile == 'without_oj' && 'gemfiles/without_oj.gemfile') || null }}
|
|
21
21
|
steps:
|
|
22
|
-
- uses: actions/checkout@
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
23
|
- name: Set up Ruby
|
|
24
24
|
uses: ruby/setup-ruby@v1
|
|
25
25
|
with:
|
data/.github/workflows/perf.yml
CHANGED
|
@@ -7,10 +7,10 @@ jobs:
|
|
|
7
7
|
strategy:
|
|
8
8
|
fail-fast: false
|
|
9
9
|
matrix:
|
|
10
|
-
ruby: [
|
|
10
|
+
ruby: ['3.0', 3.1, 3.2]
|
|
11
11
|
runs-on: ubuntu-latest
|
|
12
12
|
steps:
|
|
13
|
-
- uses: actions/checkout@
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
14
|
- name: Set up Ruby
|
|
15
15
|
uses: ruby/setup-ruby@v1
|
|
16
16
|
with:
|
data/.rubocop.yml
CHANGED
|
@@ -22,7 +22,7 @@ AllCops:
|
|
|
22
22
|
- 'script/**/*.rb'
|
|
23
23
|
NewCops: enable
|
|
24
24
|
EnabledByDefault: true
|
|
25
|
-
TargetRubyVersion:
|
|
25
|
+
TargetRubyVersion: 3.0
|
|
26
26
|
|
|
27
27
|
# Items in Gemfile is dev dependencies and we don't have to specify versions.
|
|
28
28
|
Bundler/GemVersion:
|
|
@@ -62,6 +62,12 @@ Metrics:
|
|
|
62
62
|
|
|
63
63
|
# `Resource` module is a core module and its length tends to be long...
|
|
64
64
|
# `Alba` main module is also long because it has all parts of configuration
|
|
65
|
+
Metrics/ClassLength:
|
|
66
|
+
Exclude:
|
|
67
|
+
- 'lib/alba/resource.rb'
|
|
68
|
+
- 'lib/alba.rb'
|
|
69
|
+
- 'test/**/*.rb' # Neec to specify this
|
|
70
|
+
|
|
65
71
|
Metrics/ModuleLength:
|
|
66
72
|
Exclude:
|
|
67
73
|
- 'lib/alba/resource.rb'
|
|
@@ -93,6 +99,7 @@ Style/ConstantVisibility:
|
|
|
93
99
|
- 'lib/alba/version.rb'
|
|
94
100
|
- 'test/**/*.rb'
|
|
95
101
|
|
|
102
|
+
# Copyright is in README
|
|
96
103
|
Style/Copyright:
|
|
97
104
|
Enabled: false
|
|
98
105
|
|
|
@@ -100,34 +107,41 @@ Style/Copyright:
|
|
|
100
107
|
Style/DisableCopsWithinSourceCodeDirective:
|
|
101
108
|
Enabled: false
|
|
102
109
|
|
|
110
|
+
# Test files doesn't need to have documentation
|
|
103
111
|
Style/Documentation:
|
|
104
112
|
Exclude:
|
|
105
113
|
- 'test/**/*'
|
|
106
114
|
|
|
115
|
+
# In README it's so obvious
|
|
107
116
|
Style/DocumentationMethod:
|
|
108
117
|
Exclude:
|
|
109
118
|
- 'README.md'
|
|
110
119
|
|
|
120
|
+
# This might be true in the future, but not many good things
|
|
111
121
|
Style/FrozenStringLiteralComment:
|
|
112
122
|
Enabled: false
|
|
113
123
|
|
|
124
|
+
# I don't want to think about error class in example code
|
|
114
125
|
Style/ImplicitRuntimeError:
|
|
115
126
|
Exclude:
|
|
116
127
|
- 'README.md'
|
|
117
128
|
|
|
129
|
+
# We use it, don't we?
|
|
118
130
|
Style/InlineComment:
|
|
119
131
|
Enabled: false
|
|
120
132
|
|
|
121
133
|
Style/MethodCallWithArgsParentheses:
|
|
122
134
|
AllowedMethods: ['require', 'require_relative', 'include', 'extend', 'puts', 'p', 'warn', 'raise', 'send', 'public_send', 'alias_method']
|
|
123
135
|
Exclude:
|
|
124
|
-
# There are so many `attributes`
|
|
136
|
+
# There are so many calls like `attributes` and `register_type` without parenthese and that's absolutely fine
|
|
125
137
|
- 'test/**/*.rb'
|
|
138
|
+
- 'README.md'
|
|
126
139
|
|
|
127
140
|
# There are so many cases we just want `if` expression!
|
|
128
141
|
Style/MissingElse:
|
|
129
142
|
EnforcedStyle: case
|
|
130
143
|
|
|
144
|
+
# It's example code, please forgive us
|
|
131
145
|
Style/OptionalBooleanParameter:
|
|
132
146
|
Exclude:
|
|
133
147
|
- 'README.md'
|
data/CHANGELOG.md
CHANGED
|
@@ -6,11 +6,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
-
## [
|
|
9
|
+
## [3.0.1] 2023-10-13
|
|
10
10
|
|
|
11
11
|
### Fixed
|
|
12
12
|
|
|
13
|
-
-
|
|
13
|
+
- Fixed a bug where methods such as `test` or `params` cannot be used as attribute name [#344](https://github.com/okuramasafumi/alba/pull/344)
|
|
14
|
+
- Remove redundant code
|
|
15
|
+
|
|
16
|
+
## [3.0.0] 2023-10-11
|
|
17
|
+
|
|
18
|
+
### IMPORTANT
|
|
19
|
+
|
|
20
|
+
**This release contains an important bug fix that can cause data corruption.**
|
|
21
|
+
**If you're using Ruby 3, it's highly recommended to upgrade to [v3.0.0](https://rubygems.org/gems/alba/versions/3.0.0)**
|
|
22
|
+
**If you're using Ruby 2, please upgrade to [v2.4.2](https://rubygems.org/gems/alba/versions/2.4.2) that contains bug fix only as soon as possible.**
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- Custom type [#333](https://github.com/okuramasafumi/alba/pull/333)
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- Prefer resource method [#323](https://github.com/okuramasafumi/alba/pull/323)
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- Multithread bug [No PR](https://github.com/okuramasafumi/alba/commit/d20ed9efbf2f99827c12b8a07308e2f5aea6ab6d)
|
|
35
|
+
- This is a critical bug that can cause data corruption.
|
|
36
|
+
|
|
37
|
+
### Removed
|
|
38
|
+
|
|
39
|
+
- Drop support for Ruby 2 series [No PR](https://github.com/okuramasafumi/alba/commit/20be222555bde69c31fa9cbe4408b3f638cd7580)
|
|
14
40
|
|
|
15
41
|
## [2.4.1] 2023-08-02
|
|
16
42
|
|
data/README.md
CHANGED
|
@@ -11,6 +11,14 @@
|
|
|
11
11
|
|
|
12
12
|
Alba is a JSON serializer for Ruby, JRuby, and TruffleRuby.
|
|
13
13
|
|
|
14
|
+
## IMPORTANT NOTICE
|
|
15
|
+
|
|
16
|
+
Both version `3.0.0` and `2.4.2` contain important bug fix.
|
|
17
|
+
However, version `3.0.0` has some bugs (see https://github.com/okuramasafumi/alba/issues/342).
|
|
18
|
+
Until they get fixed, it's highly recommended to upgrade to version `2.4.2`.
|
|
19
|
+
Dependabot and similar tools might create an automated Pull Request to upgrade to `3.0.0`, so it might be required to upgrade to `2.4.2` manually.
|
|
20
|
+
Sorry for the inconvenience.
|
|
21
|
+
|
|
14
22
|
## TL;DR
|
|
15
23
|
|
|
16
24
|
Alba allows you to do something like below.
|
|
@@ -83,6 +91,12 @@ Alba is easy to use because there are only a few methods to remember. It's also
|
|
|
83
91
|
|
|
84
92
|
While Alba's core is simple, it provides additional features when you need them, For example, Alba provides [a way to control circular associations](#circular-associations-control), [root key and association resource name inference](#root-key-and-association-resource-name-inference) and [supports layouts](#layout).
|
|
85
93
|
|
|
94
|
+
### Other reasons
|
|
95
|
+
|
|
96
|
+
- Dependency free, no need to install `oj` or `activesupport` while Alba works well with them
|
|
97
|
+
- Well tested, the test coverage is 99%
|
|
98
|
+
- Well maintained, gettings frequent update and new releases (see [version history](https://rubygems.org/gems/alba/versions))
|
|
99
|
+
|
|
86
100
|
## Installation
|
|
87
101
|
|
|
88
102
|
Add this line to your application's Gemfile:
|
|
@@ -101,7 +115,7 @@ Or install it yourself as:
|
|
|
101
115
|
|
|
102
116
|
## Supported Ruby versions
|
|
103
117
|
|
|
104
|
-
Alba supports CRuby
|
|
118
|
+
Alba supports CRuby 3.0 and higher and latest JRuby and TruffleRuby.
|
|
105
119
|
|
|
106
120
|
## Documentation
|
|
107
121
|
|
|
@@ -130,13 +144,13 @@ Alba's configuration is fairly simple.
|
|
|
130
144
|
|
|
131
145
|
Backend is the actual part serializing an object into JSON. Alba supports these backends.
|
|
132
146
|
|
|
133
|
-
|name|description|requires_external_gem|
|
|
134
|
-
|
|
135
|
-
|`oj`, `oj_strict`|Using Oj in `strict` mode|Yes(C extension)
|
|
136
|
-
|`oj_rails`|It's `oj` but in `rails` mode|Yes(C extension)
|
|
137
|
-
|`oj_default`|It's `oj` but respects mode set by users|Yes(C extension)
|
|
138
|
-
|`active_support`|For Rails compatibility|Yes
|
|
139
|
-
|`default`, `json`|Using `json` gem|No
|
|
147
|
+
|name|description|requires_external_gem| encoder|
|
|
148
|
+
|--|--|--|--|
|
|
149
|
+
|`oj`, `oj_strict`|Using Oj in `strict` mode|Yes(C extension)|`Oj.dump(object, mode: :strict)`|
|
|
150
|
+
|`oj_rails`|It's `oj` but in `rails` mode|Yes(C extension)|`Oj.dump(object, mode: :rails)`|
|
|
151
|
+
|`oj_default`|It's `oj` but respects mode set by users|Yes(C extension)|`Oj.dump(object)`|
|
|
152
|
+
|`active_support`|For Rails compatibility|Yes|`ActiveSupport::JSON.encode(object)`|
|
|
153
|
+
|`default`, `json`|Using `json` gem|No|`JSON.generate(object)`|
|
|
140
154
|
|
|
141
155
|
You can set a backend like this:
|
|
142
156
|
|
|
@@ -144,6 +158,12 @@ You can set a backend like this:
|
|
|
144
158
|
Alba.backend = :oj
|
|
145
159
|
```
|
|
146
160
|
|
|
161
|
+
This is equivalent as:
|
|
162
|
+
|
|
163
|
+
```ruby
|
|
164
|
+
Alba.encoder = ->(object) { Oj.dump(object, mode: :strict) }
|
|
165
|
+
```
|
|
166
|
+
|
|
147
167
|
#### Encoder configuration
|
|
148
168
|
|
|
149
169
|
You can also set JSON encoder directly with a Proc.
|
|
@@ -284,7 +304,7 @@ class User
|
|
|
284
304
|
end
|
|
285
305
|
|
|
286
306
|
def name_with_email
|
|
287
|
-
|
|
307
|
+
'dummy!'
|
|
288
308
|
end
|
|
289
309
|
end
|
|
290
310
|
|
|
@@ -1231,7 +1251,7 @@ You can control circular associations with `within` option. `within` option is a
|
|
|
1231
1251
|
|
|
1232
1252
|
For more details, please refer to [test code](https://github.com/okuramasafumi/alba/blob/main/test/usecases/circular_association_test.rb)
|
|
1233
1253
|
|
|
1234
|
-
###
|
|
1254
|
+
### Types
|
|
1235
1255
|
|
|
1236
1256
|
You can validate and convert input with types.
|
|
1237
1257
|
|
|
@@ -1270,7 +1290,26 @@ UserResource.new(user).serialize
|
|
|
1270
1290
|
# => TypeError, 'Attribute bio is expected to be String but actually nil.'
|
|
1271
1291
|
```
|
|
1272
1292
|
|
|
1273
|
-
|
|
1293
|
+
#### Custom types
|
|
1294
|
+
|
|
1295
|
+
You can define custom types to abstract data conversion logic. To define custom types, you can use `Alba.register_type` like below.
|
|
1296
|
+
|
|
1297
|
+
```ruby
|
|
1298
|
+
# Typically in initializer
|
|
1299
|
+
Alba.register_type :iso8601, converter: ->(time) { time.iso8601(3) }, auto_convert: true
|
|
1300
|
+
```
|
|
1301
|
+
|
|
1302
|
+
Then use it as regular types.
|
|
1303
|
+
|
|
1304
|
+
```rb
|
|
1305
|
+
class UserResource
|
|
1306
|
+
include Alba::Resource
|
|
1307
|
+
|
|
1308
|
+
attributes :id, created_at: :iso8601
|
|
1309
|
+
end
|
|
1310
|
+
```
|
|
1311
|
+
|
|
1312
|
+
You now get `created_at` attribute with `iso8601` format!
|
|
1274
1313
|
|
|
1275
1314
|
### Collection serialization into Hash
|
|
1276
1315
|
|
|
@@ -1376,7 +1415,7 @@ class ApplicationResource
|
|
|
1376
1415
|
include Alba::Resource
|
|
1377
1416
|
|
|
1378
1417
|
def self.with_id
|
|
1379
|
-
attributes
|
|
1418
|
+
attributes(:id)
|
|
1380
1419
|
end
|
|
1381
1420
|
end
|
|
1382
1421
|
|
|
@@ -1401,7 +1440,7 @@ class ApplicationResource
|
|
|
1401
1440
|
|
|
1402
1441
|
helper do
|
|
1403
1442
|
def with_id
|
|
1404
|
-
attributes
|
|
1443
|
+
attributes(:id)
|
|
1405
1444
|
end
|
|
1406
1445
|
end
|
|
1407
1446
|
end
|
|
@@ -1561,7 +1600,7 @@ FooResource.new(foo).serialize
|
|
|
1561
1600
|
# => "{:id=>1}" is printed
|
|
1562
1601
|
```
|
|
1563
1602
|
|
|
1564
|
-
Here, we override `serialize` method with `prepend`. In overridden method we print the result of `serializable_hash` that gives the basic hash for serialization to `serialize` method. Using `...` allows us to override without knowing method
|
|
1603
|
+
Here, we override `serialize` method with `prepend`. In overridden method we print the result of `serializable_hash` that gives the basic hash for serialization to `serialize` method. Using `...` allows us to override without knowing method signature of `serialize`.
|
|
1565
1604
|
|
|
1566
1605
|
Don't forget calling `super` in this way.
|
|
1567
1606
|
|
data/alba.gemspec
CHANGED
|
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
|
10
10
|
spec.description = "Alba is the fastest JSON serializer for Ruby. It focuses on performance, flexibility and usability."
|
|
11
11
|
spec.homepage = 'https://github.com/okuramasafumi/alba'
|
|
12
12
|
spec.license = 'MIT'
|
|
13
|
-
spec.required_ruby_version = Gem::Requirement.new('>=
|
|
13
|
+
spec.required_ruby_version = Gem::Requirement.new('>= 3.0.0')
|
|
14
14
|
|
|
15
15
|
spec.metadata = {
|
|
16
16
|
'bug_tracker_uri' => 'https://github.com/okuramasafumi/alba/issues',
|
data/lib/alba/association.rb
CHANGED
|
@@ -12,7 +12,8 @@ module Alba
|
|
|
12
12
|
|
|
13
13
|
# @param name [Symbol, String] name of the method to fetch association
|
|
14
14
|
# @param condition [Proc, nil] a proc filtering data
|
|
15
|
-
# @param resource [Class<Alba::Resource>,
|
|
15
|
+
# @param resource [Class<Alba::Resource>, Proc, String, Symbol, nil]
|
|
16
|
+
# a resource class for the association, a proc returning a resource class or a name of the resource
|
|
16
17
|
# @param params [Hash] params override for the association
|
|
17
18
|
# @param nesting [String] a namespace where source class is inferred with
|
|
18
19
|
# @param key_transformation [Symbol] key transformation type
|
|
@@ -52,14 +53,15 @@ module Alba
|
|
|
52
53
|
private
|
|
53
54
|
|
|
54
55
|
def constantize(resource)
|
|
55
|
-
case resource
|
|
56
|
+
case resource
|
|
56
57
|
when Class
|
|
57
58
|
resource
|
|
58
59
|
when Symbol, String
|
|
59
|
-
Object.const_get(resource)
|
|
60
60
|
self.class.const_cache.fetch(resource) do
|
|
61
61
|
self.class.const_cache[resource] = Object.const_get(resource)
|
|
62
62
|
end
|
|
63
|
+
else
|
|
64
|
+
raise Error, "Unexpected resource type: #{resource.class}"
|
|
63
65
|
end
|
|
64
66
|
end
|
|
65
67
|
|
|
@@ -51,6 +51,8 @@ module Alba
|
|
|
51
51
|
|
|
52
52
|
# OpenStruct is used as a simple solution for converting Hash or Array of Hash into an object
|
|
53
53
|
# Using OpenStruct is not good in general, but in this case there's no other solution
|
|
54
|
+
# rubocop:disable Style/OpenStructUse
|
|
55
|
+
# rubocop:disable Performance/OpenStruct
|
|
54
56
|
def objectize(fetched_attribute)
|
|
55
57
|
return fetched_attribute unless @body.is_a?(Alba::Association)
|
|
56
58
|
|
|
@@ -62,5 +64,7 @@ module Alba
|
|
|
62
64
|
OpenStruct.new(fetched_attribute)
|
|
63
65
|
end
|
|
64
66
|
end
|
|
67
|
+
# rubocop:enable Style/OpenStructUse
|
|
68
|
+
# rubocop:enable Performance/OpenStruct
|
|
65
69
|
end
|
|
66
70
|
end
|
data/lib/alba/resource.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require_relative 'association'
|
|
2
2
|
require_relative 'conditional_attribute'
|
|
3
3
|
require_relative 'constants'
|
|
4
|
+
require_relative 'type'
|
|
4
5
|
require_relative 'typed_attribute'
|
|
5
6
|
require_relative 'nested_attribute'
|
|
6
7
|
require_relative 'deprecation'
|
|
@@ -11,8 +12,8 @@ module Alba
|
|
|
11
12
|
module Resource
|
|
12
13
|
# @!parse include InstanceMethods
|
|
13
14
|
# @!parse extend ClassMethods
|
|
14
|
-
|
|
15
|
-
private_constant :
|
|
15
|
+
INTERNAL_VARIABLES = {_attributes: {}, _key: nil, _key_for_collection: nil, _meta: nil, _transform_type: :none, _transforming_root_key: false, _key_transformation_cascade: true, _on_error: nil, _on_nil: nil, _layout: nil, _collection_key: nil, _helper: nil, _resource_methods: []}.freeze # rubocop:disable Layout/LineLength
|
|
16
|
+
private_constant :INTERNAL_VARIABLES
|
|
16
17
|
|
|
17
18
|
WITHIN_DEFAULT = Object.new.freeze
|
|
18
19
|
private_constant :WITHIN_DEFAULT
|
|
@@ -24,7 +25,7 @@ module Alba
|
|
|
24
25
|
setup_method_body = 'private def _setup;'
|
|
25
26
|
base.class_eval do
|
|
26
27
|
# Initialize
|
|
27
|
-
|
|
28
|
+
INTERNAL_VARIABLES.each do |name, initial|
|
|
28
29
|
instance_variable_set("@#{name}", initial.dup) unless instance_variable_defined?("@#{name}")
|
|
29
30
|
setup_method_body << "@#{name} = self.class.#{name};"
|
|
30
31
|
end
|
|
@@ -59,25 +60,13 @@ module Alba
|
|
|
59
60
|
serialize_with(as_json(root_key: root_key, meta: meta))
|
|
60
61
|
end
|
|
61
62
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
def to_json(options, root_key: nil, meta: {})
|
|
70
|
-
_to_json(root_key, meta, options)
|
|
71
|
-
end
|
|
72
|
-
else
|
|
73
|
-
# For Rails compatibility
|
|
74
|
-
# The first options is a dummy parameter
|
|
75
|
-
#
|
|
76
|
-
# @see #serialize
|
|
77
|
-
# @see https://github.com/rails/rails/blob/7-0-stable/actionpack/lib/action_controller/metal/renderers.rb#L156
|
|
78
|
-
def to_json(options = {}, root_key: nil, meta: {})
|
|
79
|
-
_to_json(root_key, meta, options)
|
|
80
|
-
end
|
|
63
|
+
# For Rails compatibility
|
|
64
|
+
# The first options is a dummy parameter
|
|
65
|
+
#
|
|
66
|
+
# @see #serialize
|
|
67
|
+
# @see https://github.com/rails/rails/blob/7-0-stable/actionpack/lib/action_controller/metal/renderers.rb#L156
|
|
68
|
+
def to_json(options = {}, root_key: nil, meta: {})
|
|
69
|
+
_to_json(root_key, meta, options)
|
|
81
70
|
end
|
|
82
71
|
|
|
83
72
|
# Returns a Hash correspondng {#serialize}
|
|
@@ -265,9 +254,8 @@ module Alba
|
|
|
265
254
|
value.nil? && nil_handler ? instance_exec(obj, key, attribute, &nil_handler) : value
|
|
266
255
|
end
|
|
267
256
|
|
|
268
|
-
# TODO: from version 3, `_fetch_attribute_from_resource_first` is default
|
|
269
257
|
def fetch_attribute_from_object_and_resource(obj, attribute)
|
|
270
|
-
|
|
258
|
+
_fetch_attribute_from_resource_first(obj, attribute)
|
|
271
259
|
end
|
|
272
260
|
|
|
273
261
|
def _fetch_attribute_from_object_first(obj, attribute)
|
|
@@ -277,9 +265,15 @@ module Alba
|
|
|
277
265
|
end
|
|
278
266
|
|
|
279
267
|
def _fetch_attribute_from_resource_first(obj, attribute)
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
268
|
+
if @_resource_methods.include?(attribute)
|
|
269
|
+
begin
|
|
270
|
+
__send__(attribute, obj)
|
|
271
|
+
rescue NoMethodError
|
|
272
|
+
obj.__send__(attribute)
|
|
273
|
+
end
|
|
274
|
+
else
|
|
275
|
+
obj.__send__(attribute)
|
|
276
|
+
end
|
|
283
277
|
end
|
|
284
278
|
|
|
285
279
|
def nil_handler
|
|
@@ -312,12 +306,18 @@ module Alba
|
|
|
312
306
|
|
|
313
307
|
# Class methods
|
|
314
308
|
module ClassMethods
|
|
315
|
-
attr_reader(*
|
|
309
|
+
attr_reader(*INTERNAL_VARIABLES.keys)
|
|
310
|
+
|
|
311
|
+
# This `method_added` is used for defining "resource methods"
|
|
312
|
+
def method_added(method_name)
|
|
313
|
+
_resource_methods << method_name.to_sym unless method_name.to_sym == :_setup
|
|
314
|
+
super
|
|
315
|
+
end
|
|
316
316
|
|
|
317
317
|
# @private
|
|
318
318
|
def inherited(subclass)
|
|
319
319
|
super
|
|
320
|
-
|
|
320
|
+
INTERNAL_VARIABLES.each_key { |name| subclass.instance_variable_set("@#{name}", instance_variable_get("@#{name}").clone) }
|
|
321
321
|
end
|
|
322
322
|
|
|
323
323
|
# Defining methods for DSLs and disable parameter number check since for users' benefits increasing params is fine
|
|
@@ -382,9 +382,9 @@ module Alba
|
|
|
382
382
|
# @return [void]
|
|
383
383
|
# @see Alba::Association#initialize
|
|
384
384
|
def association(name, condition = nil, resource: nil, key: nil, params: {}, **options, &block)
|
|
385
|
-
|
|
385
|
+
transformation = @_key_transformation_cascade ? @_transform_type : :none
|
|
386
386
|
assoc = Association.new(
|
|
387
|
-
name: name, condition: condition, resource: resource, params: params, nesting: nesting, key_transformation:
|
|
387
|
+
name: name, condition: condition, resource: resource, params: params, nesting: nesting, key_transformation: transformation, helper: @_helper, &block
|
|
388
388
|
)
|
|
389
389
|
@_attributes[key&.to_sym || name.to_sym] = options[:if] ? ConditionalAttribute.new(body: assoc, condition: options[:if]) : assoc
|
|
390
390
|
end
|
|
@@ -533,4 +533,7 @@ module Alba
|
|
|
533
533
|
end
|
|
534
534
|
end
|
|
535
535
|
end
|
|
536
|
+
|
|
537
|
+
Serializer = Resource
|
|
538
|
+
public_constant :Serializer
|
|
536
539
|
end
|
data/lib/alba/type.rb
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Alba
|
|
2
|
+
# Representing type itself, combined with {Alba::TypedAttribute}
|
|
3
|
+
class Type
|
|
4
|
+
attr_reader :name
|
|
5
|
+
attr_writer :auto_convert
|
|
6
|
+
|
|
7
|
+
# @param name [Symbol, String] name of the type
|
|
8
|
+
# @param check [Proc, Boolean] proc to check type
|
|
9
|
+
# If false, type check is skipped
|
|
10
|
+
# @param converter [Proc] proc to convert type
|
|
11
|
+
# @param auto_convert [Boolean] whether to convert type automatically
|
|
12
|
+
def initialize(name, check:, converter:, auto_convert: false)
|
|
13
|
+
@name = name
|
|
14
|
+
@check = check
|
|
15
|
+
@converter = converter
|
|
16
|
+
@auto_convert = auto_convert
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Type check
|
|
20
|
+
#
|
|
21
|
+
# @param value [Object] value to check
|
|
22
|
+
# @return [Boolean] the result of type check
|
|
23
|
+
def check(value)
|
|
24
|
+
@check == false ? false : @check.call(value)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Type convert
|
|
28
|
+
# If @auto_convert is true, @convert proc is called with obj
|
|
29
|
+
# Otherwise, it raises an exception that is caught by {Alba::TypedAttribute}
|
|
30
|
+
#
|
|
31
|
+
# @param obj [Object] object to convert
|
|
32
|
+
def convert(obj)
|
|
33
|
+
@auto_convert ? @converter.call(obj) : raise(TypeError)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Enable auto convert with given converter
|
|
37
|
+
# @param converter [Proc] proc to convert type
|
|
38
|
+
def auto_convert_with(converter)
|
|
39
|
+
@converter = converter
|
|
40
|
+
@auto_convert = true
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
data/lib/alba/typed_attribute.rb
CHANGED
|
@@ -7,54 +7,27 @@ module Alba
|
|
|
7
7
|
# @param converter [Proc]
|
|
8
8
|
def initialize(name:, type:, converter:)
|
|
9
9
|
@name = name
|
|
10
|
-
|
|
11
|
-
@
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
t = Alba.find_type(type)
|
|
11
|
+
@type = case converter
|
|
12
|
+
when true then t.dup.tap { _1.auto_convert = true }
|
|
13
|
+
when false, nil then t
|
|
14
|
+
else
|
|
15
|
+
t.dup.tap { _1.auto_convert_with(converter) }
|
|
16
|
+
end
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
# @param object [Object] target to check and convert type with
|
|
19
20
|
# @return [String, Integer, Boolean] type-checked or type-converted object
|
|
20
21
|
def value(object)
|
|
21
|
-
|
|
22
|
-
result
|
|
22
|
+
v = object.__send__(@name)
|
|
23
|
+
result = @type.check(v)
|
|
24
|
+
result ? v : @type.convert(v)
|
|
23
25
|
rescue TypeError
|
|
24
|
-
raise TypeError, "Attribute #{@name} is expected to be #{@type} but actually #{display_value_for(
|
|
26
|
+
raise TypeError, "Attribute #{@name} is expected to be #{@type.name} but actually #{display_value_for(v)}."
|
|
25
27
|
end
|
|
26
28
|
|
|
27
29
|
private
|
|
28
30
|
|
|
29
|
-
def check(object)
|
|
30
|
-
value = object.__send__(@name)
|
|
31
|
-
type_correct = case @type
|
|
32
|
-
when :String, ->(klass) { klass == String } then value.is_a?(String)
|
|
33
|
-
when :Integer, ->(klass) { klass == Integer } then value.is_a?(Integer)
|
|
34
|
-
when :Boolean then [true, false].include?(value)
|
|
35
|
-
else
|
|
36
|
-
raise Alba::UnsupportedType, "Unknown type: #{@type}"
|
|
37
|
-
end
|
|
38
|
-
[value, type_correct]
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
def default_converter
|
|
42
|
-
case @type
|
|
43
|
-
when :String, ->(klass) { klass == String }
|
|
44
|
-
->(object) { object.to_s }
|
|
45
|
-
when :Integer, ->(klass) { klass == Integer }
|
|
46
|
-
->(object) { Integer(object) }
|
|
47
|
-
when :Boolean
|
|
48
|
-
->(object) { !!object }
|
|
49
|
-
else
|
|
50
|
-
raise Alba::UnsupportedType, "Unknown type: #{@type}"
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def null_converter
|
|
55
|
-
->(_) { raise TypeError }
|
|
56
|
-
end
|
|
57
|
-
|
|
58
31
|
def display_value_for(value)
|
|
59
32
|
value.nil? ? 'nil' : value.class.name
|
|
60
33
|
end
|
data/lib/alba/version.rb
CHANGED
data/lib/alba.rb
CHANGED
|
@@ -116,7 +116,7 @@ module Alba
|
|
|
116
116
|
const_parent = nesting.nil? ? Object : Object.const_get(nesting)
|
|
117
117
|
begin
|
|
118
118
|
const_parent.const_get("#{inflector.classify(name)}Resource")
|
|
119
|
-
rescue # Retry for serializer
|
|
119
|
+
rescue NameError # Retry for serializer
|
|
120
120
|
const_parent.const_get("#{inflector.classify(name)}Serializer")
|
|
121
121
|
end
|
|
122
122
|
end
|
|
@@ -142,6 +142,23 @@ module Alba
|
|
|
142
142
|
@symbolize_keys ? key.to_sym : key.to_s
|
|
143
143
|
end
|
|
144
144
|
|
|
145
|
+
# Register types, used for both builtin and custom types
|
|
146
|
+
#
|
|
147
|
+
# @see Alba::Type
|
|
148
|
+
# @return [void]
|
|
149
|
+
def register_type(name, check: false, converter: nil, auto_convert: false)
|
|
150
|
+
@types[name] = Type.new(name, check: check, converter: converter, auto_convert: auto_convert)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Find type by name
|
|
154
|
+
#
|
|
155
|
+
# @return [Alba::Type]
|
|
156
|
+
def find_type(name)
|
|
157
|
+
@types.fetch(name) do
|
|
158
|
+
raise(Alba::UnsupportedType, "Unknown type: #{name}")
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
145
162
|
# Reset config variables
|
|
146
163
|
# Useful for test cleanup
|
|
147
164
|
def reset!
|
|
@@ -149,6 +166,8 @@ module Alba
|
|
|
149
166
|
@symbolize_keys = false
|
|
150
167
|
@_on_error = :raise
|
|
151
168
|
@_on_nil = nil
|
|
169
|
+
@types = {}
|
|
170
|
+
register_default_types
|
|
152
171
|
end
|
|
153
172
|
|
|
154
173
|
private
|
|
@@ -175,7 +194,7 @@ module Alba
|
|
|
175
194
|
|
|
176
195
|
def set_encoder_from_backend
|
|
177
196
|
@encoder = case @backend
|
|
178
|
-
when :oj, :oj_strict then try_oj
|
|
197
|
+
when :oj, :oj_strict then try_oj(mode: :strict)
|
|
179
198
|
when :oj_rails then try_oj(mode: :rails)
|
|
180
199
|
when :oj_default then try_oj(mode: :default)
|
|
181
200
|
when :active_support then try_active_support
|
|
@@ -185,7 +204,7 @@ module Alba
|
|
|
185
204
|
end
|
|
186
205
|
end
|
|
187
206
|
|
|
188
|
-
def try_oj(mode:
|
|
207
|
+
def try_oj(mode:)
|
|
189
208
|
require 'oj'
|
|
190
209
|
case mode
|
|
191
210
|
when :default
|
|
@@ -219,6 +238,14 @@ module Alba
|
|
|
219
238
|
|
|
220
239
|
inflector
|
|
221
240
|
end
|
|
241
|
+
|
|
242
|
+
def register_default_types # rubocop:disable Metrics/AbcSize
|
|
243
|
+
register_type(:String, check: ->(obj) { obj.is_a?(String) }, converter: ->(obj) { obj.to_s })
|
|
244
|
+
register_type(String, check: ->(obj) { obj.is_a?(String) }, converter: ->(obj) { obj.to_s })
|
|
245
|
+
register_type(:Integer, check: ->(obj) { obj.is_a?(Integer) }, converter: ->(obj) { Integer(obj) })
|
|
246
|
+
register_type(Integer, check: ->(obj) { obj.is_a?(Integer) }, converter: ->(obj) { Integer(obj) })
|
|
247
|
+
register_type(:Boolean, check: ->(obj) { [true, false].include?(obj) }, converter: ->(obj) { !!obj })
|
|
248
|
+
end
|
|
222
249
|
end
|
|
223
250
|
|
|
224
251
|
reset!
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: alba
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 3.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- OKURA Masafumi
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-10-
|
|
11
|
+
date: 2023-10-13 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: Alba is the fastest JSON serializer for Ruby. It focuses on performance,
|
|
14
14
|
flexibility and usability.
|
|
@@ -24,6 +24,7 @@ files:
|
|
|
24
24
|
- ".github/ISSUE_TEMPLATE/feature_request.md"
|
|
25
25
|
- ".github/dependabot.yml"
|
|
26
26
|
- ".github/workflows/codeql-analysis.yml"
|
|
27
|
+
- ".github/workflows/lint.yml"
|
|
27
28
|
- ".github/workflows/main.yml"
|
|
28
29
|
- ".github/workflows/perf.yml"
|
|
29
30
|
- ".gitignore"
|
|
@@ -62,6 +63,7 @@ files:
|
|
|
62
63
|
- lib/alba/nested_attribute.rb
|
|
63
64
|
- lib/alba/railtie.rb
|
|
64
65
|
- lib/alba/resource.rb
|
|
66
|
+
- lib/alba/type.rb
|
|
65
67
|
- lib/alba/typed_attribute.rb
|
|
66
68
|
- lib/alba/version.rb
|
|
67
69
|
- logo/alba-card.png
|
|
@@ -85,14 +87,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
85
87
|
requirements:
|
|
86
88
|
- - ">="
|
|
87
89
|
- !ruby/object:Gem::Version
|
|
88
|
-
version:
|
|
90
|
+
version: 3.0.0
|
|
89
91
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
90
92
|
requirements:
|
|
91
93
|
- - ">="
|
|
92
94
|
- !ruby/object:Gem::Version
|
|
93
95
|
version: '0'
|
|
94
96
|
requirements: []
|
|
95
|
-
rubygems_version: 3.4.
|
|
97
|
+
rubygems_version: 3.4.20
|
|
96
98
|
signing_key:
|
|
97
99
|
specification_version: 4
|
|
98
100
|
summary: Alba is the fastest JSON serializer for Ruby.
|