alba 3.0.2 → 3.1.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: 0f7b454e9de7d518fe1287a46d1b14a0c8cb8d13e3b10fcce3eee8a9e3df05c9
4
- data.tar.gz: ab670cee9399503a4480f74b4533a40eb4fd67048a0b9c93b9f706712ecc1a8f
3
+ metadata.gz: d0bea04ee2e971c750f9d62eca519a5401e48ab1361199581bad50f3a89e7544
4
+ data.tar.gz: 726fc7d31243362d2ef4165a3784a1a36eb8f067af00fc9c25664ff1c15650ac
5
5
  SHA512:
6
- metadata.gz: 758481e888e55ae7a1ac87d61060cdbe76f3effea46e59b0906a125c4d804ef54f337aee014cba3df35fbd819601ad66c84eaafb820fd1a1144842a48969125a
7
- data.tar.gz: 5960c6958060c3169152c4634aaecd34af1a0920b7cb6461c9652e690e6fed661275efea1a6988513be0e7142eec6e13fe3a6f61658af68a70fc25ad04eaf430
6
+ metadata.gz: f249c1974afc00291145059d9fcb61250948138d0ccbf33cadf1b221be1318c3425b24a0a12b16233452363ec7eda292da3a798a3534465eafba9594740e301c
7
+ data.tar.gz: 3e5e3e6d6c5889fa26adf5f3164bac67f7904de765e55e67c3290e38024838bbd2439a1e4e69e2b284dd39624eb5094fd8740cc15ca56794ca1d5fdbd76d8288
@@ -42,7 +42,7 @@ jobs:
42
42
 
43
43
  # Initializes the CodeQL tools for scanning.
44
44
  - name: Initialize CodeQL
45
- uses: github/codeql-action/init@v2
45
+ uses: github/codeql-action/init@v3
46
46
  with:
47
47
  languages: ${{ matrix.language }}
48
48
  # If you wish to specify custom queries, you can do so here or in a config file.
@@ -53,7 +53,7 @@ jobs:
53
53
  # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
54
54
  # If this step fails, then you should remove it and run the build manually (see below)
55
55
  - name: Autobuild
56
- uses: github/codeql-action/autobuild@v2
56
+ uses: github/codeql-action/autobuild@v3
57
57
 
58
58
  # ℹ️ Command-line programs to run using the OS shell.
59
59
  # 📚 https://git.io/JvXDl
@@ -67,4 +67,4 @@ jobs:
67
67
  # make release
68
68
 
69
69
  - name: Perform CodeQL Analysis
70
- uses: github/codeql-action/analyze@v2
70
+ uses: github/codeql-action/analyze@v3
@@ -1,6 +1,10 @@
1
1
  name: CI
2
2
 
3
- on: [push,pull_request]
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
4
8
 
5
9
  jobs:
6
10
  build:
@@ -29,6 +33,7 @@ jobs:
29
33
  run: |
30
34
  bundle exec rake
31
35
  - name: CodeCov
32
- uses: codecov/codecov-action@v3
36
+ uses: codecov/codecov-action@v4
33
37
  with:
34
38
  files: ./coverage/coverage.xml
39
+ token: ${{ secrets.CODECOV_TOKEN }}
data/.rubocop.yml CHANGED
@@ -15,7 +15,6 @@ require:
15
15
 
16
16
  AllCops:
17
17
  Exclude:
18
- - 'Rakefile'
19
18
  - 'alba.gemspec'
20
19
  - 'benchmark/**/*.rb'
21
20
  - 'docs/**/*'
data/CHANGELOG.md CHANGED
@@ -6,6 +6,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [3.1.0] 2024-03-23
10
+
11
+ ### Added
12
+
13
+ - Add the ability to change key for metadata [#362](https://github.com/okuramasafumi/alba/pull/362)
14
+
15
+ ## [3.0.3] 2023-12-25
16
+
17
+ ### Fixed
18
+
19
+ - Make `as_json` compatible with Rails [#350](https://github.com/okuramasafumi/alba/pull/350)
20
+ - Fix circular association for nested_attribute [#353](https://github.com/okuramasafumi/alba/pull/353)
21
+
9
22
  ## [3.0.2] 2023-12-05
10
23
 
11
24
  ### Fixed
data/Gemfile CHANGED
@@ -9,12 +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', '>= 0.79.0', require: false # For lint
12
+ gem 'rubocop', '~> 1.62.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
- gem 'rubocop-minitest', '>= 0.25.0', require: false # For lint
16
- gem 'rubocop-performance', '>= 1.15.0', require: false # For lint
17
- gem 'rubocop-rake', '>= 0.5.1', require: false # For lint
15
+ gem 'rubocop-minitest', '~> 0.35.0', require: false # For lint
16
+ gem 'rubocop-performance', '~> 1.20.2', require: false # For lint
17
+ gem 'rubocop-rake', '~> 0.6.0', require: false # For lint
18
18
  gem 'ruby-lsp', require: false # For language server
19
19
  gem 'simplecov', '~> 0.22.0', require: false # For test coverage
20
20
  gem 'simplecov-cobertura', require: false # For test coverage
data/README.md CHANGED
@@ -130,6 +130,7 @@ You can find the documentation on [RubyDoc](https://rubydoc.info/github/okuramas
130
130
  * Selectable backend
131
131
  * Key transformation
132
132
  * Root key and association resource name inference
133
+ * Inline definition without explicit classes
133
134
  * Error handling
134
135
  * Nil handling
135
136
  * Circular associations control
@@ -1232,6 +1233,65 @@ UserResource.new([user]).serialize
1232
1233
 
1233
1234
  UserResource.new([user]).serialize(meta: {foo: :bar})
1234
1235
  # => '{"users":[{"id":1,"name":"Masafumi OKURA"}],"meta":{"size":1,"foo":"bar"}}'
1236
+ ```
1237
+
1238
+ You can change the key for metadata. If you change the key, it also affects the key when you pass `meta` option.
1239
+
1240
+ ```ruby
1241
+ # You can change meta key
1242
+ class UserResourceWithDifferentMetaKey
1243
+ include Alba::Resource
1244
+
1245
+ root_key :user, :users
1246
+
1247
+ attributes :id, :name
1248
+
1249
+ meta :my_meta do
1250
+ {foo: :bar}
1251
+ end
1252
+ end
1253
+
1254
+ UserResourceWithDifferentMetaKey.new([user]).serialize
1255
+ # => '{"users":[{"id":1,"name":"Masafumi OKURA"}],"my_meta":{"foo":"bar"}}'
1256
+
1257
+ UserResourceWithDifferentMetaKey.new([user]).serialize(meta: {extra: 42})
1258
+ # => '{"users":[{"id":1,"name":"Masafumi OKURA"}],"meta":{"size":1,"extra":42}}'
1259
+
1260
+ class UserResourceChangingMetaKeyOnly
1261
+ include Alba::Resource
1262
+
1263
+ root_key :user, :users
1264
+
1265
+ attributes :id, :name
1266
+
1267
+ meta :my_meta
1268
+ end
1269
+
1270
+ UserResourceChangingMetaKeyOnly.new([user]).serialize
1271
+ # => '{"users":[{"id":1,"name":"Masafumi OKURA"}]}'
1272
+
1273
+ UserResourceChangingMetaKeyOnly.new([user]).serialize(meta: {extra: 42})
1274
+ # => '{"users":[{"id":1,"name":"Masafumi OKURA"}],"my_meta":{"extra":42}}'
1275
+ ```
1276
+
1277
+ It's also possible to remove the key for metadata, resulting a flat structure.
1278
+
1279
+ ```ruby
1280
+ class UserResourceRemovingMetaKey
1281
+ include Alba::Resource
1282
+
1283
+ root_key :user, :users
1284
+
1285
+ attributes :id, :name
1286
+
1287
+ meta nil
1288
+ end
1289
+
1290
+ UserResourceRemovingMetaKey.new([user]).serialize
1291
+ # => '{"users":[{"id":1,"name":"Masafumi OKURA"}]}'
1292
+
1293
+ UserResourceRemovingMetaKey.new([user]).serialize(meta: {extra: 42})
1294
+ # => '{"users":[{"id":1,"name":"Masafumi OKURA"}],"extra":42}'
1235
1295
 
1236
1296
  # You can set metadata with `meta` option alone
1237
1297
 
@@ -1457,6 +1517,8 @@ end
1457
1517
 
1458
1518
  Within `helper` block, all methods should be defined without `self.`.
1459
1519
 
1520
+ `helper`
1521
+
1460
1522
  ### Caching
1461
1523
 
1462
1524
  Currently, Alba doesn't support caching, primarily due to the behavior of `ActiveRecord::Relation`'s cache. See [the issue](https://github.com/rails/rails/issues/41784).
@@ -1563,6 +1625,40 @@ end
1563
1625
 
1564
1626
  In this way we have shorter and cleaner code. Note that we need to use `send` or `public_send` in `attribute` block to get attribute data.
1565
1627
 
1628
+ #### Using `helper`
1629
+
1630
+ When we `extend AlbaExtension` like above, it's not available in inline associations.
1631
+
1632
+ ```ruby
1633
+ class BarResource
1634
+ include Alba::Resource
1635
+ extend AlbaExtension
1636
+ # other attributes
1637
+ formatted_time_attributes :created_at, :updated_at
1638
+
1639
+ one :something do
1640
+ # This DOES NOT work!
1641
+ formatted_time_attributes :updated_at
1642
+ end
1643
+ end
1644
+ ```
1645
+
1646
+ In this case, we can use [helper](#helper) instead of `extend`.
1647
+
1648
+ ```ruby
1649
+ class BarResource
1650
+ include Alba::Resource
1651
+ helper AlbaExtension # HERE!
1652
+ # other attributes
1653
+ formatted_time_attributes :created_at, :updated_at
1654
+
1655
+ one :something do
1656
+ # This WORKS!
1657
+ formatted_time_attributes :updated_at
1658
+ end
1659
+ end
1660
+ ```
1661
+
1566
1662
  ### Debugging
1567
1663
 
1568
1664
  Debugging is not easy. If you find Alba not working as you expect, there are a few things to do:
data/Rakefile CHANGED
@@ -1,13 +1,15 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
1
+ require 'bundler/gem_tasks'
2
+ require 'rake/testtask'
3
3
 
4
- ENV["BUNDLE_GEMFILE"] = File.expand_path("Gemfile") if ENV["BUNDLE_GEMFILE"] == File.expand_path("Gemfile") || ENV["BUNDLE_GEMFILE"].empty? || ENV["BUNDLE_GEMFILE"].nil?
4
+ if ENV['BUNDLE_GEMFILE'] == File.expand_path('Gemfile') || ENV['BUNDLE_GEMFILE'].empty? || ENV['BUNDLE_GEMFILE'].nil?
5
+ ENV['BUNDLE_GEMFILE'] = File.expand_path('Gemfile')
6
+ end
5
7
 
6
8
  Rake::TestTask.new(:test) do |t|
7
- t.libs << "test"
8
- t.libs << "lib"
9
- file_list = ENV["BUNDLE_GEMFILE"] == File.expand_path("Gemfile") ? FileList["test/**/*_test.rb"] : FileList["test/dependencies/test_dependencies.rb"]
9
+ t.libs << 'test'
10
+ t.libs << 'lib'
11
+ file_list = ENV['BUNDLE_GEMFILE'] == File.expand_path('Gemfile') ? FileList['test/**/*_test.rb'] : FileList['test/dependencies/test_dependencies.rb']
10
12
  t.test_files = file_list
11
13
  end
12
14
 
13
- task :default => :test
15
+ task default: :test
data/lib/alba/layout.rb CHANGED
@@ -12,17 +12,17 @@ module Alba
12
12
  # @param file [String] name of the layout file
13
13
  # @param inline [Proc] a proc returning JSON string or a Hash representing JSON
14
14
  def initialize(file:, inline:)
15
- if file
16
- raise ArgumentError, 'File layout must be a String representing filename' unless file.is_a?(String)
15
+ @body = if file
16
+ raise ArgumentError, 'File layout must be a String representing filename' unless file.is_a?(String)
17
17
 
18
- @body = file
19
- elsif inline
20
- raise ArgumentError, 'Inline layout must be a Proc returning a Hash or a String' unless inline.is_a?(Proc)
18
+ file
19
+ elsif inline
20
+ raise ArgumentError, 'Inline layout must be a Proc returning a Hash or a String' unless inline.is_a?(Proc)
21
21
 
22
- @body = inline
23
- else
24
- raise ArgumentError, 'Layout must be either String or Proc'
25
- end
22
+ inline
23
+ else
24
+ raise ArgumentError, 'Layout must be either String or Proc'
25
+ end
26
26
  end
27
27
 
28
28
  # Serialize within layout
@@ -11,12 +11,13 @@ module Alba
11
11
 
12
12
  # @param object [Object] the object being serialized
13
13
  # @param params [Hash] params Hash inherited from Resource
14
+ # @param within [Object, nil, false, true] determines what associations to be serialized. If not set, it serializes all associations.
14
15
  # @return [Hash] hash serialized from running the class body in the object
15
- def value(object:, params:)
16
+ def value(object:, params:, within:)
16
17
  resource_class = Alba.resource_class
17
18
  resource_class.transform_keys(@key_transformation)
18
19
  resource_class.class_eval(&@block)
19
- resource_class.new(object, params: params).serializable_hash
20
+ resource_class.new(object, params: params, within: within).serializable_hash
20
21
  end
21
22
  end
22
23
  end
data/lib/alba/resource.rb CHANGED
@@ -22,11 +22,11 @@ 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;'
25
+ setup_method_body = +'private def _setup;'
26
26
  base.class_eval do
27
27
  # Initialize
28
28
  INTERNAL_VARIABLES.each do |name, initial|
29
- instance_variable_set("@#{name}", initial.dup) unless instance_variable_defined?("@#{name}")
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
32
  base.define_method(:encode, Alba.encoder)
@@ -66,15 +66,23 @@ module Alba
66
66
  # @see #serialize
67
67
  # @see https://github.com/rails/rails/blob/7-0-stable/actionpack/lib/action_controller/metal/renderers.rb#L156
68
68
  def to_json(options = {}, root_key: nil, meta: {})
69
- _to_json(root_key, meta, options)
69
+ confusing_options = options.keys.select { |k| k.to_sym == :only || k.to_sym == :except }
70
+ unless confusing_options.empty?
71
+ confusing_options.sort!
72
+ confusing_options.map! { |s| "\"#{s}\"" }
73
+ message = "You passed #{confusing_options.join(' and ')} options but ignored. Please refer to the document: https://github.com/okuramasafumi/alba/blob/main/docs/rails.md"
74
+ Kernel.warn(message)
75
+ end
76
+ serialize(root_key: root_key, meta: meta)
70
77
  end
71
78
 
72
79
  # Returns a Hash correspondng {#serialize}
73
80
  #
81
+ # @param _options [Hash] dummy parameter for Rails compatibility
74
82
  # @param root_key [Symbol, nil, true]
75
83
  # @param meta [Hash] metadata for this seialization
76
84
  # @return [Hash]
77
- def as_json(root_key: nil, meta: {})
85
+ def as_json(_options = {}, root_key: nil, meta: {})
78
86
  key = root_key.nil? ? fetch_key : root_key
79
87
  key = Alba.regularize_key(key)
80
88
  if key && !key.empty?
@@ -95,17 +103,6 @@ module Alba
95
103
 
96
104
  private
97
105
 
98
- def _to_json(root_key, meta, options)
99
- confusing_options = options.keys.select { |k| k.to_sym == :only || k.to_sym == :except }
100
- unless confusing_options.empty?
101
- confusing_options.sort!
102
- confusing_options.map! { |s| "\"#{s}\"" }
103
- message = "You passed #{confusing_options.join(' and ')} options but ignored. Please refer to the document: https://github.com/okuramasafumi/alba/blob/main/docs/rails.md"
104
- Kernel.warn(message)
105
- end
106
- serialize(root_key: root_key, meta: meta)
107
- end
108
-
109
106
  def serialize_with(hash)
110
107
  serialized_json = encode(hash)
111
108
  return serialized_json unless @_layout
@@ -114,13 +111,22 @@ module Alba
114
111
  end
115
112
 
116
113
  def hash_with_metadata(hash, meta)
117
- return hash if meta.empty? && @_meta.nil?
114
+ return hash if meta.empty? && @_meta&.last.nil?
115
+
116
+ key, block = @_meta || :meta
118
117
 
119
- metadata = @_meta ? instance_eval(&@_meta).merge(meta) : meta
120
- hash[:meta] = metadata
118
+ if key
119
+ hash[key] = _metadata(block, meta)
120
+ else
121
+ _metadata(block, meta).each { |k, v| hash[k] = v }
122
+ end
121
123
  hash
122
124
  end
123
125
 
126
+ def _metadata(block, meta)
127
+ block ? instance_eval(&block).merge(meta) : meta
128
+ end
129
+
124
130
  def serializable_hash_for_collection
125
131
  if @_collection_key
126
132
  @object.to_h do |item|
@@ -211,14 +217,17 @@ module Alba
211
217
  end
212
218
 
213
219
  def handle_error(error, obj, key, attribute, hash)
214
- on_error = @_on_error || :raise
215
- case on_error # rubocop:disable Style/MissingElse
220
+ case (on_error = @_on_error || :raise)
216
221
  when :raise, nil then raise(error)
217
222
  when :nullify then hash[key] = nil
218
223
  when :ignore then nil
219
224
  when Proc
220
225
  key, value = on_error.call(error, obj, key, attribute, self.class)
221
226
  hash[key] = value
227
+ else
228
+ # :nocov:
229
+ raise Alba::Error, 'Impossible path'
230
+ # :nocov:
222
231
  end
223
232
  end
224
233
 
@@ -232,11 +241,15 @@ module Alba
232
241
  end
233
242
 
234
243
  def _transform_key(inflector, key)
235
- case @_transform_type # rubocop:disable Style/MissingElse
244
+ case @_transform_type
236
245
  when :camel then inflector.camelize(key)
237
246
  when :lower_camel then inflector.camelize_lower(key)
238
247
  when :dash then inflector.dasherize(key)
239
248
  when :snake then inflector.underscore(key)
249
+ else
250
+ # :nocov:
251
+ raise Alba::Error, "Unknown transform type: #{@_transform_type}"
252
+ # :nocov:
240
253
  end
241
254
  end
242
255
 
@@ -246,9 +259,11 @@ module Alba
246
259
  when Proc then instance_exec(obj, &attribute)
247
260
  when Alba::Association then yield_if_within(attribute.name.to_sym) { |within| attribute.to_h(obj, params: params, within: within) }
248
261
  when TypedAttribute then attribute.value(obj)
249
- when NestedAttribute then attribute.value(object: obj, params: params)
262
+ when NestedAttribute then attribute.value(object: obj, params: params, within: @within)
250
263
  when ConditionalAttribute then attribute.with_passing_condition(resource: self, object: obj) { |attr| fetch_attribute(obj, key, attr) }
264
+ # :nocov:
251
265
  else raise ::Alba::Error, "Unsupported type of attribute: #{attribute.class}"
266
+ # :nocov:
252
267
  end
253
268
  value.nil? && nil_handler ? instance_exec(obj, key, attribute, &nil_handler) : value
254
269
  end
@@ -265,11 +280,7 @@ module Alba
265
280
 
266
281
  def _fetch_attribute_from_resource_first(obj, attribute)
267
282
  if @_resource_methods.include?(attribute)
268
- begin
269
- __send__(attribute, obj)
270
- rescue NoMethodError
271
- obj.__send__(attribute)
272
- end
283
+ __send__(attribute, obj)
273
284
  else
274
285
  obj.__send__(attribute)
275
286
  end
@@ -316,7 +327,7 @@ module Alba
316
327
  # @private
317
328
  def inherited(subclass)
318
329
  super
319
- INTERNAL_VARIABLES.each_key { |name| subclass.instance_variable_set("@#{name}", instance_variable_get("@#{name}").clone) }
330
+ INTERNAL_VARIABLES.each_key { |name| subclass.instance_variable_set(:"@#{name}", instance_variable_get(:"@#{name}").clone) }
320
331
  end
321
332
 
322
333
  # Defining methods for DSLs and disable parameter number check since for users' benefits increasing params is fine
@@ -444,8 +455,8 @@ module Alba
444
455
  end
445
456
 
446
457
  # Set metadata
447
- def meta(&block)
448
- @_meta = block
458
+ def meta(key = :meta, &block)
459
+ @_meta = [key, block]
449
460
  end
450
461
 
451
462
  # Set layout
data/lib/alba/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Alba
2
- VERSION = '3.0.2'.freeze
2
+ VERSION = '3.1.0'.freeze
3
3
  end
data/lib/alba.rb CHANGED
@@ -239,11 +239,13 @@ module Alba
239
239
  inflector
240
240
  end
241
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) })
242
+ def register_default_types
243
+ [String, :String].each do |t|
244
+ register_type(t, check: ->(obj) { obj.is_a?(String) }, converter: ->(obj) { obj.to_s })
245
+ end
246
+ [Integer, :Integer].each do |t|
247
+ register_type(t, check: ->(obj) { obj.is_a?(Integer) }, converter: ->(obj) { Integer(obj) })
248
+ end
247
249
  register_type(:Boolean, check: ->(obj) { [true, false].include?(obj) }, converter: ->(obj) { !!obj })
248
250
  end
249
251
  end
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.0.2
4
+ version: 3.1.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: 2023-12-05 00:00:00.000000000 Z
11
+ date: 2024-03-22 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.4.22
97
+ rubygems_version: 3.5.6
98
98
  signing_key:
99
99
  specification_version: 4
100
100
  summary: Alba is the fastest JSON serializer for Ruby.