alba 1.2.0 → 1.3.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/.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
|