alba 1.3.0 → 1.4.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/perf.yml +21 -0
- data/.rubocop.yml +7 -7
- data/CHANGELOG.md +9 -0
- data/Gemfile +3 -2
- data/README.md +111 -3
- data/alba.gemspec +1 -1
- data/gemfiles/all.gemfile +1 -1
- data/gemfiles/without_active_support.gemfile +1 -1
- data/gemfiles/without_oj.gemfile +1 -1
- data/lib/alba.rb +48 -18
- data/lib/alba/association.rb +6 -17
- data/lib/alba/default_inflector.rb +3 -3
- data/lib/alba/key_transform_factory.rb +1 -1
- data/lib/alba/resource.rb +108 -60
- data/lib/alba/typed_attribute.rb +3 -6
- data/lib/alba/version.rb +1 -1
- data/script/perf_check.rb +174 -0
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 01d43d93e589197104d9ee166e6a9dcf727fd204524b52145d7d93b45ba79cd2
|
4
|
+
data.tar.gz: 023f2e0f8ff17bc78a01e2dc1eb1f98d9c41b0cd353e311f350c2704a9ffa602
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99992932a0f6a3d589e77e1b7dc4225be2e083a15320fa00a96f07a4cd9bbe9803454b94ecae409d439809a53225fb54ce283fcca074c0aed479e7d8b808d545
|
7
|
+
data.tar.gz: c77e99af9cf98781a2e961817b5c60e31fe178e6eef7bb8bd77b1af199043b8acd8395a27dd7cb10680ae82d28ec57e98106fef42f57055cb23752d63605a7b9
|
@@ -0,0 +1,21 @@
|
|
1
|
+
name: Performance Check
|
2
|
+
|
3
|
+
on: [pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
strategy:
|
8
|
+
fail-fast: false
|
9
|
+
matrix:
|
10
|
+
ruby: [2.5, 2.6, 2.7, 3.0]
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
steps:
|
13
|
+
- uses: actions/checkout@v2
|
14
|
+
- name: Set up Ruby
|
15
|
+
uses: ruby/setup-ruby@v1
|
16
|
+
with:
|
17
|
+
ruby-version: ${{ matrix.ruby }}
|
18
|
+
bundler-cache: true
|
19
|
+
- name: Run benchmark
|
20
|
+
run: |
|
21
|
+
ruby script/perf_check.rb
|
data/.rubocop.yml
CHANGED
@@ -13,12 +13,13 @@ AllCops:
|
|
13
13
|
- 'Rakefile'
|
14
14
|
- 'alba.gemspec'
|
15
15
|
- 'benchmark/**/*.rb'
|
16
|
+
- 'script/**/*.rb'
|
16
17
|
NewCops: enable
|
17
18
|
EnabledByDefault: true
|
18
19
|
TargetRubyVersion: 2.5
|
19
20
|
|
20
|
-
#
|
21
|
-
Bundler/
|
21
|
+
# Items in Gemfile is dev dependencies and we don't have to specify versions.
|
22
|
+
Bundler/GemVersion:
|
22
23
|
Enabled: false
|
23
24
|
|
24
25
|
# We'd like to write something like:
|
@@ -44,9 +45,6 @@ Metrics:
|
|
44
45
|
Exclude:
|
45
46
|
- 'test/**/*.rb'
|
46
47
|
|
47
|
-
Metrics/MethodLength:
|
48
|
-
Max: 15
|
49
|
-
|
50
48
|
# `Resource` module is a core module and its length tends to be long...
|
51
49
|
Metrics/ModuleLength:
|
52
50
|
Exclude:
|
@@ -55,7 +53,6 @@ Metrics/ModuleLength:
|
|
55
53
|
# Resource class includes DSLs, which tend to accept long list of parameters
|
56
54
|
Metrics/ParameterLists:
|
57
55
|
Exclude:
|
58
|
-
- 'lib/alba/resource.rb'
|
59
56
|
- 'test/**/*.rb'
|
60
57
|
|
61
58
|
# We need to eval resource code to test errors on resource classes
|
@@ -81,7 +78,10 @@ Style/InlineComment:
|
|
81
78
|
Enabled: false
|
82
79
|
|
83
80
|
Style/MethodCallWithArgsParentheses:
|
84
|
-
|
81
|
+
IgnoredMethods: ['require', 'require_relative', 'include', 'extend', 'puts', 'p', 'warn', 'raise', 'send', 'public_send']
|
82
|
+
Exclude:
|
83
|
+
# There are so many `attributes` call without parenthese and that's absolutely fine
|
84
|
+
- 'test/**/*.rb'
|
85
85
|
|
86
86
|
# There are so many cases we just want `if` expression!
|
87
87
|
Style/MissingElse:
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [1.4.0] 2021-06-30
|
10
|
+
|
11
|
+
- [Feat] Add a config method to set encoder directly
|
12
|
+
- [Feat] Implement `meta` method and option for metadata
|
13
|
+
- [Feat] Add `root_key` option to `Resource#serialize`
|
14
|
+
- [Feat] Enable setting key for collection with `root_key`
|
15
|
+
- [Feat] Add `Resource.root_key` and `Resource.root_key!`
|
16
|
+
- [Feat] `Alba.serialize` now infers resource class
|
17
|
+
|
9
18
|
## [1.3.0] 2021-05-31
|
10
19
|
|
11
20
|
- [Perf] Improve performance for `many` [641d8f9]
|
data/Gemfile
CHANGED
@@ -5,16 +5,17 @@ gemspec
|
|
5
5
|
|
6
6
|
gem 'activesupport', require: false # For backend
|
7
7
|
gem 'ffaker', require: false # For testing
|
8
|
+
gem 'inch', require: false # For inline documents
|
8
9
|
gem 'minitest', '~> 5.14' # For test
|
9
10
|
gem 'rake', '~> 13.0' # For test and automation
|
10
11
|
gem 'rubocop', '>= 0.79.0', require: false # For lint
|
11
|
-
gem 'rubocop-minitest', '~> 0.
|
12
|
+
gem 'rubocop-minitest', '~> 0.13.0', require: false # For lint
|
12
13
|
gem 'rubocop-performance', '~> 1.11.0', require: false # For lint
|
13
14
|
gem 'rubocop-rake', '>= 0.5.1', require: false # For lint
|
14
15
|
gem 'rubocop-sensible', '~> 0.3.0', require: false # For lint
|
15
16
|
gem 'simplecov', '~> 0.21.0', require: false # For test coverage
|
16
17
|
gem 'simplecov-cobertura', require: false # For test coverage
|
17
|
-
gem 'yard', require: false
|
18
|
+
gem 'yard', require: false # For documentation
|
18
19
|
|
19
20
|
platforms :ruby do
|
20
21
|
gem 'oj', '~> 3.11', require: false # For backend
|
data/README.md
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
[](https://github.com/okuramasafumi/alba/actions/workflows/main.yml)
|
3
3
|
[](https://codecov.io/gh/okuramasafumi/alba)
|
4
4
|
[](https://codeclimate.com/github/okuramasafumi/alba/maintainability)
|
5
|
+
[](http://inch-ci.org/github/okuramasafumi/alba)
|
5
6
|

|
6
7
|

|
7
8
|
|
@@ -98,6 +99,16 @@ You can set a backend like this:
|
|
98
99
|
Alba.backend = :oj
|
99
100
|
```
|
100
101
|
|
102
|
+
#### Encoder configuration
|
103
|
+
|
104
|
+
You can also set JSON encoder directly with a Proc.
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
Alba.encoder = ->(object) { JSON.generate(object) }
|
108
|
+
```
|
109
|
+
|
110
|
+
You can consider setting a backend with Symbol as a shortcut to set encoder.
|
111
|
+
|
101
112
|
#### Inference configuration
|
102
113
|
|
103
114
|
You can enable inference feature using `enable_inference!` method.
|
@@ -198,6 +209,35 @@ UserResource.new(user).serialize
|
|
198
209
|
# => '{"id":1,"articles":[{"title":"Hello World!"},{"title":"Super nice"}]}'
|
199
210
|
```
|
200
211
|
|
212
|
+
You can define associations inline if you don't need a class for association.
|
213
|
+
|
214
|
+
```ruby
|
215
|
+
class ArticleResource
|
216
|
+
include Alba::Resource
|
217
|
+
|
218
|
+
attributes :title
|
219
|
+
end
|
220
|
+
|
221
|
+
class UserResource
|
222
|
+
include Alba::Resource
|
223
|
+
|
224
|
+
attributes :id
|
225
|
+
|
226
|
+
many :articles, resource: ArticleResource
|
227
|
+
end
|
228
|
+
|
229
|
+
# This class works the same as `UserResource`
|
230
|
+
class AnotherUserResource
|
231
|
+
include Alba::Resource
|
232
|
+
|
233
|
+
attributes :id
|
234
|
+
|
235
|
+
many :articles do
|
236
|
+
attributes :title
|
237
|
+
end
|
238
|
+
end
|
239
|
+
```
|
240
|
+
|
201
241
|
### Inline definition with `Alba.serialize`
|
202
242
|
|
203
243
|
`Alba.serialize` method is a shortcut to define everything inline.
|
@@ -212,6 +252,13 @@ end
|
|
212
252
|
# => '{"foo":{"id":1,"articles":[{"title":"Hello World!","body":"Hello World!!!"},{"title":"Super nice","body":"Really nice!"}]}}'
|
213
253
|
```
|
214
254
|
|
255
|
+
`Alba.serialize` can be used when you don't know what kind of object you serialize. For example:
|
256
|
+
|
257
|
+
```ruby
|
258
|
+
Alba.serialize(something)
|
259
|
+
# => Same as `FooResource.new(something).serialize` when `something` is an instance of `Foo`.
|
260
|
+
```
|
261
|
+
|
215
262
|
Although this might be useful sometimes, it's generally recommended to define a class for Resource.
|
216
263
|
|
217
264
|
### Inheritance and Ignorance
|
@@ -235,13 +282,12 @@ class GenericFooResource
|
|
235
282
|
attributes :id, :name, :body
|
236
283
|
end
|
237
284
|
|
238
|
-
class
|
285
|
+
class RestrictedFooResource < GenericFooResource
|
239
286
|
ignoring :id, :body
|
240
287
|
end
|
241
288
|
|
242
|
-
|
289
|
+
RestrictedFooResource.new(foo).serialize
|
243
290
|
# => '{"name":"my foo"}'
|
244
|
-
end
|
245
291
|
```
|
246
292
|
|
247
293
|
### Key transformation
|
@@ -402,6 +448,20 @@ user = User.new(1, nil, nil)
|
|
402
448
|
UserResource.new(user).serialize # => '{"id":1}'
|
403
449
|
```
|
404
450
|
|
451
|
+
### Default
|
452
|
+
|
453
|
+
Alba doesn't support default value for attributes, but it's easy to set a default value.
|
454
|
+
|
455
|
+
```ruby
|
456
|
+
class FooResource
|
457
|
+
attribute :bar do |foo|
|
458
|
+
foo.bar || 'default bar'
|
459
|
+
end
|
460
|
+
end
|
461
|
+
```
|
462
|
+
|
463
|
+
We believe this is clearer than using some (not implemented yet) DSL such as `default` because there are some conditions where default values should be applied (`nil`, `blank?`, `empty?` etc.)
|
464
|
+
|
405
465
|
### Inference
|
406
466
|
|
407
467
|
After `Alba.enable_inference!` called, Alba tries to infer root key and association resource name.
|
@@ -510,6 +570,54 @@ Alba.on_error do |error, object, key, attribute, resource_class|
|
|
510
570
|
end
|
511
571
|
```
|
512
572
|
|
573
|
+
### Metadata
|
574
|
+
|
575
|
+
You can set a metadata with `meta` DSL or `meta` option.
|
576
|
+
|
577
|
+
```ruby
|
578
|
+
class UserResource
|
579
|
+
include Alba::Resource
|
580
|
+
|
581
|
+
root_key :user, :users
|
582
|
+
|
583
|
+
attributes :id, :name
|
584
|
+
|
585
|
+
meta do
|
586
|
+
if object.is_a?(Enumerable)
|
587
|
+
{size: object.size}
|
588
|
+
else
|
589
|
+
{foo: :bar}
|
590
|
+
end
|
591
|
+
end
|
592
|
+
end
|
593
|
+
|
594
|
+
user = User.new(1, 'Masafumi OKURA', 'masafumi@example.com')
|
595
|
+
UserResource.new([user]).serialize
|
596
|
+
# => '{"users":[{"id":1,"name":"Masafumi OKURA"}],"meta":{"size":1}}'
|
597
|
+
|
598
|
+
# You can merge metadata with `meta` option
|
599
|
+
|
600
|
+
UserResource.new([user]).serialize(meta: {foo: :bar})
|
601
|
+
# => '{"users":[{"id":1,"name":"Masafumi OKURA"}],"meta":{"size":1,"foo":"bar"}}'
|
602
|
+
|
603
|
+
# You can set metadata with `meta` option alone
|
604
|
+
|
605
|
+
class UserResourceWithoutMeta
|
606
|
+
include Alba::Resource
|
607
|
+
|
608
|
+
root_key :user, :users
|
609
|
+
|
610
|
+
attributes :id, :name
|
611
|
+
end
|
612
|
+
|
613
|
+
UserResource.new([user]).serialize(meta: {foo: :bar})
|
614
|
+
# => '{"users":[{"id":1,"name":"Masafumi OKURA"}],"meta":{"foo":"bar"}}'
|
615
|
+
```
|
616
|
+
|
617
|
+
You can use `object` method to access the underlying object and `params` to access the params in `meta` block.
|
618
|
+
|
619
|
+
Note that setting root key is required when setting a metadata.
|
620
|
+
|
513
621
|
### Circular associations control
|
514
622
|
|
515
623
|
**Note that this feature works correctly since version 1.3. In previous versions it doesn't work as expected.**
|
data/alba.gemspec
CHANGED
@@ -14,7 +14,7 @@ Gem::Specification.new do |spec|
|
|
14
14
|
|
15
15
|
spec.metadata['homepage_uri'] = spec.homepage
|
16
16
|
spec.metadata['source_code_uri'] = 'https://github.com/okuramasafumi/alba'
|
17
|
-
spec.metadata['changelog_uri'] = 'https://github.com/okuramasafumi/alba/blob/
|
17
|
+
spec.metadata['changelog_uri'] = 'https://github.com/okuramasafumi/alba/blob/main/CHANGELOG.md'
|
18
18
|
|
19
19
|
# Specify which files should be added to the gem when it is released.
|
20
20
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
data/gemfiles/all.gemfile
CHANGED
@@ -11,7 +11,7 @@ gem 'rubocop-rake', '>= 0.5.1', require: false # For lint
|
|
11
11
|
gem 'rubocop-sensible', '~> 0.3.0', require: false # For lint
|
12
12
|
gem 'simplecov', '~> 0.21.0', require: false # For test coverage
|
13
13
|
gem 'simplecov-cobertura', require: false # For test coverage
|
14
|
-
gem 'yard', require: false
|
14
|
+
gem 'yard', require: false # For documentation
|
15
15
|
|
16
16
|
platforms :ruby do
|
17
17
|
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.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'json'
|
1
2
|
require_relative 'alba/version'
|
2
3
|
require_relative 'alba/resource'
|
3
4
|
|
@@ -14,6 +15,8 @@ module Alba
|
|
14
15
|
|
15
16
|
class << self
|
16
17
|
attr_reader :backend, :encoder, :inferring, :_on_error, :transforming_root_key
|
18
|
+
|
19
|
+
# Accessor for inflector, a module responsible for incflecting strings
|
17
20
|
attr_accessor :inflector
|
18
21
|
|
19
22
|
# Set the backend, which actually serializes object into JSON
|
@@ -24,24 +27,35 @@ module Alba
|
|
24
27
|
# @raise [Alba::UnsupportedBackend] if backend is not supported
|
25
28
|
def backend=(backend)
|
26
29
|
@backend = backend&.to_sym
|
27
|
-
|
30
|
+
set_encoder_from_backend
|
31
|
+
end
|
32
|
+
|
33
|
+
# Set encoder, a Proc object that accepts an object and generates JSON from it
|
34
|
+
# Set backend as `:custom` which indicates no preset encoder is used
|
35
|
+
#
|
36
|
+
# @param encoder [Proc]
|
37
|
+
# @raise [ArgumentError] if given encoder is not a Proc or its arity is not one
|
38
|
+
def encoder=(encoder)
|
39
|
+
raise ArgumentError, 'Encoder must be a Proc accepting one argument' unless encoder.is_a?(Proc) && encoder.arity == 1
|
40
|
+
|
41
|
+
@encoder = encoder
|
42
|
+
@backend = :custom
|
28
43
|
end
|
29
44
|
|
30
45
|
# Serialize the object with inline definitions
|
31
46
|
#
|
32
47
|
# @param object [Object] the object to be serialized
|
33
|
-
# @param key [Symbol]
|
48
|
+
# @param key [Symbol, nil, true] DEPRECATED, use root_key instead
|
49
|
+
# @param root_key [Symbol, nil, true]
|
34
50
|
# @param block [Block] resource block
|
35
51
|
# @return [String] serialized JSON string
|
36
52
|
# @raise [ArgumentError] if block is absent or `with` argument's type is wrong
|
37
|
-
def serialize(object, key: nil, &block)
|
38
|
-
|
53
|
+
def serialize(object, key: nil, root_key: nil, &block)
|
54
|
+
warn '`key` option to `serialize` method is deprecated, use `root_key` instead.' if key
|
55
|
+
klass = block ? resource_class(&block) : infer_resource_class(object.class.name)
|
39
56
|
|
40
|
-
klass = Class.new
|
41
|
-
klass.include(Alba::Resource)
|
42
|
-
klass.class_eval(&block)
|
43
57
|
resource = klass.new(object)
|
44
|
-
resource.serialize(
|
58
|
+
resource.serialize(root_key: root_key || key)
|
45
59
|
end
|
46
60
|
|
47
61
|
# Enable inference for key and resource name
|
@@ -63,6 +77,9 @@ module Alba
|
|
63
77
|
#
|
64
78
|
# @param [Symbol] handler
|
65
79
|
# @param [Block]
|
80
|
+
# @raise [ArgumentError] if both handler and block params exist
|
81
|
+
# @raise [ArgumentError] if both handler and block params don't exist
|
82
|
+
# @return [void]
|
66
83
|
def on_error(handler = nil, &block)
|
67
84
|
raise ArgumentError, 'You cannot specify error handler with both Symbol and block' if handler && block
|
68
85
|
raise ArgumentError, 'You must specify error handler with either Symbol or block' unless handler || block
|
@@ -80,18 +97,32 @@ module Alba
|
|
80
97
|
@transforming_root_key = false
|
81
98
|
end
|
82
99
|
|
100
|
+
# @param block [Block] resource body
|
101
|
+
# @return [Class<Alba::Resource>] resource class
|
102
|
+
def resource_class(&block)
|
103
|
+
klass = Class.new
|
104
|
+
klass.include(Alba::Resource)
|
105
|
+
klass.class_eval(&block)
|
106
|
+
klass
|
107
|
+
end
|
108
|
+
|
109
|
+
# @param name [String] a String Alba infers resource name with
|
110
|
+
# @param nesting [String, nil] namespace Alba tries to find resource class in
|
111
|
+
# @return [Class<Alba::Resource>] resource class
|
112
|
+
def infer_resource_class(name, nesting: nil)
|
113
|
+
enable_inference!
|
114
|
+
const_parent = nesting.nil? ? Object : Object.const_get(nesting)
|
115
|
+
const_parent.const_get("#{ActiveSupport::Inflector.classify(name)}Resource")
|
116
|
+
end
|
117
|
+
|
83
118
|
private
|
84
119
|
|
85
|
-
def
|
120
|
+
def set_encoder_from_backend
|
86
121
|
@encoder = case @backend
|
87
|
-
when :oj, :oj_strict
|
88
|
-
|
89
|
-
when :
|
90
|
-
|
91
|
-
when :active_support
|
92
|
-
try_active_support
|
93
|
-
when nil, :default, :json
|
94
|
-
default_encoder
|
122
|
+
when :oj, :oj_strict then try_oj
|
123
|
+
when :oj_rails then try_oj(mode: :rails)
|
124
|
+
when :active_support then try_active_support
|
125
|
+
when nil, :default, :json then default_encoder
|
95
126
|
else
|
96
127
|
raise Alba::UnsupportedBackend, "Unsupported backend, #{backend}"
|
97
128
|
end
|
@@ -115,7 +146,6 @@ module Alba
|
|
115
146
|
|
116
147
|
def default_encoder
|
117
148
|
lambda do |hash|
|
118
|
-
require 'json'
|
119
149
|
JSON.dump(hash)
|
120
150
|
end
|
121
151
|
end
|
data/lib/alba/association.rb
CHANGED
@@ -4,9 +4,10 @@ module Alba
|
|
4
4
|
class Association
|
5
5
|
attr_reader :object, :name
|
6
6
|
|
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
|
7
|
+
# @param name [Symbol, String] name of the method to fetch association
|
8
|
+
# @param condition [Proc, nil] a proc filtering data
|
9
|
+
# @param resource [Class<Alba::Resource>, nil] a resource class for the association
|
10
|
+
# @param nesting [String] a namespace where source class is inferred with
|
10
11
|
# @param block [Block] used to define resource when resource arg is absent
|
11
12
|
def initialize(name:, condition: nil, resource: nil, nesting: nil, &block)
|
12
13
|
@name = name
|
@@ -31,24 +32,12 @@ module Alba
|
|
31
32
|
|
32
33
|
def assign_resource(nesting)
|
33
34
|
@resource = if @block
|
34
|
-
resource_class
|
35
|
+
Alba.resource_class(&@block)
|
35
36
|
elsif Alba.inferring
|
36
|
-
|
37
|
+
Alba.infer_resource_class(@name, nesting: nesting)
|
37
38
|
else
|
38
39
|
raise ArgumentError, 'When Alba.inferring is false, either resource or block is required'
|
39
40
|
end
|
40
41
|
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
42
|
end
|
54
43
|
end
|
@@ -11,7 +11,7 @@ module Alba
|
|
11
11
|
|
12
12
|
# Camelizes a key
|
13
13
|
#
|
14
|
-
# @
|
14
|
+
# @param key [String] key to be camelized
|
15
15
|
# @return [String] camelized key
|
16
16
|
def camelize(key)
|
17
17
|
ActiveSupport::Inflector.camelize(key)
|
@@ -19,7 +19,7 @@ module Alba
|
|
19
19
|
|
20
20
|
# Camelizes a key, 1st letter lowercase
|
21
21
|
#
|
22
|
-
# @
|
22
|
+
# @param key [String] key to be camelized
|
23
23
|
# @return [String] camelized key
|
24
24
|
def camelize_lower(key)
|
25
25
|
ActiveSupport::Inflector.camelize(key, false)
|
@@ -27,7 +27,7 @@ module Alba
|
|
27
27
|
|
28
28
|
# Dasherizes a key
|
29
29
|
#
|
30
|
-
# @
|
30
|
+
# @param key [String] key to be dasherized
|
31
31
|
# @return [String] dasherized key
|
32
32
|
def dasherize(key)
|
33
33
|
ActiveSupport::Inflector.dasherize(key)
|
@@ -4,7 +4,7 @@ module Alba
|
|
4
4
|
class << self
|
5
5
|
# Create key transform function for given transform_type
|
6
6
|
#
|
7
|
-
# @
|
7
|
+
# @param transform_type [Symbol] transform type
|
8
8
|
# @return [Proc] transform function
|
9
9
|
# @raise [Alba::Error] when transform_type is not supported
|
10
10
|
def create(transform_type)
|
data/lib/alba/resource.rb
CHANGED
@@ -8,7 +8,7 @@ module Alba
|
|
8
8
|
module Resource
|
9
9
|
# @!parse include InstanceMethods
|
10
10
|
# @!parse extend ClassMethods
|
11
|
-
DSLS = {_attributes: {}, _key: nil, _transform_key_function: nil, _transforming_root_key: false, _on_error: nil}.freeze
|
11
|
+
DSLS = {_attributes: {}, _key: nil, _key_for_collection: nil, _meta: nil, _transform_key_function: nil, _transforming_root_key: false, _on_error: nil}.freeze # rubocop:disable Layout/LineLength
|
12
12
|
private_constant :DSLS
|
13
13
|
|
14
14
|
WITHIN_DEFAULT = Object.new.freeze
|
@@ -33,7 +33,7 @@ module Alba
|
|
33
33
|
|
34
34
|
# @param object [Object] the object to be serialized
|
35
35
|
# @param params [Hash] user-given Hash for arbitrary data
|
36
|
-
# @param within [
|
36
|
+
# @param within [Object, nil, false, true] determines what associations to be serialized. If not set, it serializes all associations.
|
37
37
|
def initialize(object, params: {}, within: WITHIN_DEFAULT)
|
38
38
|
@object = object
|
39
39
|
@params = params.freeze
|
@@ -43,11 +43,19 @@ module Alba
|
|
43
43
|
|
44
44
|
# Serialize object into JSON string
|
45
45
|
#
|
46
|
-
# @param key [Symbol]
|
46
|
+
# @param key [Symbol, nil, true] DEPRECATED, use root_key instead
|
47
|
+
# @param root_key [Symbol, nil, true]
|
48
|
+
# @param meta [Hash] metadata for this seialization
|
47
49
|
# @return [String] serialized JSON string
|
48
|
-
def serialize(key: nil)
|
49
|
-
key
|
50
|
-
|
50
|
+
def serialize(key: nil, root_key: nil, meta: {})
|
51
|
+
warn '`key` option to `serialize` method is deprecated, use `root_key` instead.' if key
|
52
|
+
key = key.nil? && root_key.nil? ? fetch_key : root_key || key
|
53
|
+
hash = if key && key != ''
|
54
|
+
h = {key.to_s => serializable_hash}
|
55
|
+
hash_with_metadata(h, meta)
|
56
|
+
else
|
57
|
+
serializable_hash
|
58
|
+
end
|
51
59
|
Alba.encoder.call(hash)
|
52
60
|
end
|
53
61
|
|
@@ -61,15 +69,29 @@ module Alba
|
|
61
69
|
|
62
70
|
private
|
63
71
|
|
72
|
+
def hash_with_metadata(hash, meta)
|
73
|
+
base = @_meta ? instance_eval(&@_meta) : {}
|
74
|
+
metadata = base.merge(meta)
|
75
|
+
hash[:meta] = metadata unless metadata.empty?
|
76
|
+
hash
|
77
|
+
end
|
78
|
+
|
79
|
+
def fetch_key
|
80
|
+
collection? ? _key_for_collection : _key
|
81
|
+
end
|
82
|
+
|
83
|
+
def _key_for_collection
|
84
|
+
return @_key_for_collection.to_s unless @_key_for_collection == true && Alba.inferring
|
85
|
+
|
86
|
+
key = resource_name.pluralize
|
87
|
+
transforming_root_key? ? transform_key(key) : key
|
88
|
+
end
|
89
|
+
|
64
90
|
# @return [String]
|
65
91
|
def _key
|
66
92
|
return @_key.to_s unless @_key == true && Alba.inferring
|
67
93
|
|
68
|
-
transforming_root_key? ? transform_key(
|
69
|
-
end
|
70
|
-
|
71
|
-
def key_from_resource_name
|
72
|
-
collection? ? resource_name.pluralize : resource_name
|
94
|
+
transforming_root_key? ? transform_key(resource_name) : resource_name
|
73
95
|
end
|
74
96
|
|
75
97
|
def resource_name
|
@@ -105,14 +127,11 @@ module Alba
|
|
105
127
|
def conditional_attribute(object, key, attribute)
|
106
128
|
condition = attribute.last
|
107
129
|
arity = condition.arity
|
130
|
+
# We can return early to skip fetch_attribute
|
108
131
|
return [] if arity <= 1 && !instance_exec(object, &condition)
|
109
132
|
|
110
133
|
fetched_attribute = fetch_attribute(object, attribute.first)
|
111
|
-
attr =
|
112
|
-
attribute.first.object
|
113
|
-
else
|
114
|
-
fetched_attribute
|
115
|
-
end
|
134
|
+
attr = attribute.first.is_a?(Alba::Association) ? attribute.first.object : fetched_attribute
|
116
135
|
return [] if arity >= 2 && !instance_exec(object, attr, &condition)
|
117
136
|
|
118
137
|
[key, fetched_attribute]
|
@@ -121,14 +140,10 @@ module Alba
|
|
121
140
|
def handle_error(error, object, key, attribute)
|
122
141
|
on_error = @_on_error || Alba._on_error
|
123
142
|
case on_error
|
124
|
-
when :raise, nil
|
125
|
-
|
126
|
-
when :
|
127
|
-
|
128
|
-
when :ignore
|
129
|
-
[]
|
130
|
-
when Proc
|
131
|
-
on_error.call(error, object, key, attribute, self.class)
|
143
|
+
when :raise, nil then raise
|
144
|
+
when :nullify then [key, nil]
|
145
|
+
when :ignore then []
|
146
|
+
when Proc then on_error.call(error, object, key, attribute, self.class)
|
132
147
|
else
|
133
148
|
raise ::Alba::Error, "Unknown on_error: #{on_error.inspect}"
|
134
149
|
end
|
@@ -143,34 +158,27 @@ module Alba
|
|
143
158
|
|
144
159
|
def fetch_attribute(object, attribute)
|
145
160
|
case attribute
|
146
|
-
when Symbol
|
147
|
-
|
148
|
-
when
|
149
|
-
|
150
|
-
when Alba::One, Alba::Many
|
151
|
-
within = check_within(attribute.name.to_sym)
|
152
|
-
return unless within
|
153
|
-
|
154
|
-
attribute.to_hash(object, params: params, within: within)
|
155
|
-
when TypedAttribute
|
156
|
-
attribute.value(object)
|
161
|
+
when Symbol then object.public_send attribute
|
162
|
+
when Proc then instance_exec(object, &attribute)
|
163
|
+
when Alba::One, Alba::Many then yield_if_within(attribute.name.to_sym) { |within| attribute.to_hash(object, params: params, within: within) }
|
164
|
+
when TypedAttribute then attribute.value(object)
|
157
165
|
else
|
158
166
|
raise ::Alba::Error, "Unsupported type of attribute: #{attribute.class}"
|
159
167
|
end
|
160
168
|
end
|
161
169
|
|
170
|
+
def yield_if_within(association_name)
|
171
|
+
within = check_within(association_name)
|
172
|
+
yield(within) if within
|
173
|
+
end
|
174
|
+
|
162
175
|
def check_within(association_name)
|
163
176
|
case @within
|
164
|
-
when WITHIN_DEFAULT # Default value, doesn't check within tree
|
165
|
-
|
166
|
-
when
|
167
|
-
|
168
|
-
when
|
169
|
-
@within.find { |item| item.to_sym == association_name }
|
170
|
-
when Symbol # within tree could end with Symbol
|
171
|
-
@within == association_name
|
172
|
-
when nil, true, false # In these cases, Alba stops serialization here.
|
173
|
-
false
|
177
|
+
when WITHIN_DEFAULT then WITHIN_DEFAULT # Default value, doesn't check within tree
|
178
|
+
when Hash then @within.fetch(association_name, nil) # Traverse within tree
|
179
|
+
when Array then @within.find { |item| item.to_sym == association_name }
|
180
|
+
when Symbol then @within == association_name
|
181
|
+
when nil, true, false then false # Stop here
|
174
182
|
else
|
175
183
|
raise Alba::Error, "Unknown type for within option: #{@within.class}"
|
176
184
|
end
|
@@ -191,11 +199,16 @@ module Alba
|
|
191
199
|
DSLS.each_key { |name| subclass.instance_variable_set("@#{name}", instance_variable_get("@#{name}").clone) }
|
192
200
|
end
|
193
201
|
|
202
|
+
# Defining methods for DSLs and disable parameter number check since for users' benefits increasing params is fine
|
203
|
+
# rubocop:disable Metrics/ParameterLists
|
204
|
+
|
194
205
|
# Set multiple attributes at once
|
195
206
|
#
|
196
207
|
# @param attrs [Array<String, Symbol>]
|
197
|
-
# @param if [
|
198
|
-
# @param attrs_with_types [Hash]
|
208
|
+
# @param if [Proc] condition to decide if it should serialize these attributes
|
209
|
+
# @param attrs_with_types [Hash<[Symbol, String], [Array<Symbol, Proc>, Symbol]>]
|
210
|
+
# attributes with name in its key and type and optional type converter in its value
|
211
|
+
# @return [void]
|
199
212
|
def attributes(*attrs, if: nil, **attrs_with_types) # rubocop:disable Naming/MethodParameterName
|
200
213
|
if_value = binding.local_variable_get(:if)
|
201
214
|
assign_attributes(attrs, if_value)
|
@@ -224,9 +237,11 @@ module Alba
|
|
224
237
|
# Set an attribute with the given block
|
225
238
|
#
|
226
239
|
# @param name [String, Symbol] key name
|
227
|
-
# @param options [Hash]
|
240
|
+
# @param options [Hash<Symbol, Proc>]
|
241
|
+
# @option options [Proc] if a condition to decide if this attribute should be serialized
|
228
242
|
# @param block [Block] the block called during serialization
|
229
243
|
# @raise [ArgumentError] if block is absent
|
244
|
+
# @return [void]
|
230
245
|
def attribute(name, **options, &block)
|
231
246
|
raise ArgumentError, 'No block given in attribute method' unless block
|
232
247
|
|
@@ -235,12 +250,14 @@ module Alba
|
|
235
250
|
|
236
251
|
# Set One association
|
237
252
|
#
|
238
|
-
# @param name [String, Symbol]
|
239
|
-
# @param condition [Proc]
|
240
|
-
# @param resource [Class<Alba::Resource
|
241
|
-
# @param key [String, Symbol] used as key when given
|
242
|
-
# @param options [Hash]
|
253
|
+
# @param name [String, Symbol] name of the association, used as key when `key` param doesn't exist
|
254
|
+
# @param condition [Proc, nil] a Proc to modify the association
|
255
|
+
# @param resource [Class<Alba::Resource>, String, nil] representing resource for this association
|
256
|
+
# @param key [String, Symbol, nil] used as key when given
|
257
|
+
# @param options [Hash<Symbol, Proc>]
|
258
|
+
# @option options [Proc] if a condition to decide if this association should be serialized
|
243
259
|
# @param block [Block]
|
260
|
+
# @return [void]
|
244
261
|
# @see Alba::One#initialize
|
245
262
|
def one(name, condition = nil, resource: nil, key: nil, **options, &block)
|
246
263
|
nesting = self.name&.rpartition('::')&.first
|
@@ -251,12 +268,14 @@ module Alba
|
|
251
268
|
|
252
269
|
# Set Many association
|
253
270
|
#
|
254
|
-
# @param name [String, Symbol]
|
255
|
-
# @param condition [Proc]
|
256
|
-
# @param resource [Class<Alba::Resource
|
257
|
-
# @param key [String, Symbol] used as key when given
|
258
|
-
# @param options [Hash]
|
271
|
+
# @param name [String, Symbol] name of the association, used as key when `key` param doesn't exist
|
272
|
+
# @param condition [Proc, nil] a Proc to filter the collection
|
273
|
+
# @param resource [Class<Alba::Resource>, String, nil] representing resource for this association
|
274
|
+
# @param key [String, Symbol, nil] used as key when given
|
275
|
+
# @param options [Hash<Symbol, Proc>]
|
276
|
+
# @option options [Proc] if a condition to decide if this association should be serialized
|
259
277
|
# @param block [Block]
|
278
|
+
# @return [void]
|
260
279
|
# @see Alba::Many#initialize
|
261
280
|
def many(name, condition = nil, resource: nil, key: nil, **options, &block)
|
262
281
|
nesting = self.name&.rpartition('::')&.first
|
@@ -268,14 +287,40 @@ module Alba
|
|
268
287
|
# Set key
|
269
288
|
#
|
270
289
|
# @param key [String, Symbol]
|
290
|
+
# @deprecated Use {#root_key} instead
|
271
291
|
def key(key)
|
292
|
+
warn '[DEPRECATION] `key` is deprecated, use `root_key` instead.'
|
272
293
|
@_key = key.respond_to?(:to_sym) ? key.to_sym : key
|
273
294
|
end
|
274
295
|
|
296
|
+
# Set root key
|
297
|
+
#
|
298
|
+
# @param key [String, Symbol]
|
299
|
+
# @param key_for_collection [String, Symbol]
|
300
|
+
# @raise [NoMethodError] when key doesn't respond to `to_sym` method
|
301
|
+
def root_key(key, key_for_collection = nil)
|
302
|
+
@_key = key.to_sym
|
303
|
+
@_key_for_collection = key_for_collection&.to_sym
|
304
|
+
end
|
305
|
+
|
275
306
|
# Set key to true
|
276
307
|
#
|
308
|
+
# @deprecated Use {#root_key!} instead
|
277
309
|
def key!
|
310
|
+
warn '[DEPRECATION] `key!` is deprecated, use `root_key!` instead.'
|
278
311
|
@_key = true
|
312
|
+
@_key_for_collection = true
|
313
|
+
end
|
314
|
+
|
315
|
+
# Set root key to true
|
316
|
+
def root_key!
|
317
|
+
@_key = true
|
318
|
+
@_key_for_collection = true
|
319
|
+
end
|
320
|
+
|
321
|
+
# Set metadata
|
322
|
+
def meta(&block)
|
323
|
+
@_meta = block
|
279
324
|
end
|
280
325
|
|
281
326
|
# Delete attributes
|
@@ -298,15 +343,18 @@ module Alba
|
|
298
343
|
end
|
299
344
|
|
300
345
|
# Set error handler
|
346
|
+
# If this is set it's used as a error handler overriding global one
|
301
347
|
#
|
302
|
-
# @param [Symbol]
|
303
|
-
# @param [Block]
|
348
|
+
# @param handler [Symbol] `:raise`, `:ignore` or `:nullify`
|
349
|
+
# @param block [Block]
|
304
350
|
def on_error(handler = nil, &block)
|
305
351
|
raise ArgumentError, 'You cannot specify error handler with both Symbol and block' if handler && block
|
306
352
|
raise ArgumentError, 'You must specify error handler with either Symbol or block' unless handler || block
|
307
353
|
|
308
354
|
@_on_error = handler || block
|
309
355
|
end
|
356
|
+
|
357
|
+
# rubocop:enable Metrics/ParameterLists
|
310
358
|
end
|
311
359
|
end
|
312
360
|
end
|
data/lib/alba/typed_attribute.rb
CHANGED
@@ -28,12 +28,9 @@ module Alba
|
|
28
28
|
def check(object)
|
29
29
|
value = object.public_send(@name)
|
30
30
|
type_correct = case @type
|
31
|
-
when :String, ->(klass) { klass == String }
|
32
|
-
|
33
|
-
when :
|
34
|
-
value.is_a?(Integer)
|
35
|
-
when :Boolean
|
36
|
-
[true, false].include?(value)
|
31
|
+
when :String, ->(klass) { klass == String } then value.is_a?(String)
|
32
|
+
when :Integer, ->(klass) { klass == Integer } then value.is_a?(Integer)
|
33
|
+
when :Boolean then [true, false].include?(value)
|
37
34
|
else
|
38
35
|
raise Alba::UnsupportedType, "Unknown type: #{@type}"
|
39
36
|
end
|
data/lib/alba/version.rb
CHANGED
@@ -0,0 +1,174 @@
|
|
1
|
+
# Benchmark script to run varieties of JSON serializers
|
2
|
+
# Fetch Alba from local, otherwise fetch latest from RubyGems
|
3
|
+
# exit(status)
|
4
|
+
|
5
|
+
# --- Bundle dependencies ---
|
6
|
+
|
7
|
+
require "bundler/inline"
|
8
|
+
|
9
|
+
gemfile(true) do
|
10
|
+
source "https://rubygems.org"
|
11
|
+
git_source(:github) { |repo| "https://github.com/#{repo}.git" }
|
12
|
+
|
13
|
+
gem "activerecord", "~> 6.1.3"
|
14
|
+
gem "alba", path: '../'
|
15
|
+
gem "benchmark-ips"
|
16
|
+
gem "blueprinter"
|
17
|
+
gem "jbuilder"
|
18
|
+
gem "multi_json"
|
19
|
+
gem "oj"
|
20
|
+
gem "sqlite3"
|
21
|
+
end
|
22
|
+
|
23
|
+
# --- Test data model setup ---
|
24
|
+
|
25
|
+
require "active_record"
|
26
|
+
require "oj"
|
27
|
+
require "sqlite3"
|
28
|
+
Oj.optimize_rails
|
29
|
+
|
30
|
+
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
|
31
|
+
|
32
|
+
ActiveRecord::Schema.define do
|
33
|
+
create_table :posts, force: true do |t|
|
34
|
+
t.string :body
|
35
|
+
end
|
36
|
+
|
37
|
+
create_table :comments, force: true do |t|
|
38
|
+
t.integer :post_id
|
39
|
+
t.string :body
|
40
|
+
t.integer :commenter_id
|
41
|
+
end
|
42
|
+
|
43
|
+
create_table :users, force: true do |t|
|
44
|
+
t.string :name
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Post < ActiveRecord::Base
|
49
|
+
has_many :comments
|
50
|
+
has_many :commenters, through: :comments, class_name: 'User', source: :commenter
|
51
|
+
|
52
|
+
def attributes
|
53
|
+
{id: nil, body: nil, commenter_names: commenter_names}
|
54
|
+
end
|
55
|
+
|
56
|
+
def commenter_names
|
57
|
+
commenters.pluck(:name)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class Comment < ActiveRecord::Base
|
62
|
+
belongs_to :post
|
63
|
+
belongs_to :commenter, class_name: 'User'
|
64
|
+
|
65
|
+
def attributes
|
66
|
+
{id: nil, body: nil}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class User < ActiveRecord::Base
|
71
|
+
has_many :comments
|
72
|
+
end
|
73
|
+
|
74
|
+
# --- Alba serializers ---
|
75
|
+
|
76
|
+
require "alba"
|
77
|
+
|
78
|
+
class AlbaCommentResource
|
79
|
+
include ::Alba::Resource
|
80
|
+
attributes :id, :body
|
81
|
+
end
|
82
|
+
|
83
|
+
class AlbaPostResource
|
84
|
+
include ::Alba::Resource
|
85
|
+
attributes :id, :body
|
86
|
+
attribute :commenter_names do |post|
|
87
|
+
post.commenters.pluck(:name)
|
88
|
+
end
|
89
|
+
many :comments, resource: AlbaCommentResource
|
90
|
+
end
|
91
|
+
|
92
|
+
# --- Blueprint serializers ---
|
93
|
+
|
94
|
+
require "blueprinter"
|
95
|
+
|
96
|
+
class CommentBlueprint < Blueprinter::Base
|
97
|
+
fields :id, :body
|
98
|
+
end
|
99
|
+
|
100
|
+
class PostBlueprint < Blueprinter::Base
|
101
|
+
fields :id, :body, :commenter_names
|
102
|
+
association :comments, blueprint: CommentBlueprint
|
103
|
+
|
104
|
+
def commenter_names
|
105
|
+
commenters.pluck(:name)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# --- JBuilder serializers ---
|
110
|
+
|
111
|
+
require "jbuilder"
|
112
|
+
|
113
|
+
class Post
|
114
|
+
def to_builder
|
115
|
+
Jbuilder.new do |post|
|
116
|
+
post.call(self, :id, :body, :commenter_names, :comments)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def commenter_names
|
121
|
+
commenters.pluck(:name)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class Comment
|
126
|
+
def to_builder
|
127
|
+
Jbuilder.new do |comment|
|
128
|
+
comment.call(self, :id, :body)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
# --- Test data creation ---
|
134
|
+
|
135
|
+
100.times do |i|
|
136
|
+
post = Post.create!(body: "post#{i}")
|
137
|
+
user1 = User.create!(name: "John#{i}")
|
138
|
+
user2 = User.create!(name: "Jane#{i}")
|
139
|
+
10.times do |n|
|
140
|
+
post.comments.create!(commenter: user1, body: "Comment1_#{i}_#{n}")
|
141
|
+
post.comments.create!(commenter: user2, body: "Comment2_#{i}_#{n}")
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
posts = Post.all.to_a
|
146
|
+
|
147
|
+
# --- Store the serializers in procs ---
|
148
|
+
|
149
|
+
alba = Proc.new { AlbaPostResource.new(posts).serialize }
|
150
|
+
blueprinter = Proc.new { PostBlueprint.render(posts) }
|
151
|
+
jbuilder = Proc.new do
|
152
|
+
Jbuilder.new do |json|
|
153
|
+
json.array!(posts) do |post|
|
154
|
+
json.post post.to_builder
|
155
|
+
end
|
156
|
+
end.target!
|
157
|
+
end
|
158
|
+
|
159
|
+
# --- Run the benchmarks ---
|
160
|
+
|
161
|
+
require 'benchmark/ips'
|
162
|
+
result = Benchmark.ips do |x|
|
163
|
+
x.report(:alba, &alba)
|
164
|
+
x.report(:blueprinter, &blueprinter)
|
165
|
+
x.report(:jbuilder, &jbuilder)
|
166
|
+
end
|
167
|
+
|
168
|
+
entries = result.entries.map {|entry| [entry.label, entry.iterations]}
|
169
|
+
alba_ips = entries.find {|e| e.first == :alba }.last
|
170
|
+
blueprinter_ips = entries.find {|e| e.first == :blueprinter }.last
|
171
|
+
jbuidler_ips = entries.find {|e| e.first == :jbuilder }.last
|
172
|
+
# Alba should be as fast as jbuilder and faster than blueprinter
|
173
|
+
alba_is_fast_enough = (alba_ips - jbuidler_ips) > -10.0 && (alba_ips - blueprinter_ips) > 10.0
|
174
|
+
exit(alba_is_fast_enough)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: alba
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.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-
|
11
|
+
date: 2021-06-30 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Alba is the fastest JSON serializer for Ruby. It focuses on performance,
|
14
14
|
flexibility and usability.
|
@@ -22,6 +22,7 @@ files:
|
|
22
22
|
- ".github/ISSUE_TEMPLATE/feature_request.md"
|
23
23
|
- ".github/dependabot.yml"
|
24
24
|
- ".github/workflows/main.yml"
|
25
|
+
- ".github/workflows/perf.yml"
|
25
26
|
- ".gitignore"
|
26
27
|
- ".rubocop.yml"
|
27
28
|
- ".yardopts"
|
@@ -50,6 +51,7 @@ files:
|
|
50
51
|
- lib/alba/resource.rb
|
51
52
|
- lib/alba/typed_attribute.rb
|
52
53
|
- lib/alba/version.rb
|
54
|
+
- script/perf_check.rb
|
53
55
|
- sider.yml
|
54
56
|
homepage: https://github.com/okuramasafumi/alba
|
55
57
|
licenses:
|
@@ -57,7 +59,7 @@ licenses:
|
|
57
59
|
metadata:
|
58
60
|
homepage_uri: https://github.com/okuramasafumi/alba
|
59
61
|
source_code_uri: https://github.com/okuramasafumi/alba
|
60
|
-
changelog_uri: https://github.com/okuramasafumi/alba/blob/
|
62
|
+
changelog_uri: https://github.com/okuramasafumi/alba/blob/main/CHANGELOG.md
|
61
63
|
post_install_message:
|
62
64
|
rdoc_options: []
|
63
65
|
require_paths:
|