Skip to content

Commit aae5168

Browse files
committed
[Feat] Improve select to take third parameter
This makes `select` to behave better based on the kind of the attribute. When `select` is not overridden, we don't call it anymore. We check the arity of `select` at the definition step, so there should be no performance penalty other than calling cost.
1 parent dae4084 commit aae5168

File tree

3 files changed

+63
-7
lines changed

3 files changed

+63
-7
lines changed

README.md

+4-1
Original file line numberDiff line numberDiff line change
@@ -840,7 +840,7 @@ To filter attributes, you can use `select` instance method. Using `attributes` i
840840

841841
#### Filtering attributes with `select`
842842

843-
`select` takes two parameters, the name of an attribute and the value of an attribute. If it returns false that attribute is rejected.
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.
844844

845845
```ruby
846846
class Foo
@@ -863,6 +863,9 @@ class RestrictedFooResource < GenericFooResource
863863
def select(_key, value)
864864
!value.nil?
865865
end
866+
867+
# This is also possible
868+
# def select(_key, _value, _attribute)
866869
end
867870

868871
foo = Foo.new(1, nil, 'body')

lib/alba/resource.rb

+11-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ module Alba
1414
module Resource
1515
# @!parse include InstanceMethods
1616
# @!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}.freeze # rubocop:disable Layout/LineLength
1818
private_constant :INTERNAL_VARIABLES
1919

2020
WITHIN_DEFAULT = Object.new.freeze
@@ -225,14 +225,20 @@ def attributes
225225

226226
# Default implementation for selecting attributes
227227
# Override this method to filter attributes based on key and value
228-
def select(_key, _value)
228+
def select(_key, _value, _attribute)
229229
true
230230
end
231231

232232
def set_key_and_attribute_body_from(obj, key, attribute, hash)
233233
key = transform_key(key)
234234
value = fetch_attribute(obj, key, attribute)
235-
return unless select(key, value)
235+
# When `select` is not overridden, skip calling it for better performance
236+
unless @_select_arity.nil?
237+
# `select` can be overridden with both 2 and 3 parameters
238+
# Here we check the arity and build arguments accordingly
239+
args = @_select_arity == 3 ? [key, value, attribute] : [key, value]
240+
return unless select(*args)
241+
end
236242

237243
hash[key] = value unless Alba::REMOVE_KEY == value # rubocop:disable Style/YodaCondition
238244
end
@@ -332,6 +338,8 @@ def method_added(method_name) # rubocop:disable Metrics/MethodLength
332338
alias_method :to_h, :deprecated_serializable_hash
333339
when :attributes
334340
warn 'Overriding `attributes` is deprecated, use `select` instead.', category: :deprecated, uplevel: 1
341+
when :select
342+
@_select_arity = instance_method(:select).arity
335343
when :_setup # noop
336344
else
337345
_resource_methods << method_name.to_sym

test/usecases/nil_filtering_test.rb

+48-3
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,12 @@
44

55
class NilFilteringTest < Minitest::Test
66
class User
7-
attr_accessor :id, :name, :email, :created_at, :updated_at
7+
attr_accessor :id, :name, :email, :parent_user
88

99
def initialize(id, name, email)
1010
@id = id
1111
@name = name
1212
@email = email
13-
@created_at = Time.now
14-
@updated_at = Time.now
1513
end
1614
end
1715

@@ -35,4 +33,51 @@ def test_it_filters_nil_attributes_with_select
3533
UserResource.new(@user).serialize
3634
)
3735
end
36+
37+
class UserResource2 < UserResource
38+
def select(_key, _value, _attribute)
39+
true
40+
end
41+
end
42+
43+
def test_it_filters_with_select_with_three_parameters
44+
assert_equal(
45+
'{"id":1,"name":null,"email":null}',
46+
UserResource2.new(@user).serialize
47+
)
48+
end
49+
50+
class UserResourceWithAssociation
51+
include Alba::Resource
52+
53+
attributes :id, :name, :email
54+
55+
one :parent_user do
56+
attributes :id, :name, :email
57+
end
58+
end
59+
60+
class UserResourceWithAssociationFilteringIt < UserResourceWithAssociation
61+
def select(_key, _value, attribute)
62+
!attribute.is_a?(Alba::Association)
63+
end
64+
end
65+
66+
class UserResourceWithAssociationOnlySelectingIt < UserResourceWithAssociation
67+
def select(_key, _value, attribute)
68+
attribute.is_a?(Alba::Association)
69+
end
70+
end
71+
72+
def test_it_filters_with_select_with_attribute_parameter
73+
@user.parent_user = User.new(2, nil, nil)
74+
assert_equal(
75+
'{"id":1,"name":null,"email":null}',
76+
UserResourceWithAssociationFilteringIt.new(@user).serialize
77+
)
78+
assert_equal(
79+
'{"parent_user":{"id":2,"name":null,"email":null}}',
80+
UserResourceWithAssociationOnlySelectingIt.new(@user).serialize
81+
)
82+
end
3883
end

0 commit comments

Comments
 (0)