class RuboCop::Cop::Style::FileOpen
File.read(‘file’)
# good - use File.read for one-shot reads
end
File.open(‘file’)
def json_key_io
# good - return an open file object for the caller to manage
process(io: File.open(‘file’))
# good - pass an open file object to an API that manages its lifecycle
File.open(‘file’, &:read)
# good
end
f.read
File.open(‘file’) do |f|
# good
File.open(‘file’).read
# bad
f = File.open(‘file’)
# bad
@example
the file descriptor is closed by the garbage collector.
still flags intentional one-shot reads (‘File.open(“f”).read`) where
verify whether the file descriptor is safely managed. For example, it
This cop is unsafe because it relies on syntax heuristics and cannot
@safety
caller is likely managing the file descriptor intentionally.
`File.open` is used as a return value or passed as an argument, the
clearest indicators that the block form should be used instead. When
assigned to a variable or has a method chained on it, as those are the
This cop only registers an offense when the result of `File.open` is
automatically closed when the block exits.
to resource exhaustion. Using the block form ensures the file is
will only be closed when the garbage collector runs, which may lead
for closing the file descriptor. If it is not explicitly closed, it
When `File.open` is called without a block, the caller is responsible
Checks for `File.open` without a block, which can leak file descriptors.
def offensive_usage?(node)
def offensive_usage?(node) return true unless node.value_used? node.parent.lvasgn_type? || receiver_of_chained_call?(node) end
def on_send(node)
def on_send(node) return unless file_open?(node) return if node.block_argument? return unless offensive_usage?(node) add_offense(node) end
def receiver_of_chained_call?(node)
def receiver_of_chained_call?(node) node.parent.receiver == node end