alba 3.1.0 → 3.2.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: d0bea04ee2e971c750f9d62eca519a5401e48ab1361199581bad50f3a89e7544
4
- data.tar.gz: 726fc7d31243362d2ef4165a3784a1a36eb8f067af00fc9c25664ff1c15650ac
3
+ metadata.gz: b3b0683f5d43a9e7ce7eaea591c2a67267f56d7327a045ab0e2da081256a4543
4
+ data.tar.gz: 0dd80fd3c618fe35bc7b727f01c6ce8c9357fe1b0d303e2a756daf245f6d7e50
5
5
  SHA512:
6
- metadata.gz: f249c1974afc00291145059d9fcb61250948138d0ccbf33cadf1b221be1318c3425b24a0a12b16233452363ec7eda292da3a798a3534465eafba9594740e301c
7
- data.tar.gz: 3e5e3e6d6c5889fa26adf5f3164bac67f7904de765e55e67c3290e38024838bbd2439a1e4e69e2b284dd39624eb5094fd8740cc15ca56794ca1d5fdbd76d8288
6
+ metadata.gz: ec6b56b091d9fe52bf05dd8ac7f9d67bc33db86e480e2281e2914cc075939a3c21b3b2cc1e6680a435c0a69069b3e73db0983b3a3d6433bb1c12b65f83ee307a
7
+ data.tar.gz: afb843e8dbaa8e45181526128704a4fbd0ed427306bb294d73dc6889477e037d86091ec2fc94bf9db6d973f5da07551ce74977d9469184c67fbc3354c904f906
@@ -12,7 +12,7 @@ jobs:
12
12
  fail-fast: false
13
13
  matrix:
14
14
  os: [ubuntu-latest, windows-latest, macos-latest]
15
- ruby: ['3.0', 3.1, 3.2, head, truffleruby] # TODO: Add jruby after the error is resolved
15
+ ruby: ['3.0', 3.1, 3.2, 3.3, head, jruby, truffleruby]
16
16
  gemfile: [all, without_active_support, without_oj]
17
17
  exclude:
18
18
  - os: windows-latest
@@ -4,18 +4,13 @@ on: [pull_request]
4
4
 
5
5
  jobs:
6
6
  build:
7
- strategy:
8
- fail-fast: false
9
- matrix:
10
- ruby: ['3.0', 3.1, 3.2]
11
7
  runs-on: ubuntu-latest
12
8
  steps:
13
9
  - uses: actions/checkout@v4
14
10
  - name: Set up Ruby
15
11
  uses: ruby/setup-ruby@v1
16
12
  with:
17
- ruby-version: ${{ matrix.ruby }}
18
- bundler-cache: true
13
+ ruby-version: 3.3
19
14
  - name: Run benchmark
20
15
  run: |
21
16
  ruby script/perf_check.rb
data/.rubocop.yml CHANGED
@@ -88,6 +88,11 @@ Minitest/NoTestCases:
88
88
  Exclude:
89
89
  - 'test/dependencies/test_dependencies.rb'
90
90
 
91
+ # We need to use `OpenStruct` to wrap object in ConfitionalAttribute
92
+ Performance/OpenStruct:
93
+ Exclude:
94
+ - 'lib/alba/conditional_attribute.rb'
95
+
91
96
  # We need to eval resource code to test errors on resource classes
92
97
  Security/Eval:
93
98
  Exclude:
@@ -140,6 +145,11 @@ Style/MethodCallWithArgsParentheses:
140
145
  Style/MissingElse:
141
146
  EnforcedStyle: case
142
147
 
148
+ # We need to use `OpenStruct` to wrap object in ConfitionalAttribute
149
+ Style/OpenStructUse:
150
+ Exclude:
151
+ - 'lib/alba/conditional_attribute.rb'
152
+
143
153
  # It's example code, please forgive us
144
154
  Style/OptionalBooleanParameter:
145
155
  Exclude:
data/CHANGELOG.md CHANGED
@@ -1,11 +1,18 @@
1
1
  # Changelog
2
2
  All notable changes to this project will be documented in this file.
3
3
 
4
- The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [3.2.0] 2024-06-21
10
+
11
+ ### Added
12
+
13
+ - Rails controller integration [#370](https://github.com/okuramasafumi/alba/pull/370)
14
+ - Modification API: `transform_keys!` [#372](https://github.com/okuramasafumi/alba/pull/372)
15
+
9
16
  ## [3.1.0] 2024-03-23
10
17
 
11
18
  ### Added
data/Gemfile CHANGED
@@ -9,13 +9,12 @@ gem 'ffaker', require: false # For testing
9
9
  gem 'minitest', '~> 5.14' # For test
10
10
  gem 'railties', require: false # For Rails integration testing
11
11
  gem 'rake', '~> 13.0' # For test and automation
12
- gem 'rubocop', '~> 1.62.0', require: false # For lint
12
+ gem 'rubocop', '~> 1.64.0', require: false # For lint
13
13
  gem 'rubocop-gem_dev', '>= 0.3.0', require: false # For lint
14
14
  gem 'rubocop-md', '~> 1.0', require: false # For lint
15
15
  gem 'rubocop-minitest', '~> 0.35.0', require: false # For lint
16
- gem 'rubocop-performance', '~> 1.20.2', require: false # For lint
16
+ gem 'rubocop-performance', '~> 1.21.0', require: false # For lint
17
17
  gem 'rubocop-rake', '~> 0.6.0', require: false # For lint
18
- gem 'ruby-lsp', require: false # For language server
19
18
  gem 'simplecov', '~> 0.22.0', require: false # For test coverage
20
19
  gem 'simplecov-cobertura', require: false # For test coverage
21
20
  # gem 'steep', require: false # For language server and typing
data/README.md CHANGED
@@ -92,7 +92,7 @@ Alba is easy to use because there are only a few methods to remember. It's also
92
92
 
93
93
  ### Feature rich
94
94
 
95
- 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).
95
+ 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).
96
96
 
97
97
  ### Other reasons
98
98
 
@@ -363,7 +363,7 @@ end
363
363
  FooResource.new(Foo.new).serialize
364
364
  # => '{"bar":"This is Foo"}'
365
365
  ```
366
-
366
+
367
367
  #### Params
368
368
 
369
369
  You can pass a Hash to the resource for internal use. It can be used as "flags" to control attribute content.
@@ -1517,7 +1517,36 @@ end
1517
1517
 
1518
1518
  Within `helper` block, all methods should be defined without `self.`.
1519
1519
 
1520
- `helper`
1520
+ ### Experimental: modification API
1521
+
1522
+ Alba now provides an experimental API to modify existing resource class without adding new classes. Currently only `transform_keys!` is implemented.
1523
+
1524
+ Modification API returns a new class with given modifications. It's useful when you want lots of resource classes with small changes. See it in action:
1525
+
1526
+ ```ruby
1527
+ class FooResource
1528
+ include Alba::Resource
1529
+
1530
+ transform_keys :camel
1531
+
1532
+ attributes :id
1533
+ end
1534
+
1535
+ # Rails app
1536
+ class FoosController < ApplicationController
1537
+ def index
1538
+ foos = Foo.where(some: :condition)
1539
+ key_transformation_type = params[:key_transformation_type] # Say it's "lower_camel"
1540
+ # When params is absent, do not use modification API since it's slower
1541
+ resource_class = key_transformation_type ? FooResource.transform_keys!(key_transformation_type) : FooResource
1542
+ render json: resource_class.new(foos).serialize # The keys are lower_camel
1543
+ end
1544
+ end
1545
+ ```
1546
+
1547
+ The point is that there's no need to define classes for each key transformation type (dash, camel, lower_camel and snake). This gives even more flexibility.
1548
+
1549
+ There are some drawbacks with this approach. For example, it creates an internal, anonymous class when it's called, so there is a performance penalty and debugging difficulty. It's recommended to define classes manually when you don't need high flexibility.
1521
1550
 
1522
1551
  ### Caching
1523
1552
 
@@ -1659,6 +1688,20 @@ class BarResource
1659
1688
  end
1660
1689
  ```
1661
1690
 
1691
+ You can also pass options to your helpers.
1692
+
1693
+ ```ruby
1694
+ module AlbaExtension
1695
+ def time_attributes(*attrs, **options)
1696
+ attrs.each do |attr|
1697
+ attribute(attr, **options) do |object|
1698
+ object.__send__(attr).iso8601
1699
+ end
1700
+ end
1701
+ end
1702
+ end
1703
+ ```
1704
+
1662
1705
  ### Debugging
1663
1706
 
1664
1707
  Debugging is not easy. If you find Alba not working as you expect, there are a few things to do:
@@ -1695,7 +1738,7 @@ module Logging
1695
1738
  # `...` was added in Ruby 2.7
1696
1739
  def serialize(...)
1697
1740
  puts serializable_hash
1698
- super(...)
1741
+ super
1699
1742
  end
1700
1743
  end
1701
1744
 
data/benchmark/prep.rb CHANGED
@@ -7,7 +7,7 @@ gemfile(true) do
7
7
  git_source(:github) { |repo| "https://github.com/#{repo}.git" }
8
8
 
9
9
  gem "active_model_serializers"
10
- gem "activerecord", "6.1.3"
10
+ gem "activerecord", "~> 7.1"
11
11
  gem "alba", path: '../'
12
12
  gem "benchmark-ips"
13
13
  gem "benchmark-memory"
@@ -23,7 +23,7 @@ gemfile(true) do
23
23
  gem "oj"
24
24
  gem "representable"
25
25
  gem "simple_ams"
26
- gem "sqlite3"
26
+ gem "sqlite3", "~> 1.4"
27
27
  end
28
28
 
29
29
  # --- Test data model setup ---
data/docs/rails.md CHANGED
@@ -44,3 +44,13 @@ render json: FooResource.new(foo), only: [:id]
44
44
  # This is OK
45
45
  render json: FooResource.new(foo), status: 200
46
46
  ```
47
+
48
+ ### Shorthand for rendering JSON with Alba
49
+
50
+ Now you can render JSON with shorthand.
51
+
52
+ First, using `render json: serialize(target)` renders JSON for given target object. You can pass `with: SomeSerializer` option to render with `SomeSerializer` in this case. If you skip `with` option Alba tries to find the correct serialize automatically.
53
+
54
+ There is a shorter version: `render_serialized_json(target)`. It also accepts `with` option.
55
+
56
+ It's recommended to use `with` option now since it cannot automatically find correct serializers sometimes.
@@ -29,6 +29,11 @@ module Alba
29
29
  assign_resource(nesting, key_transformation, block, helper)
30
30
  end
31
31
 
32
+ # This is the same API in `NestedAttribute`
33
+ def key_transformation=(type)
34
+ @resource.transform_keys(type) unless @resource.is_a?(Proc)
35
+ end
36
+
32
37
  # Recursively converts an object into a Hash
33
38
  #
34
39
  # @param target [Object] the object having an association method
@@ -65,13 +70,9 @@ module Alba
65
70
  end
66
71
  end
67
72
 
68
- def assign_resource(nesting, key_transformation, block, helper) # rubocop:disable Metrics/MethodLength
73
+ def assign_resource(nesting, key_transformation, block, helper)
69
74
  @resource = if block
70
- klass = Alba.resource_class
71
- klass.helper(helper) if helper
72
- klass.transform_keys(key_transformation)
73
- klass.class_eval(&block)
74
- klass
75
+ charged_resource_class(helper, key_transformation, block)
75
76
  elsif Alba.inflector
76
77
  Alba.infer_resource_class(@name, nesting: nesting)
77
78
  else
@@ -79,6 +80,14 @@ module Alba
79
80
  end
80
81
  end
81
82
 
83
+ def charged_resource_class(helper, key_transformation, block)
84
+ klass = Alba.resource_class
85
+ klass.helper(helper) if helper
86
+ klass.transform_keys(key_transformation)
87
+ klass.class_eval(&block)
88
+ klass
89
+ end
90
+
82
91
  def to_h_with_each_resource(object, within, params)
83
92
  object.map do |item|
84
93
  @resource.call(item).new(item, within: within, params: params).to_h
@@ -51,8 +51,6 @@ 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
56
54
  def objectize(fetched_attribute)
57
55
  return fetched_attribute unless @body.is_a?(Alba::Association)
58
56
 
@@ -64,7 +62,5 @@ module Alba
64
62
  OpenStruct.new(fetched_attribute)
65
63
  end
66
64
  end
67
- # rubocop:enable Style/OpenStructUse
68
- # rubocop:enable Performance/OpenStruct
69
65
  end
70
66
  end
@@ -2,6 +2,9 @@ module Alba
2
2
  # Representing nested attribute
3
3
  # @api private
4
4
  class NestedAttribute
5
+ # Setter for key_transformation, used when it's changed after class definition
6
+ attr_writer :key_transformation
7
+
5
8
  # @param key_transformation [Symbol] determines how to transform keys
6
9
  # @param block [Proc] class body
7
10
  def initialize(key_transformation: :none, &block)
data/lib/alba/railtie.rb CHANGED
@@ -3,6 +3,17 @@ module Alba
3
3
  class Railtie < Rails::Railtie
4
4
  initializer 'alba.initialize' do
5
5
  Alba.inflector = :active_support
6
+
7
+ ActiveSupport.on_load(:action_controller) do
8
+ ActionController::Base.define_method(:serialize) do |obj, with: nil, &block|
9
+ with.nil? ? Alba.resource_with(obj, &block) : with.new(obj)
10
+ end
11
+
12
+ ActionController::Base.define_method(:render_serialized_json) do |obj, with: nil, &block|
13
+ json = with.nil? ? Alba.resource_with(obj, &block) : with.new(obj)
14
+ render json: json
15
+ end
16
+ end
6
17
  end
7
18
  end
8
19
  end
data/lib/alba/resource.rb CHANGED
@@ -22,17 +22,17 @@ module Alba
22
22
  # @private
23
23
  def self.included(base) # rubocop:disable Metrics/MethodLength
24
24
  super
25
- setup_method_body = +'private def _setup;'
26
25
  base.class_eval do
27
26
  # Initialize
27
+ setup_method_body = +'private def _setup;'
28
28
  INTERNAL_VARIABLES.each do |name, initial|
29
29
  instance_variable_set(:"@#{name}", initial.dup) unless instance_variable_defined?(:"@#{name}")
30
30
  setup_method_body << "@#{name} = self.class.#{name};"
31
31
  end
32
- base.define_method(:encode, Alba.encoder)
32
+ setup_method_body << 'end'
33
+ class_eval(setup_method_body, __FILE__, __LINE__ + 1)
34
+ define_method(:encode, Alba.encoder)
33
35
  end
34
- setup_method_body << 'end'
35
- base.class_eval(setup_method_body, __FILE__, __LINE__ + 1)
36
36
  base.include InstanceMethods
37
37
  base.extend ClassMethods
38
38
  end
@@ -486,6 +486,26 @@ module Alba
486
486
  @_key_transformation_cascade = cascade
487
487
  end
488
488
 
489
+ # Transform keys as specified type AFTER the class is defined
490
+ # Note that this is an experimental API and may be removed/changed
491
+ #
492
+ # @see #transform_keys
493
+ def transform_keys!(type)
494
+ dup.class_eval do
495
+ transform_keys(type, root: @_transforming_root_key, cascade: @_key_transformation_cascade)
496
+
497
+ if @_key_transformation_cascade
498
+ # We need to update key transformation of associations and nested attributes
499
+ @_attributes.each_value do |attr|
500
+ next unless attr.is_a?(Association) || attr.is_a?(NestedAttribute)
501
+
502
+ attr.key_transformation = type
503
+ end
504
+ end
505
+ self # Return the new class
506
+ end
507
+ end
508
+
489
509
  # Sets key for collection serialization
490
510
  #
491
511
  # @param key [String, Symbol]
data/lib/alba/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Alba
2
- VERSION = '3.1.0'.freeze
2
+ VERSION = '3.2.0'.freeze
3
3
  end
data/lib/alba.rb CHANGED
@@ -170,8 +170,6 @@ module Alba
170
170
  register_default_types
171
171
  end
172
172
 
173
- private
174
-
175
173
  # This method could be part of public API, but for now it's private
176
174
  def resource_with(object, &block)
177
175
  klass = block ? resource_class(&block) : infer_resource_class(object.class.name)
@@ -179,6 +177,8 @@ module Alba
179
177
  klass.new(object)
180
178
  end
181
179
 
180
+ private
181
+
182
182
  def inflector_from(name_or_module)
183
183
  case name_or_module
184
184
  when nil then nil
@@ -241,7 +241,7 @@ module Alba
241
241
 
242
242
  def register_default_types
243
243
  [String, :String].each do |t|
244
- register_type(t, check: ->(obj) { obj.is_a?(String) }, converter: ->(obj) { obj.to_s })
244
+ register_type(t, check: ->(obj) { obj.is_a?(String) }, converter: lambda(&:to_s))
245
245
  end
246
246
  [Integer, :Integer].each do |t|
247
247
  register_type(t, check: ->(obj) { obj.is_a?(Integer) }, converter: ->(obj) { Integer(obj) })
data/script/perf_check.rb CHANGED
@@ -2,74 +2,7 @@
2
2
  # Fetch Alba from local, otherwise fetch latest from RubyGems
3
3
  # exit(status)
4
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
5
+ require_relative '../benchmark/prep'
73
6
 
74
7
  # --- Alba serializers ---
75
8
 
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: 3.1.0
4
+ version: 3.2.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: 2024-03-22 00:00:00.000000000 Z
11
+ date: 2024-06-21 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.
@@ -94,7 +94,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
94
  - !ruby/object:Gem::Version
95
95
  version: '0'
96
96
  requirements: []
97
- rubygems_version: 3.5.6
97
+ rubygems_version: 3.5.11
98
98
  signing_key:
99
99
  specification_version: 4
100
100
  summary: Alba is the fastest JSON serializer for Ruby.