Validate offset value at call time like limit
ActiveRecord now validates the argument passed to offset in the same way as limit, raising ArgumentError for non‑integer values instead of silently coercing them.
背景
limit already validates its argument with Integer(value), so an invalid call such as Post.limit("abc") raises an error immediately. In contrast, offset previously stored the raw value and only performed offset_value.to_i when generating SQL, causing inputs like "abc" to become 0 and "5x" to become 5 without any warning. This discrepancy meant that pure‑SQL queries appeared to work, but any in‑memory finder path that used the stored offset would break or produce incorrect results. Aligning offset with limit eliminates this hidden source of bugs.
技術的な変更
offset! now coerces its argument with Integer(value) unless the value is nil, mirroring the existing behavior of limit!.
変更前:
def offset!(value) # :nodoc:
self.offset_value = value
self
end
変更後:
def offset!(value) # :nodoc:
value = Integer(value) unless value.nil?
self.offset_value = value
self
end
The test suite was extended accordingly. test_invalid_offset in activerecord/test/cases/base_test.rb asserts that Topic.offset("asdfadf") raises ArgumentError, matching the existing test_invalid_limit. Additionally, activerecord/test/cases/relation/mutation_test.rb adds a #offset! mutation test confirming that the method returns the relation object and correctly sets offset_value.
設計判断
The implementation reuses the same Integer conversion pattern already employed by limit!, avoiding the introduction of a new validation helper or API surface. This choice preserves backward compatibility for callers that already pass clean numeric strings, while providing a clear, fail‑fast error for truly invalid input. The change is categorized as a consistency/clarity improvement rather than a security fix, because the prior to_i conversion already mitigated injection risks.