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 (
&.) -
trymethod in Rails -
fetchmethod 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
Arrayspecifically rather thanEnumerable? - Should we consider any additional edge cases?
Updated by jeremyevans0 (Jeremy Evans) about 1 year ago
rogerconsul (Roger Consul) wrote:
The current
include?method raises an error when passednilas 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) about 1 year ago
jeremyevans0 (Jeremy Evans) wrote in #note-1:
rogerconsul (Roger Consul) wrote:
The current
include?method raises an error when passednilas 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) about 1 year ago
- Status changed from Open to Closed