alba 2.4.2 → 3.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e96e153192419f36d10cdd2377720a4d818bd935e8b4d7364e0272f460f74c30
4
- data.tar.gz: 2ca78d61d8e01c1655f294f1836c386bb561db3fca45e964bd3c2b0023912b03
3
+ metadata.gz: d76423c125b10f3a012066e6df3924c1dde50c1842e78be87179d99da8707a9e
4
+ data.tar.gz: 23bba5622260ac6926a669b1d4d4aa44e6a4189838b2c77b23f6e846c56ea14f
5
5
  SHA512:
6
- metadata.gz: 45c70af557b2fcb5ec1e8e7d2269fa6639e086d506b5b8706361a208c24a436ce61807d5d6466accfc516c4f1e556d23a3535236ed2d9047f3c72491702028bb
7
- data.tar.gz: 148818585445adde1fa4f992df975400863856e227e12fe706f241523a06901e34d099511d13ae5fff4cbd5a9ea29c659551034ffb188bc1cc3d519c72e2a88e
6
+ metadata.gz: d57037b9ea77550953a6e8dc4f6896e314ce1e63e36925ec960ebca1e2cce043f61a289091f09c74ef308de50d541e2b2624ce977dcf613165175328c546b32c
7
+ data.tar.gz: 21ab608e1844f3a575985927f81ed892f1cb50c8bf9008302c4572d1acc9c9bebbc66c1afce173af29a22b03f67a6e0b117197d037ae64061401e5a19d1779e6
@@ -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,24 @@ 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.0] 2023-10-11
10
+
11
+ ### Added
12
+
13
+ - Custom type [#333](https://github.com/okuramasafumi/alba/pull/333)
14
+
15
+ ### Changed
16
+
17
+ - Prefer resource method [#323](https://github.com/okuramasafumi/alba/pull/323)
10
18
 
11
19
  ### Fixed
12
20
 
13
- - Multithread bug
21
+ - Multithread bug [No PR](https://github.com/okuramasafumi/alba/commit/d20ed9efbf2f99827c12b8a07308e2f5aea6ab6d)
22
+ - This is a critical bug that can cause data corruption.
23
+
24
+ ### Removed
25
+
26
+ - Drop support for Ruby 2 series [No PR](https://github.com/okuramasafumi/alba/commit/20be222555bde69c31fa9cbe4408b3f638cd7580)
14
27
 
15
28
  ## [2.4.1] 2023-08-02
16
29
 
data/README.md CHANGED
@@ -83,6 +83,12 @@ Alba is easy to use because there are only a few methods to remember. It's also
83
83
 
84
84
  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
85
 
86
+ ### Other reasons
87
+
88
+ - Dependency free, no need to install `oj` or `activesupport` while Alba works well with them
89
+ - Well tested, the test coverage is 99%
90
+ - Well maintained, gettings frequent update and new releases (see [version history](https://rubygems.org/gems/alba/versions))
91
+
86
92
  ## Installation
87
93
 
88
94
  Add this line to your application's Gemfile:
@@ -101,7 +107,7 @@ Or install it yourself as:
101
107
 
102
108
  ## Supported Ruby versions
103
109
 
104
- Alba supports CRuby 2.7 and higher and latest JRuby and TruffleRuby.
110
+ Alba supports CRuby 3.0 and higher and latest JRuby and TruffleRuby.
105
111
 
106
112
  ## Documentation
107
113
 
@@ -130,13 +136,13 @@ Alba's configuration is fairly simple.
130
136
 
131
137
  Backend is the actual part serializing an object into JSON. Alba supports these backends.
132
138
 
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|
139
+ |name|description|requires_external_gem| encoder|
140
+ |--|--|--|--|
141
+ |`oj`, `oj_strict`|Using Oj in `strict` mode|Yes(C extension)|`Oj.dump(object, mode: :strict)`|
142
+ |`oj_rails`|It's `oj` but in `rails` mode|Yes(C extension)|`Oj.dump(object, mode: :rails)`|
143
+ |`oj_default`|It's `oj` but respects mode set by users|Yes(C extension)|`Oj.dump(object)`|
144
+ |`active_support`|For Rails compatibility|Yes|`ActiveSupport::JSON.encode(object)`|
145
+ |`default`, `json`|Using `json` gem|No|`JSON.generate(object)`|
140
146
 
141
147
  You can set a backend like this:
142
148
 
@@ -144,6 +150,12 @@ You can set a backend like this:
144
150
  Alba.backend = :oj
145
151
  ```
146
152
 
153
+ This is equivalent as:
154
+
155
+ ```ruby
156
+ Alba.encoder = ->(object) { Oj.dump(object, mode: :strict) }
157
+ ```
158
+
147
159
  #### Encoder configuration
148
160
 
149
161
  You can also set JSON encoder directly with a Proc.
@@ -284,7 +296,7 @@ class User
284
296
  end
285
297
 
286
298
  def name_with_email
287
- "dummy!"
299
+ 'dummy!'
288
300
  end
289
301
  end
290
302
 
@@ -1231,7 +1243,7 @@ You can control circular associations with `within` option. `within` option is a
1231
1243
 
1232
1244
  For more details, please refer to [test code](https://github.com/okuramasafumi/alba/blob/main/test/usecases/circular_association_test.rb)
1233
1245
 
1234
- ### Experimental support of types
1246
+ ### Types
1235
1247
 
1236
1248
  You can validate and convert input with types.
1237
1249
 
@@ -1270,7 +1282,26 @@ UserResource.new(user).serialize
1270
1282
  # => TypeError, 'Attribute bio is expected to be String but actually nil.'
1271
1283
  ```
1272
1284
 
1273
- Note that this feature is experimental and interfaces are subject to change.
1285
+ #### Custom types
1286
+
1287
+ You can define custom types to abstract data conversion logic. To define custom types, you can use `Alba.register_type` like below.
1288
+
1289
+ ```ruby
1290
+ # Typically in initializer
1291
+ Alba.register_type :iso8601, converter: ->(time) { time.iso8601(3) }, auto_convert: true
1292
+ ```
1293
+
1294
+ Then use it as regular types.
1295
+
1296
+ ```rb
1297
+ class UserResource
1298
+ include Alba::Resource
1299
+
1300
+ attributes :id, created_at: :iso8601
1301
+ end
1302
+ ```
1303
+
1304
+ You now get `created_at` attribute with `iso8601` format!
1274
1305
 
1275
1306
  ### Collection serialization into Hash
1276
1307
 
@@ -1376,7 +1407,7 @@ class ApplicationResource
1376
1407
  include Alba::Resource
1377
1408
 
1378
1409
  def self.with_id
1379
- attributes :id
1410
+ attributes(:id)
1380
1411
  end
1381
1412
  end
1382
1413
 
@@ -1401,7 +1432,7 @@ class ApplicationResource
1401
1432
 
1402
1433
  helper do
1403
1434
  def with_id
1404
- attributes :id
1435
+ attributes(:id)
1405
1436
  end
1406
1437
  end
1407
1438
  end
@@ -1561,7 +1592,7 @@ FooResource.new(foo).serialize
1561
1592
  # => "{:id=>1}" is printed
1562
1593
  ```
1563
1594
 
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`.
1595
+ 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
1596
 
1566
1597
  Don't forget calling `super` in this way.
1567
1598
 
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,7 +53,7 @@ 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
@@ -60,6 +61,8 @@ module Alba
60
61
  self.class.const_cache.fetch(resource) do
61
62
  self.class.const_cache[resource] = Object.const_get(resource)
62
63
  end
64
+ else
65
+ raise Error, "Unexpected resource type: #{resource.class}"
63
66
  end
64
67
  end
65
68
 
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}.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)
@@ -312,12 +300,12 @@ module Alba
312
300
 
313
301
  # Class methods
314
302
  module ClassMethods
315
- attr_reader(*DSLS.keys)
303
+ attr_reader(*INTERNAL_VARIABLES.keys)
316
304
 
317
305
  # @private
318
306
  def inherited(subclass)
319
307
  super
320
- DSLS.each_key { |name| subclass.instance_variable_set("@#{name}", instance_variable_get("@#{name}").clone) }
308
+ INTERNAL_VARIABLES.each_key { |name| subclass.instance_variable_set("@#{name}", instance_variable_get("@#{name}").clone) }
321
309
  end
322
310
 
323
311
  # Defining methods for DSLs and disable parameter number check since for users' benefits increasing params is fine
@@ -382,9 +370,9 @@ module Alba
382
370
  # @return [void]
383
371
  # @see Alba::Association#initialize
384
372
  def association(name, condition = nil, resource: nil, key: nil, params: {}, **options, &block)
385
- key_transformation = @_key_transformation_cascade ? @_transform_type : :none
373
+ transformation = @_key_transformation_cascade ? @_transform_type : :none
386
374
  assoc = Association.new(
387
- name: name, condition: condition, resource: resource, params: params, nesting: nesting, key_transformation: key_transformation, helper: @_helper, &block
375
+ name: name, condition: condition, resource: resource, params: params, nesting: nesting, key_transformation: transformation, helper: @_helper, &block
388
376
  )
389
377
  @_attributes[key&.to_sym || name.to_sym] = options[:if] ? ConditionalAttribute.new(body: assoc, condition: options[:if]) : assoc
390
378
  end
@@ -533,4 +521,7 @@ module Alba
533
521
  end
534
522
  end
535
523
  end
524
+
525
+ Serializer = Resource
526
+ public_constant :Serializer
536
527
  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.0'.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,7 +1,7 @@
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.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - OKURA Masafumi
@@ -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,7 +87,7 @@ 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
  - - ">="