alba 2.4.2 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e96e153192419f36d10cdd2377720a4d818bd935e8b4d7364e0272f460f74c30
4
- data.tar.gz: 2ca78d61d8e01c1655f294f1836c386bb561db3fca45e964bd3c2b0023912b03
3
+ metadata.gz: a659a06525b2938d8115b64712344d9dc173ad86a3c6c2b392c283eccd5048fd
4
+ data.tar.gz: 9a9fa379c582928259580396277472438ce1afe566088214b08cccddd5836bae
5
5
  SHA512:
6
- metadata.gz: 45c70af557b2fcb5ec1e8e7d2269fa6639e086d506b5b8706361a208c24a436ce61807d5d6466accfc516c4f1e556d23a3535236ed2d9047f3c72491702028bb
7
- data.tar.gz: 148818585445adde1fa4f992df975400863856e227e12fe706f241523a06901e34d099511d13ae5fff4cbd5a9ea29c659551034ffb188bc1cc3d519c72e2a88e
6
+ metadata.gz: 6985bbc1306bc15d7173cab450df98aa8382dcaf9384975558b0a590e2f056c1d3fa16e53684033baabbeabc202fce975487afa34170691f174eafcca43043f7
7
+ data.tar.gz: 16eab414db7324830348eff012f269a670879138110c0595341101649acc7cf6432a9d916d44b65b885d91923d1387e6a19ca4094654f82ef60b152d94778d1d
@@ -38,7 +38,7 @@ jobs:
38
38
 
39
39
  steps:
40
40
  - name: Checkout repository
41
- uses: actions/checkout@v3
41
+ uses: actions/checkout@v4
42
42
 
43
43
  # Initializes the CodeQL tools for scanning.
44
44
  - name: Initialize CodeQL
@@ -0,0 +1,17 @@
1
+ name: Lint
2
+
3
+ on: [push,pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ runs-on: ubuntu-latest
8
+ steps:
9
+ - uses: actions/checkout@v4
10
+ - name: Set up Ruby
11
+ uses: ruby/setup-ruby@v1
12
+ with:
13
+ ruby-version: 3.0 # The lowest version Alba supports
14
+ bundler-cache: true
15
+ - name: Run RuboCop
16
+ run: |
17
+ bundle exec rubocop
@@ -8,7 +8,7 @@ jobs:
8
8
  fail-fast: false
9
9
  matrix:
10
10
  os: [ubuntu-latest, windows-latest, macos-latest]
11
- ruby: [2.7, 3.0, 3.1, 3.2, head, jruby, truffleruby]
11
+ ruby: ['3.0', 3.1, 3.2, head, jruby, truffleruby]
12
12
  gemfile: [all, without_active_support, without_oj]
13
13
  exclude:
14
14
  - os: windows-latest
@@ -19,7 +19,7 @@ jobs:
19
19
  env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
20
20
  BUNDLE_GEMFILE: ${{ (matrix.gemfile == 'without_active_support' && 'gemfiles/without_active_support.gemfile') || (matrix.gemfile == 'without_oj' && 'gemfiles/without_oj.gemfile') || null }}
21
21
  steps:
22
- - uses: actions/checkout@v3
22
+ - uses: actions/checkout@v4
23
23
  - name: Set up Ruby
24
24
  uses: ruby/setup-ruby@v1
25
25
  with:
@@ -7,10 +7,10 @@ jobs:
7
7
  strategy:
8
8
  fail-fast: false
9
9
  matrix:
10
- ruby: [2.7, 3.0, 3.1]
10
+ ruby: ['3.0', 3.1, 3.2]
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
- - uses: actions/checkout@v3
13
+ - uses: actions/checkout@v4
14
14
  - name: Set up Ruby
15
15
  uses: ruby/setup-ruby@v1
16
16
  with:
data/.rubocop.yml CHANGED
@@ -22,7 +22,7 @@ AllCops:
22
22
  - 'script/**/*.rb'
23
23
  NewCops: enable
24
24
  EnabledByDefault: true
25
- TargetRubyVersion: 2.7
25
+ TargetRubyVersion: 3.0
26
26
 
27
27
  # Items in Gemfile is dev dependencies and we don't have to specify versions.
28
28
  Bundler/GemVersion:
@@ -62,6 +62,12 @@ Metrics:
62
62
 
63
63
  # `Resource` module is a core module and its length tends to be long...
64
64
  # `Alba` main module is also long because it has all parts of configuration
65
+ Metrics/ClassLength:
66
+ Exclude:
67
+ - 'lib/alba/resource.rb'
68
+ - 'lib/alba.rb'
69
+ - 'test/**/*.rb' # Neec to specify this
70
+
65
71
  Metrics/ModuleLength:
66
72
  Exclude:
67
73
  - 'lib/alba/resource.rb'
@@ -93,6 +99,7 @@ Style/ConstantVisibility:
93
99
  - 'lib/alba/version.rb'
94
100
  - 'test/**/*.rb'
95
101
 
102
+ # Copyright is in README
96
103
  Style/Copyright:
97
104
  Enabled: false
98
105
 
@@ -100,34 +107,41 @@ Style/Copyright:
100
107
  Style/DisableCopsWithinSourceCodeDirective:
101
108
  Enabled: false
102
109
 
110
+ # Test files doesn't need to have documentation
103
111
  Style/Documentation:
104
112
  Exclude:
105
113
  - 'test/**/*'
106
114
 
115
+ # In README it's so obvious
107
116
  Style/DocumentationMethod:
108
117
  Exclude:
109
118
  - 'README.md'
110
119
 
120
+ # This might be true in the future, but not many good things
111
121
  Style/FrozenStringLiteralComment:
112
122
  Enabled: false
113
123
 
124
+ # I don't want to think about error class in example code
114
125
  Style/ImplicitRuntimeError:
115
126
  Exclude:
116
127
  - 'README.md'
117
128
 
129
+ # We use it, don't we?
118
130
  Style/InlineComment:
119
131
  Enabled: false
120
132
 
121
133
  Style/MethodCallWithArgsParentheses:
122
134
  AllowedMethods: ['require', 'require_relative', 'include', 'extend', 'puts', 'p', 'warn', 'raise', 'send', 'public_send', 'alias_method']
123
135
  Exclude:
124
- # There are so many `attributes` call without parenthese and that's absolutely fine
136
+ # There are so many calls like `attributes` and `register_type` without parenthese and that's absolutely fine
125
137
  - 'test/**/*.rb'
138
+ - 'README.md'
126
139
 
127
140
  # There are so many cases we just want `if` expression!
128
141
  Style/MissingElse:
129
142
  EnforcedStyle: case
130
143
 
144
+ # It's example code, please forgive us
131
145
  Style/OptionalBooleanParameter:
132
146
  Exclude:
133
147
  - 'README.md'
data/CHANGELOG.md CHANGED
@@ -6,11 +6,37 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
- ## [2.4.2] 2023-10-11
9
+ ## [3.0.1] 2023-10-13
10
10
 
11
11
  ### Fixed
12
12
 
13
- - Multithread bug
13
+ - Fixed a bug where methods such as `test` or `params` cannot be used as attribute name [#344](https://github.com/okuramasafumi/alba/pull/344)
14
+ - Remove redundant code
15
+
16
+ ## [3.0.0] 2023-10-11
17
+
18
+ ### IMPORTANT
19
+
20
+ **This release contains an important bug fix that can cause data corruption.**
21
+ **If you're using Ruby 3, it's highly recommended to upgrade to [v3.0.0](https://rubygems.org/gems/alba/versions/3.0.0)**
22
+ **If you're using Ruby 2, please upgrade to [v2.4.2](https://rubygems.org/gems/alba/versions/2.4.2) that contains bug fix only as soon as possible.**
23
+
24
+ ### Added
25
+
26
+ - Custom type [#333](https://github.com/okuramasafumi/alba/pull/333)
27
+
28
+ ### Changed
29
+
30
+ - Prefer resource method [#323](https://github.com/okuramasafumi/alba/pull/323)
31
+
32
+ ### Fixed
33
+
34
+ - Multithread bug [No PR](https://github.com/okuramasafumi/alba/commit/d20ed9efbf2f99827c12b8a07308e2f5aea6ab6d)
35
+ - This is a critical bug that can cause data corruption.
36
+
37
+ ### Removed
38
+
39
+ - Drop support for Ruby 2 series [No PR](https://github.com/okuramasafumi/alba/commit/20be222555bde69c31fa9cbe4408b3f638cd7580)
14
40
 
15
41
  ## [2.4.1] 2023-08-02
16
42
 
data/README.md CHANGED
@@ -11,6 +11,14 @@
11
11
 
12
12
  Alba is a JSON serializer for Ruby, JRuby, and TruffleRuby.
13
13
 
14
+ ## IMPORTANT NOTICE
15
+
16
+ Both version `3.0.0` and `2.4.2` contain important bug fix.
17
+ However, version `3.0.0` has some bugs (see https://github.com/okuramasafumi/alba/issues/342).
18
+ Until they get fixed, it's highly recommended to upgrade to version `2.4.2`.
19
+ Dependabot and similar tools might create an automated Pull Request to upgrade to `3.0.0`, so it might be required to upgrade to `2.4.2` manually.
20
+ Sorry for the inconvenience.
21
+
14
22
  ## TL;DR
15
23
 
16
24
  Alba allows you to do something like below.
@@ -83,6 +91,12 @@ Alba is easy to use because there are only a few methods to remember. It's also
83
91
 
84
92
  While Alba's core is simple, it provides additional features when you need them, For example, Alba provides [a way to control circular associations](#circular-associations-control), [root key and association resource name inference](#root-key-and-association-resource-name-inference) and [supports layouts](#layout).
85
93
 
94
+ ### Other reasons
95
+
96
+ - Dependency free, no need to install `oj` or `activesupport` while Alba works well with them
97
+ - Well tested, the test coverage is 99%
98
+ - Well maintained, gettings frequent update and new releases (see [version history](https://rubygems.org/gems/alba/versions))
99
+
86
100
  ## Installation
87
101
 
88
102
  Add this line to your application's Gemfile:
@@ -101,7 +115,7 @@ Or install it yourself as:
101
115
 
102
116
  ## Supported Ruby versions
103
117
 
104
- Alba supports CRuby 2.7 and higher and latest JRuby and TruffleRuby.
118
+ Alba supports CRuby 3.0 and higher and latest JRuby and TruffleRuby.
105
119
 
106
120
  ## Documentation
107
121
 
@@ -130,13 +144,13 @@ Alba's configuration is fairly simple.
130
144
 
131
145
  Backend is the actual part serializing an object into JSON. Alba supports these backends.
132
146
 
133
- |name|description|requires_external_gem|
134
- |--|--|--|
135
- |`oj`, `oj_strict`|Using Oj in `strict` mode|Yes(C extension)|
136
- |`oj_rails`|It's `oj` but in `rails` mode|Yes(C extension)|
137
- |`oj_default`|It's `oj` but respects mode set by users|Yes(C extension)|
138
- |`active_support`|For Rails compatibility|Yes|
139
- |`default`, `json`|Using `json` gem|No|
147
+ |name|description|requires_external_gem| encoder|
148
+ |--|--|--|--|
149
+ |`oj`, `oj_strict`|Using Oj in `strict` mode|Yes(C extension)|`Oj.dump(object, mode: :strict)`|
150
+ |`oj_rails`|It's `oj` but in `rails` mode|Yes(C extension)|`Oj.dump(object, mode: :rails)`|
151
+ |`oj_default`|It's `oj` but respects mode set by users|Yes(C extension)|`Oj.dump(object)`|
152
+ |`active_support`|For Rails compatibility|Yes|`ActiveSupport::JSON.encode(object)`|
153
+ |`default`, `json`|Using `json` gem|No|`JSON.generate(object)`|
140
154
 
141
155
  You can set a backend like this:
142
156
 
@@ -144,6 +158,12 @@ You can set a backend like this:
144
158
  Alba.backend = :oj
145
159
  ```
146
160
 
161
+ This is equivalent as:
162
+
163
+ ```ruby
164
+ Alba.encoder = ->(object) { Oj.dump(object, mode: :strict) }
165
+ ```
166
+
147
167
  #### Encoder configuration
148
168
 
149
169
  You can also set JSON encoder directly with a Proc.
@@ -284,7 +304,7 @@ class User
284
304
  end
285
305
 
286
306
  def name_with_email
287
- "dummy!"
307
+ 'dummy!'
288
308
  end
289
309
  end
290
310
 
@@ -1231,7 +1251,7 @@ You can control circular associations with `within` option. `within` option is a
1231
1251
 
1232
1252
  For more details, please refer to [test code](https://github.com/okuramasafumi/alba/blob/main/test/usecases/circular_association_test.rb)
1233
1253
 
1234
- ### Experimental support of types
1254
+ ### Types
1235
1255
 
1236
1256
  You can validate and convert input with types.
1237
1257
 
@@ -1270,7 +1290,26 @@ UserResource.new(user).serialize
1270
1290
  # => TypeError, 'Attribute bio is expected to be String but actually nil.'
1271
1291
  ```
1272
1292
 
1273
- Note that this feature is experimental and interfaces are subject to change.
1293
+ #### Custom types
1294
+
1295
+ You can define custom types to abstract data conversion logic. To define custom types, you can use `Alba.register_type` like below.
1296
+
1297
+ ```ruby
1298
+ # Typically in initializer
1299
+ Alba.register_type :iso8601, converter: ->(time) { time.iso8601(3) }, auto_convert: true
1300
+ ```
1301
+
1302
+ Then use it as regular types.
1303
+
1304
+ ```rb
1305
+ class UserResource
1306
+ include Alba::Resource
1307
+
1308
+ attributes :id, created_at: :iso8601
1309
+ end
1310
+ ```
1311
+
1312
+ You now get `created_at` attribute with `iso8601` format!
1274
1313
 
1275
1314
  ### Collection serialization into Hash
1276
1315
 
@@ -1376,7 +1415,7 @@ class ApplicationResource
1376
1415
  include Alba::Resource
1377
1416
 
1378
1417
  def self.with_id
1379
- attributes :id
1418
+ attributes(:id)
1380
1419
  end
1381
1420
  end
1382
1421
 
@@ -1401,7 +1440,7 @@ class ApplicationResource
1401
1440
 
1402
1441
  helper do
1403
1442
  def with_id
1404
- attributes :id
1443
+ attributes(:id)
1405
1444
  end
1406
1445
  end
1407
1446
  end
@@ -1561,7 +1600,7 @@ FooResource.new(foo).serialize
1561
1600
  # => "{:id=>1}" is printed
1562
1601
  ```
1563
1602
 
1564
- Here, we override `serialize` method with `prepend`. In overridden method we print the result of `serializable_hash` that gives the basic hash for serialization to `serialize` method. Using `...` allows us to override without knowing method signiture of `serialize`.
1603
+ Here, we override `serialize` method with `prepend`. In overridden method we print the result of `serializable_hash` that gives the basic hash for serialization to `serialize` method. Using `...` allows us to override without knowing method signature of `serialize`.
1565
1604
 
1566
1605
  Don't forget calling `super` in this way.
1567
1606
 
data/alba.gemspec CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
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
- spec.required_ruby_version = Gem::Requirement.new('>= 2.7.0')
13
+ spec.required_ruby_version = Gem::Requirement.new('>= 3.0.0')
14
14
 
15
15
  spec.metadata = {
16
16
  'bug_tracker_uri' => 'https://github.com/okuramasafumi/alba/issues',
@@ -12,7 +12,8 @@ module Alba
12
12
 
13
13
  # @param name [Symbol, String] name of the method to fetch association
14
14
  # @param condition [Proc, nil] a proc filtering data
15
- # @param resource [Class<Alba::Resource>, nil] a resource class for the association
15
+ # @param resource [Class<Alba::Resource>, Proc, String, Symbol, nil]
16
+ # a resource class for the association, a proc returning a resource class or a name of the resource
16
17
  # @param params [Hash] params override for the association
17
18
  # @param nesting [String] a namespace where source class is inferred with
18
19
  # @param key_transformation [Symbol] key transformation type
@@ -52,14 +53,15 @@ module Alba
52
53
  private
53
54
 
54
55
  def constantize(resource)
55
- case resource # rubocop:disable Style/MissingElse
56
+ case resource
56
57
  when Class
57
58
  resource
58
59
  when Symbol, String
59
- Object.const_get(resource)
60
60
  self.class.const_cache.fetch(resource) do
61
61
  self.class.const_cache[resource] = Object.const_get(resource)
62
62
  end
63
+ else
64
+ raise Error, "Unexpected resource type: #{resource.class}"
63
65
  end
64
66
  end
65
67
 
@@ -51,6 +51,8 @@ module Alba
51
51
 
52
52
  # OpenStruct is used as a simple solution for converting Hash or Array of Hash into an object
53
53
  # Using OpenStruct is not good in general, but in this case there's no other solution
54
+ # rubocop:disable Style/OpenStructUse
55
+ # rubocop:disable Performance/OpenStruct
54
56
  def objectize(fetched_attribute)
55
57
  return fetched_attribute unless @body.is_a?(Alba::Association)
56
58
 
@@ -62,5 +64,7 @@ module Alba
62
64
  OpenStruct.new(fetched_attribute)
63
65
  end
64
66
  end
67
+ # rubocop:enable Style/OpenStructUse
68
+ # rubocop:enable Performance/OpenStruct
65
69
  end
66
70
  end
data/lib/alba/resource.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  require_relative 'association'
2
2
  require_relative 'conditional_attribute'
3
3
  require_relative 'constants'
4
+ require_relative 'type'
4
5
  require_relative 'typed_attribute'
5
6
  require_relative 'nested_attribute'
6
7
  require_relative 'deprecation'
@@ -11,8 +12,8 @@ module Alba
11
12
  module Resource
12
13
  # @!parse include InstanceMethods
13
14
  # @!parse extend ClassMethods
14
- DSLS = {_attributes: {}, _key: nil, _key_for_collection: nil, _meta: nil, _transform_type: :none, _transforming_root_key: false, _key_transformation_cascade: true, _on_error: nil, _on_nil: nil, _layout: nil, _collection_key: nil, _helper: nil}.freeze # rubocop:disable Layout/LineLength
15
- private_constant :DSLS
15
+ INTERNAL_VARIABLES = {_attributes: {}, _key: nil, _key_for_collection: nil, _meta: nil, _transform_type: :none, _transforming_root_key: false, _key_transformation_cascade: true, _on_error: nil, _on_nil: nil, _layout: nil, _collection_key: nil, _helper: nil, _resource_methods: []}.freeze # rubocop:disable Layout/LineLength
16
+ private_constant :INTERNAL_VARIABLES
16
17
 
17
18
  WITHIN_DEFAULT = Object.new.freeze
18
19
  private_constant :WITHIN_DEFAULT
@@ -24,7 +25,7 @@ module Alba
24
25
  setup_method_body = 'private def _setup;'
25
26
  base.class_eval do
26
27
  # Initialize
27
- DSLS.each do |name, initial|
28
+ INTERNAL_VARIABLES.each do |name, initial|
28
29
  instance_variable_set("@#{name}", initial.dup) unless instance_variable_defined?("@#{name}")
29
30
  setup_method_body << "@#{name} = self.class.#{name};"
30
31
  end
@@ -59,25 +60,13 @@ module Alba
59
60
  serialize_with(as_json(root_key: root_key, meta: meta))
60
61
  end
61
62
 
62
- if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.0')
63
- # For Rails compatibility
64
- # The first options is a dummy parameter but required
65
- # You can pass empty Hash if you don't want to pass any arguments
66
- #
67
- # @see #serialize
68
- # @see https://github.com/rails/rails/blob/7-0-stable/actionpack/lib/action_controller/metal/renderers.rb#L156
69
- def to_json(options, root_key: nil, meta: {})
70
- _to_json(root_key, meta, options)
71
- end
72
- else
73
- # For Rails compatibility
74
- # The first options is a dummy parameter
75
- #
76
- # @see #serialize
77
- # @see https://github.com/rails/rails/blob/7-0-stable/actionpack/lib/action_controller/metal/renderers.rb#L156
78
- def to_json(options = {}, root_key: nil, meta: {})
79
- _to_json(root_key, meta, options)
80
- end
63
+ # For Rails compatibility
64
+ # The first options is a dummy parameter
65
+ #
66
+ # @see #serialize
67
+ # @see https://github.com/rails/rails/blob/7-0-stable/actionpack/lib/action_controller/metal/renderers.rb#L156
68
+ def to_json(options = {}, root_key: nil, meta: {})
69
+ _to_json(root_key, meta, options)
81
70
  end
82
71
 
83
72
  # Returns a Hash correspondng {#serialize}
@@ -265,9 +254,8 @@ module Alba
265
254
  value.nil? && nil_handler ? instance_exec(obj, key, attribute, &nil_handler) : value
266
255
  end
267
256
 
268
- # TODO: from version 3, `_fetch_attribute_from_resource_first` is default
269
257
  def fetch_attribute_from_object_and_resource(obj, attribute)
270
- _fetch_attribute_from_object_first(obj, attribute)
258
+ _fetch_attribute_from_resource_first(obj, attribute)
271
259
  end
272
260
 
273
261
  def _fetch_attribute_from_object_first(obj, attribute)
@@ -277,9 +265,15 @@ module Alba
277
265
  end
278
266
 
279
267
  def _fetch_attribute_from_resource_first(obj, attribute)
280
- __send__(attribute, obj)
281
- rescue NoMethodError
282
- obj.__send__(attribute)
268
+ if @_resource_methods.include?(attribute)
269
+ begin
270
+ __send__(attribute, obj)
271
+ rescue NoMethodError
272
+ obj.__send__(attribute)
273
+ end
274
+ else
275
+ obj.__send__(attribute)
276
+ end
283
277
  end
284
278
 
285
279
  def nil_handler
@@ -312,12 +306,18 @@ module Alba
312
306
 
313
307
  # Class methods
314
308
  module ClassMethods
315
- attr_reader(*DSLS.keys)
309
+ attr_reader(*INTERNAL_VARIABLES.keys)
310
+
311
+ # This `method_added` is used for defining "resource methods"
312
+ def method_added(method_name)
313
+ _resource_methods << method_name.to_sym unless method_name.to_sym == :_setup
314
+ super
315
+ end
316
316
 
317
317
  # @private
318
318
  def inherited(subclass)
319
319
  super
320
- DSLS.each_key { |name| subclass.instance_variable_set("@#{name}", instance_variable_get("@#{name}").clone) }
320
+ INTERNAL_VARIABLES.each_key { |name| subclass.instance_variable_set("@#{name}", instance_variable_get("@#{name}").clone) }
321
321
  end
322
322
 
323
323
  # Defining methods for DSLs and disable parameter number check since for users' benefits increasing params is fine
@@ -382,9 +382,9 @@ module Alba
382
382
  # @return [void]
383
383
  # @see Alba::Association#initialize
384
384
  def association(name, condition = nil, resource: nil, key: nil, params: {}, **options, &block)
385
- key_transformation = @_key_transformation_cascade ? @_transform_type : :none
385
+ transformation = @_key_transformation_cascade ? @_transform_type : :none
386
386
  assoc = Association.new(
387
- name: name, condition: condition, resource: resource, params: params, nesting: nesting, key_transformation: key_transformation, helper: @_helper, &block
387
+ name: name, condition: condition, resource: resource, params: params, nesting: nesting, key_transformation: transformation, helper: @_helper, &block
388
388
  )
389
389
  @_attributes[key&.to_sym || name.to_sym] = options[:if] ? ConditionalAttribute.new(body: assoc, condition: options[:if]) : assoc
390
390
  end
@@ -533,4 +533,7 @@ module Alba
533
533
  end
534
534
  end
535
535
  end
536
+
537
+ Serializer = Resource
538
+ public_constant :Serializer
536
539
  end
data/lib/alba/type.rb ADDED
@@ -0,0 +1,43 @@
1
+ module Alba
2
+ # Representing type itself, combined with {Alba::TypedAttribute}
3
+ class Type
4
+ attr_reader :name
5
+ attr_writer :auto_convert
6
+
7
+ # @param name [Symbol, String] name of the type
8
+ # @param check [Proc, Boolean] proc to check type
9
+ # If false, type check is skipped
10
+ # @param converter [Proc] proc to convert type
11
+ # @param auto_convert [Boolean] whether to convert type automatically
12
+ def initialize(name, check:, converter:, auto_convert: false)
13
+ @name = name
14
+ @check = check
15
+ @converter = converter
16
+ @auto_convert = auto_convert
17
+ end
18
+
19
+ # Type check
20
+ #
21
+ # @param value [Object] value to check
22
+ # @return [Boolean] the result of type check
23
+ def check(value)
24
+ @check == false ? false : @check.call(value)
25
+ end
26
+
27
+ # Type convert
28
+ # If @auto_convert is true, @convert proc is called with obj
29
+ # Otherwise, it raises an exception that is caught by {Alba::TypedAttribute}
30
+ #
31
+ # @param obj [Object] object to convert
32
+ def convert(obj)
33
+ @auto_convert ? @converter.call(obj) : raise(TypeError)
34
+ end
35
+
36
+ # Enable auto convert with given converter
37
+ # @param converter [Proc] proc to convert type
38
+ def auto_convert_with(converter)
39
+ @converter = converter
40
+ @auto_convert = true
41
+ end
42
+ end
43
+ end
@@ -7,54 +7,27 @@ module Alba
7
7
  # @param converter [Proc]
8
8
  def initialize(name:, type:, converter:)
9
9
  @name = name
10
- @type = type
11
- @converter = case converter
12
- when true then default_converter
13
- when false, nil then null_converter
14
- else converter
15
- end
10
+ t = Alba.find_type(type)
11
+ @type = case converter
12
+ when true then t.dup.tap { _1.auto_convert = true }
13
+ when false, nil then t
14
+ else
15
+ t.dup.tap { _1.auto_convert_with(converter) }
16
+ end
16
17
  end
17
18
 
18
19
  # @param object [Object] target to check and convert type with
19
20
  # @return [String, Integer, Boolean] type-checked or type-converted object
20
21
  def value(object)
21
- value, result = check(object)
22
- result ? value : @converter.call(value)
22
+ v = object.__send__(@name)
23
+ result = @type.check(v)
24
+ result ? v : @type.convert(v)
23
25
  rescue TypeError
24
- raise TypeError, "Attribute #{@name} is expected to be #{@type} but actually #{display_value_for(value)}."
26
+ raise TypeError, "Attribute #{@name} is expected to be #{@type.name} but actually #{display_value_for(v)}."
25
27
  end
26
28
 
27
29
  private
28
30
 
29
- def check(object)
30
- value = object.__send__(@name)
31
- type_correct = case @type
32
- when :String, ->(klass) { klass == String } then value.is_a?(String)
33
- when :Integer, ->(klass) { klass == Integer } then value.is_a?(Integer)
34
- when :Boolean then [true, false].include?(value)
35
- else
36
- raise Alba::UnsupportedType, "Unknown type: #{@type}"
37
- end
38
- [value, type_correct]
39
- end
40
-
41
- def default_converter
42
- case @type
43
- when :String, ->(klass) { klass == String }
44
- ->(object) { object.to_s }
45
- when :Integer, ->(klass) { klass == Integer }
46
- ->(object) { Integer(object) }
47
- when :Boolean
48
- ->(object) { !!object }
49
- else
50
- raise Alba::UnsupportedType, "Unknown type: #{@type}"
51
- end
52
- end
53
-
54
- def null_converter
55
- ->(_) { raise TypeError }
56
- end
57
-
58
31
  def display_value_for(value)
59
32
  value.nil? ? 'nil' : value.class.name
60
33
  end
data/lib/alba/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Alba
2
- VERSION = '2.4.2'.freeze
2
+ VERSION = '3.0.1'.freeze
3
3
  end
data/lib/alba.rb CHANGED
@@ -116,7 +116,7 @@ module Alba
116
116
  const_parent = nesting.nil? ? Object : Object.const_get(nesting)
117
117
  begin
118
118
  const_parent.const_get("#{inflector.classify(name)}Resource")
119
- rescue # Retry for serializer
119
+ rescue NameError # Retry for serializer
120
120
  const_parent.const_get("#{inflector.classify(name)}Serializer")
121
121
  end
122
122
  end
@@ -142,6 +142,23 @@ module Alba
142
142
  @symbolize_keys ? key.to_sym : key.to_s
143
143
  end
144
144
 
145
+ # Register types, used for both builtin and custom types
146
+ #
147
+ # @see Alba::Type
148
+ # @return [void]
149
+ def register_type(name, check: false, converter: nil, auto_convert: false)
150
+ @types[name] = Type.new(name, check: check, converter: converter, auto_convert: auto_convert)
151
+ end
152
+
153
+ # Find type by name
154
+ #
155
+ # @return [Alba::Type]
156
+ def find_type(name)
157
+ @types.fetch(name) do
158
+ raise(Alba::UnsupportedType, "Unknown type: #{name}")
159
+ end
160
+ end
161
+
145
162
  # Reset config variables
146
163
  # Useful for test cleanup
147
164
  def reset!
@@ -149,6 +166,8 @@ module Alba
149
166
  @symbolize_keys = false
150
167
  @_on_error = :raise
151
168
  @_on_nil = nil
169
+ @types = {}
170
+ register_default_types
152
171
  end
153
172
 
154
173
  private
@@ -175,7 +194,7 @@ module Alba
175
194
 
176
195
  def set_encoder_from_backend
177
196
  @encoder = case @backend
178
- when :oj, :oj_strict then try_oj
197
+ when :oj, :oj_strict then try_oj(mode: :strict)
179
198
  when :oj_rails then try_oj(mode: :rails)
180
199
  when :oj_default then try_oj(mode: :default)
181
200
  when :active_support then try_active_support
@@ -185,7 +204,7 @@ module Alba
185
204
  end
186
205
  end
187
206
 
188
- def try_oj(mode: :strict)
207
+ def try_oj(mode:)
189
208
  require 'oj'
190
209
  case mode
191
210
  when :default
@@ -219,6 +238,14 @@ module Alba
219
238
 
220
239
  inflector
221
240
  end
241
+
242
+ def register_default_types # rubocop:disable Metrics/AbcSize
243
+ register_type(:String, check: ->(obj) { obj.is_a?(String) }, converter: ->(obj) { obj.to_s })
244
+ register_type(String, check: ->(obj) { obj.is_a?(String) }, converter: ->(obj) { obj.to_s })
245
+ register_type(:Integer, check: ->(obj) { obj.is_a?(Integer) }, converter: ->(obj) { Integer(obj) })
246
+ register_type(Integer, check: ->(obj) { obj.is_a?(Integer) }, converter: ->(obj) { Integer(obj) })
247
+ register_type(:Boolean, check: ->(obj) { [true, false].include?(obj) }, converter: ->(obj) { !!obj })
248
+ end
222
249
  end
223
250
 
224
251
  reset!
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: 2.4.2
4
+ version: 3.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - OKURA Masafumi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-10-11 00:00:00.000000000 Z
11
+ date: 2023-10-13 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.
@@ -24,6 +24,7 @@ files:
24
24
  - ".github/ISSUE_TEMPLATE/feature_request.md"
25
25
  - ".github/dependabot.yml"
26
26
  - ".github/workflows/codeql-analysis.yml"
27
+ - ".github/workflows/lint.yml"
27
28
  - ".github/workflows/main.yml"
28
29
  - ".github/workflows/perf.yml"
29
30
  - ".gitignore"
@@ -62,6 +63,7 @@ files:
62
63
  - lib/alba/nested_attribute.rb
63
64
  - lib/alba/railtie.rb
64
65
  - lib/alba/resource.rb
66
+ - lib/alba/type.rb
65
67
  - lib/alba/typed_attribute.rb
66
68
  - lib/alba/version.rb
67
69
  - logo/alba-card.png
@@ -85,14 +87,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
85
87
  requirements:
86
88
  - - ">="
87
89
  - !ruby/object:Gem::Version
88
- version: 2.7.0
90
+ version: 3.0.0
89
91
  required_rubygems_version: !ruby/object:Gem::Requirement
90
92
  requirements:
91
93
  - - ">="
92
94
  - !ruby/object:Gem::Version
93
95
  version: '0'
94
96
  requirements: []
95
- rubygems_version: 3.4.14
97
+ rubygems_version: 3.4.20
96
98
  signing_key:
97
99
  specification_version: 4
98
100
  summary: Alba is the fastest JSON serializer for Ruby.