alba 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/codeql-analysis.yml +70 -0
- data/.github/workflows/main.yml +3 -1
- data/.rubocop.yml +2 -0
- data/CHANGELOG.md +10 -0
- data/Gemfile +2 -2
- data/README.md +240 -92
- data/alba.gemspec +7 -3
- data/benchmark/collection.rb +60 -4
- data/benchmark/single_resource.rb +33 -2
- data/gemfiles/all.gemfile +1 -0
- data/lib/alba/association.rb +28 -8
- data/lib/alba/default_inflector.rb +19 -1
- data/lib/alba/errors.rb +10 -0
- data/lib/alba/resource.rb +117 -61
- data/lib/alba/version.rb +1 -1
- data/lib/alba.rb +44 -16
- metadata +9 -8
- data/lib/alba/key_transform_factory.rb +0 -33
- data/lib/alba/many.rb +0 -21
- data/lib/alba/one.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c7ab62feaab9747cd1441aea57b8d8f7f1d149261d86e4e64e9f859ae8f1091c
|
4
|
+
data.tar.gz: 4442a77416ee235ad6537580ec0600c4b1c9b0bea7b154b3a96010514316531d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 373f00c4b2bf57f18d65d86a9c8209f9d0c94038f44a98df8e7c7768158d23e03f427729006eb113f0e88fde17a829364cf561026858c83bbb861a7d7eefba13
|
7
|
+
data.tar.gz: ad75036a0f554d13637d32413906f17166347459f2e3bcd3e1fe55f4ddc5d805da59ece5c1e46babadea434d667927aab4e3b7f6b0d37887ee6f58cae10396c9
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# For most projects, this workflow file will not need changing; you simply need
|
2
|
+
# to commit it to your repository.
|
3
|
+
#
|
4
|
+
# You may wish to alter this file to override the set of languages analyzed,
|
5
|
+
# or to provide custom queries or build logic.
|
6
|
+
#
|
7
|
+
# ******** NOTE ********
|
8
|
+
# We have attempted to detect the languages in your repository. Please check
|
9
|
+
# the `language` matrix defined below to confirm you have the correct set of
|
10
|
+
# supported CodeQL languages.
|
11
|
+
#
|
12
|
+
name: "CodeQL"
|
13
|
+
|
14
|
+
on:
|
15
|
+
push:
|
16
|
+
branches: [ main ]
|
17
|
+
pull_request:
|
18
|
+
# The branches below must be a subset of the branches above
|
19
|
+
branches: [ main ]
|
20
|
+
schedule:
|
21
|
+
- cron: '21 4 * * 5'
|
22
|
+
|
23
|
+
jobs:
|
24
|
+
analyze:
|
25
|
+
name: Analyze
|
26
|
+
runs-on: ubuntu-latest
|
27
|
+
permissions:
|
28
|
+
actions: read
|
29
|
+
contents: read
|
30
|
+
security-events: write
|
31
|
+
|
32
|
+
strategy:
|
33
|
+
fail-fast: false
|
34
|
+
matrix:
|
35
|
+
language: [ 'ruby' ]
|
36
|
+
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ]
|
37
|
+
# Learn more about CodeQL language support at https://git.io/codeql-language-support
|
38
|
+
|
39
|
+
steps:
|
40
|
+
- name: Checkout repository
|
41
|
+
uses: actions/checkout@v2
|
42
|
+
|
43
|
+
# Initializes the CodeQL tools for scanning.
|
44
|
+
- name: Initialize CodeQL
|
45
|
+
uses: github/codeql-action/init@v1
|
46
|
+
with:
|
47
|
+
languages: ${{ matrix.language }}
|
48
|
+
# If you wish to specify custom queries, you can do so here or in a config file.
|
49
|
+
# By default, queries listed here will override any specified in a config file.
|
50
|
+
# Prefix the list here with "+" to use these queries and those in the config file.
|
51
|
+
# queries: ./path/to/local/query, your-org/your-repo/queries@main
|
52
|
+
|
53
|
+
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
54
|
+
# If this step fails, then you should remove it and run the build manually (see below)
|
55
|
+
- name: Autobuild
|
56
|
+
uses: github/codeql-action/autobuild@v1
|
57
|
+
|
58
|
+
# ℹ️ Command-line programs to run using the OS shell.
|
59
|
+
# 📚 https://git.io/JvXDl
|
60
|
+
|
61
|
+
# ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
|
62
|
+
# and modify them (or add more) to build your code if your project
|
63
|
+
# uses a compiled language
|
64
|
+
|
65
|
+
#- run: |
|
66
|
+
# make bootstrap
|
67
|
+
# make release
|
68
|
+
|
69
|
+
- name: Perform CodeQL Analysis
|
70
|
+
uses: github/codeql-action/analyze@v1
|
data/.github/workflows/main.yml
CHANGED
@@ -8,9 +8,11 @@ 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, jruby, truffleruby]
|
11
|
+
ruby: [2.5, 2.6, 2.7, 3.0, 3.1, head, jruby, truffleruby]
|
12
12
|
gemfile: [all, without_active_support, without_oj]
|
13
13
|
exclude:
|
14
|
+
- os: windows-latest
|
15
|
+
ruby: 3.1
|
14
16
|
- os: windows-latest
|
15
17
|
ruby: jruby
|
16
18
|
- os: windows-latest
|
data/.rubocop.yml
CHANGED
@@ -46,9 +46,11 @@ Metrics:
|
|
46
46
|
- 'test/**/*.rb'
|
47
47
|
|
48
48
|
# `Resource` module is a core module and its length tends to be long...
|
49
|
+
# `Alba` main module is also long because it has all parts of configuration
|
49
50
|
Metrics/ModuleLength:
|
50
51
|
Exclude:
|
51
52
|
- 'lib/alba/resource.rb'
|
53
|
+
- 'lib/alba.rb'
|
52
54
|
|
53
55
|
# Resource class includes DSLs, which tend to accept long list of parameters
|
54
56
|
Metrics/ParameterLists:
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [1.6.0] 2022-03-16
|
10
|
+
|
11
|
+
- [Feat] Support instance method as an attribute
|
12
|
+
- [Fix] Explicitly raise error when inference is disabled
|
13
|
+
- [Improve] `enable_inference!` now takes inflector as argument
|
14
|
+
- [Improve] `transform_keys` now accepts `:snake` and `:none`
|
15
|
+
- [Deprecate] `to_hash` is special method and should not be used
|
16
|
+
- [Deprecate] `ignoring` in favor of `attributes` overriding
|
17
|
+
- [Deprecate] `Alba.on_nil`, `Alba.on_error` and `Alba.enable_root_key_transformation!`
|
18
|
+
|
9
19
|
## [1.5.0] 2021-11-28
|
10
20
|
|
11
21
|
- [Feat] Add nil handler
|
data/Gemfile
CHANGED
@@ -9,8 +9,8 @@ gem 'inch', require: false # For inline documents
|
|
9
9
|
gem 'minitest', '~> 5.14' # For test
|
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.18.0', require: false # For lint
|
13
|
+
gem 'rubocop-performance', '~> 1.13.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
16
|
gem 'simplecov', '~> 0.21.0', require: false # For test coverage
|
data/README.md
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
[](https://github.com/okuramasafumi/alba/actions/workflows/main.yml)
|
3
3
|
[](https://codecov.io/gh/okuramasafumi/alba)
|
4
4
|
[](https://codeclimate.com/github/okuramasafumi/alba/maintainability)
|
5
|
-
[](http://inch-ci.org/github/okuramasafumi/alba)
|
6
5
|

|
7
6
|

|
8
7
|
|
@@ -20,19 +19,19 @@ If you have feature requests or interesting ideas, join us with [Ideas](https://
|
|
20
19
|
|
21
20
|
## Why Alba?
|
22
21
|
|
23
|
-
Because it's fast,
|
22
|
+
Because it's fast, easy-to-use and extensible!
|
24
23
|
|
25
24
|
### Fast
|
26
25
|
|
27
26
|
Alba is faster than most of the alternatives. We have a [benchmark](https://github.com/okuramasafumi/alba/tree/master/benchmark).
|
28
27
|
|
29
|
-
###
|
28
|
+
### Easy to use
|
30
29
|
|
31
|
-
Alba provides
|
30
|
+
Alba provides four DSLs, `attributes` to fetch attribute with its name, `attribute` to execute block for the attribute, `one` to seriaize single attribute with another resource, and `many` to serialize collection attribute with another resource. When you want to do something complex, there are many examples in this README so you can mimic them to get started.
|
32
31
|
|
33
|
-
###
|
32
|
+
### Extensible
|
34
33
|
|
35
|
-
Alba
|
34
|
+
Alba embraces extensibility through common techniques such as class inheritance and module inclusion. Alba provides its capacity with one module so you can still have your own class hierarchy.
|
36
35
|
|
37
36
|
## Installation
|
38
37
|
|
@@ -72,15 +71,6 @@ You can find the documentation on [RubyDoc](https://rubydoc.info/github/okuramas
|
|
72
71
|
* Layout
|
73
72
|
* No runtime dependencies
|
74
73
|
|
75
|
-
## Anti features
|
76
|
-
|
77
|
-
* Sorting keys
|
78
|
-
* Class level support of parameters
|
79
|
-
* Supporting all existing JSON encoder/decoder
|
80
|
-
* Cache
|
81
|
-
* [JSON:API](https://jsonapi.org) support
|
82
|
-
* And many others
|
83
|
-
|
84
74
|
## Usage
|
85
75
|
|
86
76
|
### Configuration
|
@@ -116,23 +106,21 @@ You can consider setting a backend with Symbol as a shortcut to set encoder.
|
|
116
106
|
You can enable inference feature using `enable_inference!` method.
|
117
107
|
|
118
108
|
```ruby
|
119
|
-
Alba.enable_inference!
|
109
|
+
Alba.enable_inference!(with: :active_support)
|
120
110
|
```
|
121
111
|
|
122
|
-
You
|
123
|
-
|
124
|
-
#### Error handling configuration
|
112
|
+
You can choose which inflector Alba uses for inference. Possible value for `with` option are:
|
125
113
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
Alba.on_error :ignore
|
130
|
-
```
|
114
|
+
- `:active_support` for `ActiveSupport::Inflector`
|
115
|
+
- `:dry` for `Dry::Inflector`
|
116
|
+
- any object which responds to some methods (see [below](#custom-inflector))
|
131
117
|
|
132
118
|
For the details, see [Error handling section](#error-handling)
|
133
119
|
|
134
120
|
### Simple serialization with root key
|
135
121
|
|
122
|
+
You can define attributes with (yes) `attributes` macro with attribute names. If your attribute need some calculations, you can use `attribute` with block.
|
123
|
+
|
136
124
|
```ruby
|
137
125
|
class User
|
138
126
|
attr_accessor :id, :name, :email, :created_at, :updated_at
|
@@ -159,7 +147,34 @@ end
|
|
159
147
|
|
160
148
|
user = User.new(1, 'Masafumi OKURA', 'masafumi@example.com')
|
161
149
|
UserResource.new(user).serialize
|
162
|
-
# => "{\"id\":1,\"name\":\"Masafumi OKURA\",\"name_with_email\":\"Masafumi OKURA: masafumi@example.com\"}"
|
150
|
+
# => "{\"user\":{\"id\":1,\"name\":\"Masafumi OKURA\",\"name_with_email\":\"Masafumi OKURA: masafumi@example.com\"}}"
|
151
|
+
```
|
152
|
+
|
153
|
+
You can define instance methods on resources so that you can use it as attribute name in `attributes`.
|
154
|
+
|
155
|
+
```ruby
|
156
|
+
# The serialization result is the same as above
|
157
|
+
class UserResource
|
158
|
+
include Alba::Resource
|
159
|
+
|
160
|
+
root_key :user, :users # Later is for plural
|
161
|
+
|
162
|
+
attributes :id, :name, :name_with_email
|
163
|
+
|
164
|
+
# Attribute methods must accept one argument for each serialized object
|
165
|
+
def name_with_email(user)
|
166
|
+
"#{user.name}: #{user.email}"
|
167
|
+
end
|
168
|
+
end
|
169
|
+
```
|
170
|
+
|
171
|
+
This even works with users collection.
|
172
|
+
|
173
|
+
```ruby
|
174
|
+
user1 = User.new(1, 'Masafumi OKURA', 'masafumi@example.com')
|
175
|
+
user2 = User.new(2, 'Test User', 'test@example.com')
|
176
|
+
UserResource.new([user1, user2]).serialize
|
177
|
+
# => "{\"users\":[{\"id\":1,\"name\":\"Masafumi OKURA\",\"name_with_email\":\"Masafumi OKURA: masafumi@example.com\"},{\"id\":2,\"name\":\"Test User\",\"name_with_email\":\"Test User: test@example.com\"}]}"
|
163
178
|
```
|
164
179
|
|
165
180
|
### Serialization with associations
|
@@ -240,6 +255,63 @@ class AnotherUserResource
|
|
240
255
|
end
|
241
256
|
```
|
242
257
|
|
258
|
+
You can "filter" association using second proc argument. This proc takes association object and `params`.
|
259
|
+
|
260
|
+
This feature is useful when you want to modify association, such as adding `includes` or `order` to ActiveRecord relations.
|
261
|
+
|
262
|
+
```ruby
|
263
|
+
class User
|
264
|
+
attr_reader :id
|
265
|
+
attr_accessor :articles
|
266
|
+
|
267
|
+
def initialize(id)
|
268
|
+
@id = id
|
269
|
+
@articles = []
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
class Article
|
274
|
+
attr_accessor :id, :title, :body
|
275
|
+
|
276
|
+
def initialize(id, title, body)
|
277
|
+
@id = id
|
278
|
+
@title = title
|
279
|
+
@body = body
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
class ArticleResource
|
284
|
+
include Alba::Resource
|
285
|
+
|
286
|
+
attributes :title
|
287
|
+
end
|
288
|
+
|
289
|
+
class UserResource
|
290
|
+
include Alba::Resource
|
291
|
+
|
292
|
+
attributes :id
|
293
|
+
|
294
|
+
# Second proc works as a filter
|
295
|
+
many :articles,
|
296
|
+
proc { |articles, params|
|
297
|
+
filter = params[:filter] || :odd?
|
298
|
+
articles.select {|a| a.id.send(filter) }
|
299
|
+
},
|
300
|
+
resource: ArticleResource
|
301
|
+
end
|
302
|
+
|
303
|
+
user = User.new(1)
|
304
|
+
article1 = Article.new(1, 'Hello World!', 'Hello World!!!')
|
305
|
+
user.articles << article1
|
306
|
+
article2 = Article.new(2, 'Super nice', 'Really nice!')
|
307
|
+
user.articles << article2
|
308
|
+
|
309
|
+
UserResource.new(user).serialize
|
310
|
+
# => '{"id":1,"articles":[{"title":"Hello World!"}]}'
|
311
|
+
UserResource.new(user, params: {filter: :even?}).serialize
|
312
|
+
# => '{"id":1,"articles":[{"title":"Super nice"}]}'
|
313
|
+
```
|
314
|
+
|
243
315
|
### Inline definition with `Alba.serialize`
|
244
316
|
|
245
317
|
`Alba.serialize` method is a shortcut to define everything inline.
|
@@ -263,9 +335,11 @@ Alba.serialize(something)
|
|
263
335
|
|
264
336
|
Although this might be useful sometimes, it's generally recommended to define a class for Resource.
|
265
337
|
|
266
|
-
### Inheritance and
|
338
|
+
### Inheritance and attributes filter
|
267
339
|
|
268
|
-
You can
|
340
|
+
You can filter out certain attributes by overriding `attributes` instance method. This is useful when you want to customize existing resource with inheritance.
|
341
|
+
|
342
|
+
You can access raw attributes via `super` call. It returns a Hash whose keys are the name of the attribute and whose values are the body. Usually you need only keys to filter out, like below.
|
269
343
|
|
270
344
|
```ruby
|
271
345
|
class Foo
|
@@ -285,7 +359,9 @@ class GenericFooResource
|
|
285
359
|
end
|
286
360
|
|
287
361
|
class RestrictedFooResource < GenericFooResource
|
288
|
-
|
362
|
+
def attributes
|
363
|
+
super.select { |key, _| key.to_sym == :name }
|
364
|
+
end
|
289
365
|
end
|
290
366
|
|
291
367
|
RestrictedFooResource.new(foo).serialize
|
@@ -324,14 +400,22 @@ UserResourceCamel.new(user).serialize
|
|
324
400
|
# => '{"id":1,"firstName":"Masafumi","lastName":"Okura"}'
|
325
401
|
```
|
326
402
|
|
403
|
+
Possible values for `transform_keys` argument are:
|
404
|
+
|
405
|
+
* `:camel` for CamelCase
|
406
|
+
* `:lower_camel` for lowerCamelCase
|
407
|
+
* `:dash` for dash-case
|
408
|
+
* `:snake` for snake_case
|
409
|
+
* `:none` for not transforming keys
|
410
|
+
|
327
411
|
You can also transform root key when:
|
328
412
|
|
329
413
|
* `Alba.enable_inference!` is called
|
330
414
|
* `root_key!` is called in Resource class
|
331
|
-
* `root` option of `transform_keys` is set to true
|
415
|
+
* `root` option of `transform_keys` is set to true
|
332
416
|
|
333
417
|
```ruby
|
334
|
-
Alba.enable_inference!
|
418
|
+
Alba.enable_inference!(with: :active_support) # with :dry also works
|
335
419
|
|
336
420
|
class BankAccount
|
337
421
|
attr_reader :account_number
|
@@ -361,30 +445,29 @@ Supported transformation types are :camel, :lower_camel and :dash.
|
|
361
445
|
|
362
446
|
#### Custom inflector
|
363
447
|
|
364
|
-
A custom inflector can be plugged in as follows
|
365
|
-
|
366
|
-
Alba.inflector = MyCustomInflector
|
367
|
-
```
|
368
|
-
...and has to implement following interface (the parameter `key` is of type `String`):
|
448
|
+
A custom inflector can be plugged in as follows.
|
449
|
+
|
369
450
|
```ruby
|
370
|
-
module
|
371
|
-
|
372
|
-
|
451
|
+
module CustomInflector
|
452
|
+
module_function
|
453
|
+
|
454
|
+
def camelize(string)
|
455
|
+
end
|
456
|
+
|
457
|
+
def camelize_lower(string)
|
373
458
|
end
|
374
459
|
|
375
|
-
def
|
376
|
-
raise "Not implemented"
|
460
|
+
def dasherize(string)
|
377
461
|
end
|
378
462
|
|
379
|
-
def
|
380
|
-
|
463
|
+
def underscore(string)
|
464
|
+
end
|
465
|
+
|
466
|
+
def classify(string)
|
381
467
|
end
|
382
468
|
end
|
383
469
|
|
384
|
-
|
385
|
-
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:
|
386
|
-
```ruby
|
387
|
-
Alba.inflector = Dry::Inflector.new
|
470
|
+
Alba.enable_inference!(with: CustomInflector)
|
388
471
|
```
|
389
472
|
|
390
473
|
### Filtering attributes
|
@@ -469,7 +552,7 @@ We believe this is clearer than using some (not implemented yet) DSL such as `de
|
|
469
552
|
After `Alba.enable_inference!` called, Alba tries to infer root key and association resource name.
|
470
553
|
|
471
554
|
```ruby
|
472
|
-
Alba.enable_inference!
|
555
|
+
Alba.enable_inference!(with: :active_support) # with :dry also works
|
473
556
|
|
474
557
|
class User
|
475
558
|
attr_reader :id
|
@@ -517,8 +600,6 @@ This resource automatically sets its root key to either "users" or "user", depen
|
|
517
600
|
|
518
601
|
Also, you don't have to specify which resource class to use with `many`. Alba infers it from association name.
|
519
602
|
|
520
|
-
Note that to enable this feature you must install `ActiveSupport` gem.
|
521
|
-
|
522
603
|
### Error handling
|
523
604
|
|
524
605
|
You can set error handler globally or per resource using `on_error`.
|
@@ -562,12 +643,14 @@ There are four possible arguments `on_error` method accepts.
|
|
562
643
|
The block receives five arguments, `error`, `object`, `key`, `attribute` and `resource class` and must return a two-element array. Below is an example.
|
563
644
|
|
564
645
|
```ruby
|
565
|
-
|
566
|
-
Alba
|
567
|
-
|
568
|
-
|
569
|
-
|
570
|
-
|
646
|
+
class ExampleResource
|
647
|
+
include Alba::Resource
|
648
|
+
on_error do |error, object, key, attribute, resource_class|
|
649
|
+
if resource_class == MyResource
|
650
|
+
['error_fallback', object.error_fallback]
|
651
|
+
else
|
652
|
+
[key, error.message]
|
653
|
+
end
|
571
654
|
end
|
572
655
|
end
|
573
656
|
```
|
@@ -624,43 +707,6 @@ UserResource.new(User.new(1)).serialize
|
|
624
707
|
# => '{"user":{"id":1,"name":"User1","age":20}}'
|
625
708
|
```
|
626
709
|
|
627
|
-
You can also set global nil handler.
|
628
|
-
|
629
|
-
```ruby
|
630
|
-
Alba.on_nil { 'default name' }
|
631
|
-
|
632
|
-
class Foo
|
633
|
-
attr_reader :name
|
634
|
-
def initialize(name)
|
635
|
-
@name = name
|
636
|
-
end
|
637
|
-
end
|
638
|
-
|
639
|
-
class FooResource
|
640
|
-
include Alba::Resource
|
641
|
-
|
642
|
-
key :foo
|
643
|
-
|
644
|
-
attributes :name
|
645
|
-
end
|
646
|
-
|
647
|
-
FooResource.new(Foo.new).serialize
|
648
|
-
# => '{"foo":{"name":"default name"}}'
|
649
|
-
|
650
|
-
class FooResource2
|
651
|
-
include Alba::Resource
|
652
|
-
|
653
|
-
key :foo
|
654
|
-
|
655
|
-
on_nil { '' } # This is applied instead of global handler
|
656
|
-
|
657
|
-
attributes :name
|
658
|
-
end
|
659
|
-
|
660
|
-
FooResource2.new(Foo.new).serialize
|
661
|
-
# => '{"foo":{"name":""}}'
|
662
|
-
```
|
663
|
-
|
664
710
|
### Metadata
|
665
711
|
|
666
712
|
You can set a metadata with `meta` DSL or `meta` option.
|
@@ -824,6 +870,108 @@ Also note that we use percentage notation here to use double quotes. Using singl
|
|
824
870
|
|
825
871
|
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).
|
826
872
|
|
873
|
+
### Extend Alba
|
874
|
+
|
875
|
+
Sometimes we have shared behaviors across resources. In such cases we can have a module for common logic.
|
876
|
+
|
877
|
+
In `attribute` block we can call instance method so we can improve the code below:
|
878
|
+
|
879
|
+
```ruby
|
880
|
+
class FooResource
|
881
|
+
include Alba::Resource
|
882
|
+
# other attributes
|
883
|
+
attribute :created_at do |foo|
|
884
|
+
foo.created_at.strftime('%m/%d/%Y')
|
885
|
+
end
|
886
|
+
|
887
|
+
attribute :updated_at do |foo|
|
888
|
+
foo.updated_at.strftime('%m/%d/%Y')
|
889
|
+
end
|
890
|
+
end
|
891
|
+
|
892
|
+
class BarResource
|
893
|
+
include Alba::Resource
|
894
|
+
# other attributes
|
895
|
+
attribute :created_at do |bar|
|
896
|
+
bar.created_at.strftime('%m/%d/%Y')
|
897
|
+
end
|
898
|
+
|
899
|
+
attribute :updated_at do |bar|
|
900
|
+
bar.updated_at.strftime('%m/%d/%Y')
|
901
|
+
end
|
902
|
+
end
|
903
|
+
```
|
904
|
+
|
905
|
+
to:
|
906
|
+
|
907
|
+
```ruby
|
908
|
+
module SharedLogic
|
909
|
+
def format_time(time)
|
910
|
+
time.strftime('%m/%d/%Y')
|
911
|
+
end
|
912
|
+
end
|
913
|
+
|
914
|
+
class FooResource
|
915
|
+
include Alba::Resource
|
916
|
+
include SharedLogic
|
917
|
+
# other attributes
|
918
|
+
attribute :created_at do |foo|
|
919
|
+
format_time(foo.created_at)
|
920
|
+
end
|
921
|
+
|
922
|
+
attribute :updated_at do |foo|
|
923
|
+
format_time(foo.updated_at)
|
924
|
+
end
|
925
|
+
end
|
926
|
+
|
927
|
+
class BarResource
|
928
|
+
include Alba::Resource
|
929
|
+
include SharedLogic
|
930
|
+
# other attributes
|
931
|
+
attribute :created_at do |bar|
|
932
|
+
format_time(bar.created_at)
|
933
|
+
end
|
934
|
+
|
935
|
+
attribute :updated_at do |bar|
|
936
|
+
format_time(bar.updated_at)
|
937
|
+
end
|
938
|
+
end
|
939
|
+
```
|
940
|
+
|
941
|
+
We can even add our own DSL to serialize attributes for readability and removing code duplications.
|
942
|
+
|
943
|
+
To do so, we need to `extend` our module. Let's see how we can achieve the same goal with this approach.
|
944
|
+
|
945
|
+
```ruby
|
946
|
+
module AlbaExtension
|
947
|
+
# Here attrs are an Array of Symbol
|
948
|
+
def formatted_time_attributes(*attrs)
|
949
|
+
attrs.each do |attr|
|
950
|
+
attribute attr do |object|
|
951
|
+
time = object.send(attr)
|
952
|
+
time.strftime('%m/%d/%Y')
|
953
|
+
end
|
954
|
+
end
|
955
|
+
end
|
956
|
+
end
|
957
|
+
|
958
|
+
class FooResource
|
959
|
+
include Alba::Resource
|
960
|
+
extend AlbaExtension
|
961
|
+
# other attributes
|
962
|
+
formatted_time_attributes :created_at, :updated_at
|
963
|
+
end
|
964
|
+
|
965
|
+
class BarResource
|
966
|
+
include Alba::Resource
|
967
|
+
extend AlbaExtension
|
968
|
+
# other attributes
|
969
|
+
formatted_time_attributes :created_at, :updated_at
|
970
|
+
end
|
971
|
+
```
|
972
|
+
|
973
|
+
In this way we have shorter and cleaner code. Note that we need to use `send` or `public_send` in `attribute` block to get attribute data.
|
974
|
+
|
827
975
|
## Rails
|
828
976
|
|
829
977
|
When you use Alba in Rails, you can create an initializer file with the line below for compatibility with Rails JSON encoder.
|
data/alba.gemspec
CHANGED
@@ -12,9 +12,13 @@ Gem::Specification.new do |spec|
|
|
12
12
|
spec.license = 'MIT'
|
13
13
|
spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
|
14
14
|
|
15
|
-
spec.metadata
|
16
|
-
|
17
|
-
|
15
|
+
spec.metadata = {
|
16
|
+
'bug_tracker_uri' => 'https://github.com/okuramasafumi/issues',
|
17
|
+
'changelog_uri' => 'https://github.com/okuramasafumi/alba/blob/main/CHANGELOG.md',
|
18
|
+
'documentation_uri' => 'https://rubydoc.info/github/okuramasafumi/alba',
|
19
|
+
'source_code_uri' => 'https://github.com/okuramasafumi/alba',
|
20
|
+
'rubygems_mfa_required' => 'true'
|
21
|
+
}
|
18
22
|
|
19
23
|
# Specify which files should be added to the gem when it is released.
|
20
24
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|