alba 0.13.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f4a5b866825838f457bb17c0c1037f68c3e88a9b96c86dd9e0c456d101f69158
4
- data.tar.gz: d1f451fff558385b3ae4610355a3abb9aced052ce138eff9913169a12c8a23c6
3
+ metadata.gz: bbb5260b5a6f3697f2e9c42ba198e3e17a1b0dc2aa0ea1bd2feb9ed4b18364a2
4
+ data.tar.gz: 794c146a493068865d7284de00bedda11c3b2fabd2ce7dccaa781e72181d957e
5
5
  SHA512:
6
- metadata.gz: 71fdab1911c6fc9e1ba88e8cbdc553311b99aecd975462b05958e5be1e491aa10734b6b6f6f15f24ae98c8ab5a1b1021d6c544a172befcedd23ca80283529e98
7
- data.tar.gz: 05f06f2e7f3ec93e0a616bec6e3d71a90590b68c7ce397050bb9c019d2bbf206b070ed1cf44603cd301fa5dd2634df50c4e08b75a8c478b2f0063b6551b5bdcc
6
+ metadata.gz: 46d179154f3981879a4cda7f5ea0360d7eeb3481788d895acefcbb5df156fde10ee1b0eb78bd399ab247449ea85100aceaa2dcb757a54ba64cce9450d82d1825
7
+ data.tar.gz: a3ee5f28b7bbc626d4dcf92db32017eba85f60df42df02f07d464cdaa8c190f5c07914db5e9f7db21f73fd1356529add373ef2c79ac82038d22f2c5dd5ee273c
@@ -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
@@ -0,0 +1,34 @@
1
+ name: Ruby
2
+
3
+ on: [push,pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ strategy:
8
+ fail-fast: false
9
+ matrix:
10
+ os: [ubuntu-latest, windows-latest, macos-latest]
11
+ ruby: [2.5, 2.6, 2.7, 3.0, head, jruby, truffleruby]
12
+ gemfile: [all, without_active_support, without_oj]
13
+ exclude:
14
+ - os: windows-latest
15
+ ruby: jruby
16
+ - os: windows-latest
17
+ ruby: truffleruby
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
21
+ steps:
22
+ - uses: actions/checkout@v2
23
+ - name: Set up Ruby
24
+ uses: ruby/setup-ruby@v1
25
+ with:
26
+ ruby-version: ${{ matrix.ruby }}
27
+ bundler-cache: true
28
+ - name: Run the default task
29
+ run: |
30
+ bundle exec rake
31
+ - name: CodeCov
32
+ uses: codecov/codecov-action@v1
33
+ with:
34
+ files: ./coverage/coverage.xml
data/.gitignore CHANGED
@@ -6,3 +6,6 @@
6
6
  /pkg/
7
7
  /spec/reports/
8
8
  /tmp/
9
+
10
+ Gemfile.lock
11
+ /gemfiles/*.lock
data/.rubocop.yml CHANGED
@@ -12,13 +12,24 @@ AllCops:
12
12
  Exclude:
13
13
  - 'Rakefile'
14
14
  - 'alba.gemspec'
15
+ - 'benchmark/**/*.rb'
15
16
  NewCops: enable
16
17
  EnabledByDefault: true
18
+ TargetRubyVersion: 2.5
17
19
 
18
20
  # Oneline comment is not valid so until it gets valid, we disable it
19
21
  Bundler/GemComment:
20
22
  Enabled: false
21
23
 
24
+ # We'd like to write something like:
25
+ # assert_equal(
26
+ # expected,
27
+ # actual
28
+ # )
29
+ Layout/RedundantLineBreak:
30
+ Exclude:
31
+ - 'test/**/*'
32
+
22
33
  Layout/SpaceInsideHashLiteralBraces:
23
34
  EnforcedStyle: no_space
24
35
 
@@ -28,13 +39,29 @@ Layout/MultilineAssignmentLayout:
28
39
  Lint/ConstantResolution:
29
40
  Enabled: false
30
41
 
31
- Metrics/ClassLength:
42
+ # In test code we don't care about the metrics!
43
+ Metrics:
32
44
  Exclude:
33
- - 'test/alba_test.rb'
45
+ - 'test/**/*.rb'
34
46
 
35
47
  Metrics/MethodLength:
36
48
  Max: 15
37
49
 
50
+ # `Resource` module is a core module and its length tends to be long...
51
+ Metrics/ModuleLength:
52
+ Max: 150
53
+
54
+ # Resource class includes DSLs, which tend to accept long list of parameters
55
+ Metrics/ParameterLists:
56
+ Exclude:
57
+ - 'lib/alba/resource.rb'
58
+ - 'test/**/*.rb'
59
+
60
+ # We need to eval resource code to test errors on resource classes
61
+ Security/Eval:
62
+ Exclude:
63
+ - 'test/**/*.rb'
64
+
38
65
  Style/ConstantVisibility:
39
66
  Exclude:
40
67
  - 'lib/alba/version.rb'
@@ -54,3 +81,7 @@ Style/InlineComment:
54
81
 
55
82
  Style/MethodCallWithArgsParentheses:
56
83
  Enabled: false
84
+
85
+ # There are so many cases we just want `if` expression!
86
+ Style/MissingElse:
87
+ EnforcedStyle: case
data/.yardopts CHANGED
@@ -1,2 +1,4 @@
1
1
  --no-private
2
2
  --exclude lib/alba/version.rb
3
+ --embed-mixin ClassMethods
4
+ --embed-mixin InstanceMethods
data/CHANGELOG.md ADDED
@@ -0,0 +1,35 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## [Unreleased]
8
+
9
+ ## [1.2.0] 2021-05-09
10
+
11
+ - [Fix] multiple word key inference [6c18e73]
12
+ - https://github.com/okuramasafumi/alba/pull/120
13
+ - Thank you @alfonsojimenez !
14
+ - [Feat] Add `Alba.enable_root_key_transformation!` [f172839]
15
+ - https://github.com/okuramasafumi/alba/pull/121
16
+ - [Feat] Implement type validation and auto conversion [cbe00c7]
17
+ - https://github.com/okuramasafumi/alba/pull/122
18
+
19
+ ## [1.1.0] - 2021-04-23
20
+
21
+ - [Feat] Implement circular associations control [71e1543]
22
+ - [Feat] Support :oj_rails backend [76e519e]
23
+
24
+ ## [1.0.1] - 2021-04-15
25
+
26
+ - [Fix] Don't cache resource class for `Alba.serialize` [9ed5253]
27
+ - [Improve] Warn when `ActiveSupport` or `Oj` are absent [d3ab3eb]
28
+ - [Fix] Delete unreachable `to_hash` method on Association [1ba1f90]
29
+ - [Fix] Stringify key before transforming [b4eb79e]
30
+ - [Misc] Support Ruby 2.5.0 and above, not 2.5.7 and above [43f1d17]
31
+ - [Fix] Remove accidentally added `p` debug [5d0324b]
32
+
33
+ ## [1.0.0] - 2021-04-07
34
+
35
+ 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,13 +4,19 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  gem 'activesupport', require: false # For backend
7
- gem 'coveralls', require: false # For test coverage
7
+ gem 'ffaker', require: false # For testing
8
8
  gem 'minitest', '~> 5.14' # For test
9
- gem 'oj', '~> 3.11', platform: :ruby, require: false # For backend
10
9
  gem 'rake', '~> 13.0' # For test and automation
11
10
  gem 'rubocop', '>= 0.79.0', require: false # For lint
12
- gem 'rubocop-minitest', '~> 0.10.3', require: false # For lint
13
- gem 'rubocop-performance', '~> 1.9.2', require: false # For lint
11
+ gem 'rubocop-minitest', '~> 0.12.0', require: false # For lint
12
+ gem 'rubocop-performance', '~> 1.11.0', require: false # For lint
14
13
  gem 'rubocop-rake', '>= 0.5.1', require: false # For lint
15
14
  gem 'rubocop-sensible', '~> 0.3.0', require: false # For lint
15
+ gem 'simplecov', '~> 0.21.0', require: false # For test coverage
16
+ gem 'simplecov-cobertura', require: false # For test coverage
16
17
  gem 'yard', require: false
18
+
19
+ platforms :ruby do
20
+ gem 'oj', '~> 3.11', require: false # For backend
21
+ gem 'ruby-prof', require: false # For performance profiling
22
+ end
data/README.md CHANGED
@@ -1,29 +1,37 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/alba.svg)](https://badge.fury.io/rb/alba)
2
- [![Build Status](https://travis-ci.com/okuramasafumi/alba.svg?branch=master)](https://travis-ci.com/okuramasafumi/alba)
3
- [![Coverage Status](https://coveralls.io/repos/github/okuramasafumi/alba/badge.svg?branch=master)](https://coveralls.io/github/okuramasafumi/alba?branch=master)
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
5
  ![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/okuramasafumi/alba)
6
6
  ![GitHub](https://img.shields.io/github/license/okuramasafumi/alba)
7
7
 
8
8
  # Alba
9
9
 
10
- `Alba` is the fastest JSON serializer for Ruby.
10
+ Alba is the fastest JSON serializer for Ruby, JRuby, and TruffleRuby.
11
11
 
12
- ## Why yet another JSON serializer?
12
+ ## Discussions
13
13
 
14
- We know that there are several other JSON serializers for Ruby around, but none of them made us satisfied.
14
+ Alba uses [GitHub Discussions](https://github.com/okuramasafumi/alba/discussions) to openly discuss the project.
15
15
 
16
- Alba has some advantages over other JSON serializers which we've wanted to have.
16
+ 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
17
 
18
- ### Easy to understand
18
+ 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
19
 
20
- DSL is great. It makes the coding experience natural and intuitive. However, remembering lots of DSL requires us a lot of effort. Unfortunately, most of the existing libraries have implemented their features via DSL and it's not easy to understand how they behave entirely. Alba's core DSL are only four (`attributes`, `attribute`, `one` and `many`) so it's easy to understand how to use.
20
+ ## Why Alba?
21
21
 
22
- Alba is also understandable internally. The codebase is much smaller than the alternatives. In fact, it's less than 300 lines of code. Look at the code on [GitHub](https://github.com/okuramasafumi/alba/tree/master/lib) and you'll be surprised how simple it is!
22
+ Because it's fast, flexible and well-maintained!
23
23
 
24
- ### Performance
24
+ ### Fast
25
25
 
26
- Alba is faster than most of the alternatives. We have a [benchmark](https://gist.github.com/okuramasafumi/4e375525bd3a28e4ca812d2a3b3e5829).
26
+ Alba is faster than most of the alternatives. We have a [benchmark](https://github.com/okuramasafumi/alba/tree/master/benchmark).
27
+
28
+ ### Flexible
29
+
30
+ 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.
31
+
32
+ ### Maintained
33
+
34
+ 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.
27
35
 
28
36
  ## Installation
29
37
 
@@ -43,22 +51,22 @@ Or install it yourself as:
43
51
 
44
52
  ## Supported Ruby versions
45
53
 
46
- Alba supports CRuby 2.5.7 and higher and latest TruffleRuby.
54
+ Alba supports CRuby 2.5 and higher and latest JRuby and TruffleRuby.
47
55
 
48
56
  ## Documentation
49
57
 
50
- You can find the documentation on [RubyDoc](https://rubydoc.info/gems/alba).
58
+ You can find the documentation on [RubyDoc](https://rubydoc.info/github/okuramasafumi/alba).
51
59
 
52
60
  ## Features
53
61
 
54
- * Resource-based serialization
55
- * Arbitrary attribute definition
56
- * One and many association with the ability to define them inline
57
- * Adding condition and filter to association
58
- * Parameters can be injected and used in attributes and associations
59
- * Setting root key separately in Serializer
60
- * Adding metadata
62
+ * Conditional attributes and associations
61
63
  * Selectable backend
64
+ * Key transformation
65
+ * Root key inference
66
+ * Error handling
67
+ * Resource name inflection based on association name
68
+ * Circular associations control
69
+ * Types for validation and conversion
62
70
  * No runtime dependencies
63
71
 
64
72
  ## Anti features
@@ -68,7 +76,6 @@ You can find the documentation on [RubyDoc](https://rubydoc.info/gems/alba).
68
76
  * Supporting all existing JSON encoder/decoder
69
77
  * Cache
70
78
  * [JSON:API](https://jsonapi.org) support
71
- * Association name inflection
72
79
  * And many others
73
80
 
74
81
  ## Usage
@@ -77,7 +84,7 @@ You can find the documentation on [RubyDoc](https://rubydoc.info/gems/alba).
77
84
 
78
85
  Alba's configuration is fairly simple.
79
86
 
80
- #### Backend
87
+ #### Backend configuration
81
88
 
82
89
  Backend is the actual part serializing an object into JSON. Alba supports these backends.
83
90
 
@@ -91,6 +98,26 @@ You can set a backend like this:
91
98
  Alba.backend = :oj
92
99
  ```
93
100
 
101
+ #### Inference configuration
102
+
103
+ You can enable inference feature using `enable_inference!` method.
104
+
105
+ ```ruby
106
+ Alba.enable_inference!
107
+ ```
108
+
109
+ You must install `ActiveSupport` to enable inference.
110
+
111
+ #### Error handling configuration
112
+
113
+ You can configure error handling with `on_error` method.
114
+
115
+ ```ruby
116
+ Alba.on_error :ignore
117
+ ```
118
+
119
+ For the details, see [Error handling section](#error-handling)
120
+
94
121
  ### Simple serialization with key
95
122
 
96
123
  ```ruby
@@ -108,6 +135,8 @@ end
108
135
  class UserResource
109
136
  include Alba::Resource
110
137
 
138
+ key :user
139
+
111
140
  attributes :id, :name
112
141
 
113
142
  attribute :name_with_email do |resource|
@@ -115,12 +144,6 @@ class UserResource
115
144
  end
116
145
  end
117
146
 
118
- class SerializerWithKey
119
- include Alba::Serializer
120
-
121
- set key: :user
122
- end
123
-
124
147
  user = User.new(1, 'Masafumi OKURA', 'masafumi@example.com')
125
148
  UserResource.new(user).serialize
126
149
  # => "{\"id\":1,\"name\":\"Masafumi OKURA\",\"name_with_email\":\"Masafumi OKURA: masafumi@example.com\"}"
@@ -180,7 +203,7 @@ UserResource.new(user).serialize
180
203
  `Alba.serialize` method is a shortcut to define everything inline.
181
204
 
182
205
  ```ruby
183
- Alba.serialize(user, with: proc { set key: :foo }) do
206
+ Alba.serialize(user, key: :foo) do
184
207
  attributes :id
185
208
  many :articles do
186
209
  attributes :title, :body
@@ -189,7 +212,7 @@ end
189
212
  # => '{"foo":{"id":1,"articles":[{"title":"Hello World!","body":"Hello World!!!"},{"title":"Super nice","body":"Really nice!"}]}}'
190
213
  ```
191
214
 
192
- Although this might be useful sometimes, it's generally recommended to define a class for both Resource and Serializer.
215
+ Although this might be useful sometimes, it's generally recommended to define a class for Resource.
193
216
 
194
217
  ### Inheritance and Ignorance
195
218
 
@@ -221,7 +244,7 @@ RestrictedFooResouce.new(foo).serialize
221
244
  end
222
245
  ```
223
246
 
224
- ### Attribute key transformation
247
+ ### Key transformation
225
248
 
226
249
  ** Note: You need to install `active_support` gem to use `transform_keys` DSL.
227
250
 
@@ -251,12 +274,260 @@ UserResourceCamel.new(user).serialize
251
274
  # => '{"id":1,"firstName":"Masafumi","lastName":"Okura"}'
252
275
  ```
253
276
 
277
+ You can also transform root key when:
278
+
279
+ * `Alba.enable_inference!` is called
280
+ * `key!` is called in Resource class
281
+ * `root` option of `transform_keys` is set to true or `Alba.enable_root_key_transformation!` is called.
282
+
283
+ ```ruby
284
+ Alba.enable_inference!
285
+
286
+ class BankAccount
287
+ attr_reader :account_number
288
+
289
+ def initialize(account_number)
290
+ @account_number = account_number
291
+ end
292
+ end
293
+
294
+ class BankAccountResource
295
+ include Alba::Resource
296
+
297
+ key!
298
+
299
+ attributes :account_number
300
+ transform_keys :dash, root: true
301
+ end
302
+
303
+ bank_account = BankAccount.new(123_456_789)
304
+ BankAccountResource.new(bank_account).serialize
305
+ # => '{"bank-account":{"account-number":123456789}}'
306
+ ```
307
+
308
+ This behavior to transform root key will become default at version 2.
309
+
254
310
  Supported transformation types are :camel, :lower_camel and :dash.
255
311
 
256
- ## Comparison
312
+ ### Filtering attributes
313
+
314
+ You can filter attributes by overriding `Alba::Resource#converter` method, but it's a bit tricky.
315
+
316
+ ```ruby
317
+ class User
318
+ attr_accessor :id, :name, :email, :created_at, :updated_at
319
+
320
+ def initialize(id, name, email)
321
+ @id = id
322
+ @name = name
323
+ @email = email
324
+ end
325
+ end
326
+
327
+ class UserResource
328
+ include Alba::Resource
329
+
330
+ attributes :id, :name, :email
331
+
332
+ private
333
+
334
+ # Here using `Proc#>>` method to compose a proc from `super`
335
+ def converter
336
+ super >> proc { |hash| hash.compact }
337
+ end
338
+ end
339
+
340
+ user = User.new(1, nil, nil)
341
+ UserResource.new(user).serialize # => '{"id":1}'
342
+ ```
343
+
344
+ The key part is the use of `Proc#>>` since `Alba::Resource#converter` returns a `Proc` which contains the basic logic and it's impossible to change its behavior by just overriding the method.
345
+
346
+ It's not recommended to swap the whole conversion logic. It's recommended to always call `super` when you override `converter`.
347
+
348
+ ### Conditional attributes
349
+
350
+ Filtering attributes with overriding `convert` works well for simple cases. However, It's cumbersome when we want to filter various attributes based on different conditions for keys.
351
+
352
+ In these cases, conditional attributes works well. We can pass `if` option to `attributes`, `attribute`, `one` and `many`. Below is an example for the same effect as [filtering attributes section](#filtering-attributes).
353
+
354
+ ```ruby
355
+ class User
356
+ attr_accessor :id, :name, :email, :created_at, :updated_at
357
+
358
+ def initialize(id, name, email)
359
+ @id = id
360
+ @name = name
361
+ @email = email
362
+ end
363
+ end
364
+
365
+ class UserResource
366
+ include Alba::Resource
367
+
368
+ attributes :id, :name, :email, if: proc { |user, attribute| !attribute.nil? }
369
+ end
370
+
371
+ user = User.new(1, nil, nil)
372
+ UserResource.new(user).serialize # => '{"id":1}'
373
+ ```
374
+
375
+ ### Inference
376
+
377
+ After `Alba.enable_inference!` called, Alba tries to infer root key and association resource name.
378
+
379
+ ```ruby
380
+ Alba.enable_inference!
381
+
382
+ class User
383
+ attr_reader :id
384
+ attr_accessor :articles
385
+
386
+ def initialize(id)
387
+ @id = id
388
+ @articles = []
389
+ end
390
+ end
391
+
392
+ class Article
393
+ attr_accessor :id, :title
394
+
395
+ def initialize(id, title)
396
+ @id = id
397
+ @title = title
398
+ end
399
+ end
400
+
401
+ class ArticleResource
402
+ include Alba::Resource
403
+
404
+ attributes :title
405
+ end
406
+
407
+ class UserResource
408
+ include Alba::Resource
409
+
410
+ key!
411
+
412
+ attributes :id
413
+
414
+ many :articles
415
+ end
416
+
417
+ user = User.new(1)
418
+ user.articles << Article.new(1, 'The title')
419
+
420
+ UserResource.new(user).serialize # => '{"user":{"id":1,"articles":[{"title":"The title"}]}}'
421
+ UserResource.new([user]).serialize # => '{"users":[{"id":1,"articles":[{"title":"The title"}]}]}'
422
+ ```
423
+
424
+ This resource automatically sets its root key to either "users" or "user", depending on the given object is collection or not.
425
+
426
+ Also, you don't have to specify which resource class to use with `many`. Alba infers it from association name.
427
+
428
+ Note that to enable this feature you must install `ActiveSupport` gem.
429
+
430
+ ### Error handling
431
+
432
+ You can set error handler globally or per resource using `on_error`.
433
+
434
+ ```ruby
435
+ class User
436
+ attr_accessor :id, :name
437
+
438
+ def initialize(id, name, email)
439
+ @id = id
440
+ @name = name
441
+ @email = email
442
+ end
443
+
444
+ def email
445
+ raise RuntimeError, 'Error!'
446
+ end
447
+ end
448
+
449
+ class UserResource
450
+ include Alba::Resource
451
+
452
+ attributes :id, :name, :email
453
+
454
+ on_error :ignore
455
+ end
456
+
457
+ user = User.new(1, 'Test', 'email@example.com')
458
+ UserResource.new(user).serialize # => '{"id":1,"name":"Test"}'
459
+ ```
460
+
461
+ This way you can exclude an entry when fetching an attribute gives an exception.
462
+
463
+ There are four possible arguments `on_error` method accepts.
464
+
465
+ * `:raise` re-raises an error. This is the default behavior.
466
+ * `:ignore` ignores the entry with the error.
467
+ * `:nullify` sets the attribute with the error to `nil`.
468
+ * Block gives you more control over what to be returned.
469
+
470
+ The block receives five arguments, `error`, `object`, `key`, `attribute` and `resource class` and must return a two-element array. Below is an example.
471
+
472
+ ```ruby
473
+ # Global error handling
474
+ Alba.on_error do |error, object, key, attribute, resource_class|
475
+ if resource_class == MyResource
476
+ ['error_fallback', object.error_fallback]
477
+ else
478
+ [key, error.message]
479
+ end
480
+ end
481
+ ```
482
+
483
+ ### Circular associations control
484
+
485
+ 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.
486
+
487
+ For more details, please refer to [test code](https://github.com/okuramasafumi/alba/blob/master/test/usecases/circular_association_test.rb)
488
+
489
+ ### Types
490
+
491
+ You can validate and convert input with types.
492
+
493
+ ```ruby
494
+ class User
495
+ attr_reader :id, :name, :age, :bio, :admin, :created_at
496
+
497
+ def initialize(id, name, age, bio = '', admin = false) # rubocop:disable Style/OptionalBooleanParameter
498
+ @id = id
499
+ @name = name
500
+ @age = age
501
+ @admin = admin
502
+ @bio = bio
503
+ @created_at = Time.new(2020, 10, 10)
504
+ end
505
+ end
506
+
507
+ class UserResource
508
+ include Alba::Resource
509
+
510
+ attributes :name, id: [String, true], age: [Integer, true], bio: String, admin: [:Boolean, true], created_at: [String, ->(object) { object.strftime('%F') }]
511
+ end
257
512
 
258
- Alba is faster than alternatives.
259
- For a performance benchmark, see https://gist.github.com/okuramasafumi/4e375525bd3a28e4ca812d2a3b3e5829.
513
+ user = User.new(1, 'Masafumi OKURA', '32', 'Ruby dev')
514
+ UserResource.new(user).serialize
515
+ # => '{"name":"Masafumi OKURA","id":"1","age":32,"bio":"Ruby dev","admin":false,"created_at":"2020-10-10"}'
516
+ ```
517
+
518
+ Notice that `id` and `created_at` are converted to String and `age` is converted to Integer.
519
+
520
+ If type is not correct and auto conversion is disabled (default), `TypeError` occurs.
521
+
522
+ ```ruby
523
+ user = User.new(1, 'Masafumi OKURA', '32', nil) # bio is nil and auto conversion is disabled for bio
524
+ UserResource.new(user).serialize
525
+ # => TypeError, 'Attribute bio is expected to be String but actually nil.'
526
+ ```
527
+
528
+ ### Caching
529
+
530
+ 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).
260
531
 
261
532
  ## Rails
262
533
 
@@ -264,23 +535,20 @@ When you use Alba in Rails, you can create an initializer file with the line bel
264
535
 
265
536
  ```ruby
266
537
  Alba.backend = :active_support
538
+ # or
539
+ Alba.backend = :oj_rails
267
540
  ```
268
541
 
269
542
  ## Why named "Alba"?
270
543
 
271
544
  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.
272
545
 
273
- ## Alba internals
274
-
275
- Alba has three component, `Serializer`, `Resource` and `Value` (`Value` is conceptual and not implemented directly).
276
-
277
- `Serializer` is a component responsible for rendering JSON output with `Resource`. `Serializer` can add more data to `Resource` such as `metadata`. Users can define one single `Serializer` and reuse it for all `Resource`s. The main interface is `#serialize`.
278
-
279
- `Resource` is a component responsible for defining how an object (or a collection of objects) is converted into JSON. The difference between `Serializer` and `Resource` is that while `Serializer` can add arbitrary data into JSON, `Resource` can get data only from the object under it. The main interface is `#serializable_hash`.
546
+ ## Pioneers
280
547
 
281
- `One` and `Many` are the special object fetching other resources and converting them into Hash.
548
+ There are great pioneers in Ruby's ecosystem which does basically the same thing as Alba does. To name a few:
282
549
 
283
- The main `Alba` module holds config values and one convenience method, `.serialize`.
550
+ * [ActiveModelSerializers](https://github.com/rails-api/active_model_serializers) a.k.a AMS, the most famous implementation of JSON serializer for Ruby
551
+ * [Blueprinter](https://github.com/procore/blueprinter) shares some concepts with Alba
284
552
 
285
553
  ## Development
286
554