alba 1.0.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.md +26 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.github/dependabot.yml +26 -0
- data/.github/workflows/main.yml +10 -1
- data/.github/workflows/perf.yml +21 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +33 -8
- data/.yardopts +2 -0
- data/CHANGELOG.md +45 -0
- data/Gemfile +12 -6
- data/README.md +259 -24
- data/Rakefile +4 -1
- data/SECURITY.md +12 -0
- data/alba.gemspec +3 -3
- data/benchmark/collection.rb +392 -0
- data/benchmark/single_resource.rb +370 -0
- data/codecov.yml +8 -0
- data/gemfiles/all.gemfile +19 -0
- data/gemfiles/without_active_support.gemfile +17 -0
- data/gemfiles/without_oj.gemfile +17 -0
- data/lib/alba.rb +69 -26
- data/lib/alba/association.rb +14 -22
- data/lib/alba/default_inflector.rb +36 -0
- data/lib/alba/key_transform_factory.rb +33 -0
- data/lib/alba/many.rb +3 -2
- data/lib/alba/one.rb +3 -2
- data/lib/alba/resource.rb +171 -62
- data/lib/alba/typed_attribute.rb +61 -0
- data/lib/alba/version.rb +1 -1
- data/script/perf_check.rb +174 -0
- data/sider.yml +2 -4
- metadata +22 -10
- data/benchmark/local.rb +0 -198
- data/lib/alba/key_transformer.rb +0 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 01d43d93e589197104d9ee166e6a9dcf727fd204524b52145d7d93b45ba79cd2
|
4
|
+
data.tar.gz: 023f2e0f8ff17bc78a01e2dc1eb1f98d9c41b0cd353e311f350c2704a9ffa602
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99992932a0f6a3d589e77e1b7dc4225be2e083a15320fa00a96f07a4cd9bbe9803454b94ecae409d439809a53225fb54ce283fcca074c0aed479e7d8b808d545
|
7
|
+
data.tar.gz: c77e99af9cf98781a2e961817b5c60e31fe178e6eef7bb8bd77b1af199043b8acd8395a27dd7cb10680ae82d28ec57e98106fef42f57055cb23752d63605a7b9
|
@@ -0,0 +1,26 @@
|
|
1
|
+
---
|
2
|
+
name: Bug report
|
3
|
+
about: Create a report to help us improve
|
4
|
+
title: ''
|
5
|
+
labels: bug
|
6
|
+
assignees: ''
|
7
|
+
|
8
|
+
---
|
9
|
+
|
10
|
+
## Describe the bug
|
11
|
+
A clear and concise description of what the bug is.
|
12
|
+
|
13
|
+
## To Reproduce
|
14
|
+
Steps to reproduce the behavior:
|
15
|
+
|
16
|
+
## Expected behavior
|
17
|
+
A clear and concise description of what you expected to happen.
|
18
|
+
|
19
|
+
## Actual behavior
|
20
|
+
A clear and concise description of what actually happened.
|
21
|
+
|
22
|
+
## Environment
|
23
|
+
- Ruby version:
|
24
|
+
|
25
|
+
## Additional context
|
26
|
+
Add any other context about the problem here.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
---
|
2
|
+
name: Feature request
|
3
|
+
about: Suggest an idea for this project
|
4
|
+
title: ''
|
5
|
+
labels: enhancement
|
6
|
+
assignees: ''
|
7
|
+
|
8
|
+
---
|
9
|
+
|
10
|
+
## Is your feature request related to a problem? Please describe.
|
11
|
+
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
12
|
+
|
13
|
+
## Describe the solution you'd like
|
14
|
+
A clear and concise description of what you want to happen.
|
15
|
+
|
16
|
+
## Describe alternatives you've considered
|
17
|
+
A clear and concise description of any alternative solutions or features you've considered.
|
18
|
+
|
19
|
+
## Additional context
|
20
|
+
Add any other context or screenshots about the feature request here.
|
@@ -0,0 +1,26 @@
|
|
1
|
+
version: 2
|
2
|
+
updates:
|
3
|
+
- package-ecosystem: bundler
|
4
|
+
directory: "/"
|
5
|
+
schedule:
|
6
|
+
interval: daily
|
7
|
+
time: "20:00"
|
8
|
+
open-pull-requests-limit: 10
|
9
|
+
ignore:
|
10
|
+
- dependency-name: rubocop
|
11
|
+
versions:
|
12
|
+
- 1.12.0
|
13
|
+
- 1.9.0
|
14
|
+
- dependency-name: rubocop-performance
|
15
|
+
versions:
|
16
|
+
- 1.10.0
|
17
|
+
- 1.10.2
|
18
|
+
- dependency-name: oj
|
19
|
+
versions:
|
20
|
+
- 3.11.3
|
21
|
+
- dependency-name: minitest
|
22
|
+
versions:
|
23
|
+
- 5.14.4
|
24
|
+
- dependency-name: activesupport
|
25
|
+
versions:
|
26
|
+
- 6.1.2
|
data/.github/workflows/main.yml
CHANGED
@@ -8,11 +8,16 @@ jobs:
|
|
8
8
|
fail-fast: false
|
9
9
|
matrix:
|
10
10
|
os: [ubuntu-latest, windows-latest, macos-latest]
|
11
|
-
ruby: [2.5, 2.6, 2.7, 3.0, head, truffleruby]
|
11
|
+
ruby: [2.5, 2.6, 2.7, 3.0, head, jruby, truffleruby]
|
12
|
+
gemfile: [all, without_active_support, without_oj]
|
12
13
|
exclude:
|
14
|
+
- os: windows-latest
|
15
|
+
ruby: jruby
|
13
16
|
- os: windows-latest
|
14
17
|
ruby: truffleruby
|
15
18
|
runs-on: ${{ matrix.os }}
|
19
|
+
env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
|
20
|
+
BUNDLE_GEMFILE: gemfiles/${{ matrix.gemfile }}.gemfile
|
16
21
|
steps:
|
17
22
|
- uses: actions/checkout@v2
|
18
23
|
- name: Set up Ruby
|
@@ -23,3 +28,7 @@ jobs:
|
|
23
28
|
- name: Run the default task
|
24
29
|
run: |
|
25
30
|
bundle exec rake
|
31
|
+
- name: CodeCov
|
32
|
+
uses: codecov/codecov-action@v1
|
33
|
+
with:
|
34
|
+
files: ./coverage/coverage.xml
|
@@ -0,0 +1,21 @@
|
|
1
|
+
name: Performance Check
|
2
|
+
|
3
|
+
on: [pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
strategy:
|
8
|
+
fail-fast: false
|
9
|
+
matrix:
|
10
|
+
ruby: [2.5, 2.6, 2.7, 3.0]
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
steps:
|
13
|
+
- uses: actions/checkout@v2
|
14
|
+
- name: Set up Ruby
|
15
|
+
uses: ruby/setup-ruby@v1
|
16
|
+
with:
|
17
|
+
ruby-version: ${{ matrix.ruby }}
|
18
|
+
bundler-cache: true
|
19
|
+
- name: Run benchmark
|
20
|
+
run: |
|
21
|
+
ruby script/perf_check.rb
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -13,14 +13,24 @@ AllCops:
|
|
13
13
|
- 'Rakefile'
|
14
14
|
- 'alba.gemspec'
|
15
15
|
- 'benchmark/**/*.rb'
|
16
|
+
- 'script/**/*.rb'
|
16
17
|
NewCops: enable
|
17
18
|
EnabledByDefault: true
|
18
19
|
TargetRubyVersion: 2.5
|
19
20
|
|
20
|
-
#
|
21
|
-
Bundler/
|
21
|
+
# Items in Gemfile is dev dependencies and we don't have to specify versions.
|
22
|
+
Bundler/GemVersion:
|
22
23
|
Enabled: false
|
23
24
|
|
25
|
+
# We'd like to write something like:
|
26
|
+
# assert_equal(
|
27
|
+
# expected,
|
28
|
+
# actual
|
29
|
+
# )
|
30
|
+
Layout/RedundantLineBreak:
|
31
|
+
Exclude:
|
32
|
+
- 'test/**/*'
|
33
|
+
|
24
34
|
Layout/SpaceInsideHashLiteralBraces:
|
25
35
|
EnforcedStyle: no_space
|
26
36
|
|
@@ -30,17 +40,25 @@ Layout/MultilineAssignmentLayout:
|
|
30
40
|
Lint/ConstantResolution:
|
31
41
|
Enabled: false
|
32
42
|
|
33
|
-
|
43
|
+
# In test code we don't care about the metrics!
|
44
|
+
Metrics:
|
34
45
|
Exclude:
|
35
|
-
- 'test
|
46
|
+
- 'test/**/*.rb'
|
36
47
|
|
37
|
-
|
38
|
-
|
48
|
+
# `Resource` module is a core module and its length tends to be long...
|
49
|
+
Metrics/ModuleLength:
|
50
|
+
Exclude:
|
51
|
+
- 'lib/alba/resource.rb'
|
39
52
|
|
40
53
|
# Resource class includes DSLs, which tend to accept long list of parameters
|
41
54
|
Metrics/ParameterLists:
|
42
55
|
Exclude:
|
43
|
-
- '
|
56
|
+
- 'test/**/*.rb'
|
57
|
+
|
58
|
+
# We need to eval resource code to test errors on resource classes
|
59
|
+
Security/Eval:
|
60
|
+
Exclude:
|
61
|
+
- 'test/**/*.rb'
|
44
62
|
|
45
63
|
Style/ConstantVisibility:
|
46
64
|
Exclude:
|
@@ -60,4 +78,11 @@ Style/InlineComment:
|
|
60
78
|
Enabled: false
|
61
79
|
|
62
80
|
Style/MethodCallWithArgsParentheses:
|
63
|
-
|
81
|
+
IgnoredMethods: ['require', 'require_relative', 'include', 'extend', 'puts', 'p', 'warn', 'raise', 'send', 'public_send']
|
82
|
+
Exclude:
|
83
|
+
# There are so many `attributes` call without parenthese and that's absolutely fine
|
84
|
+
- 'test/**/*.rb'
|
85
|
+
|
86
|
+
# There are so many cases we just want `if` expression!
|
87
|
+
Style/MissingElse:
|
88
|
+
EnforcedStyle: case
|
data/.yardopts
CHANGED
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,51 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [1.4.0] 2021-06-30
|
10
|
+
|
11
|
+
- [Feat] Add a config method to set encoder directly
|
12
|
+
- [Feat] Implement `meta` method and option for metadata
|
13
|
+
- [Feat] Add `root_key` option to `Resource#serialize`
|
14
|
+
- [Feat] Enable setting key for collection with `root_key`
|
15
|
+
- [Feat] Add `Resource.root_key` and `Resource.root_key!`
|
16
|
+
- [Feat] `Alba.serialize` now infers resource class
|
17
|
+
|
18
|
+
## [1.3.0] 2021-05-31
|
19
|
+
|
20
|
+
- [Perf] Improve performance for `many` [641d8f9]
|
21
|
+
- https://github.com/okuramasafumi/alba/pull/125
|
22
|
+
- [Feat] Add custom inflector feature (#126) [ad73291]
|
23
|
+
- https://github.com/okuramasafumi/alba/pull/126
|
24
|
+
- Thank you @wuarmin !
|
25
|
+
- [Feat] Support params in if condition [6e9915e]
|
26
|
+
- https://github.com/okuramasafumi/alba/pull/128
|
27
|
+
- [Fix] fundamentally broken "circular association control" [fbbc9a1]
|
28
|
+
- https://github.com/okuramasafumi/alba/pull/130
|
29
|
+
|
30
|
+
## [1.2.0] 2021-05-09
|
31
|
+
|
32
|
+
- [Fix] multiple word key inference [6c18e73]
|
33
|
+
- https://github.com/okuramasafumi/alba/pull/120
|
34
|
+
- Thank you @alfonsojimenez !
|
35
|
+
- [Feat] Add `Alba.enable_root_key_transformation!` [f172839]
|
36
|
+
- https://github.com/okuramasafumi/alba/pull/121
|
37
|
+
- [Feat] Implement type validation and auto conversion [cbe00c7]
|
38
|
+
- https://github.com/okuramasafumi/alba/pull/122
|
39
|
+
|
40
|
+
## [1.1.0] - 2021-04-23
|
41
|
+
|
42
|
+
- [Feat] Implement circular associations control [71e1543]
|
43
|
+
- [Feat] Support :oj_rails backend [76e519e]
|
44
|
+
|
45
|
+
## [1.0.1] - 2021-04-15
|
46
|
+
|
47
|
+
- [Fix] Don't cache resource class for `Alba.serialize` [9ed5253]
|
48
|
+
- [Improve] Warn when `ActiveSupport` or `Oj` are absent [d3ab3eb]
|
49
|
+
- [Fix] Delete unreachable `to_hash` method on Association [1ba1f90]
|
50
|
+
- [Fix] Stringify key before transforming [b4eb79e]
|
51
|
+
- [Misc] Support Ruby 2.5.0 and above, not 2.5.7 and above [43f1d17]
|
52
|
+
- [Fix] Remove accidentally added `p` debug [5d0324b]
|
53
|
+
|
9
54
|
## [1.0.0] - 2021-04-07
|
10
55
|
|
11
56
|
This is the first major release of Alba and it includes so many features. To see all the features you can have a look at [README](https://github.com/okuramasafumi/alba/blob/master/README.md#features).
|
data/Gemfile
CHANGED
@@ -4,14 +4,20 @@ source 'https://rubygems.org'
|
|
4
4
|
gemspec
|
5
5
|
|
6
6
|
gem 'activesupport', require: false # For backend
|
7
|
-
gem '
|
7
|
+
gem 'ffaker', require: false # For testing
|
8
|
+
gem 'inch', require: false # For inline documents
|
8
9
|
gem 'minitest', '~> 5.14' # For test
|
9
|
-
gem 'oj', '~> 3.11', platform: :ruby, require: false # For backend
|
10
10
|
gem 'rake', '~> 13.0' # For test and automation
|
11
11
|
gem 'rubocop', '>= 0.79.0', require: false # For lint
|
12
|
-
gem 'rubocop-minitest', '~> 0.
|
13
|
-
gem 'rubocop-performance', '~> 1.
|
12
|
+
gem 'rubocop-minitest', '~> 0.13.0', require: false # For lint
|
13
|
+
gem 'rubocop-performance', '~> 1.11.0', require: false # For lint
|
14
14
|
gem 'rubocop-rake', '>= 0.5.1', require: false # For lint
|
15
15
|
gem 'rubocop-sensible', '~> 0.3.0', require: false # For lint
|
16
|
-
gem '
|
17
|
-
gem '
|
16
|
+
gem 'simplecov', '~> 0.21.0', require: false # For test coverage
|
17
|
+
gem 'simplecov-cobertura', require: false # For test coverage
|
18
|
+
gem 'yard', require: false # For documentation
|
19
|
+
|
20
|
+
platforms :ruby do
|
21
|
+
gem 'oj', '~> 3.11', require: false # For backend
|
22
|
+
gem 'ruby-prof', require: false # For performance profiling
|
23
|
+
end
|
data/README.md
CHANGED
@@ -1,28 +1,39 @@
|
|
1
1
|
[![Gem Version](https://badge.fury.io/rb/alba.svg)](https://badge.fury.io/rb/alba)
|
2
|
-
[![
|
3
|
-
[![
|
2
|
+
[![CI](https://github.com/okuramasafumi/alba/actions/workflows/main.yml/badge.svg)](https://github.com/okuramasafumi/alba/actions/workflows/main.yml)
|
3
|
+
[![codecov](https://codecov.io/gh/okuramasafumi/alba/branch/master/graph/badge.svg?token=3D3HEZ5OXT)](https://codecov.io/gh/okuramasafumi/alba)
|
4
4
|
[![Maintainability](https://api.codeclimate.com/v1/badges/fdab4cc0de0b9addcfe8/maintainability)](https://codeclimate.com/github/okuramasafumi/alba/maintainability)
|
5
|
+
[![Inline docs](http://inch-ci.org/github/okuramasafumi/alba.svg?branch=main)](http://inch-ci.org/github/okuramasafumi/alba)
|
5
6
|
![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/okuramasafumi/alba)
|
6
7
|
![GitHub](https://img.shields.io/github/license/okuramasafumi/alba)
|
7
8
|
|
8
9
|
# Alba
|
9
10
|
|
10
|
-
|
11
|
+
Alba is the fastest JSON serializer for Ruby, JRuby, and TruffleRuby.
|
11
12
|
|
12
|
-
##
|
13
|
+
## Discussions
|
13
14
|
|
14
|
-
|
15
|
+
Alba uses [GitHub Discussions](https://github.com/okuramasafumi/alba/discussions) to openly discuss the project.
|
15
16
|
|
16
|
-
Alba
|
17
|
+
If you've already used Alba, please consider posting your thoughts and feelings on [Feedback](https://github.com/okuramasafumi/alba/discussions/categories/feedback). The fact that you enjoy using Alba gives me energy to keep developing Alba!
|
17
18
|
|
18
|
-
|
19
|
+
If you have feature requests or interesting ideas, join us with [Ideas](https://github.com/okuramasafumi/alba/discussions/categories/ideas). Let's make Alba even better, together!
|
19
20
|
|
20
|
-
|
21
|
+
## Why Alba?
|
21
22
|
|
22
|
-
|
23
|
+
Because it's fast, flexible and well-maintained!
|
24
|
+
|
25
|
+
### Fast
|
23
26
|
|
24
27
|
Alba is faster than most of the alternatives. We have a [benchmark](https://github.com/okuramasafumi/alba/tree/master/benchmark).
|
25
28
|
|
29
|
+
### Flexible
|
30
|
+
|
31
|
+
Alba provides a small set of DSL to define your serialization logic. It also provides methods you can override to alter and filter serialized hash so that you have full control over the result.
|
32
|
+
|
33
|
+
### Maintained
|
34
|
+
|
35
|
+
Alba is well-maintained and adds features quickly. [Coverage Status](https://coveralls.io/github/okuramasafumi/alba?branch=master) and [CodeClimate Maintainability](https://codeclimate.com/github/okuramasafumi/alba/maintainability) show the code base is quite healthy.
|
36
|
+
|
26
37
|
## Installation
|
27
38
|
|
28
39
|
Add this line to your application's Gemfile:
|
@@ -41,7 +52,7 @@ Or install it yourself as:
|
|
41
52
|
|
42
53
|
## Supported Ruby versions
|
43
54
|
|
44
|
-
Alba supports CRuby 2.5
|
55
|
+
Alba supports CRuby 2.5 and higher and latest JRuby and TruffleRuby.
|
45
56
|
|
46
57
|
## Documentation
|
47
58
|
|
@@ -49,17 +60,14 @@ You can find the documentation on [RubyDoc](https://rubydoc.info/github/okuramas
|
|
49
60
|
|
50
61
|
## Features
|
51
62
|
|
52
|
-
* Resource-based serialization
|
53
|
-
* Arbitrary attribute definition
|
54
|
-
* One and many association with the ability to define them inline
|
55
|
-
* Adding condition and filter to association
|
56
|
-
* Parameters can be injected and used in attributes and associations
|
57
63
|
* Conditional attributes and associations
|
58
64
|
* Selectable backend
|
59
65
|
* Key transformation
|
60
66
|
* Root key inference
|
61
67
|
* Error handling
|
62
68
|
* Resource name inflection based on association name
|
69
|
+
* Circular associations control
|
70
|
+
* [Experimental] Types for validation and conversion
|
63
71
|
* No runtime dependencies
|
64
72
|
|
65
73
|
## Anti features
|
@@ -91,6 +99,16 @@ You can set a backend like this:
|
|
91
99
|
Alba.backend = :oj
|
92
100
|
```
|
93
101
|
|
102
|
+
#### Encoder configuration
|
103
|
+
|
104
|
+
You can also set JSON encoder directly with a Proc.
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
Alba.encoder = ->(object) { JSON.generate(object) }
|
108
|
+
```
|
109
|
+
|
110
|
+
You can consider setting a backend with Symbol as a shortcut to set encoder.
|
111
|
+
|
94
112
|
#### Inference configuration
|
95
113
|
|
96
114
|
You can enable inference feature using `enable_inference!` method.
|
@@ -191,6 +209,35 @@ UserResource.new(user).serialize
|
|
191
209
|
# => '{"id":1,"articles":[{"title":"Hello World!"},{"title":"Super nice"}]}'
|
192
210
|
```
|
193
211
|
|
212
|
+
You can define associations inline if you don't need a class for association.
|
213
|
+
|
214
|
+
```ruby
|
215
|
+
class ArticleResource
|
216
|
+
include Alba::Resource
|
217
|
+
|
218
|
+
attributes :title
|
219
|
+
end
|
220
|
+
|
221
|
+
class UserResource
|
222
|
+
include Alba::Resource
|
223
|
+
|
224
|
+
attributes :id
|
225
|
+
|
226
|
+
many :articles, resource: ArticleResource
|
227
|
+
end
|
228
|
+
|
229
|
+
# This class works the same as `UserResource`
|
230
|
+
class AnotherUserResource
|
231
|
+
include Alba::Resource
|
232
|
+
|
233
|
+
attributes :id
|
234
|
+
|
235
|
+
many :articles do
|
236
|
+
attributes :title
|
237
|
+
end
|
238
|
+
end
|
239
|
+
```
|
240
|
+
|
194
241
|
### Inline definition with `Alba.serialize`
|
195
242
|
|
196
243
|
`Alba.serialize` method is a shortcut to define everything inline.
|
@@ -205,6 +252,13 @@ end
|
|
205
252
|
# => '{"foo":{"id":1,"articles":[{"title":"Hello World!","body":"Hello World!!!"},{"title":"Super nice","body":"Really nice!"}]}}'
|
206
253
|
```
|
207
254
|
|
255
|
+
`Alba.serialize` can be used when you don't know what kind of object you serialize. For example:
|
256
|
+
|
257
|
+
```ruby
|
258
|
+
Alba.serialize(something)
|
259
|
+
# => Same as `FooResource.new(something).serialize` when `something` is an instance of `Foo`.
|
260
|
+
```
|
261
|
+
|
208
262
|
Although this might be useful sometimes, it's generally recommended to define a class for Resource.
|
209
263
|
|
210
264
|
### Inheritance and Ignorance
|
@@ -228,20 +282,21 @@ class GenericFooResource
|
|
228
282
|
attributes :id, :name, :body
|
229
283
|
end
|
230
284
|
|
231
|
-
class
|
285
|
+
class RestrictedFooResource < GenericFooResource
|
232
286
|
ignoring :id, :body
|
233
287
|
end
|
234
288
|
|
235
|
-
|
289
|
+
RestrictedFooResource.new(foo).serialize
|
236
290
|
# => '{"name":"my foo"}'
|
237
|
-
end
|
238
291
|
```
|
239
292
|
|
240
|
-
###
|
293
|
+
### Key transformation
|
241
294
|
|
242
|
-
|
295
|
+
If you want to use `transform_keys` DSL and you already have `active_support` installed, key transformation will work out of the box, using `ActiveSupport::Inflector`. If `active_support` is not around, you have 2 possibilities:
|
296
|
+
* install it
|
297
|
+
* use a [custom inflector](#custom-inflector)
|
243
298
|
|
244
|
-
With `
|
299
|
+
With `transform_keys` DSL, you can transform attribute keys.
|
245
300
|
|
246
301
|
```ruby
|
247
302
|
class User
|
@@ -267,8 +322,69 @@ UserResourceCamel.new(user).serialize
|
|
267
322
|
# => '{"id":1,"firstName":"Masafumi","lastName":"Okura"}'
|
268
323
|
```
|
269
324
|
|
325
|
+
You can also transform root key when:
|
326
|
+
|
327
|
+
* `Alba.enable_inference!` is called
|
328
|
+
* `key!` is called in Resource class
|
329
|
+
* `root` option of `transform_keys` is set to true or `Alba.enable_root_key_transformation!` is called.
|
330
|
+
|
331
|
+
```ruby
|
332
|
+
Alba.enable_inference!
|
333
|
+
|
334
|
+
class BankAccount
|
335
|
+
attr_reader :account_number
|
336
|
+
|
337
|
+
def initialize(account_number)
|
338
|
+
@account_number = account_number
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
class BankAccountResource
|
343
|
+
include Alba::Resource
|
344
|
+
|
345
|
+
key!
|
346
|
+
|
347
|
+
attributes :account_number
|
348
|
+
transform_keys :dash, root: true
|
349
|
+
end
|
350
|
+
|
351
|
+
bank_account = BankAccount.new(123_456_789)
|
352
|
+
BankAccountResource.new(bank_account).serialize
|
353
|
+
# => '{"bank-account":{"account-number":123456789}}'
|
354
|
+
```
|
355
|
+
|
356
|
+
This behavior to transform root key will become default at version 2.
|
357
|
+
|
270
358
|
Supported transformation types are :camel, :lower_camel and :dash.
|
271
359
|
|
360
|
+
#### Custom inflector
|
361
|
+
|
362
|
+
A custom inflector can be plugged in as follows...
|
363
|
+
```ruby
|
364
|
+
Alba.inflector = MyCustomInflector
|
365
|
+
```
|
366
|
+
...and has to implement following interface (the parameter `key` is of type `String`):
|
367
|
+
```ruby
|
368
|
+
module InflectorInterface
|
369
|
+
def camelize(key)
|
370
|
+
raise "Not implemented"
|
371
|
+
end
|
372
|
+
|
373
|
+
def camelize_lower(key)
|
374
|
+
raise "Not implemented"
|
375
|
+
end
|
376
|
+
|
377
|
+
def dasherize(key)
|
378
|
+
raise "Not implemented"
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
```
|
383
|
+
For example you could use `Dry::Inflector`, which implements exactly the above interface. If you are developing a `Hanami`-Application `Dry::Inflector` is around. In this case the following would be sufficient:
|
384
|
+
```ruby
|
385
|
+
Alba.inflector = Dry::Inflector.new
|
386
|
+
```
|
387
|
+
|
272
388
|
### Filtering attributes
|
273
389
|
|
274
390
|
You can filter attributes by overriding `Alba::Resource#converter` method, but it's a bit tricky.
|
@@ -332,6 +448,20 @@ user = User.new(1, nil, nil)
|
|
332
448
|
UserResource.new(user).serialize # => '{"id":1}'
|
333
449
|
```
|
334
450
|
|
451
|
+
### Default
|
452
|
+
|
453
|
+
Alba doesn't support default value for attributes, but it's easy to set a default value.
|
454
|
+
|
455
|
+
```ruby
|
456
|
+
class FooResource
|
457
|
+
attribute :bar do |foo|
|
458
|
+
foo.bar || 'default bar'
|
459
|
+
end
|
460
|
+
end
|
461
|
+
```
|
462
|
+
|
463
|
+
We believe this is clearer than using some (not implemented yet) DSL such as `default` because there are some conditions where default values should be applied (`nil`, `blank?`, `empty?` etc.)
|
464
|
+
|
335
465
|
### Inference
|
336
466
|
|
337
467
|
After `Alba.enable_inference!` called, Alba tries to infer root key and association resource name.
|
@@ -440,10 +570,106 @@ Alba.on_error do |error, object, key, attribute, resource_class|
|
|
440
570
|
end
|
441
571
|
```
|
442
572
|
|
443
|
-
|
573
|
+
### Metadata
|
574
|
+
|
575
|
+
You can set a metadata with `meta` DSL or `meta` option.
|
576
|
+
|
577
|
+
```ruby
|
578
|
+
class UserResource
|
579
|
+
include Alba::Resource
|
580
|
+
|
581
|
+
root_key :user, :users
|
582
|
+
|
583
|
+
attributes :id, :name
|
584
|
+
|
585
|
+
meta do
|
586
|
+
if object.is_a?(Enumerable)
|
587
|
+
{size: object.size}
|
588
|
+
else
|
589
|
+
{foo: :bar}
|
590
|
+
end
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
user = User.new(1, 'Masafumi OKURA', 'masafumi@example.com')
|
595
|
+
UserResource.new([user]).serialize
|
596
|
+
# => '{"users":[{"id":1,"name":"Masafumi OKURA"}],"meta":{"size":1}}'
|
597
|
+
|
598
|
+
# You can merge metadata with `meta` option
|
599
|
+
|
600
|
+
UserResource.new([user]).serialize(meta: {foo: :bar})
|
601
|
+
# => '{"users":[{"id":1,"name":"Masafumi OKURA"}],"meta":{"size":1,"foo":"bar"}}'
|
602
|
+
|
603
|
+
# You can set metadata with `meta` option alone
|
604
|
+
|
605
|
+
class UserResourceWithoutMeta
|
606
|
+
include Alba::Resource
|
607
|
+
|
608
|
+
root_key :user, :users
|
609
|
+
|
610
|
+
attributes :id, :name
|
611
|
+
end
|
612
|
+
|
613
|
+
UserResource.new([user]).serialize(meta: {foo: :bar})
|
614
|
+
# => '{"users":[{"id":1,"name":"Masafumi OKURA"}],"meta":{"foo":"bar"}}'
|
615
|
+
```
|
616
|
+
|
617
|
+
You can use `object` method to access the underlying object and `params` to access the params in `meta` block.
|
618
|
+
|
619
|
+
Note that setting root key is required when setting a metadata.
|
620
|
+
|
621
|
+
### Circular associations control
|
622
|
+
|
623
|
+
**Note that this feature works correctly since version 1.3. In previous versions it doesn't work as expected.**
|
624
|
+
|
625
|
+
You can control circular associations with `within` option. `within` option is a nested Hash such as `{book: {authors: books}}`. In this example, Alba serializes a book's authors' books. This means you can reference `BookResource` from `AuthorResource` and vice versa. This is really powerful when you have a complex data structure and serialize certain parts of it.
|
626
|
+
|
627
|
+
For more details, please refer to [test code](https://github.com/okuramasafumi/alba/blob/master/test/usecases/circular_association_test.rb)
|
628
|
+
|
629
|
+
### Experimental support of types
|
630
|
+
|
631
|
+
You can validate and convert input with types.
|
632
|
+
|
633
|
+
```ruby
|
634
|
+
class User
|
635
|
+
attr_reader :id, :name, :age, :bio, :admin, :created_at
|
636
|
+
|
637
|
+
def initialize(id, name, age, bio = '', admin = false) # rubocop:disable Style/OptionalBooleanParameter
|
638
|
+
@id = id
|
639
|
+
@name = name
|
640
|
+
@age = age
|
641
|
+
@admin = admin
|
642
|
+
@bio = bio
|
643
|
+
@created_at = Time.new(2020, 10, 10)
|
644
|
+
end
|
645
|
+
end
|
646
|
+
|
647
|
+
class UserResource
|
648
|
+
include Alba::Resource
|
649
|
+
|
650
|
+
attributes :name, id: [String, true], age: [Integer, true], bio: String, admin: [:Boolean, true], created_at: [String, ->(object) { object.strftime('%F') }]
|
651
|
+
end
|
652
|
+
|
653
|
+
user = User.new(1, 'Masafumi OKURA', '32', 'Ruby dev')
|
654
|
+
UserResource.new(user).serialize
|
655
|
+
# => '{"name":"Masafumi OKURA","id":"1","age":32,"bio":"Ruby dev","admin":false,"created_at":"2020-10-10"}'
|
656
|
+
```
|
657
|
+
|
658
|
+
Notice that `id` and `created_at` are converted to String and `age` is converted to Integer.
|
444
659
|
|
445
|
-
|
446
|
-
|
660
|
+
If type is not correct and auto conversion is disabled (default), `TypeError` occurs.
|
661
|
+
|
662
|
+
```ruby
|
663
|
+
user = User.new(1, 'Masafumi OKURA', '32', nil) # bio is nil and auto conversion is disabled for bio
|
664
|
+
UserResource.new(user).serialize
|
665
|
+
# => TypeError, 'Attribute bio is expected to be String but actually nil.'
|
666
|
+
```
|
667
|
+
|
668
|
+
Note that this feature is experimental and interfaces are subject to change.
|
669
|
+
|
670
|
+
### Caching
|
671
|
+
|
672
|
+
Currently, Alba doesn't support caching, primarily due to the behavior of `ActiveRecord::Relation`'s cache. See [the issue](https://github.com/rails/rails/issues/41784).
|
447
673
|
|
448
674
|
## Rails
|
449
675
|
|
@@ -451,12 +677,21 @@ When you use Alba in Rails, you can create an initializer file with the line bel
|
|
451
677
|
|
452
678
|
```ruby
|
453
679
|
Alba.backend = :active_support
|
680
|
+
# or
|
681
|
+
Alba.backend = :oj_rails
|
454
682
|
```
|
455
683
|
|
456
684
|
## Why named "Alba"?
|
457
685
|
|
458
686
|
The name "Alba" comes from "albatross", a kind of birds. In Japanese, this bird is called "Aho-dori", which means "stupid bird". I find it funny because in fact albatrosses fly really fast. I hope Alba looks stupid but in fact it does its job quick.
|
459
687
|
|
688
|
+
## Pioneers
|
689
|
+
|
690
|
+
There are great pioneers in Ruby's ecosystem which does basically the same thing as Alba does. To name a few:
|
691
|
+
|
692
|
+
* [ActiveModelSerializers](https://github.com/rails-api/active_model_serializers) a.k.a AMS, the most famous implementation of JSON serializer for Ruby
|
693
|
+
* [Blueprinter](https://github.com/procore/blueprinter) shares some concepts with Alba
|
694
|
+
|
460
695
|
## Development
|
461
696
|
|
462
697
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|