alba 1.2.0 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +2 -1
- data/CHANGELOG.md +12 -0
- data/README.md +38 -4
- data/alba.gemspec +1 -1
- data/benchmark/collection.rb +392 -0
- data/benchmark/{local.rb → single_resource.rb} +45 -16
- data/lib/alba.rb +1 -0
- data/lib/alba/association.rb +17 -9
- data/lib/alba/default_inflector.rb +36 -0
- data/lib/alba/key_transform_factory.rb +33 -0
- data/lib/alba/many.rb +1 -1
- data/lib/alba/resource.rb +58 -72
- data/lib/alba/typed_attribute.rb +64 -0
- data/lib/alba/version.rb +1 -1
- metadata +9 -7
- data/lib/alba/key_transformer.rb +0 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b2c2e8c84ddccf4db9f9f18dd155361567f2a1733c55f817f5b4d97d573675d5
|
4
|
+
data.tar.gz: 3f11dd120c8b57aef909d79f6570482b8af810cd19c57ddcaa0d7b2ee70c2d6b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8948a681fb88b3d84e3751e6df0f5ca28314d6c9b5e183666780be95b14fd3c7728e6ad1eb13309d4b7114115b56edbb28fe929c2376bbcc5b762846a7d00404
|
7
|
+
data.tar.gz: 9a2430efc4cf3b7a24b27bb40228dad67e00bd0cb10c9e0fc3764eb49e9caafda5b6d9fa0cc0335c77edc42e0a60416b20343dee5cee23dcfbfe350bed90e9a6
|
data/.rubocop.yml
CHANGED
@@ -49,7 +49,8 @@ Metrics/MethodLength:
|
|
49
49
|
|
50
50
|
# `Resource` module is a core module and its length tends to be long...
|
51
51
|
Metrics/ModuleLength:
|
52
|
-
|
52
|
+
Exclude:
|
53
|
+
- 'lib/alba/resource.rb'
|
53
54
|
|
54
55
|
# Resource class includes DSLs, which tend to accept long list of parameters
|
55
56
|
Metrics/ParameterLists:
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [1.3.0] 2021-05-31
|
10
|
+
|
11
|
+
- [Perf] Improve performance for `many` [641d8f9]
|
12
|
+
- https://github.com/okuramasafumi/alba/pull/125
|
13
|
+
- [Feat] Add custom inflector feature (#126) [ad73291]
|
14
|
+
- https://github.com/okuramasafumi/alba/pull/126
|
15
|
+
- Thank you @wuarmin !
|
16
|
+
- [Feat] Support params in if condition [6e9915e]
|
17
|
+
- https://github.com/okuramasafumi/alba/pull/128
|
18
|
+
- [Fix] fundamentally broken "circular association control" [fbbc9a1]
|
19
|
+
- https://github.com/okuramasafumi/alba/pull/130
|
20
|
+
|
9
21
|
## [1.2.0] 2021-05-09
|
10
22
|
|
11
23
|
- [Fix] multiple word key inference [6c18e73]
|
data/README.md
CHANGED
@@ -66,7 +66,7 @@ You can find the documentation on [RubyDoc](https://rubydoc.info/github/okuramas
|
|
66
66
|
* Error handling
|
67
67
|
* Resource name inflection based on association name
|
68
68
|
* Circular associations control
|
69
|
-
* Types for validation and conversion
|
69
|
+
* [Experimental] Types for validation and conversion
|
70
70
|
* No runtime dependencies
|
71
71
|
|
72
72
|
## Anti features
|
@@ -246,9 +246,11 @@ end
|
|
246
246
|
|
247
247
|
### Key transformation
|
248
248
|
|
249
|
-
|
249
|
+
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:
|
250
|
+
* install it
|
251
|
+
* use a [custom inflector](#custom-inflector)
|
250
252
|
|
251
|
-
With `
|
253
|
+
With `transform_keys` DSL, you can transform attribute keys.
|
252
254
|
|
253
255
|
```ruby
|
254
256
|
class User
|
@@ -309,6 +311,34 @@ This behavior to transform root key will become default at version 2.
|
|
309
311
|
|
310
312
|
Supported transformation types are :camel, :lower_camel and :dash.
|
311
313
|
|
314
|
+
#### Custom inflector
|
315
|
+
|
316
|
+
A custom inflector can be plugged in as follows...
|
317
|
+
```ruby
|
318
|
+
Alba.inflector = MyCustomInflector
|
319
|
+
```
|
320
|
+
...and has to implement following interface (the parameter `key` is of type `String`):
|
321
|
+
```ruby
|
322
|
+
module InflectorInterface
|
323
|
+
def camelize(key)
|
324
|
+
raise "Not implemented"
|
325
|
+
end
|
326
|
+
|
327
|
+
def camelize_lower(key)
|
328
|
+
raise "Not implemented"
|
329
|
+
end
|
330
|
+
|
331
|
+
def dasherize(key)
|
332
|
+
raise "Not implemented"
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
```
|
337
|
+
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:
|
338
|
+
```ruby
|
339
|
+
Alba.inflector = Dry::Inflector.new
|
340
|
+
```
|
341
|
+
|
312
342
|
### Filtering attributes
|
313
343
|
|
314
344
|
You can filter attributes by overriding `Alba::Resource#converter` method, but it's a bit tricky.
|
@@ -482,11 +512,13 @@ end
|
|
482
512
|
|
483
513
|
### Circular associations control
|
484
514
|
|
515
|
+
**Note that this feature works correctly since version 1.3. In previous versions it doesn't work as expected.**
|
516
|
+
|
485
517
|
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
518
|
|
487
519
|
For more details, please refer to [test code](https://github.com/okuramasafumi/alba/blob/master/test/usecases/circular_association_test.rb)
|
488
520
|
|
489
|
-
###
|
521
|
+
### Experimental support of types
|
490
522
|
|
491
523
|
You can validate and convert input with types.
|
492
524
|
|
@@ -525,6 +557,8 @@ UserResource.new(user).serialize
|
|
525
557
|
# => TypeError, 'Attribute bio is expected to be String but actually nil.'
|
526
558
|
```
|
527
559
|
|
560
|
+
Note that this feature is experimental and interfaces are subject to change.
|
561
|
+
|
528
562
|
### Caching
|
529
563
|
|
530
564
|
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).
|
data/alba.gemspec
CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |spec|
|
|
7
7
|
spec.email = ['masafumi.o1988@gmail.com']
|
8
8
|
|
9
9
|
spec.summary = 'Alba is the fastest JSON serializer for Ruby.'
|
10
|
-
spec.description = "Alba is
|
10
|
+
spec.description = "Alba is the fastest JSON serializer for Ruby. It focuses on performance, flexibility and usability."
|
11
11
|
spec.homepage = 'https://github.com/okuramasafumi/alba'
|
12
12
|
spec.license = 'MIT'
|
13
13
|
spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
|
@@ -0,0 +1,392 @@
|
|
1
|
+
# Benchmark script to run varieties of JSON serializers
|
2
|
+
# Fetch Alba from local, otherwise fetch latest from RubyGems
|
3
|
+
|
4
|
+
# --- Bundle dependencies ---
|
5
|
+
|
6
|
+
require "bundler/inline"
|
7
|
+
|
8
|
+
gemfile(true) do
|
9
|
+
source "https://rubygems.org"
|
10
|
+
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
11
|
+
|
12
|
+
gem "active_model_serializers"
|
13
|
+
gem "activerecord", "6.1.3"
|
14
|
+
gem "alba", path: '../'
|
15
|
+
gem "benchmark-ips"
|
16
|
+
gem "benchmark-memory"
|
17
|
+
gem "blueprinter"
|
18
|
+
gem "jbuilder"
|
19
|
+
gem "jsonapi-serializer" # successor of fast_jsonapi
|
20
|
+
gem "multi_json"
|
21
|
+
gem "primalize"
|
22
|
+
gem "oj"
|
23
|
+
gem "representable"
|
24
|
+
gem "simple_ams"
|
25
|
+
gem "sqlite3"
|
26
|
+
end
|
27
|
+
|
28
|
+
# --- Test data model setup ---
|
29
|
+
|
30
|
+
require "active_record"
|
31
|
+
require "logger"
|
32
|
+
require "oj"
|
33
|
+
require "sqlite3"
|
34
|
+
Oj.optimize_rails
|
35
|
+
|
36
|
+
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
|
37
|
+
# ActiveRecord::Base.logger = Logger.new($stdout)
|
38
|
+
|
39
|
+
ActiveRecord::Schema.define do
|
40
|
+
create_table :posts, force: true do |t|
|
41
|
+
t.string :body
|
42
|
+
end
|
43
|
+
|
44
|
+
create_table :comments, force: true do |t|
|
45
|
+
t.integer :post_id
|
46
|
+
t.string :body
|
47
|
+
t.integer :commenter_id
|
48
|
+
end
|
49
|
+
|
50
|
+
create_table :users, force: true do |t|
|
51
|
+
t.string :name
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Post < ActiveRecord::Base
|
56
|
+
has_many :comments
|
57
|
+
has_many :commenters, through: :comments, class_name: 'User', source: :commenter
|
58
|
+
|
59
|
+
def attributes
|
60
|
+
{id: nil, body: nil, commenter_names: commenter_names}
|
61
|
+
end
|
62
|
+
|
63
|
+
def commenter_names
|
64
|
+
commenters.pluck(:name)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
class Comment < ActiveRecord::Base
|
69
|
+
belongs_to :post
|
70
|
+
belongs_to :commenter, class_name: 'User'
|
71
|
+
|
72
|
+
def attributes
|
73
|
+
{id: nil, body: nil}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class User < ActiveRecord::Base
|
78
|
+
has_many :comments
|
79
|
+
end
|
80
|
+
|
81
|
+
# --- Alba serializers ---
|
82
|
+
|
83
|
+
require "alba"
|
84
|
+
|
85
|
+
class AlbaCommentResource
|
86
|
+
include ::Alba::Resource
|
87
|
+
attributes :id, :body
|
88
|
+
end
|
89
|
+
|
90
|
+
class AlbaPostResource
|
91
|
+
include ::Alba::Resource
|
92
|
+
attributes :id, :body
|
93
|
+
attribute :commenter_names do |post|
|
94
|
+
post.commenters.pluck(:name)
|
95
|
+
end
|
96
|
+
many :comments, resource: AlbaCommentResource
|
97
|
+
end
|
98
|
+
|
99
|
+
# --- ActiveModelSerializer serializers ---
|
100
|
+
|
101
|
+
require "active_model_serializers"
|
102
|
+
|
103
|
+
ActiveModelSerializers.logger = Logger.new(nil)
|
104
|
+
|
105
|
+
class AMSCommentSerializer < ActiveModel::Serializer
|
106
|
+
attributes :id, :body
|
107
|
+
end
|
108
|
+
|
109
|
+
class AMSPostSerializer < ActiveModel::Serializer
|
110
|
+
attributes :id, :body
|
111
|
+
attribute :commenter_names
|
112
|
+
has_many :comments, serializer: AMSCommentSerializer
|
113
|
+
|
114
|
+
def commenter_names
|
115
|
+
object.commenters.pluck(:name)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# --- Blueprint serializers ---
|
120
|
+
|
121
|
+
require "blueprinter"
|
122
|
+
|
123
|
+
class CommentBlueprint < Blueprinter::Base
|
124
|
+
fields :id, :body
|
125
|
+
end
|
126
|
+
|
127
|
+
class PostBlueprint < Blueprinter::Base
|
128
|
+
fields :id, :body, :commenter_names
|
129
|
+
association :comments, blueprint: CommentBlueprint
|
130
|
+
|
131
|
+
def commenter_names
|
132
|
+
commenters.pluck(:name)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# --- JBuilder serializers ---
|
137
|
+
|
138
|
+
require "jbuilder"
|
139
|
+
|
140
|
+
class Post
|
141
|
+
def to_builder
|
142
|
+
Jbuilder.new do |post|
|
143
|
+
post.call(self, :id, :body, :commenter_names, :comments)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def commenter_names
|
148
|
+
commenters.pluck(:name)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
class Comment
|
153
|
+
def to_builder
|
154
|
+
Jbuilder.new do |comment|
|
155
|
+
comment.call(self, :id, :body)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# --- JSONAPI:Serializer serializers / (successor of fast_jsonapi) ---
|
161
|
+
|
162
|
+
class JsonApiStandardCommentSerializer
|
163
|
+
include JSONAPI::Serializer
|
164
|
+
|
165
|
+
attribute :id
|
166
|
+
attribute :body
|
167
|
+
end
|
168
|
+
|
169
|
+
class JsonApiStandardPostSerializer
|
170
|
+
include JSONAPI::Serializer
|
171
|
+
|
172
|
+
# set_type :post # optional
|
173
|
+
attribute :id
|
174
|
+
attribute :body
|
175
|
+
attribute :commenter_names
|
176
|
+
|
177
|
+
attribute :comments do |post|
|
178
|
+
post.comments.map { |comment| JsonApiSameFormatCommentSerializer.new(comment) }
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
# --- JSONAPI:Serializer serializers that format the code the same flat way as the other gems here ---
|
183
|
+
|
184
|
+
# code to convert from JSON:API output to "flat" JSON, like the other serializers build
|
185
|
+
class JsonApiSameFormatSerializer
|
186
|
+
include JSONAPI::Serializer
|
187
|
+
|
188
|
+
def as_json(*_options)
|
189
|
+
hash = serializable_hash
|
190
|
+
|
191
|
+
if hash[:data].is_a? Hash
|
192
|
+
hash[:data][:attributes]
|
193
|
+
|
194
|
+
elsif hash[:data].is_a? Array
|
195
|
+
hash[:data].pluck(:attributes)
|
196
|
+
|
197
|
+
elsif hash[:data].nil?
|
198
|
+
{ }
|
199
|
+
|
200
|
+
else
|
201
|
+
raise "unexpected data type #{hash[:data].class}"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
class JsonApiSameFormatCommentSerializer < JsonApiSameFormatSerializer
|
207
|
+
attribute :id
|
208
|
+
attribute :body
|
209
|
+
end
|
210
|
+
|
211
|
+
class JsonApiSameFormatPostSerializer < JsonApiSameFormatSerializer
|
212
|
+
attribute :id
|
213
|
+
attribute :body
|
214
|
+
attribute :commenter_names
|
215
|
+
|
216
|
+
attribute :comments do |post|
|
217
|
+
post.comments.map { |comment| JsonApiSameFormatCommentSerializer.new(comment) }
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
# --- Primalize serializers ---
|
222
|
+
#
|
223
|
+
class PrimalizeCommentResource < Primalize::Single
|
224
|
+
attributes id: integer, body: string
|
225
|
+
end
|
226
|
+
|
227
|
+
class PrimalizePostResource < Primalize::Single
|
228
|
+
alias post object
|
229
|
+
|
230
|
+
attributes(
|
231
|
+
id: integer,
|
232
|
+
body: string,
|
233
|
+
comments: array(primalize(PrimalizeCommentResource)),
|
234
|
+
commenter_names: array(string),
|
235
|
+
)
|
236
|
+
|
237
|
+
def commenter_names
|
238
|
+
post.commenters.pluck(:name)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
class PrimalizePostsResource < Primalize::Many
|
243
|
+
attributes posts: enumerable(PrimalizePostResource)
|
244
|
+
end
|
245
|
+
|
246
|
+
# --- Representable serializers ---
|
247
|
+
|
248
|
+
require "representable"
|
249
|
+
|
250
|
+
class CommentRepresenter < Representable::Decorator
|
251
|
+
include Representable::JSON
|
252
|
+
|
253
|
+
property :id
|
254
|
+
property :body
|
255
|
+
end
|
256
|
+
|
257
|
+
class PostsRepresenter < Representable::Decorator
|
258
|
+
include Representable::JSON::Collection
|
259
|
+
|
260
|
+
items class: Post do
|
261
|
+
property :id
|
262
|
+
property :body
|
263
|
+
property :commenter_names
|
264
|
+
collection :comments
|
265
|
+
end
|
266
|
+
|
267
|
+
def commenter_names
|
268
|
+
commenters.pluck(:name)
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
# --- SimpleAMS serializers ---
|
273
|
+
|
274
|
+
require "simple_ams"
|
275
|
+
|
276
|
+
class SimpleAMSCommentSerializer
|
277
|
+
include SimpleAMS::DSL
|
278
|
+
|
279
|
+
attributes :id, :body
|
280
|
+
end
|
281
|
+
|
282
|
+
class SimpleAMSPostSerializer
|
283
|
+
include SimpleAMS::DSL
|
284
|
+
|
285
|
+
attributes :id, :body
|
286
|
+
attribute :commenter_names
|
287
|
+
has_many :comments, serializer: SimpleAMSCommentSerializer
|
288
|
+
|
289
|
+
def commenter_names
|
290
|
+
object.commenters.pluck(:name)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
# --- Test data creation ---
|
295
|
+
|
296
|
+
100.times do |i|
|
297
|
+
post = Post.create!(body: "post#{i}")
|
298
|
+
user1 = User.create!(name: "John#{i}")
|
299
|
+
user2 = User.create!(name: "Jane#{i}")
|
300
|
+
10.times do |n|
|
301
|
+
post.comments.create!(commenter: user1, body: "Comment1_#{i}_#{n}")
|
302
|
+
post.comments.create!(commenter: user2, body: "Comment2_#{i}_#{n}")
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
posts = Post.all.to_a
|
307
|
+
|
308
|
+
# --- Store the serializers in procs ---
|
309
|
+
|
310
|
+
alba = Proc.new { AlbaPostResource.new(posts).serialize }
|
311
|
+
alba_inline = Proc.new do
|
312
|
+
Alba.serialize(posts) do
|
313
|
+
attributes :id, :body
|
314
|
+
attribute :commenter_names do |post|
|
315
|
+
post.commenters.pluck(:name)
|
316
|
+
end
|
317
|
+
many :comments do
|
318
|
+
attributes :id, :body
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
ams = Proc.new { ActiveModelSerializers::SerializableResource.new(posts, {}).as_json }
|
323
|
+
blueprinter = Proc.new { PostBlueprint.render(posts) }
|
324
|
+
jbuilder = Proc.new do
|
325
|
+
Jbuilder.new do |json|
|
326
|
+
json.array!(posts) do |post|
|
327
|
+
json.post post.to_builder
|
328
|
+
end
|
329
|
+
end.target!
|
330
|
+
end
|
331
|
+
jsonapi = proc { JsonApiStandardPostSerializer.new(posts).to_json }
|
332
|
+
jsonapi_same_format = proc { JsonApiSameFormatPostSerializer.new(posts).to_json }
|
333
|
+
primalize = proc { PrimalizePostsResource.new(posts: posts).to_json }
|
334
|
+
rails = Proc.new do
|
335
|
+
ActiveSupport::JSON.encode(posts.map{ |post| post.serializable_hash(include: :comments) })
|
336
|
+
end
|
337
|
+
representable = Proc.new { PostsRepresenter.new(posts).to_json }
|
338
|
+
simple_ams = Proc.new { SimpleAMS::Renderer::Collection.new(posts, serializer: SimpleAMSPostSerializer).to_json }
|
339
|
+
|
340
|
+
# --- Execute the serializers to check their output ---
|
341
|
+
|
342
|
+
puts "Serializer outputs ----------------------------------"
|
343
|
+
{
|
344
|
+
alba: alba,
|
345
|
+
alba_inline: alba_inline,
|
346
|
+
ams: ams,
|
347
|
+
blueprinter: blueprinter,
|
348
|
+
jbuilder: jbuilder, # different order
|
349
|
+
jsonapi: jsonapi, # nested JSON:API format
|
350
|
+
jsonapi_same_format: jsonapi_same_format,
|
351
|
+
primalize: primalize,
|
352
|
+
rails: rails,
|
353
|
+
representable: representable,
|
354
|
+
simple_ams: simple_ams,
|
355
|
+
}.each { |name, serializer| puts "#{name.to_s.ljust(24, ' ')} #{serializer.call}" }
|
356
|
+
|
357
|
+
# --- Run the benchmarks ---
|
358
|
+
|
359
|
+
require 'benchmark/ips'
|
360
|
+
Benchmark.ips do |x|
|
361
|
+
x.report(:alba, &alba)
|
362
|
+
x.report(:alba_inline, &alba_inline)
|
363
|
+
x.report(:ams, &ams)
|
364
|
+
x.report(:blueprinter, &blueprinter)
|
365
|
+
x.report(:jbuilder, &jbuilder)
|
366
|
+
x.report(:jsonapi, &jsonapi)
|
367
|
+
x.report(:jsonapi_same_format, &jsonapi_same_format)
|
368
|
+
x.report(:primalize, &primalize)
|
369
|
+
x.report(:rails, &rails)
|
370
|
+
x.report(:representable, &representable)
|
371
|
+
x.report(:simple_ams, &simple_ams)
|
372
|
+
|
373
|
+
x.compare!
|
374
|
+
end
|
375
|
+
|
376
|
+
|
377
|
+
require 'benchmark/memory'
|
378
|
+
Benchmark.memory do |x|
|
379
|
+
x.report(:alba, &alba)
|
380
|
+
x.report(:alba_inline, &alba_inline)
|
381
|
+
x.report(:ams, &ams)
|
382
|
+
x.report(:blueprinter, &blueprinter)
|
383
|
+
x.report(:jbuilder, &jbuilder)
|
384
|
+
x.report(:jsonapi, &jsonapi)
|
385
|
+
x.report(:jsonapi_same_format, &jsonapi_same_format)
|
386
|
+
x.report(:primalize, &primalize)
|
387
|
+
x.report(:rails, &rails)
|
388
|
+
x.report(:representable, &representable)
|
389
|
+
x.report(:simple_ams, &simple_ams)
|
390
|
+
|
391
|
+
x.compare!
|
392
|
+
end
|
@@ -20,6 +20,7 @@ gemfile(true) do
|
|
20
20
|
gem "primalize"
|
21
21
|
gem "oj"
|
22
22
|
gem "representable"
|
23
|
+
gem "simple_ams"
|
23
24
|
gem "sqlite3"
|
24
25
|
end
|
25
26
|
|
@@ -259,6 +260,28 @@ class PostRepresenter < Representable::Decorator
|
|
259
260
|
end
|
260
261
|
end
|
261
262
|
|
263
|
+
# --- SimpleAMS serializers ---
|
264
|
+
|
265
|
+
require "simple_ams"
|
266
|
+
|
267
|
+
class SimpleAMSCommentSerializer
|
268
|
+
include SimpleAMS::DSL
|
269
|
+
|
270
|
+
attributes :id, :body
|
271
|
+
end
|
272
|
+
|
273
|
+
class SimpleAMSPostSerializer
|
274
|
+
include SimpleAMS::DSL
|
275
|
+
|
276
|
+
attributes :id, :body
|
277
|
+
attribute :commenter_names
|
278
|
+
has_many :comments, serializer: SimpleAMSCommentSerializer
|
279
|
+
|
280
|
+
def commenter_names
|
281
|
+
object.commenters.pluck(:name)
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
262
285
|
# --- Test data creation ---
|
263
286
|
|
264
287
|
post = Post.create!(body: 'post')
|
@@ -290,6 +313,7 @@ jsonapi_same_format = proc { JsonApiSameFormatPostSerializer.new(post).to_json }
|
|
290
313
|
primalize = proc { PrimalizePostResource.new(post).to_json }
|
291
314
|
rails = Proc.new { ActiveSupport::JSON.encode(post.serializable_hash(include: :comments)) }
|
292
315
|
representable = Proc.new { PostRepresenter.new(post).to_json }
|
316
|
+
simple_ams = Proc.new { SimpleAMS::Renderer.new(post, serializer: SimpleAMSPostSerializer).to_json }
|
293
317
|
|
294
318
|
# --- Execute the serializers to check their output ---
|
295
319
|
|
@@ -304,26 +328,12 @@ puts "Serializer outputs ----------------------------------"
|
|
304
328
|
jsonapi_same_format: jsonapi_same_format,
|
305
329
|
primalize: primalize,
|
306
330
|
rails: rails,
|
307
|
-
representable: representable
|
331
|
+
representable: representable,
|
332
|
+
simple_ams: simple_ams,
|
308
333
|
}.each { |name, serializer| puts "#{name.to_s.ljust(24, ' ')} #{serializer.call}" }
|
309
334
|
|
310
335
|
# --- Run the benchmarks ---
|
311
336
|
|
312
|
-
require 'benchmark'
|
313
|
-
time = 1000
|
314
|
-
Benchmark.bmbm do |x|
|
315
|
-
x.report(:alba) { time.times(&alba) }
|
316
|
-
x.report(:alba_inline) { time.times(&alba_inline) }
|
317
|
-
x.report(:ams) { time.times(&ams) }
|
318
|
-
x.report(:blueprinter) { time.times(&blueprinter) }
|
319
|
-
x.report(:jbuilder) { time.times(&jbuilder) }
|
320
|
-
x.report(:jsonapi) { time.times(&jsonapi) }
|
321
|
-
x.report(:jsonapi_same_format) { time.times(&jsonapi_same_format) }
|
322
|
-
x.report(:primalize) { time.times(&primalize) }
|
323
|
-
x.report(:rails) { time.times(&rails) }
|
324
|
-
x.report(:representable) { time.times(&representable) }
|
325
|
-
end
|
326
|
-
|
327
337
|
require 'benchmark/ips'
|
328
338
|
Benchmark.ips do |x|
|
329
339
|
x.report(:alba, &alba)
|
@@ -336,6 +346,25 @@ Benchmark.ips do |x|
|
|
336
346
|
x.report(:primalize, &primalize)
|
337
347
|
x.report(:rails, &rails)
|
338
348
|
x.report(:representable, &representable)
|
349
|
+
x.report(:simple_ams, &simple_ams)
|
350
|
+
|
351
|
+
x.compare!
|
352
|
+
end
|
353
|
+
|
354
|
+
|
355
|
+
require 'benchmark/memory'
|
356
|
+
Benchmark.memory do |x|
|
357
|
+
x.report(:alba, &alba)
|
358
|
+
x.report(:alba_inline, &alba_inline)
|
359
|
+
x.report(:ams, &ams)
|
360
|
+
x.report(:blueprinter, &blueprinter)
|
361
|
+
x.report(:jbuilder, &jbuilder)
|
362
|
+
x.report(:jsonapi, &jsonapi)
|
363
|
+
x.report(:jsonapi_same_format, &jsonapi_same_format)
|
364
|
+
x.report(:primalize, &primalize)
|
365
|
+
x.report(:rails, &rails)
|
366
|
+
x.report(:representable, &representable)
|
367
|
+
x.report(:simple_ams, &simple_ams)
|
339
368
|
|
340
369
|
x.compare!
|
341
370
|
end
|
data/lib/alba.rb
CHANGED
data/lib/alba/association.rb
CHANGED
@@ -2,7 +2,7 @@ module Alba
|
|
2
2
|
# Base class for `One` and `Many`
|
3
3
|
# Child class should implement `to_hash` method
|
4
4
|
class Association
|
5
|
-
attr_reader :object
|
5
|
+
attr_reader :object, :name
|
6
6
|
|
7
7
|
# @param name [Symbol] name of the method to fetch association
|
8
8
|
# @param condition [Proc] a proc filtering data
|
@@ -15,14 +15,7 @@ module Alba
|
|
15
15
|
@resource = resource
|
16
16
|
return if @resource
|
17
17
|
|
18
|
-
|
19
|
-
@resource = resource_class
|
20
|
-
elsif Alba.inferring
|
21
|
-
const_parent = nesting.nil? ? Object : Object.const_get(nesting)
|
22
|
-
@resource = const_parent.const_get("#{ActiveSupport::Inflector.classify(@name)}Resource")
|
23
|
-
else
|
24
|
-
raise ArgumentError, 'When Alba.inferring is false, either resource or block is required'
|
25
|
-
end
|
18
|
+
assign_resource(nesting)
|
26
19
|
end
|
27
20
|
|
28
21
|
private
|
@@ -36,11 +29,26 @@ module Alba
|
|
36
29
|
end
|
37
30
|
end
|
38
31
|
|
32
|
+
def assign_resource(nesting)
|
33
|
+
@resource = if @block
|
34
|
+
resource_class
|
35
|
+
elsif Alba.inferring
|
36
|
+
resource_class_with_nesting(nesting)
|
37
|
+
else
|
38
|
+
raise ArgumentError, 'When Alba.inferring is false, either resource or block is required'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
39
42
|
def resource_class
|
40
43
|
klass = Class.new
|
41
44
|
klass.include(Alba::Resource)
|
42
45
|
klass.class_eval(&@block)
|
43
46
|
klass
|
44
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
|
45
53
|
end
|
46
54
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Alba
|
2
|
+
# This module represents the inflector, which is used by default
|
3
|
+
module DefaultInflector
|
4
|
+
begin
|
5
|
+
require 'active_support/inflector'
|
6
|
+
rescue LoadError
|
7
|
+
raise ::Alba::Error, 'To use transform_keys, please install `ActiveSupport` gem.'
|
8
|
+
end
|
9
|
+
|
10
|
+
module_function
|
11
|
+
|
12
|
+
# Camelizes a key
|
13
|
+
#
|
14
|
+
# @params key [String] key to be camelized
|
15
|
+
# @return [String] camelized key
|
16
|
+
def camelize(key)
|
17
|
+
ActiveSupport::Inflector.camelize(key)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Camelizes a key, 1st letter lowercase
|
21
|
+
#
|
22
|
+
# @params key [String] key to be camelized
|
23
|
+
# @return [String] camelized key
|
24
|
+
def camelize_lower(key)
|
25
|
+
ActiveSupport::Inflector.camelize(key, false)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Dasherizes a key
|
29
|
+
#
|
30
|
+
# @params key [String] key to be dasherized
|
31
|
+
# @return [String] dasherized key
|
32
|
+
def dasherize(key)
|
33
|
+
ActiveSupport::Inflector.dasherize(key)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Alba
|
2
|
+
# This module creates key transform functions
|
3
|
+
module KeyTransformFactory
|
4
|
+
class << self
|
5
|
+
# Create key transform function for given transform_type
|
6
|
+
#
|
7
|
+
# @params transform_type [Symbol] transform type
|
8
|
+
# @return [Proc] transform function
|
9
|
+
# @raise [Alba::Error] when transform_type is not supported
|
10
|
+
def create(transform_type)
|
11
|
+
case transform_type
|
12
|
+
when :camel
|
13
|
+
->(key) { _inflector.camelize(key) }
|
14
|
+
when :lower_camel
|
15
|
+
->(key) { _inflector.camelize_lower(key) }
|
16
|
+
when :dash
|
17
|
+
->(key) { _inflector.dasherize(key) }
|
18
|
+
else
|
19
|
+
raise ::Alba::Error, "Unknown transform_type: #{transform_type}. Supported transform_type are :camel, :lower_camel and :dash."
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def _inflector
|
26
|
+
Alba.inflector || begin
|
27
|
+
require_relative './default_inflector'
|
28
|
+
Alba::DefaultInflector
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/lib/alba/many.rb
CHANGED
data/lib/alba/resource.rb
CHANGED
@@ -1,14 +1,19 @@
|
|
1
1
|
require_relative 'one'
|
2
2
|
require_relative 'many'
|
3
|
+
require_relative 'key_transform_factory'
|
4
|
+
require_relative 'typed_attribute'
|
3
5
|
|
4
6
|
module Alba
|
5
7
|
# This module represents what should be serialized
|
6
8
|
module Resource
|
7
9
|
# @!parse include InstanceMethods
|
8
10
|
# @!parse extend ClassMethods
|
9
|
-
DSLS = {_attributes: {}, _key: nil,
|
11
|
+
DSLS = {_attributes: {}, _key: nil, _transform_key_function: nil, _transforming_root_key: false, _on_error: nil}.freeze
|
10
12
|
private_constant :DSLS
|
11
13
|
|
14
|
+
WITHIN_DEFAULT = Object.new.freeze
|
15
|
+
private_constant :WITHIN_DEFAULT
|
16
|
+
|
12
17
|
# @private
|
13
18
|
def self.included(base)
|
14
19
|
super
|
@@ -29,7 +34,7 @@ module Alba
|
|
29
34
|
# @param object [Object] the object to be serialized
|
30
35
|
# @param params [Hash] user-given Hash for arbitrary data
|
31
36
|
# @param within [Hash] determines what associations to be serialized. If not set, it serializes all associations.
|
32
|
-
def initialize(object, params: {}, within:
|
37
|
+
def initialize(object, params: {}, within: WITHIN_DEFAULT)
|
33
38
|
@object = object
|
34
39
|
@params = params.freeze
|
35
40
|
@within = within
|
@@ -60,21 +65,25 @@ module Alba
|
|
60
65
|
def _key
|
61
66
|
return @_key.to_s unless @_key == true && Alba.inferring
|
62
67
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
68
|
+
transforming_root_key? ? transform_key(key_from_resource_name) : key_from_resource_name
|
69
|
+
end
|
70
|
+
|
71
|
+
def key_from_resource_name
|
72
|
+
collection? ? resource_name.pluralize : resource_name
|
73
|
+
end
|
74
|
+
|
75
|
+
def resource_name
|
76
|
+
self.class.name.demodulize.delete_suffix('Resource').underscore
|
77
|
+
end
|
78
|
+
|
79
|
+
def transforming_root_key?
|
80
|
+
@_transforming_root_key.nil? ? Alba.transforming_root_key : @_transforming_root_key
|
67
81
|
end
|
68
82
|
|
69
83
|
def converter
|
70
84
|
lambda do |object|
|
71
85
|
arrays = @_attributes.map do |key, attribute|
|
72
|
-
key
|
73
|
-
if attribute.is_a?(Array) # Conditional
|
74
|
-
conditional_attribute(object, key, attribute)
|
75
|
-
else
|
76
|
-
[key, fetch_attribute(object, attribute)]
|
77
|
-
end
|
86
|
+
key_and_attribute_body_from(object, key, attribute)
|
78
87
|
rescue ::Alba::Error, FrozenError, TypeError
|
79
88
|
raise
|
80
89
|
rescue StandardError => e
|
@@ -84,10 +93,19 @@ module Alba
|
|
84
93
|
end
|
85
94
|
end
|
86
95
|
|
96
|
+
def key_and_attribute_body_from(object, key, attribute)
|
97
|
+
key = transform_key(key)
|
98
|
+
if attribute.is_a?(Array) # Conditional
|
99
|
+
conditional_attribute(object, key, attribute)
|
100
|
+
else
|
101
|
+
[key, fetch_attribute(object, attribute)]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
87
105
|
def conditional_attribute(object, key, attribute)
|
88
106
|
condition = attribute.last
|
89
107
|
arity = condition.arity
|
90
|
-
return [] if arity <= 1 && !
|
108
|
+
return [] if arity <= 1 && !instance_exec(object, &condition)
|
91
109
|
|
92
110
|
fetched_attribute = fetch_attribute(object, attribute.first)
|
93
111
|
attr = if attribute.first.is_a?(Alba::Association)
|
@@ -95,7 +113,7 @@ module Alba
|
|
95
113
|
else
|
96
114
|
fetched_attribute
|
97
115
|
end
|
98
|
-
return [] if arity >= 2 && !
|
116
|
+
return [] if arity >= 2 && !instance_exec(object, attr, &condition)
|
99
117
|
|
100
118
|
[key, fetched_attribute]
|
101
119
|
end
|
@@ -118,10 +136,9 @@ module Alba
|
|
118
136
|
|
119
137
|
# Override this method to supply custom key transform method
|
120
138
|
def transform_key(key)
|
121
|
-
return key
|
139
|
+
return key if @_transform_key_function.nil?
|
122
140
|
|
123
|
-
|
124
|
-
KeyTransformer.transform(key, @_transform_keys)
|
141
|
+
@_transform_key_function.call(key.to_s)
|
125
142
|
end
|
126
143
|
|
127
144
|
def fetch_attribute(object, attribute)
|
@@ -131,70 +148,28 @@ module Alba
|
|
131
148
|
when Proc
|
132
149
|
instance_exec(object, &attribute)
|
133
150
|
when Alba::One, Alba::Many
|
134
|
-
within = check_within
|
151
|
+
within = check_within(attribute.name.to_sym)
|
135
152
|
return unless within
|
136
153
|
|
137
154
|
attribute.to_hash(object, params: params, within: within)
|
138
|
-
when
|
139
|
-
|
155
|
+
when TypedAttribute
|
156
|
+
attribute.value(object)
|
140
157
|
else
|
141
158
|
raise ::Alba::Error, "Unsupported type of attribute: #{attribute.class}"
|
142
159
|
end
|
143
160
|
end
|
144
161
|
|
145
|
-
def
|
146
|
-
attr_name = hash[:attr_name]
|
147
|
-
type = hash[:type]
|
148
|
-
type_converter = hash[:type_converter]
|
149
|
-
value, result = type_check(object, attr_name, type)
|
150
|
-
return value if result
|
151
|
-
raise TypeError if !result && !type_converter
|
152
|
-
|
153
|
-
type_converter = type_converter_for(type) if type_converter == true
|
154
|
-
type_converter.call(value)
|
155
|
-
rescue TypeError
|
156
|
-
raise TypeError, "Attribute #{attr_name} is expected to be #{type} but actually #{value.nil? ? 'nil' : value.class.name}."
|
157
|
-
end
|
158
|
-
|
159
|
-
def type_check(object, attr_name, type)
|
160
|
-
value = object.public_send(attr_name)
|
161
|
-
type_correct = case type
|
162
|
-
when :String, ->(klass) { klass == String }
|
163
|
-
value.is_a?(String)
|
164
|
-
when :Integer, ->(klass) { klass == Integer }
|
165
|
-
value.is_a?(Integer)
|
166
|
-
when :Boolean
|
167
|
-
[true, false].include?(attr_name)
|
168
|
-
else
|
169
|
-
raise Alba::UnsupportedType, "Unknown type: #{type}"
|
170
|
-
end
|
171
|
-
[value, type_correct]
|
172
|
-
end
|
173
|
-
|
174
|
-
def type_converter_for(type)
|
175
|
-
case type
|
176
|
-
when :String, ->(klass) { klass == String }
|
177
|
-
->(object) { object.to_s }
|
178
|
-
when :Integer, ->(klass) { klass == Integer }
|
179
|
-
->(object) { Integer(object) }
|
180
|
-
when :Boolean
|
181
|
-
->(object) { !!object }
|
182
|
-
else
|
183
|
-
raise Alba::UnsupportedType, "Unknown type: #{type}"
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
def check_within
|
162
|
+
def check_within(association_name)
|
188
163
|
case @within
|
164
|
+
when WITHIN_DEFAULT # Default value, doesn't check within tree
|
165
|
+
WITHIN_DEFAULT
|
189
166
|
when Hash # Traverse within tree
|
190
|
-
@within.fetch(
|
167
|
+
@within.fetch(association_name, nil)
|
191
168
|
when Array # within tree ends with Array
|
192
|
-
@within.find { |item| item.to_sym ==
|
169
|
+
@within.find { |item| item.to_sym == association_name }
|
193
170
|
when Symbol # within tree could end with Symbol
|
194
|
-
@within ==
|
195
|
-
when true # In
|
196
|
-
true
|
197
|
-
when nil, false # In these cases, Alba stops serialization here.
|
171
|
+
@within == association_name
|
172
|
+
when nil, true, false # In these cases, Alba stops serialization here.
|
198
173
|
false
|
199
174
|
else
|
200
175
|
raise Alba::Error, "Unknown type for within option: #{@within.class}"
|
@@ -219,21 +194,32 @@ module Alba
|
|
219
194
|
# Set multiple attributes at once
|
220
195
|
#
|
221
196
|
# @param attrs [Array<String, Symbol>]
|
222
|
-
# @param
|
197
|
+
# @param if [Boolean] condition to decide if it should render these attributes
|
198
|
+
# @param attrs_with_types [Hash] attributes with name in its key and type and optional type converter in its value
|
223
199
|
def attributes(*attrs, if: nil, **attrs_with_types) # rubocop:disable Naming/MethodParameterName
|
224
200
|
if_value = binding.local_variable_get(:if)
|
201
|
+
assign_attributes(attrs, if_value)
|
202
|
+
assign_attributes_with_types(attrs_with_types, if_value)
|
203
|
+
end
|
204
|
+
|
205
|
+
def assign_attributes(attrs, if_value)
|
225
206
|
attrs.each do |attr_name|
|
226
207
|
attr = if_value ? [attr_name.to_sym, if_value] : attr_name.to_sym
|
227
208
|
@_attributes[attr_name.to_sym] = attr
|
228
209
|
end
|
210
|
+
end
|
211
|
+
private :assign_attributes
|
212
|
+
|
213
|
+
def assign_attributes_with_types(attrs_with_types, if_value)
|
229
214
|
attrs_with_types.each do |attr_name, type_and_converter|
|
230
215
|
attr_name = attr_name.to_sym
|
231
216
|
type, type_converter = type_and_converter
|
232
|
-
typed_attr =
|
217
|
+
typed_attr = TypedAttribute.new(name: attr_name, type: type, converter: type_converter)
|
233
218
|
attr = if_value ? [typed_attr, if_value] : typed_attr
|
234
219
|
@_attributes[attr_name] = attr
|
235
220
|
end
|
236
221
|
end
|
222
|
+
private :assign_attributes_with_types
|
237
223
|
|
238
224
|
# Set an attribute with the given block
|
239
225
|
#
|
@@ -307,7 +293,7 @@ module Alba
|
|
307
293
|
# @param type [String, Symbol]
|
308
294
|
# @param root [Boolean] decides if root key also should be transformed
|
309
295
|
def transform_keys(type, root: nil)
|
310
|
-
@
|
296
|
+
@_transform_key_function = KeyTransformFactory.create(type.to_sym)
|
311
297
|
@_transforming_root_key = root
|
312
298
|
end
|
313
299
|
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Alba
|
2
|
+
# Representing typed attributes to encapsulate logic about types
|
3
|
+
class TypedAttribute
|
4
|
+
# @param name [Symbol, String]
|
5
|
+
# @param type [Symbol, Class]
|
6
|
+
# @param converter [Proc]
|
7
|
+
def initialize(name:, type:, converter:)
|
8
|
+
@name = name
|
9
|
+
@type = type
|
10
|
+
@converter = case converter
|
11
|
+
when true then default_converter
|
12
|
+
when false, nil then null_converter
|
13
|
+
else converter
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# @param object [Object] target to check and convert type with
|
18
|
+
# @return [String, Integer, Boolean] type-checked or type-converted object
|
19
|
+
def value(object)
|
20
|
+
value, result = check(object)
|
21
|
+
result ? value : @converter.call(value)
|
22
|
+
rescue TypeError
|
23
|
+
raise TypeError, "Attribute #{@name} is expected to be #{@type} but actually #{display_value_for(value)}."
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def check(object)
|
29
|
+
value = object.public_send(@name)
|
30
|
+
type_correct = case @type
|
31
|
+
when :String, ->(klass) { klass == String }
|
32
|
+
value.is_a?(String)
|
33
|
+
when :Integer, ->(klass) { klass == Integer }
|
34
|
+
value.is_a?(Integer)
|
35
|
+
when :Boolean
|
36
|
+
[true, false].include?(value)
|
37
|
+
else
|
38
|
+
raise Alba::UnsupportedType, "Unknown type: #{@type}"
|
39
|
+
end
|
40
|
+
[value, type_correct]
|
41
|
+
end
|
42
|
+
|
43
|
+
def default_converter
|
44
|
+
case @type
|
45
|
+
when :String, ->(klass) { klass == String }
|
46
|
+
->(object) { object.to_s }
|
47
|
+
when :Integer, ->(klass) { klass == Integer }
|
48
|
+
->(object) { Integer(object) }
|
49
|
+
when :Boolean
|
50
|
+
->(object) { !!object }
|
51
|
+
else
|
52
|
+
raise Alba::UnsupportedType, "Unknown type: #{@type}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def null_converter
|
57
|
+
->(_) { raise TypeError }
|
58
|
+
end
|
59
|
+
|
60
|
+
def display_value_for(value)
|
61
|
+
value.nil? ? 'nil' : value.class.name
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/alba/version.rb
CHANGED
metadata
CHANGED
@@ -1,18 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: alba
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- OKURA Masafumi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-05-
|
11
|
+
date: 2021-05-31 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description: Alba is
|
14
|
-
|
15
|
-
do similar things. The internal is so simple that it's easy to hack and maintain.
|
13
|
+
description: Alba is the fastest JSON serializer for Ruby. It focuses on performance,
|
14
|
+
flexibility and usability.
|
16
15
|
email:
|
17
16
|
- masafumi.o1988@gmail.com
|
18
17
|
executables: []
|
@@ -34,7 +33,8 @@ files:
|
|
34
33
|
- Rakefile
|
35
34
|
- SECURITY.md
|
36
35
|
- alba.gemspec
|
37
|
-
- benchmark/
|
36
|
+
- benchmark/collection.rb
|
37
|
+
- benchmark/single_resource.rb
|
38
38
|
- bin/console
|
39
39
|
- bin/setup
|
40
40
|
- codecov.yml
|
@@ -43,10 +43,12 @@ files:
|
|
43
43
|
- gemfiles/without_oj.gemfile
|
44
44
|
- lib/alba.rb
|
45
45
|
- lib/alba/association.rb
|
46
|
-
- lib/alba/
|
46
|
+
- lib/alba/default_inflector.rb
|
47
|
+
- lib/alba/key_transform_factory.rb
|
47
48
|
- lib/alba/many.rb
|
48
49
|
- lib/alba/one.rb
|
49
50
|
- lib/alba/resource.rb
|
51
|
+
- lib/alba/typed_attribute.rb
|
50
52
|
- lib/alba/version.rb
|
51
53
|
- sider.yml
|
52
54
|
homepage: https://github.com/okuramasafumi/alba
|
data/lib/alba/key_transformer.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
module Alba
|
2
|
-
# Transform keys using `ActiveSupport::Inflector`
|
3
|
-
module KeyTransformer
|
4
|
-
begin
|
5
|
-
require 'active_support/inflector'
|
6
|
-
rescue LoadError
|
7
|
-
raise ::Alba::Error, 'To use transform_keys, please install `ActiveSupport` gem.'
|
8
|
-
end
|
9
|
-
|
10
|
-
module_function
|
11
|
-
|
12
|
-
# Transform key as given transform_type
|
13
|
-
#
|
14
|
-
# @params key [String] key to be transformed
|
15
|
-
# @params transform_type [Symbol] transform type
|
16
|
-
# @return [String] transformed key
|
17
|
-
# @raise [Alba::Error] when transform_type is not supported
|
18
|
-
def transform(key, transform_type)
|
19
|
-
key = key.to_s
|
20
|
-
case transform_type
|
21
|
-
when :camel
|
22
|
-
ActiveSupport::Inflector.camelize(key)
|
23
|
-
when :lower_camel
|
24
|
-
ActiveSupport::Inflector.camelize(key, false)
|
25
|
-
when :dash
|
26
|
-
ActiveSupport::Inflector.dasherize(key)
|
27
|
-
else
|
28
|
-
raise ::Alba::Error, "Unknown transform_type: #{transform_type}. Supported transform_type are :camel, :lower_camel and :dash."
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|