alba 3.5.0 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +27 -0
- data/README.md +96 -40
- data/lib/alba/association.rb +4 -1
- data/lib/alba/conditional_attribute.rb +11 -14
- data/lib/alba/layout.rb +8 -6
- data/lib/alba/railtie.rb +2 -2
- data/lib/alba/resource.rb +90 -17
- data/lib/alba/typed_attribute.rb +2 -0
- data/lib/alba/version.rb +1 -1
- data/lib/alba.rb +51 -32
- metadata +5 -53
- data/.codeclimate.yml +0 -12
- data/.editorconfig +0 -10
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -26
- data/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
- data/.github/dependabot.yml +0 -12
- data/.github/workflows/codeql-analysis.yml +0 -70
- data/.github/workflows/lint.yml +0 -17
- data/.github/workflows/main.yml +0 -41
- data/.gitignore +0 -11
- data/.rubocop.yml +0 -156
- data/.yardopts +0 -4
- data/CODE_OF_CONDUCT.md +0 -132
- data/CONTRIBUTING.md +0 -30
- data/Gemfile +0 -39
- data/HACKING.md +0 -42
- data/Rakefile +0 -17
- data/SECURITY.md +0 -12
- data/alba.gemspec +0 -33
- data/benchmark/Gemfile +0 -26
- data/benchmark/README.md +0 -137
- data/benchmark/collection.rb +0 -297
- data/benchmark/prep.rb +0 -56
- data/benchmark/single_resource.rb +0 -300
- data/bin/console +0 -15
- data/bin/setup +0 -8
- data/codecov.yml +0 -8
- data/docs/migrate_from_active_model_serializers.md +0 -359
- data/docs/migrate_from_jbuilder.md +0 -237
- data/docs/rails.md +0 -56
- data/gemfiles/without_active_support.gemfile +0 -19
- data/gemfiles/without_oj.gemfile +0 -19
- data/logo/alba-card.png +0 -0
- data/logo/alba-sign.png +0 -0
- data/logo/alba-typography.png +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 14523f0f31f5993205b02095c3e6a8115ef83728961cf720dde4d9599be3168a
|
4
|
+
data.tar.gz: 9a2de9344c9b7bf95daa07e0d61339a08285f0cdadf9b694b58f73e7b0773970
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e7f8062cca1bbccbe4d4e871fd5ad2ab42a5b25acc0d2b9edd3e8247af021edaca981988eeb8428675c8bdc8bcbc14fa5ee45d90d524ca76987ab469fc2a6203
|
7
|
+
data.tar.gz: ea0cd1598c2ef8f76e740309ff925b8c26ef60bef6174755b23549d3fd9a98aefeab26bb6d47b73c92c9b92d84125717b157369f25ba2d3631532bec522a9765
|
data/CHANGELOG.md
CHANGED
@@ -6,6 +6,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
6
6
|
|
7
7
|
## [Unreleased]
|
8
8
|
|
9
|
+
## [3.7.0] 2025-05-08
|
10
|
+
|
11
|
+
### Added
|
12
|
+
|
13
|
+
- Traits [#434](https://github.com/okuramasafumi/alba/pull/434)
|
14
|
+
|
15
|
+
## [3.6.0] 2025-03-11
|
16
|
+
|
17
|
+
### Added
|
18
|
+
|
19
|
+
- Add serializer keyword argument as an alias for resource [#408](https://github.com/okuramasafumi/alba/pull/408)
|
20
|
+
- `Alba.resource_for` as a replacement to `resource_with`
|
21
|
+
- Hash serialization [#427](https://github.com/okuramasafumi/alba/pull/427)
|
22
|
+
|
23
|
+
### Changed
|
24
|
+
|
25
|
+
- Performance improvements [#421](https://github.com/okuramasafumi/alba/pull/421) and [#423](https://github.com/okuramasafumi/alba/pull/423)
|
26
|
+
|
27
|
+
### Fixed
|
28
|
+
|
29
|
+
- Range is not a collection [#417](https://github.com/okuramasafumi/alba/issues/417)
|
30
|
+
|
31
|
+
### Deprecated
|
32
|
+
|
33
|
+
- `Alba.resource_with` is deprecated in favor of `Alba.resource_for`
|
34
|
+
- Overriding `Alba::Resource#attributes` is now deprecated in favor of `Alba::Resource#select`
|
35
|
+
|
9
36
|
## [3.5.0] 2025-01-01
|
10
37
|
|
11
38
|
### Added
|
data/README.md
CHANGED
@@ -122,7 +122,7 @@ Alba supports CRuby 3.0 and higher and latest JRuby and TruffleRuby.
|
|
122
122
|
|
123
123
|
## Documentation
|
124
124
|
|
125
|
-
You can find the documentation on [
|
125
|
+
You can find the documentation on [GitHub Pages](https://okuramasafumi.github.io/alba/).
|
126
126
|
|
127
127
|
## Features
|
128
128
|
|
@@ -836,48 +836,11 @@ In this example we add `baz` attribute and change `root_key`. This way, you can
|
|
836
836
|
|
837
837
|
### Filtering attributes
|
838
838
|
|
839
|
-
|
840
|
-
|
841
|
-
`select` is a new and more intuitive API, so generally it's recommended to use `select`.
|
842
|
-
|
843
|
-
#### Filtering attributes with `attributes`
|
844
|
-
|
845
|
-
You can filter out certain attributes by overriding `attributes` instance method. This is useful when you want to customize existing resource with inheritance.
|
846
|
-
|
847
|
-
You can access raw attributes via `super` call. It returns a Hash whose keys are the name of the attribute and whose values are the body. Usually you need only keys to filter out, like below.
|
848
|
-
|
849
|
-
```ruby
|
850
|
-
class Foo
|
851
|
-
attr_accessor :id, :name, :body
|
852
|
-
|
853
|
-
def initialize(id, name, body)
|
854
|
-
@id = id
|
855
|
-
@name = name
|
856
|
-
@body = body
|
857
|
-
end
|
858
|
-
end
|
859
|
-
|
860
|
-
class GenericFooResource
|
861
|
-
include Alba::Resource
|
862
|
-
|
863
|
-
attributes :id, :name, :body
|
864
|
-
end
|
865
|
-
|
866
|
-
class RestrictedFooResource < GenericFooResource
|
867
|
-
def attributes
|
868
|
-
super.select { |key, _| key.to_sym == :name }
|
869
|
-
end
|
870
|
-
end
|
871
|
-
|
872
|
-
foo = Foo.new(1, 'my foo', 'body')
|
873
|
-
|
874
|
-
RestrictedFooResource.new(foo).serialize
|
875
|
-
# => '{"name":"my foo"}'
|
876
|
-
```
|
839
|
+
To filter attributes, you can use `select` instance method. Using `attributes` instance method is deprecated and will be removed in the future.
|
877
840
|
|
878
841
|
#### Filtering attributes with `select`
|
879
842
|
|
880
|
-
|
843
|
+
`select` takes two or three parameters, the name of an attribute, the value of an attribute and the attribute object (`Alba::Association`, for example). If it returns false that attribute is rejected.
|
881
844
|
|
882
845
|
```ruby
|
883
846
|
class Foo
|
@@ -900,6 +863,9 @@ class RestrictedFooResource < GenericFooResource
|
|
900
863
|
def select(_key, value)
|
901
864
|
!value.nil?
|
902
865
|
end
|
866
|
+
|
867
|
+
# This is also possible
|
868
|
+
# def select(_key, _value, _attribute)
|
903
869
|
end
|
904
870
|
|
905
871
|
foo = Foo.new(1, nil, 'body')
|
@@ -1107,6 +1073,40 @@ class UserResource2
|
|
1107
1073
|
end
|
1108
1074
|
```
|
1109
1075
|
|
1076
|
+
### Traits
|
1077
|
+
|
1078
|
+
Traits is an easy way to a group of attributes and apply it to the resource.
|
1079
|
+
|
1080
|
+
```ruby
|
1081
|
+
class User
|
1082
|
+
attr_accessor :id, :name, :email
|
1083
|
+
|
1084
|
+
def initialize(id, name, email)
|
1085
|
+
@id = id
|
1086
|
+
@name = name
|
1087
|
+
@email = email
|
1088
|
+
end
|
1089
|
+
end
|
1090
|
+
|
1091
|
+
class UserResource
|
1092
|
+
include Alba::Resource
|
1093
|
+
|
1094
|
+
attributes :id
|
1095
|
+
|
1096
|
+
trait :additional do
|
1097
|
+
attributes :name, :email
|
1098
|
+
end
|
1099
|
+
end
|
1100
|
+
|
1101
|
+
user = User.new(1, 'Foo', 'foo@example.org')
|
1102
|
+
UserResource.new(user).serialize # => '{"id":1}'
|
1103
|
+
UserResource.new(user, with_traits: :additional).serialize # => '{"id":1,"name":"Foo","email":"foo@example.com"}'
|
1104
|
+
```
|
1105
|
+
|
1106
|
+
This way, we can keep the resource class simple and inject conditions from outside. We can get the same result with the combination of `if` and `params`, but using `traits` DSL can make the resource class readable.
|
1107
|
+
|
1108
|
+
We can specify multiple traits at once with `with_traits: []` keyword argument.
|
1109
|
+
|
1110
1110
|
### Default
|
1111
1111
|
|
1112
1112
|
Alba doesn't support default value for attributes, but it's easy to set a default value.
|
@@ -1841,6 +1841,58 @@ Here, we override `serialize` method with `prepend`. In overridden method we pri
|
|
1841
1841
|
|
1842
1842
|
Don't forget calling `super` in this way.
|
1843
1843
|
|
1844
|
+
## Tips and Tricks
|
1845
|
+
|
1846
|
+
### Treating specific classes as non-collection
|
1847
|
+
|
1848
|
+
Sometimes we need to serialize an object that's `Enumerable` but not a collection. By default, Alba treats `Hash`, `Range` and `Struct` as non-collection object, but if we want to add some classes to this list, we can override `Alba.collection?` method like following:
|
1849
|
+
|
1850
|
+
```ruby
|
1851
|
+
Alba.singleton_class.prepend(
|
1852
|
+
Module.new do
|
1853
|
+
def collection?(object)
|
1854
|
+
super && !object.is_a?(SomeClass)
|
1855
|
+
end
|
1856
|
+
end
|
1857
|
+
)
|
1858
|
+
```
|
1859
|
+
|
1860
|
+
### Adding indexes to `many` association
|
1861
|
+
|
1862
|
+
Let's say an author has many books. We want returned JSON to include indexes of each book. In this case, we can reduce the number of executed SQL by fetching indexes ahead and push indexes into `param`.
|
1863
|
+
|
1864
|
+
```ruby
|
1865
|
+
Author = Data.define(:id, :books)
|
1866
|
+
Book = Data.define(:id, :name)
|
1867
|
+
|
1868
|
+
book1 = Book.new(1, 'book1')
|
1869
|
+
book2 = Book.new(2, 'book2')
|
1870
|
+
book3 = Book.new(3, 'book3')
|
1871
|
+
|
1872
|
+
author = Author.new(2, [book2, book3, book1])
|
1873
|
+
|
1874
|
+
class AuthorResource
|
1875
|
+
include Alba::Resource
|
1876
|
+
|
1877
|
+
attributes :id
|
1878
|
+
many :books do
|
1879
|
+
attributes :id, :name
|
1880
|
+
attribute :index do |bar|
|
1881
|
+
params[:index][bar.id]
|
1882
|
+
end
|
1883
|
+
end
|
1884
|
+
end
|
1885
|
+
|
1886
|
+
AuthorResource.new(
|
1887
|
+
author,
|
1888
|
+
params: {
|
1889
|
+
index: author.books.map.with_index { |book, index| [book.id, index] }
|
1890
|
+
.to_h
|
1891
|
+
}
|
1892
|
+
).serialize
|
1893
|
+
# => {"id":2,"books":[{"id":2,"name":"book2","index":0},{"id":3,"name":"book3","index":1},{"id":1,"name":"book1","index":2}]}
|
1894
|
+
```
|
1895
|
+
|
1844
1896
|
## Rails
|
1845
1897
|
|
1846
1898
|
When you use Alba in Rails, you can create an initializer file with the line below for compatibility with Rails JSON encoder.
|
@@ -1874,6 +1926,10 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
1874
1926
|
|
1875
1927
|
Thank you for begin interested in contributing to Alba! Please see [contributors guide](https://github.com/okuramasafumi/alba/blob/main/CONTRIBUTING.md) before start contributing. If you have any questions, please feel free to ask in [Discussions](https://github.com/okuramasafumi/alba/discussions).
|
1876
1928
|
|
1929
|
+
## Versioning
|
1930
|
+
|
1931
|
+
Alba follows [Semver 2.0.0](https://semver.org/spec/v2.0.0.html).
|
1932
|
+
|
1877
1933
|
## License
|
1878
1934
|
|
1879
1935
|
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/lib/alba/association.rb
CHANGED
@@ -32,6 +32,9 @@ module Alba
|
|
32
32
|
end
|
33
33
|
|
34
34
|
# This is the same API in `NestedAttribute`
|
35
|
+
#
|
36
|
+
# @param type [String, Symbol] one of `snake`, `:camel`, `:lower_camel`, `:dash` and `none`
|
37
|
+
# @return [void]
|
35
38
|
def key_transformation=(type)
|
36
39
|
@resource.transform_keys(type) unless @resource.is_a?(Proc)
|
37
40
|
end
|
@@ -44,7 +47,7 @@ module Alba
|
|
44
47
|
# @return [Hash]
|
45
48
|
def to_h(target, within: nil, params: {})
|
46
49
|
params = params.merge(@params)
|
47
|
-
object = target.__send__(@name)
|
50
|
+
object = target.is_a?(Hash) ? target.fetch(@name) : target.__send__(@name)
|
48
51
|
object = @condition.call(object, params, target) if @condition
|
49
52
|
return if object.nil?
|
50
53
|
|
@@ -2,13 +2,12 @@
|
|
2
2
|
|
3
3
|
require_relative 'association'
|
4
4
|
require_relative 'constants'
|
5
|
-
require 'ostruct'
|
6
5
|
|
7
6
|
module Alba
|
8
7
|
# Represents attribute with `if` option
|
9
8
|
# @api private
|
10
9
|
class ConditionalAttribute
|
11
|
-
# @param body [Symbol, Proc, Alba::Association, Alba::TypedAttribute] real attribute wrapped with condition
|
10
|
+
# @param body [Symbol, Proc, Alba::Association, Alba::TypedAttribute, Alba::NestedAttribute] real attribute wrapped with condition
|
12
11
|
# @param condition [Symbol, Proc] condition to check
|
13
12
|
def initialize(body:, condition:)
|
14
13
|
@body = body
|
@@ -26,7 +25,7 @@ module Alba
|
|
26
25
|
fetched_attribute = yield(@body)
|
27
26
|
return fetched_attribute unless with_two_arity_proc_condition
|
28
27
|
|
29
|
-
return Alba::REMOVE_KEY unless resource.instance_exec(object,
|
28
|
+
return Alba::REMOVE_KEY unless resource.instance_exec(object, second_object(object), &@condition)
|
30
29
|
|
31
30
|
fetched_attribute
|
32
31
|
end
|
@@ -51,17 +50,15 @@ module Alba
|
|
51
50
|
@condition.is_a?(Proc) && @condition.arity >= 2
|
52
51
|
end
|
53
52
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
else
|
64
|
-
OpenStruct.new(fetched_attribute)
|
53
|
+
def second_object(object)
|
54
|
+
case @body
|
55
|
+
when Symbol, Alba::Association, Alba::TypedAttribute
|
56
|
+
object.__send__(@body.name)
|
57
|
+
when Alba::NestedAttribute
|
58
|
+
nil
|
59
|
+
when Proc
|
60
|
+
@body.call(object)
|
61
|
+
else raise Alba::Error, "Unreachable code, @body is: #{@body.inspect}"
|
65
62
|
end
|
66
63
|
end
|
67
64
|
end
|
data/lib/alba/layout.rb
CHANGED
@@ -15,13 +15,9 @@ module Alba
|
|
15
15
|
# @param inline [Proc] a proc returning JSON string or a Hash representing JSON
|
16
16
|
def initialize(file:, inline:)
|
17
17
|
@body = if file
|
18
|
-
|
19
|
-
|
20
|
-
file
|
18
|
+
check_and_return(file, 'File layout must be a String representing filename', String)
|
21
19
|
elsif inline
|
22
|
-
|
23
|
-
|
24
|
-
inline
|
20
|
+
check_and_return(inline, 'Inline layout must be a Proc returning a Hash or a String', Proc)
|
25
21
|
else
|
26
22
|
raise ArgumentError, 'Layout must be either String or Proc'
|
27
23
|
end
|
@@ -47,6 +43,12 @@ module Alba
|
|
47
43
|
|
48
44
|
attr_reader :serialized_json
|
49
45
|
|
46
|
+
def check_and_return(obj, message, klass)
|
47
|
+
raise ArgumentError, message unless obj.is_a?(klass)
|
48
|
+
|
49
|
+
obj
|
50
|
+
end
|
51
|
+
|
50
52
|
def serialize_within_string_layout(bnd)
|
51
53
|
ERB.new(File.read(@body)).result(bnd)
|
52
54
|
end
|
data/lib/alba/railtie.rb
CHANGED
@@ -8,12 +8,12 @@ module Alba
|
|
8
8
|
|
9
9
|
ActiveSupport.on_load(:action_controller) do
|
10
10
|
define_method(:serialize) do |obj, with: nil, root_key: nil, meta: {}, &block|
|
11
|
-
resource = with.nil? ? Alba.
|
11
|
+
resource = with.nil? ? Alba.resource_for(obj, &block) : with.new(obj)
|
12
12
|
resource.to_json(root_key: root_key, meta: meta)
|
13
13
|
end
|
14
14
|
|
15
15
|
define_method(:render_serialized_json) do |obj, with: nil, root_key: nil, meta: {}, &block|
|
16
|
-
json = with.nil? ? Alba.
|
16
|
+
json = with.nil? ? Alba.resource_for(obj, &block) : with.new(obj)
|
17
17
|
render json: json.to_json(root_key: root_key, meta: meta)
|
18
18
|
end
|
19
19
|
end
|
data/lib/alba/resource.rb
CHANGED
@@ -14,14 +14,14 @@ module Alba
|
|
14
14
|
module Resource
|
15
15
|
# @!parse include InstanceMethods
|
16
16
|
# @!parse extend ClassMethods
|
17
|
-
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
|
17
|
+
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: [], _select_arity: nil, _traits: {}}.freeze # rubocop:disable Layout/LineLength
|
18
18
|
private_constant :INTERNAL_VARIABLES
|
19
19
|
|
20
20
|
WITHIN_DEFAULT = Object.new.freeze
|
21
21
|
private_constant :WITHIN_DEFAULT
|
22
22
|
|
23
23
|
# `setup` method is meta-programmatically defined here for performance.
|
24
|
-
# @private
|
24
|
+
# @api private
|
25
25
|
def self.included(base) # rubocop:disable Metrics/MethodLength
|
26
26
|
super
|
27
27
|
base.class_eval do
|
@@ -46,10 +46,12 @@ module Alba
|
|
46
46
|
# @param object [Object] the object to be serialized
|
47
47
|
# @param params [Hash] user-given Hash for arbitrary data
|
48
48
|
# @param within [Object, nil, false, true] determines what associations to be serialized. If not set, it serializes all associations.
|
49
|
-
|
49
|
+
# @param with_traits [Symbol, Array<Symbol>, nil] specified traits
|
50
|
+
def initialize(object, params: {}, within: WITHIN_DEFAULT, with_traits: nil)
|
50
51
|
@object = object
|
51
52
|
@params = params
|
52
53
|
@within = within
|
54
|
+
@with_traits = with_traits
|
53
55
|
_setup
|
54
56
|
end
|
55
57
|
|
@@ -68,7 +70,8 @@ module Alba
|
|
68
70
|
# @see #serialize
|
69
71
|
# @see https://github.com/rails/rails/blob/7-0-stable/actionpack/lib/action_controller/metal/renderers.rb#L156
|
70
72
|
def to_json(options = {}, root_key: nil, meta: {})
|
71
|
-
|
73
|
+
confusing_keys = [:only, :except]
|
74
|
+
confusing_options = options.keys.select { |k| confusing_keys.include?(k.to_sym) }
|
72
75
|
unless confusing_options.empty?
|
73
76
|
confusing_options.sort!
|
74
77
|
confusing_options.map! { |s| "\"#{s}\"" }
|
@@ -99,12 +102,30 @@ module Alba
|
|
99
102
|
#
|
100
103
|
# @return [Hash]
|
101
104
|
def serializable_hash
|
102
|
-
Alba.collection?(@object) ? serializable_hash_for_collection :
|
105
|
+
Alba.collection?(@object) ? serializable_hash_for_collection : attributes_to_hash(@object, {})
|
103
106
|
end
|
104
107
|
alias to_h serializable_hash
|
105
108
|
|
106
109
|
private
|
107
110
|
|
111
|
+
def hash_from_traits(obj)
|
112
|
+
h = {}
|
113
|
+
return h if @with_traits.nil?
|
114
|
+
|
115
|
+
Array(@with_traits).each do |trait|
|
116
|
+
body = @_traits.fetch(trait) { raise Alba::Error, "Trait not found: #{trait}" }
|
117
|
+
|
118
|
+
resource_class = Alba.resource_class
|
119
|
+
resource_class.class_eval(&body)
|
120
|
+
h.merge!(resource_class.new(obj, params: params, within: @within).serializable_hash)
|
121
|
+
end
|
122
|
+
h
|
123
|
+
end
|
124
|
+
|
125
|
+
def deprecated_serializable_hash
|
126
|
+
Alba.collection?(@object) ? serializable_hash_for_collection : converter.call(@object)
|
127
|
+
end
|
128
|
+
|
108
129
|
def serialize_with(hash)
|
109
130
|
serialized_json = encode(hash)
|
110
131
|
return serialized_json unless @_layout
|
@@ -130,6 +151,18 @@ module Alba
|
|
130
151
|
end
|
131
152
|
|
132
153
|
def serializable_hash_for_collection
|
154
|
+
if @_collection_key
|
155
|
+
@object.to_h do |item|
|
156
|
+
k = item.public_send(@_collection_key)
|
157
|
+
key = Alba.regularize_key(k)
|
158
|
+
[key, attributes_to_hash(item, {})]
|
159
|
+
end
|
160
|
+
else
|
161
|
+
@object.map { |obj| attributes_to_hash(obj, {}) }
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def deprecated_serializable_hash_for_collection
|
133
166
|
if @_collection_key
|
134
167
|
@object.to_h do |item|
|
135
168
|
k = item.public_send(@_collection_key)
|
@@ -195,27 +228,35 @@ module Alba
|
|
195
228
|
rescue StandardError => e
|
196
229
|
handle_error(e, obj, key, attribute, hash)
|
197
230
|
end
|
198
|
-
hash
|
231
|
+
@with_traits.nil? ? hash : hash.merge!(hash_from_traits(obj))
|
199
232
|
end
|
200
233
|
|
201
234
|
# This is default behavior for getting attributes for serialization
|
202
235
|
# Override this method to filter certain attributes
|
236
|
+
#
|
237
|
+
# @deprecated in favor of `select`
|
203
238
|
def attributes
|
204
239
|
@_attributes
|
205
240
|
end
|
206
241
|
|
207
242
|
# Default implementation for selecting attributes
|
208
243
|
# Override this method to filter attributes based on key and value
|
209
|
-
def select(_key, _value)
|
244
|
+
def select(_key, _value, _attribute)
|
210
245
|
true
|
211
246
|
end
|
212
247
|
|
213
248
|
def set_key_and_attribute_body_from(obj, key, attribute, hash)
|
214
249
|
key = transform_key(key)
|
215
250
|
value = fetch_attribute(obj, key, attribute)
|
216
|
-
|
251
|
+
# When `select` is not overridden, skip calling it for better performance
|
252
|
+
unless @_select_arity.nil?
|
253
|
+
# `select` can be overridden with both 2 and 3 parameters
|
254
|
+
# Here we check the arity and build arguments accordingly
|
255
|
+
args = @_select_arity == 3 ? [key, value, attribute] : [key, value]
|
256
|
+
return unless select(*args)
|
257
|
+
end
|
217
258
|
|
218
|
-
hash[key] = value unless
|
259
|
+
hash[key] = value unless Alba::REMOVE_KEY == value # rubocop:disable Style/YodaCondition
|
219
260
|
end
|
220
261
|
|
221
262
|
def handle_error(error, obj, key, attribute, hash)
|
@@ -259,12 +300,16 @@ module Alba
|
|
259
300
|
end
|
260
301
|
|
261
302
|
def _fetch_attribute_from_object_first(obj, attribute)
|
303
|
+
return obj.fetch(attribute) if obj.is_a?(Hash)
|
304
|
+
|
262
305
|
obj.__send__(attribute)
|
263
306
|
rescue NoMethodError
|
264
307
|
__send__(attribute, obj)
|
265
308
|
end
|
266
309
|
|
267
310
|
def _fetch_attribute_from_resource_first(obj, attribute)
|
311
|
+
return obj.fetch(attribute) if obj.is_a?(Hash)
|
312
|
+
|
268
313
|
if @_resource_methods.include?(attribute)
|
269
314
|
__send__(attribute, obj)
|
270
315
|
else
|
@@ -299,12 +344,27 @@ module Alba
|
|
299
344
|
attr_reader(*INTERNAL_VARIABLES.keys)
|
300
345
|
|
301
346
|
# This `method_added` is used for defining "resource methods"
|
302
|
-
def method_added(method_name)
|
303
|
-
|
347
|
+
def method_added(method_name) # rubocop:disable Metrics/MethodLength
|
348
|
+
case method_name
|
349
|
+
when :collection_converter, :converter
|
350
|
+
warn "Defining ##{method_name} methods is deprecated", category: :deprecated, uplevel: 1
|
351
|
+
alias_method :serializable_hash_for_collection, :deprecated_serializable_hash_for_collection
|
352
|
+
private(:serializable_hash_for_collection)
|
353
|
+
alias_method :serializable_hash, :deprecated_serializable_hash
|
354
|
+
alias_method :to_h, :deprecated_serializable_hash
|
355
|
+
when :attributes
|
356
|
+
warn 'Overriding `attributes` is deprecated, use `select` instead.', category: :deprecated, uplevel: 1
|
357
|
+
when :select
|
358
|
+
@_select_arity = instance_method(:select).arity
|
359
|
+
when :_setup # noop
|
360
|
+
else
|
361
|
+
_resource_methods << method_name.to_sym
|
362
|
+
end
|
363
|
+
|
304
364
|
super
|
305
365
|
end
|
306
366
|
|
307
|
-
# @private
|
367
|
+
# @api private
|
308
368
|
def inherited(subclass)
|
309
369
|
super
|
310
370
|
INTERNAL_VARIABLES.each_key { |name| subclass.instance_variable_set(:"@#{name}", instance_variable_get(:"@#{name}").clone) }
|
@@ -316,7 +376,7 @@ module Alba
|
|
316
376
|
#
|
317
377
|
# @param attrs [Array<String, Symbol>]
|
318
378
|
# @param if [Proc] condition to decide if it should serialize these attributes
|
319
|
-
# @param attrs_with_types [Hash
|
379
|
+
# @param attrs_with_types [Hash{Symbol, String => Array<Symbol, Proc>, Symbol}]
|
320
380
|
# attributes with name in its key and type and optional type converter in its value
|
321
381
|
# @return [void]
|
322
382
|
def attributes(*attrs, if: nil, **attrs_with_types)
|
@@ -364,6 +424,7 @@ module Alba
|
|
364
424
|
# @param name [String, Symbol] name of the association, used as key when `key` param doesn't exist
|
365
425
|
# @param condition [Proc, nil] a Proc to modify the association
|
366
426
|
# @param resource [Class<Alba::Resource>, String, Proc, nil] representing resource for this association
|
427
|
+
# @param serializer [Class<Alba::Resource>, String, Proc, nil] alias for `resource`
|
367
428
|
# @param key [String, Symbol, nil] used as key when given
|
368
429
|
# @param params [Hash] params override for the association
|
369
430
|
# @param options [Hash<Symbol, Proc>]
|
@@ -371,7 +432,8 @@ module Alba
|
|
371
432
|
# @param block [Block]
|
372
433
|
# @return [void]
|
373
434
|
# @see Alba::Association#initialize
|
374
|
-
def association(name, condition = nil, resource: nil, key: nil, params: {}, **options, &block)
|
435
|
+
def association(name, condition = nil, resource: nil, serializer: nil, key: nil, params: {}, **options, &block)
|
436
|
+
resource ||= serializer
|
375
437
|
transformation = @_key_transformation_cascade ? @_transform_type : :none
|
376
438
|
assoc = Association.new(
|
377
439
|
name: name, condition: condition, resource: resource, params: params, nesting: nesting, key_transformation: transformation, helper: @_helper, &block
|
@@ -409,6 +471,19 @@ module Alba
|
|
409
471
|
end
|
410
472
|
alias nested nested_attribute
|
411
473
|
|
474
|
+
# Set a trait
|
475
|
+
#
|
476
|
+
# @param name [String, Symbol] name of the trait
|
477
|
+
# @param block [Block] the "content" of the trait
|
478
|
+
# @raise [ArgumentError] if block is absent
|
479
|
+
# @return [void]
|
480
|
+
def trait(name, &block)
|
481
|
+
raise ArgumentError, 'No block given in trait method' unless block
|
482
|
+
|
483
|
+
name = name.to_sym
|
484
|
+
@_traits[name] = block
|
485
|
+
end
|
486
|
+
|
412
487
|
# Set root key
|
413
488
|
#
|
414
489
|
# @param key [String, Symbol]
|
@@ -477,9 +552,7 @@ module Alba
|
|
477
552
|
if @_key_transformation_cascade
|
478
553
|
# We need to update key transformation of associations and nested attributes
|
479
554
|
@_attributes.each_value do |attr|
|
480
|
-
|
481
|
-
|
482
|
-
attr.key_transformation = type
|
555
|
+
attr.key_transformation = type if attr.is_a?(Association) || attr.is_a?(NestedAttribute)
|
483
556
|
end
|
484
557
|
end
|
485
558
|
self # Return the new class
|
data/lib/alba/typed_attribute.rb
CHANGED
data/lib/alba/version.rb
CHANGED