alba 1.3.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/.github/workflows/perf.yml +21 -0
- data/.rubocop.yml +9 -7
- data/CHANGELOG.md +28 -0
- data/Gemfile +4 -3
- data/README.md +469 -60
- data/alba.gemspec +7 -3
- data/benchmark/collection.rb +108 -3
- data/benchmark/single_resource.rb +82 -1
- data/docs/migrate_from_active_model_serializers.md +359 -0
- data/docs/migrate_from_jbuilder.md +223 -0
- data/gemfiles/all.gemfile +2 -1
- data/gemfiles/without_active_support.gemfile +1 -1
- data/gemfiles/without_oj.gemfile +1 -1
- data/lib/alba/association.rb +33 -24
- data/lib/alba/default_inflector.rb +22 -4
- data/lib/alba/deprecation.rb +14 -0
- data/lib/alba/errors.rb +10 -0
- data/lib/alba/resource.rb +260 -100
- data/lib/alba/typed_attribute.rb +3 -6
- data/lib/alba/version.rb +1 -1
- data/lib/alba.rb +110 -36
- data/script/perf_check.rb +174 -0
- metadata +14 -8
- data/lib/alba/key_transform_factory.rb +0 -33
- data/lib/alba/many.rb +0 -21
- data/lib/alba/one.rb +0 -21
@@ -0,0 +1,359 @@
|
|
1
|
+
---
|
2
|
+
title: Upgrading from ActiveModelSerializers
|
3
|
+
---
|
4
|
+
|
5
|
+
<!-- @format -->
|
6
|
+
|
7
|
+
This guide is aimed at helping ActiveModelSerializers users transition to Alba, and it consists of three parts:
|
8
|
+
|
9
|
+
1. Basic serialization
|
10
|
+
2. Complex serialization
|
11
|
+
3. Unsupported features
|
12
|
+
|
13
|
+
## Example class
|
14
|
+
|
15
|
+
Example clsss is inherited `ActiveRecord::Base`, because [serializing PORO with AMS is pretty hard](https://github.com/rails-api/active_model_serializers/blob/0-10-stable/docs/howto/serialize_poro.md).
|
16
|
+
|
17
|
+
```rb
|
18
|
+
class User < ActiveRecord::Base
|
19
|
+
# columns: id, created_at, updated_at
|
20
|
+
has_one :profile
|
21
|
+
has_many :articles
|
22
|
+
end
|
23
|
+
|
24
|
+
class Profile < ActiveRecord::Base
|
25
|
+
# columns: id, user_id, email, created_at, updated_at
|
26
|
+
belongs_to :user
|
27
|
+
end
|
28
|
+
|
29
|
+
class Article < ActiveRecord::Base
|
30
|
+
# columns: id, user_id, title, body, created_at, updated_at
|
31
|
+
belongs_to :user
|
32
|
+
end
|
33
|
+
```
|
34
|
+
|
35
|
+
## 1. Basic serialization
|
36
|
+
|
37
|
+
### #serializable_hash
|
38
|
+
|
39
|
+
- or #as_json, #to_json
|
40
|
+
|
41
|
+
#### ActiveModelSerializer
|
42
|
+
|
43
|
+
```rb
|
44
|
+
# Infer and use by "#{MODEL_NAME}Serializer" in app/serializers/user_serializer.rb
|
45
|
+
class UserSerializer < ActiveModel::Serializer
|
46
|
+
type :user
|
47
|
+
attributes :id, :created_at, :updated_at
|
48
|
+
end
|
49
|
+
|
50
|
+
# serialze
|
51
|
+
user = User.create!
|
52
|
+
ActiveModelSerializers::SerializableResource.new(
|
53
|
+
user
|
54
|
+
).serializable_hash
|
55
|
+
# => {
|
56
|
+
# user: {
|
57
|
+
# id: id,
|
58
|
+
# created_at: created_at,
|
59
|
+
# updated_at: updated_at
|
60
|
+
# }
|
61
|
+
# }
|
62
|
+
```
|
63
|
+
|
64
|
+
#### Alba
|
65
|
+
|
66
|
+
```rb
|
67
|
+
# Infer and use by "#{MODEL_NAME}Resource"
|
68
|
+
# In app/resources/user_resource.rb
|
69
|
+
class UserResource
|
70
|
+
include Alba::Resource
|
71
|
+
attributes :id, :created_at, :updated_at
|
72
|
+
end
|
73
|
+
|
74
|
+
# serialze
|
75
|
+
user = User.create!
|
76
|
+
UserResource.new(user).serializable_hash
|
77
|
+
# => {
|
78
|
+
# id: id,
|
79
|
+
# created_at: created_at,
|
80
|
+
# updated_at: updated_at,
|
81
|
+
# }
|
82
|
+
|
83
|
+
# If want `user key`
|
84
|
+
class UserResource
|
85
|
+
include Alba::Resource
|
86
|
+
root_key :user # Call root_key method like ActiveModel::Serializer#type
|
87
|
+
attributes :id, :created_at, :updated_at
|
88
|
+
end
|
89
|
+
|
90
|
+
# serialze
|
91
|
+
user = User.create!
|
92
|
+
JSON.parse UserResource.new(user).serialize # !!!!serializable_hash does not support root key!!! Must use JSON.parse and serialize
|
93
|
+
# => {
|
94
|
+
# "user"=>{
|
95
|
+
# "id"=>id,
|
96
|
+
# "created_at"=>created_at,
|
97
|
+
# "updated_at"=>updated_at
|
98
|
+
# }
|
99
|
+
# }
|
100
|
+
# If want symbolize keys with #deep_symbolize_keys in Rails
|
101
|
+
user = User.create!
|
102
|
+
JSON.parse(UserResource.new(user).serialize).deep_symbolize_keys
|
103
|
+
# => {
|
104
|
+
# user: {
|
105
|
+
# id: id,
|
106
|
+
# created_at: created_at,
|
107
|
+
# updated_at: updated_at
|
108
|
+
# }
|
109
|
+
# }
|
110
|
+
```
|
111
|
+
|
112
|
+
## 2. Complex serialization
|
113
|
+
|
114
|
+
### Serialize collections
|
115
|
+
|
116
|
+
#### ActiveModelSerializer
|
117
|
+
|
118
|
+
```rb
|
119
|
+
class UserSerializer < ActiveModel::Serializer
|
120
|
+
type :user
|
121
|
+
attributes :id, :created_at, :updated_at
|
122
|
+
end
|
123
|
+
3.times { User.create! }
|
124
|
+
users = User.limit 3
|
125
|
+
ActiveModelSerializers::SerializableResource.new(
|
126
|
+
users,
|
127
|
+
adapter: :attributes # Comment out this line if you want users key
|
128
|
+
# Want to specified key to call with root: args
|
129
|
+
).serializable_hash
|
130
|
+
# => [{:id=>1, :created_at=>created_at, :updated_at=>updated_at},
|
131
|
+
# {:id=>2, :created_at=>created_at, :updated_at=>updated_at},
|
132
|
+
# {:id=>3, :created_at=>created_at, :updated_at=>updated_at}]
|
133
|
+
```
|
134
|
+
|
135
|
+
#### Alba
|
136
|
+
|
137
|
+
```rb
|
138
|
+
class UserResource
|
139
|
+
include Alba::Resource
|
140
|
+
attributes :id, :created_at, :updated_at
|
141
|
+
end
|
142
|
+
3.times { User.create! }
|
143
|
+
users = User.limit 3
|
144
|
+
UserResource.new(users).serializable_hash
|
145
|
+
# =>[{:id=>1, :created_at=>created_at, :updated_at=>updated_at},
|
146
|
+
# {:id=>2, :created_at=>created_at, :updated_at=>updated_at},
|
147
|
+
# {:id=>3, :created_at=>created_at, :updated_at=>updated_at}]
|
148
|
+
# or
|
149
|
+
JSON.parse UserResource.new(users).serialize(root_key: :users)
|
150
|
+
# => {"users"=>
|
151
|
+
# [{"id"=>1, "created_at"=>created_at, "updated_at"=>updated_at},
|
152
|
+
# {"id"=>2, "created_at"=>created_at, "updated_at"=>updated_at},
|
153
|
+
# {"id"=>3, "created_at"=>created_at, "updated_at"=>updated_at}]}
|
154
|
+
```
|
155
|
+
|
156
|
+
### Nested serialization
|
157
|
+
|
158
|
+
#### ActiveModelSerializer
|
159
|
+
|
160
|
+
```rb
|
161
|
+
class ProfileSerializer < ActiveModel::Serializer
|
162
|
+
type :profile
|
163
|
+
attributes :email
|
164
|
+
end
|
165
|
+
|
166
|
+
class ArticleSerializer < ActiveModel::Serializer
|
167
|
+
type :article
|
168
|
+
attributes :title, :body
|
169
|
+
end
|
170
|
+
|
171
|
+
class UserSerializer < ActiveModel::Serializer
|
172
|
+
type :user
|
173
|
+
attributes :id, :created_at, :updated_at
|
174
|
+
has_one :profile, serializer: ProfileSerializer # For has_one relation
|
175
|
+
has_many :articles, serializer: ArticleSerializer # For has_many relation
|
176
|
+
end
|
177
|
+
user = User.create!
|
178
|
+
user.craete_profile! email: email
|
179
|
+
user.articles.create! title: title, body: body
|
180
|
+
ActiveModelSerializers::SerializableResource.new(
|
181
|
+
user
|
182
|
+
).serializable_hash
|
183
|
+
# => {
|
184
|
+
# :user=> {
|
185
|
+
# :id=>1,
|
186
|
+
# :created_at=>created_at,
|
187
|
+
# :updated_at=>updated_at,
|
188
|
+
# :profile=> {
|
189
|
+
# :email=>email
|
190
|
+
# },
|
191
|
+
# :articles => [
|
192
|
+
# {
|
193
|
+
# :title=>title,
|
194
|
+
# :body=>body
|
195
|
+
# }
|
196
|
+
# ]
|
197
|
+
# }
|
198
|
+
# }
|
199
|
+
```
|
200
|
+
|
201
|
+
#### Alba
|
202
|
+
|
203
|
+
```rb
|
204
|
+
class ProfileResource
|
205
|
+
include Alba::Resource
|
206
|
+
root_key :profile
|
207
|
+
attributes :email
|
208
|
+
end
|
209
|
+
|
210
|
+
class ArticleResource
|
211
|
+
include Alba::Resource
|
212
|
+
root_key :article
|
213
|
+
attributes :title, :body
|
214
|
+
end
|
215
|
+
|
216
|
+
class UserResource
|
217
|
+
include Alba::Resource
|
218
|
+
root_key :user
|
219
|
+
attributes :id, :created_at, :updated_at
|
220
|
+
one :profile, resource: ProfileResource # For has_one relation
|
221
|
+
many :articles, resource: ArticleResource # For has_many relation
|
222
|
+
end
|
223
|
+
|
224
|
+
user = User.create!
|
225
|
+
user.craete_profile! email: email
|
226
|
+
user.articles.create! title: title, body: body
|
227
|
+
UserResource.new(user).serializable_hash
|
228
|
+
# => {
|
229
|
+
# :id=>1,
|
230
|
+
# :created_at=>created_at,
|
231
|
+
# :updated_at=>updated_at,
|
232
|
+
# :profile=> {
|
233
|
+
# :email=>email
|
234
|
+
# },
|
235
|
+
# :articles => [
|
236
|
+
# {
|
237
|
+
# :title=>title,
|
238
|
+
# :body=>body
|
239
|
+
# }
|
240
|
+
# ]
|
241
|
+
# }
|
242
|
+
```
|
243
|
+
|
244
|
+
### Serialize with custom serializer
|
245
|
+
|
246
|
+
#### ActiveModelSerializer
|
247
|
+
|
248
|
+
```rb
|
249
|
+
class CustomUserSerializer < ActiveModel::Serializer
|
250
|
+
type :user
|
251
|
+
attribute :email do
|
252
|
+
object.profile.email
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
# serialze
|
257
|
+
user = User.create!
|
258
|
+
user.craete_profile! email: email
|
259
|
+
ActiveModelSerializers::SerializableResource.new(
|
260
|
+
user,
|
261
|
+
serializer: ::CustomUserSerializer # Call with serializer arg
|
262
|
+
).serializable_hash
|
263
|
+
# => {
|
264
|
+
# user: {
|
265
|
+
# email: email
|
266
|
+
# }
|
267
|
+
# }
|
268
|
+
```
|
269
|
+
|
270
|
+
#### Alba
|
271
|
+
|
272
|
+
```rb
|
273
|
+
class CustomUserResource
|
274
|
+
include Alba::Resource
|
275
|
+
root_key :user
|
276
|
+
attribute :email do
|
277
|
+
object.profile.email
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# serialze
|
282
|
+
user = User.create!
|
283
|
+
user.craete_profile! email: email
|
284
|
+
CustomUserResource.new(user).serializable_hash
|
285
|
+
# => {
|
286
|
+
# email: email
|
287
|
+
# }
|
288
|
+
```
|
289
|
+
|
290
|
+
### Passing arbitrary options to a serializer
|
291
|
+
|
292
|
+
#### ActiveModelSerializer
|
293
|
+
|
294
|
+
```rb
|
295
|
+
class UserSerializer < ApplicationSerializer
|
296
|
+
type :user
|
297
|
+
attributes :id, :created_at, :updated_at
|
298
|
+
attribute :custom_params do
|
299
|
+
pp instance_options
|
300
|
+
# => given_params: { a: :b }
|
301
|
+
instance_options # Access by instance_options method
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
# serialze
|
306
|
+
user = User.create!
|
307
|
+
ActiveModelSerializers::SerializableResource.new(
|
308
|
+
user,
|
309
|
+
given_params: { a: :b } # Give with your favorite keyword argument
|
310
|
+
).serializable_hash
|
311
|
+
# => {
|
312
|
+
# :id=>1,
|
313
|
+
# :created_at=>created_at,
|
314
|
+
# :updated_at=>updated_at,
|
315
|
+
# :custom_params=>{
|
316
|
+
# :given_params=>{
|
317
|
+
# :a=>:b
|
318
|
+
# }
|
319
|
+
# }
|
320
|
+
# }
|
321
|
+
```
|
322
|
+
|
323
|
+
#### Alba
|
324
|
+
|
325
|
+
```rb
|
326
|
+
class UserResource
|
327
|
+
include Alba::Resource
|
328
|
+
root_key :user
|
329
|
+
attributes :id, :created_at, :updated_at
|
330
|
+
attribute :custom_params do
|
331
|
+
pp params
|
332
|
+
# => { :a=>:b }
|
333
|
+
params
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
# serialze
|
338
|
+
user = User.create!
|
339
|
+
UserResource.new(
|
340
|
+
user,
|
341
|
+
params: { a: :b } # Give with :params keyword argument
|
342
|
+
).serializable_hash
|
343
|
+
# => {
|
344
|
+
# :id=>1,
|
345
|
+
# :created_at=>created_at,
|
346
|
+
# :updated_at=>updated_at,
|
347
|
+
# :custom_params=>{
|
348
|
+
# :a=>:b
|
349
|
+
# }
|
350
|
+
# }
|
351
|
+
```
|
352
|
+
|
353
|
+
## 3. Unsupported features
|
354
|
+
|
355
|
+
- [RelationshipLinks](https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/howto/add_relationship_links.md)
|
356
|
+
- [PaginationLinks](https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/howto/add_pagination_links.md)
|
357
|
+
- [Logging](https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/general/logging.md)
|
358
|
+
- [Caching](https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/general/caching.md)
|
359
|
+
- [Rendering](https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/general/rendering.md)
|
@@ -0,0 +1,223 @@
|
|
1
|
+
---
|
2
|
+
title: Upgrading from Jbuilder
|
3
|
+
---
|
4
|
+
|
5
|
+
<!-- @format -->
|
6
|
+
|
7
|
+
This guide is aimed at helping Jbuilder users transition to Alba, and it consists of three parts:
|
8
|
+
|
9
|
+
1. Basic serialization
|
10
|
+
2. Complex serialization
|
11
|
+
3. Unsupported features
|
12
|
+
|
13
|
+
## Example class
|
14
|
+
|
15
|
+
This example will also be replaced by ActiveReord.
|
16
|
+
|
17
|
+
```rb
|
18
|
+
class User
|
19
|
+
attr_reader :id, :created_at, :updated_at
|
20
|
+
attr_accessor :profile, :articles
|
21
|
+
|
22
|
+
def initialize(id)
|
23
|
+
@id = id
|
24
|
+
@created_at = Time.now
|
25
|
+
@updated_at = Time.now
|
26
|
+
@articles = []
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Profile
|
31
|
+
attr_reader :email
|
32
|
+
|
33
|
+
def initialize(email)
|
34
|
+
@email = email
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Article
|
39
|
+
attr_accessor :title, :body
|
40
|
+
|
41
|
+
def initialize(title, body)
|
42
|
+
@title = title
|
43
|
+
@body = body
|
44
|
+
end
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
## 1. Basic serialization
|
49
|
+
|
50
|
+
#### Jbuilder
|
51
|
+
|
52
|
+
```rb
|
53
|
+
# show.json.jbuilder
|
54
|
+
# With block
|
55
|
+
@user = User.new(id)
|
56
|
+
json.user do |user|
|
57
|
+
user.id @user.id
|
58
|
+
user.created_at @user.created_at
|
59
|
+
user.updated_at @user.updated_at
|
60
|
+
end
|
61
|
+
# => '{"user":{"id":id, "created_at": created_at, "updated_at": updated_at}'
|
62
|
+
# or #extract!
|
63
|
+
json.extract! @user, :id, :created_at, :updated_at
|
64
|
+
# => '{"id":id, "created_at": created_at, "updated_at": updated_at}'
|
65
|
+
```
|
66
|
+
|
67
|
+
#### Alba
|
68
|
+
|
69
|
+
```rb
|
70
|
+
# With block
|
71
|
+
user = User.new(id)
|
72
|
+
Alba.serialize(user, root_key: :user) do
|
73
|
+
attributes :id, :created_at, :updated_at
|
74
|
+
end
|
75
|
+
# => '{"user":{"id":id, "created_at": created_at, "updated_at": updated_at}'
|
76
|
+
# or with resourceClass.
|
77
|
+
# Infer and use by "#{MODEL_NAME}Resource"
|
78
|
+
class UserResource
|
79
|
+
include Alba::Resource
|
80
|
+
root_key :user
|
81
|
+
attributes :id, :created_at, :updated_at
|
82
|
+
end
|
83
|
+
UserResource.new(user).serialize
|
84
|
+
# => '{"user":{"id":id, "created_at": created_at, "updated_at": updated_at}'
|
85
|
+
```
|
86
|
+
|
87
|
+
## 2. Complex serialization
|
88
|
+
|
89
|
+
### Serialize collections
|
90
|
+
|
91
|
+
#### Jbuilder
|
92
|
+
|
93
|
+
```rb
|
94
|
+
@users = ids.map { |id| User.new(id) }
|
95
|
+
# index.json.jbuilder
|
96
|
+
json.array! @users, :id, :created_at, :updated_at
|
97
|
+
# => '[{"id":id, "created_at": created_at, "updated_at": updated_at}, {"id":id, "created_at": created_at, "updated_at": updated_at}, {"id":id, "created_at": created_at, "updated_at": updated_at}]'
|
98
|
+
```
|
99
|
+
|
100
|
+
#### Alba
|
101
|
+
|
102
|
+
```rb
|
103
|
+
class UserResource
|
104
|
+
include Alba::Resource
|
105
|
+
root_key :user
|
106
|
+
attributes :id, :created_at, :updated_at
|
107
|
+
end
|
108
|
+
users = ids.map { |id| User.new(id) }
|
109
|
+
UserResource.new(users).serialize
|
110
|
+
# => '[{"id":id, "created_at": created_at, "updated_at": updated_at}, {"id":id, "created_at": created_at, "updated_at": updated_at}, {"id":id, "created_at": created_at, "updated_at": updated_at}]'
|
111
|
+
|
112
|
+
```
|
113
|
+
|
114
|
+
### Nested serialization
|
115
|
+
|
116
|
+
#### Jbuilder
|
117
|
+
|
118
|
+
```rb
|
119
|
+
# show.json.jbuilder
|
120
|
+
@user = User.new(id)
|
121
|
+
@user.profile = Profile.new(email)
|
122
|
+
@user.articles = [Article.new(title, body)]
|
123
|
+
json.user do |user|
|
124
|
+
user.id @user.id
|
125
|
+
user.created_at @user.created_at
|
126
|
+
user.updated_at @user.updated_at
|
127
|
+
json.profile do
|
128
|
+
json.email @user.profile.email
|
129
|
+
end
|
130
|
+
json.articles do
|
131
|
+
json.array! @user.articles, :title, :body
|
132
|
+
end
|
133
|
+
end
|
134
|
+
# => '{"user":{"id":id, "created_at": created_at, "updated_at": updated_at, "profile": {"email": email}, articles: [{"title": title, "body": body}]}'
|
135
|
+
# or #merge!
|
136
|
+
profile_hash = { profile: { email: @user.profile.email } }
|
137
|
+
articles_hash = { articles: @user.articles.map { |article| { title: article.title, body: article.body } } }
|
138
|
+
json.user do |user|
|
139
|
+
user.id @user.id
|
140
|
+
user.created_at @user.created_at
|
141
|
+
user.updated_at @user.updated_at
|
142
|
+
json.merge! profile_hash
|
143
|
+
json.merge! articles_hash
|
144
|
+
end
|
145
|
+
# => '{"user":{"id":id, "created_at": created_at, "updated_at": updated_at, "profile": {"email": email}, articles: [{"title": title, "body": body}]}'
|
146
|
+
# or #partial!
|
147
|
+
# profiles/_profile.json.jbuilder
|
148
|
+
json.profile do
|
149
|
+
json.email @profile.email
|
150
|
+
end
|
151
|
+
# articles/_article.json.jbuilder
|
152
|
+
json.extract! article, :title, :body
|
153
|
+
# user/show.json.jbuilder
|
154
|
+
json.user do |user|
|
155
|
+
user.id @user.id
|
156
|
+
user.created_at @user.created_at
|
157
|
+
user.updated_at @user.updated_at
|
158
|
+
json.partial! @user.profile, as: :profile
|
159
|
+
json.articles @user.articles do |article|
|
160
|
+
json.partial! article, partial: 'articles/article'
|
161
|
+
end
|
162
|
+
end
|
163
|
+
```
|
164
|
+
|
165
|
+
#### Alba
|
166
|
+
|
167
|
+
```rb
|
168
|
+
# With ResourceClass by each resources
|
169
|
+
class ProfileResource
|
170
|
+
include Alba::Resource
|
171
|
+
root_key :profile
|
172
|
+
attributes :email
|
173
|
+
end
|
174
|
+
class ArticleResource
|
175
|
+
include Alba::Resource
|
176
|
+
root_key :article
|
177
|
+
attributes :title, :body
|
178
|
+
end
|
179
|
+
class UserResource
|
180
|
+
include Alba::Resource
|
181
|
+
root_key :user
|
182
|
+
attributes :id, :created_at, :updated_at
|
183
|
+
one :profile, resource: ProfileResource
|
184
|
+
many :articles, resource: ArticleResource
|
185
|
+
end
|
186
|
+
user = User.new(id)
|
187
|
+
user.profile = Profile.new(email)
|
188
|
+
user.articles = [Article.new(title, body)]
|
189
|
+
UserResource.new(user).serialize
|
190
|
+
# => '{"user":{"id":id, "created_at": created_at, "updated_at": updated_at, "profile": {"email": email}, articles: [{"title": title, "body": body}]}'
|
191
|
+
|
192
|
+
# or #attribute
|
193
|
+
class UserResource
|
194
|
+
include Alba::Resource
|
195
|
+
root_key :user
|
196
|
+
attributes :id, :created_at, :updated_at
|
197
|
+
|
198
|
+
attribute :profile do
|
199
|
+
{
|
200
|
+
email: object.profile.email # Can access to received resource by #object method
|
201
|
+
}
|
202
|
+
end
|
203
|
+
|
204
|
+
attribute :articles do
|
205
|
+
object.articles.map do |article|
|
206
|
+
{
|
207
|
+
title: article.title,
|
208
|
+
body: article.body,
|
209
|
+
}
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
user = User.new(id)
|
214
|
+
user.profile = Profile.new(email)
|
215
|
+
UserResource.new(user).serialize
|
216
|
+
# => '{"user":{"id":id, "created_at": created_at, "updated_at": updated_at, "profile": {"email": email}, articles: [{"title": title, "body": body}]}'
|
217
|
+
```
|
218
|
+
|
219
|
+
## 3. Unsupported features
|
220
|
+
|
221
|
+
- Jbuilder#ignore_nil!
|
222
|
+
- Jbuilder#cache!
|
223
|
+
- Jbuilder.key_format! and Jbuilder.deep_format_keys!
|
data/gemfiles/all.gemfile
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
3
|
gem 'activesupport', require: false # For backend
|
4
|
+
gem 'dry-inflector', require: false # For inflector
|
4
5
|
gem 'ffaker', require: false # For testing
|
5
6
|
gem 'minitest', '~> 5.14' # For test
|
6
7
|
gem 'rake', '~> 13.0' # For test and automation
|
@@ -11,7 +12,7 @@ gem 'rubocop-rake', '>= 0.5.1', require: false # For lint
|
|
11
12
|
gem 'rubocop-sensible', '~> 0.3.0', require: false # For lint
|
12
13
|
gem 'simplecov', '~> 0.21.0', require: false # For test coverage
|
13
14
|
gem 'simplecov-cobertura', require: false # For test coverage
|
14
|
-
gem 'yard', require: false
|
15
|
+
gem 'yard', require: false # For documentation
|
15
16
|
|
16
17
|
platforms :ruby do
|
17
18
|
gem 'oj', '~> 3.11', require: false # For backend
|
@@ -9,7 +9,7 @@ gem 'rubocop-rake', '>= 0.5.1', require: false # For lint
|
|
9
9
|
gem 'rubocop-sensible', '~> 0.3.0', require: false # For lint
|
10
10
|
gem 'simplecov', '~> 0.21.0', require: false # For test coverage
|
11
11
|
gem 'simplecov-cobertura', require: false # For test coverage
|
12
|
-
gem 'yard', require: false
|
12
|
+
gem 'yard', require: false # For documentation
|
13
13
|
|
14
14
|
platforms :ruby do
|
15
15
|
gem 'oj', '~> 3.11', require: false # For backend
|
data/gemfiles/without_oj.gemfile
CHANGED
@@ -10,7 +10,7 @@ gem 'rubocop-rake', '>= 0.5.1', require: false # For lint
|
|
10
10
|
gem 'rubocop-sensible', '~> 0.3.0', require: false # For lint
|
11
11
|
gem 'simplecov', '~> 0.21.0', require: false # For test coverage
|
12
12
|
gem 'simplecov-cobertura', require: false # For test coverage
|
13
|
-
gem 'yard', require: false
|
13
|
+
gem 'yard', require: false # For documentation
|
14
14
|
|
15
15
|
platforms :ruby do
|
16
16
|
gem 'ruby-prof', require: false # For performance profiling
|
data/lib/alba/association.rb
CHANGED
@@ -1,21 +1,40 @@
|
|
1
1
|
module Alba
|
2
|
-
#
|
3
|
-
# Child class should implement `to_hash` method
|
2
|
+
# Representing association
|
4
3
|
class Association
|
4
|
+
@const_cache = {}
|
5
|
+
class << self
|
6
|
+
attr_reader :const_cache
|
7
|
+
end
|
8
|
+
|
5
9
|
attr_reader :object, :name
|
6
10
|
|
7
|
-
# @param name [Symbol] name of the method to fetch association
|
8
|
-
# @param condition [Proc] a proc filtering data
|
9
|
-
# @param resource [Class<Alba::Resource
|
11
|
+
# @param name [Symbol, String] name of the method to fetch association
|
12
|
+
# @param condition [Proc, nil] a proc filtering data
|
13
|
+
# @param resource [Class<Alba::Resource>, nil] a resource class for the association
|
14
|
+
# @param nesting [String] a namespace where source class is inferred with
|
10
15
|
# @param block [Block] used to define resource when resource arg is absent
|
11
16
|
def initialize(name:, condition: nil, resource: nil, nesting: nil, &block)
|
12
17
|
@name = name
|
13
18
|
@condition = condition
|
14
|
-
@block = block
|
15
19
|
@resource = resource
|
16
20
|
return if @resource
|
17
21
|
|
18
|
-
assign_resource(nesting)
|
22
|
+
assign_resource(nesting, block)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Recursively converts an object into a Hash
|
26
|
+
#
|
27
|
+
# @param target [Object] the object having an association method
|
28
|
+
# @param within [Hash] determines what associations to be serialized. If not set, it serializes all associations.
|
29
|
+
# @param params [Hash] user-given Hash for arbitrary data
|
30
|
+
# @return [Hash]
|
31
|
+
def to_h(target, within: nil, params: {})
|
32
|
+
@object = target.public_send(@name)
|
33
|
+
@object = @condition.call(object, params) if @condition
|
34
|
+
return if @object.nil?
|
35
|
+
|
36
|
+
@resource = constantize(@resource)
|
37
|
+
@resource.new(object, params: params, within: within).to_h
|
19
38
|
end
|
20
39
|
|
21
40
|
private
|
@@ -25,30 +44,20 @@ module Alba
|
|
25
44
|
when Class
|
26
45
|
resource
|
27
46
|
when Symbol, String
|
28
|
-
|
47
|
+
self.class.const_cache.fetch(resource) do
|
48
|
+
self.class.const_cache[resource] = Object.const_get(resource)
|
49
|
+
end
|
29
50
|
end
|
30
51
|
end
|
31
52
|
|
32
|
-
def assign_resource(nesting)
|
33
|
-
@resource = if
|
34
|
-
resource_class
|
53
|
+
def assign_resource(nesting, block)
|
54
|
+
@resource = if block
|
55
|
+
Alba.resource_class(&block)
|
35
56
|
elsif Alba.inferring
|
36
|
-
|
57
|
+
Alba.infer_resource_class(@name, nesting: nesting)
|
37
58
|
else
|
38
59
|
raise ArgumentError, 'When Alba.inferring is false, either resource or block is required'
|
39
60
|
end
|
40
61
|
end
|
41
|
-
|
42
|
-
def resource_class
|
43
|
-
klass = Class.new
|
44
|
-
klass.include(Alba::Resource)
|
45
|
-
klass.class_eval(&@block)
|
46
|
-
klass
|
47
|
-
end
|
48
|
-
|
49
|
-
def resource_class_with_nesting(nesting)
|
50
|
-
const_parent = nesting.nil? ? Object : Object.const_get(nesting)
|
51
|
-
const_parent.const_get("#{ActiveSupport::Inflector.classify(@name)}Resource")
|
52
|
-
end
|
53
62
|
end
|
54
63
|
end
|