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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c3999a63b9564e6bf0a7b00dfeb64a19d6d076c94047f7ee869a4ac4a38c6646
4
- data.tar.gz: dd7244bf63111e4795639a62dac91a744e51adb38f6a88d851c9cc6de3043dc0
3
+ metadata.gz: 01d43d93e589197104d9ee166e6a9dcf727fd204524b52145d7d93b45ba79cd2
4
+ data.tar.gz: 023f2e0f8ff17bc78a01e2dc1eb1f98d9c41b0cd353e311f350c2704a9ffa602
5
5
  SHA512:
6
- metadata.gz: f93aaadf9b8fae4b9ae334f87647c3b0f1645147fd237b62f12451f4306f5cb6699ed25c935b563b68ab45c965bb1cfb8f2c73e08b23899a477384c37124d325
7
- data.tar.gz: adb456a4796782e725bffa5720d1b5e6f831c1e0ec215721a814b331890f4a1d0ebe592e7e906239138729dd81586ad6e1c850ece61c5495ce829551feada9a1
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
@@ -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
@@ -8,3 +8,4 @@
8
8
  /tmp/
9
9
 
10
10
  Gemfile.lock
11
+ /gemfiles/*.lock
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
- # Oneline comment is not valid so until it gets valid, we disable it
21
- Bundler/GemComment:
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
- Metrics/ClassLength:
43
+ # In test code we don't care about the metrics!
44
+ Metrics:
34
45
  Exclude:
35
- - 'test/alba_test.rb'
46
+ - 'test/**/*.rb'
36
47
 
37
- Metrics/MethodLength:
38
- Max: 15
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
- - 'lib/alba/resource.rb'
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
- Enabled: false
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
@@ -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 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 'coveralls', require: false # For test coverage
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.11.0', require: false # For lint
13
- gem 'rubocop-performance', '~> 1.10.1', require: false # For lint
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 'ruby-prof', require: false # For performance profiling
17
- gem 'yard', require: false
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
- [![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
+ [![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
- `Alba` is the fastest JSON serializer for Ruby.
11
+ Alba is the fastest JSON serializer for Ruby, JRuby, and TruffleRuby.
11
12
 
12
- ## Why yet another JSON serializer?
13
+ ## Discussions
13
14
 
14
- We know that there are several other JSON serializers for Ruby around, but none of them made us satisfied.
15
+ Alba uses [GitHub Discussions](https://github.com/okuramasafumi/alba/discussions) to openly discuss the project.
15
16
 
16
- Alba has some advantages over other JSON serializers which we've wanted to have.
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
- ### Easy to understand
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
- 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.
21
+ ## Why Alba?
21
22
 
22
- ### Performance
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.7 and higher and latest TruffleRuby.
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 RestrictedFooResouce < GenericFooResource
285
+ class RestrictedFooResource < GenericFooResource
232
286
  ignoring :id, :body
233
287
  end
234
288
 
235
- RestrictedFooResouce.new(foo).serialize
289
+ RestrictedFooResource.new(foo).serialize
236
290
  # => '{"name":"my foo"}'
237
- end
238
291
  ```
239
292
 
240
- ### Attribute key transformation
293
+ ### Key transformation
241
294
 
242
- ** Note: You need to install `active_support` gem to use `transform_keys` DSL.
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 `active_support` installed, you can transform attribute keys.
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
- ## Comparison
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
- Alba is faster than alternatives.
446
- For a performance benchmark, see https://gist.github.com/okuramasafumi/4e375525bd3a28e4ca812d2a3b3e5829.
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.