Skip to content

Commit

Permalink
Merge pull request #4 from utkarsh2102/add-relative_require_to_lib-cop
Browse files Browse the repository at this point in the history
Implement the new RelativeRequireToLib cop + add tests
  • Loading branch information
utkarsh2102 authored Jul 23, 2020
2 parents c94a47d + aababc1 commit afa055d
Show file tree
Hide file tree
Showing 10 changed files with 216 additions and 11 deletions.
7 changes: 4 additions & 3 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ AllCops:
- 'vendor/**/*'
TargetRubyVersion: 2.4

Packaging/RelativeRequireToLib:
Include:
- spec/**/*.rb

Naming/FileName:
Exclude:
- lib/rubocop-packaging.rb
Expand All @@ -28,6 +32,3 @@ Metrics/BlockLength:
Metrics/MethodLength:
Exclude:
- tasks/*.rake

Layout/LineLength:
Max: 120
1 change: 0 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ language: ruby
cache: bundler
before_install: gem install bundler -v 2.1.4
rvm:
- 2.4.10
- 2.5.8
- 2.6.6
- 2.7.1
1 change: 0 additions & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ RuboCop::RakeTask.new
task default: %i[
spec
rubocop
generate_cops_documentation
]

desc 'Generate a new cop with a template'
Expand Down
8 changes: 7 additions & 1 deletion config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,10 @@
Packaging/GemspecGit:
Description: 'Use pure Ruby alternative instead of `git ls-files`.'
Enabled: true
VersionAdded: '0.86'
VersionAdded: '0.1'
VersionChanged: '0.1'

Packaging/RelativeRequireToLib:
Description: 'Avoid using `require_relative` with relative path to lib.'
Enabled: true
VersionAdded: '0.2'
1 change: 1 addition & 0 deletions docs/modules/ROOT/pages/cops.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
= Department xref:cops_packaging.adoc[Packaging]

* xref:cops_packaging.adoc#packaginggemspecgit[Packaging/GemspecGit]
* xref:cops_packaging.adoc#packagingrelativerequiretolib[Packaging/RelativeRequireToLib]

// END_COP_LIST
41 changes: 39 additions & 2 deletions docs/modules/ROOT/pages/cops_packaging.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
| Enabled
| Yes
| No
| 0.86
| -
| 0.1
| 0.1
|===

This cop is used to identify the usage of `git ls-files`
Expand Down Expand Up @@ -57,3 +57,40 @@ Gem::Specification.new do |spec|
spec.executables = Dir.glob('bin/*').map{ |f| File.basename(f) }
end
----

== Packaging/RelativeRequireToLib

|===
| Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged

| Enabled
| Yes
| No
| 0.2
| -
|===

This cop is used to identify the `require_relative` calls,
mapping to the "lib" directory and suggests to use `require`
instead.

=== Examples

[source,ruby]
----
# bad
require_relative 'lib/foo.rb'
# bad
require_relative '../../lib/foo/bar'
# good
require 'foo.rb'
# good
require 'foo/bar'
# good
require_relative 'spec_helper'
require_relative 'foo/bar'
----
67 changes: 67 additions & 0 deletions lib/rubocop/cop/packaging/relative_require_to_lib.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# frozen_string_literal: true

module RuboCop # :nodoc:
module Cop # :nodoc:
module Packaging # :nodoc:
# This cop is used to identify the `require_relative` calls,
# mapping to the "lib" directory and suggests to use `require`
# instead.
#
# @example
#
# # bad
# require_relative 'lib/foo.rb'
#
# # bad
# require_relative '../../lib/foo/bar'
#
# # good
# require 'foo.rb'
#
# # good
# require 'foo/bar'
#
# # good
# require_relative 'spec_helper'
# require_relative 'foo/bar'
#
class RelativeRequireToLib < Base
# This is the message that will be displayed when RuboCop finds an
# offense of using `require_relative` with relative path to lib.
MSG = 'Avoid using `require_relative` with relative path to lib. ' \
'Use `require` instead.'

def_node_matcher :require_relative, <<~PATTERN
(send nil? :require_relative
(str #target_falls_in_lib?))
PATTERN

# Extended from the Base class.
# More about the `#on_new_investigation` method can be found here:
# https://github.com/rubocop-hq/rubocop/blob/343f62e4555be0470326f47af219689e21c61a37/lib/rubocop/cop/base.rb
#
# Processing of the AST happens here.
def on_new_investigation
@file_path = processed_source.file_path
@file_directory = File.dirname(@file_path)
end

# Extended from AST::Traversal.
# More about the `#on_send` method can be found here:
# https://github.com/rubocop-hq/rubocop-ast/blob/08d0f49a47af1e9a30a6d8f67533ba793c843d67/lib/rubocop/ast/traversal.rb#L112
def on_send(node)
return unless require_relative(node)

add_offense(node)
end

# This method is called from inside `#def_node_matcher`.
# It is used to find paths which starts with "lib".
def target_falls_in_lib?(str)
root_dir = RuboCop::ConfigLoader.project_root
File.expand_path(str, @file_directory).start_with?(root_dir + '/lib')
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/rubocop/cop/packaging_cops.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# frozen_string_literal: true

require_relative 'packaging/gemspec_git'
require_relative 'packaging/relative_require_to_lib'
6 changes: 3 additions & 3 deletions rubocop-packaging.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ Gem::Specification.new do |spec|

spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')

spec.add_development_dependency 'bump', '>= 0.8.0'
spec.add_development_dependency 'pry', '>= 0.13.0'
spec.add_development_dependency 'bump', '~> 0.8'
spec.add_development_dependency 'pry', '~> 0.13'
spec.add_development_dependency 'rake', '~> 13.0'
spec.add_development_dependency 'rspec', '~> 3.0'
spec.add_development_dependency 'yard', '~> 0.9'
spec.add_runtime_dependency 'rubocop', '>= 0.75.0'
spec.add_runtime_dependency 'rubocop', '~> 0.88'
end
94 changes: 94 additions & 0 deletions spec/rubocop/cop/packaging/relative_require_to_lib_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Packaging::RelativeRequireToLib, :config do
let(:message) { RuboCop::Cop::Packaging::RelativeRequireToLib::MSG }

let(:project_root) { RuboCop::ConfigLoader.project_root }

context 'when `require_relative` call lies outside spec/' do
let(:filename) { "#{project_root}/spec/foo_spec.rb" }
let(:source) { 'require_relative "../lib/foo.rb"' }

it 'registers an offense' do
expect_offense(<<~RUBY, filename)
#{source}
#{'^' * source.length} #{message}
RUBY
end
end

context 'when `require_relative` call with nested path lies outside test/' do
let(:filename) { "#{project_root}/test/rubocop/cop/bar_spec.rb" }
let(:source) { 'require_relative "../../../lib/bar"' }

it 'registers an offense' do
expect_offense(<<~RUBY, filename)
#{source}
#{'^' * source.length} #{message}
RUBY
end
end

context 'when one `require_relative` call lies outside specs/' do
let(:filename) { "#{project_root}/specs/baz_spec.rb" }
let(:source) { <<~RUBY.chomp }
require_relative 'spec_helper'
require_relative '../lib/rubocop/baz'
RUBY

it 'registers an offense' do
expect_offense(<<~RUBY, filename)
#{source}
#{'^' * 37} #{message}
RUBY
end
end

context 'when `require_relative` call with `unshift` lies outside tests/' do
let(:filename) { "#{project_root}/tests/qux_spec.rb" }
let(:source) { <<~RUBY.chomp }
$:.unshift('../lib')
require_relative "../lib/qux"
RUBY

it 'registers an offense' do
expect_offense(<<~RUBY, filename)
#{source}
#{'^' * 29} #{message}
RUBY
end
end

context 'when the `require_relative` call to `lib` lies inside spec/' do
let(:filename) { "#{project_root}/spec/rubocop/cop/foo_spec.rb" }
let(:source) { 'require_relative "../lib/foo"' }

it 'does not register an offense' do
expect_no_offenses(<<~RUBY, filename)
#{source}
RUBY
end
end

context 'when the `require_relative` call lies inside tests/' do
let(:filename) { "#{project_root}/tests/rubocop/cop/bar_spec.rb" }
let(:source) { 'require_relative "../bar"' }

it 'does not register an offense' do
expect_no_offenses(<<~RUBY, filename)
#{source}
RUBY
end
end

context 'when the `require_relative` call lies inside test/' do
let(:filename) { "#{project_root}/test/qux_spec.rb" }
let(:source) { 'require_relative "spec/rubocop/qux.rb"' }

it 'does not register an offense' do
expect_no_offenses(<<~RUBY, filename)
#{source}
RUBY
end
end
end

0 comments on commit afa055d

Please sign in to comment.