Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Closes #9734
- Loading branch information
1 parent
d1bd888
commit 928faeb
Showing
5 changed files
with
172 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
* [#9734](https://github.com/rubocop/rubocop/pull/9734): Add `Style/TopLevelMethodDefinition` cop. ([@tejasbubane][]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# frozen_string_literal: true | ||
|
||
module RuboCop | ||
module Cop | ||
module Style | ||
# Newcomers to ruby applications may write top-level methods, | ||
# when ideally they should be organized in appropriate classes or modules. | ||
# This cop looks for definitions of top-level methods and warns about them. | ||
# | ||
# However for ruby scripts it is perfectly fine to use top-level methods. | ||
# Hence this cop is disabled by default. | ||
# | ||
# @example | ||
# # bad | ||
# def some_method | ||
# end | ||
# | ||
# # bad | ||
# def self.some_method | ||
# end | ||
# | ||
# # bad | ||
# define_method(:foo) { puts 1 } | ||
# | ||
# # good | ||
# module Foo | ||
# def some_method | ||
# end | ||
# end | ||
# | ||
# # good | ||
# class Foo | ||
# def self.some_method | ||
# end | ||
# end | ||
# | ||
# # good | ||
# Struct.new do | ||
# def some_method | ||
# end | ||
# end | ||
# | ||
# # good | ||
# class Foo | ||
# define_method(:foo) { puts 1 } | ||
# end | ||
class TopLevelMethodDefinition < Base | ||
MSG = 'Do not define methods at the top-level.' | ||
|
||
RESTRICT_ON_SEND = %i[define_method].freeze | ||
|
||
def on_def(node) | ||
return unless node.root? | ||
|
||
add_offense(node) | ||
end | ||
alias on_defs on_def | ||
alias on_send on_def | ||
|
||
def on_block(node) | ||
return unless define_method_block?(node) && node.root? | ||
|
||
add_offense(node) | ||
end | ||
|
||
private | ||
|
||
# @!method define_method_block?(node) | ||
def_node_matcher :define_method_block?, <<~PATTERN | ||
(block (send _ {:define_method} _) ...) | ||
PATTERN | ||
end | ||
end | ||
end | ||
end |
90 changes: 90 additions & 0 deletions
90
spec/rubocop/cop/style/top_level_method_definition_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,90 @@ | ||
# frozen_string_literal: true | ||
|
||
RSpec.describe RuboCop::Cop::Style::TopLevelMethodDefinition, :config do | ||
it 'registers an offense top-level methods' do | ||
expect_offense(<<~RUBY) | ||
def foo; end | ||
^^^^^^^^^^^^ Do not define methods at the top-level. | ||
RUBY | ||
end | ||
|
||
it 'registers an offense top-level class methods' do | ||
expect_offense(<<~RUBY) | ||
def self.foo; end | ||
^^^^^^^^^^^^^^^^^ Do not define methods at the top-level. | ||
RUBY | ||
end | ||
|
||
context 'top-level define_method' do | ||
it 'registers offense with inline block' do | ||
expect_offense(<<~RUBY) | ||
define_method(:foo) { puts 1 } | ||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not define methods at the top-level. | ||
RUBY | ||
end | ||
|
||
it 'registers offense for multi-line block' do | ||
expect_offense(<<~RUBY) | ||
define_method(:foo) do |x| | ||
^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not define methods at the top-level. | ||
puts 1 | ||
end | ||
RUBY | ||
end | ||
|
||
it 'registers offense for proc argument' do | ||
expect_offense(<<~RUBY) | ||
define_method(:foo, instance_method(:bar)) | ||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Do not define methods at the top-level. | ||
RUBY | ||
end | ||
end | ||
|
||
it 'does not register an offense when using module' do | ||
expect_no_offenses(<<~RUBY) | ||
module Foo | ||
def foo; end | ||
end | ||
RUBY | ||
end | ||
|
||
it 'does not register an offense when using class' do | ||
expect_no_offenses(<<~RUBY) | ||
class Foo | ||
def self.foo; end | ||
end | ||
RUBY | ||
end | ||
|
||
it 'does not register an offense when using Struct' do | ||
expect_no_offenses(<<~RUBY) | ||
Foo = Struct.new do | ||
def foo; end | ||
end | ||
RUBY | ||
end | ||
|
||
it 'does not register an offense when defined within arbitrary block' do | ||
expect_no_offenses(<<~RUBY) | ||
Foo = types.each do |type| | ||
def foo(type) | ||
puts type | ||
end | ||
end | ||
RUBY | ||
end | ||
|
||
it 'does not register an offense when define_method is not top-level' do | ||
expect_no_offenses(<<~RUBY) | ||
class Foo | ||
define_method(:a) { puts 1 } | ||
define_method(:b) do |x| | ||
puts x | ||
end | ||
define_method(:c, instance_method(:d)) | ||
end | ||
RUBY | ||
end | ||
end |