Feature #20812
closedProposal for Safe Include Method in Ruby
Description
Proposal for Safe Include Method in Ruby¶
Description¶
Add a new method include_safe?
to Ruby's Enumerable
module that safely handles nil arguments in inclusion checks.
Problem Statement¶
The current include?
method raises an error when passed nil
as an argument. This requires developers to write defensive code with explicit nil checks, leading to less readable and more error-prone code.
# Current problematic scenarios:
collection.include?(nil) # Works, checks for nil in collection
collection.include?(value) # Raises error if value is nil
# Current workarounds:
value && collection.include?(value)
collection.include?(value.to_s)
Proposed Solution¶
Add include_safe?
method that returns false
when the argument is nil
:
module Enumerable
def include_safe?(obj)
return false if obj.nil?
include?(obj)
end
end
Usage Examples¶
numbers = [1, 2, 3]
document_id = nil
# Current approach
document_id && numbers.include?(document_id) # false
# Proposed approach
numbers.include_safe?(document_id) # false
# Works normally for non-nil values
numbers.include_safe?(2) # true
numbers.include_safe?(4) # false
# Edge cases handled
[nil].include_safe?(nil) # false
[].include_safe?(nil) # false
Benefits¶
- Improved Safety: Eliminates a common source of runtime errors
- Better Readability: Removes need for explicit nil checks
- Consistent Behavior: Provides predictable handling of nil values
- Rails Alignment: Similar to Rails' safe navigation patterns
- Reduced Boilerplate: Eliminates common defensive coding patterns
Implementation Notes¶
This would be implemented in C as part of Ruby's core, but here's a Ruby reference implementation:
module Enumerable
def include_safe?(obj)
return false if obj.nil?
include?(obj)
end
end
Testing Strategy¶
require 'minitest/autorun'
class TestIncludeSafe < Minitest::Test
def setup
@array = [1, 2, 3]
@array_with_nil = [1, nil, 3]
end
def test_basic_inclusion
assert @array.include_safe?(1)
refute @array.include_safe?(4)
end
def test_nil_handling
refute @array.include_safe?(nil)
refute @array_with_nil.include_safe?(nil)
end
def test_empty_collection
refute [].include_safe?(nil)
refute [].include_safe?(1)
end
end
Alternative Considerations¶
-
Name Alternatives:
try_include?
safe_include?
includes_safely?
-
Alternative Approaches:
- Modify existing
include?
behavior (rejected due to backward compatibility) - Add parameter to existing
include?
(rejected for clarity)
- Modify existing
Impact Analysis¶
Positive Impact¶
- Reduces runtime errors
- Improves code readability
- Follows principle of least surprise
Potential Concerns¶
- Another method to learn
- Possible confusion with regular
include?
Similar Features in Ruby¶
- Safe navigation operator (
&.
) -
try
method in Rails -
fetch
method with default value
Backward Compatibility¶
This change is fully backward compatible as it introduces a new method without modifying existing behavior.
Reference Implementation¶
A gem implementing this feature is available at: [Link to gem if created]
Questions for Core Team¶
- Is
include_safe?
the best name for this method? - Should this be added to
Array
specifically rather thanEnumerable
? - Should we consider any additional edge cases?
Updated by jeremyevans0 (Jeremy Evans) 14 days ago
rogerconsul (Roger Consul) wrote:
The current
include?
method raises an error when passednil
as an argument.
Are you sure?
# No Error
[1, 2].include?(nil)
[Object.new].include?(nil)
# Error, but due to custom ==, not include?
o = Object.new
def o.==(v) = (raise unless v)
[o].include?(nil)
Maybe you are using some core extension that causes include?
to raise an error if given nil
? Can you provide a self-contained reproducible example that shows include?
raising if the argument is nil
?
Updated by rogerconsul (Roger Consul) 14 days ago
jeremyevans0 (Jeremy Evans) wrote in #note-1:
rogerconsul (Roger Consul) wrote:
The current
include?
method raises an error when passednil
as an argument.Are you sure?
# No Error [1, 2].include?(nil) [Object.new].include?(nil) # Error, but due to custom ==, not include? o = Object.new def o.==(v) = (raise unless v) [o].include?(nil)
Maybe you are using some core extension that causes
include?
to raise an error if givennil
? Can you provide a self-contained reproducible example that showsinclude?
raising if the argument isnil
?
Well... After you mentioned it, I realized we were using version 2.7.5 🥲
Error didn't reproduce on newer versions. We can close this thread.
Thanks for your time, Jeremy <3
Updated by jeremyevans0 (Jeremy Evans) 14 days ago
- Status changed from Open to Closed