Skip to content

Add Rake/RequireOutsideTask cop - #64

Open
corsonknowles wants to merge 1 commit into
rubocop:masterfrom
corsonknowles:add-require-outside-task-cop
Open

Add Rake/RequireOutsideTask cop#64
corsonknowles wants to merge 1 commit into
rubocop:masterfrom
corsonknowles:add-require-outside-task-cop

Conversation

@corsonknowles

@corsonknowles corsonknowles commented Jul 1, 2026

Copy link
Copy Markdown
Contributor

What

Adds a new cop, Rake/RequireOutsideTask, that flags require/require_relative calls not inside a task block.

# bad
require 'foo'

task :foo do
  Foo.do_something
end

# bad - a namespace body is still evaluated on load
namespace :foo do
  require 'foo'

  task :bar do
    Foo.do_something
  end
end

# good
task :foo do
  require 'foo'
  Foo.do_something
end

# good
Rake::Task['db:seed'].enhance do
  require 'foo'
  Foo.do_something
end

Why

A rake file's top level — including the body of a namespace block — is evaluated every time the file is loaded (i.e. on every rake invocation), not only when the task runs. A require placed there is paid on every load. Moving it into the task (or a Rake::Task#enhance block) loads the dependency lazily, only when the task that needs it runs. This complements the existing Rake/MethodDefinitionInTask / Rake/ClassDefinitionInTask "the block scope is misleading" family.

Known tradeoff (why Enabled: pending, and the open question)

Some top-level requires must stay at load time because they feed task-defining DSLs — require "bundler/gem_tasks", require 'rspec/core/rake_task' (then RSpec::Core::RakeTask.new), require 'rubocop/rake_task' (then RuboCop::RakeTask.new).

This repo's own Rakefile has two such requires; We could mark them with inline # rubocop:disable Rake/RequireOutsideTask + a comment to demonstrate the escape hatch, but since Rakefile is the preferred place to do this over individual rake tasks, we exclude it from the rule by default.

Open questions for maintainers:

  • Should the cop add significant new logic to exempt a require that is immediately followed by a task-defining call, rather than relying on inline disables for that case?
  • Should the default Include be **/*.rake only (not Rakefile), since Rakefiles are the canonical place to require task libraries at the top? (currently, yes)

Origin:
extracted and generalized from a custom cop thst has run for over a year at Gusto.

corsonknowles added a commit to corsonknowles/rubocop-rake that referenced this pull request Jul 1, 2026
@corsonknowles
corsonknowles force-pushed the add-require-outside-task-cop branch from 0576947 to 7953ad5 Compare July 1, 2026 21:13
@corsonknowles
corsonknowles marked this pull request as ready for review July 2, 2026 04:48
Adds a new cop, `Rake/RequireOutsideTask`, that flags `require`/`require_relative`
calls not inside a task block.

```ruby
require 'foo'

task :foo do
  Foo.do_something
end

namespace :foo do
  require 'foo'

  task :bar do
    Foo.do_something
  end
end

task :foo do
  require 'foo'
  Foo.do_something
end

Rake::Task['db:seed'].enhance do
  require 'foo'
  Foo.do_something
end
```

A rake file's top level - including the body of a `namespace` block - is
evaluated every time the file is loaded (i.e. on every `rake` invocation), not
only when the task runs. A require placed there is paid on every load. Moving it
into the task (or a `Rake::Task#enhance` block) loads the dependency lazily, only
when the task that needs it runs. This complements the existing
`Rake/MethodDefinitionInTask` / `Rake/ClassDefinitionInTask` family.

Rakefiles are excluded by default, since they are the canonical place to require
task libraries at the top (e.g. `require "bundler/gem_tasks"`,
`require "rspec/core/rake_task"`). Added as `Enabled: pending` per the guidance
for new cops.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@corsonknowles
corsonknowles force-pushed the add-require-outside-task-cop branch from 7953ad5 to 7b7d947 Compare August 10, 2026 11:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant