diff options
-rw-r--r-- | rubocop/cop/project_path_helper.rb | 51 | ||||
-rw-r--r-- | rubocop/rubocop.rb | 1 | ||||
-rw-r--r-- | spec/rubocop/cop/project_path_helper_spec.rb | 41 |
3 files changed, 93 insertions, 0 deletions
diff --git a/rubocop/cop/project_path_helper.rb b/rubocop/cop/project_path_helper.rb new file mode 100644 index 00000000000..3e1ce71ac06 --- /dev/null +++ b/rubocop/cop/project_path_helper.rb @@ -0,0 +1,51 @@ +module RuboCop + module Cop + class ProjectPathHelper < RuboCop::Cop::Cop + MSG = 'Use short project path helpers without explicitly passing the namespace: ' \ + '`foo_project_bar_path(project, bar)` instead of ' \ + '`foo_namespace_project_bar_path(project.namespace, project, bar)`.'.freeze + + METHOD_NAME_PATTERN = /\A([a-z_]+_)?namespace_project(?:_[a-z_]+)?_(?:url|path)\z/.freeze + + def on_send(node) + return unless method_name(node).to_s =~ METHOD_NAME_PATTERN + + namespace_expr, project_expr = arguments(node) + return unless namespace_expr && project_expr + + return unless namespace_expr.type == :send + return unless method_name(namespace_expr) == :namespace + return unless receiver(namespace_expr) == project_expr + + add_offense(node, :selector) + end + + def autocorrect(node) + helper_name = method_name(node).to_s.sub('namespace_project', 'project') + + arguments = arguments(node) + arguments.shift # Remove namespace argument + + replacement = "#{helper_name}(#{arguments.map(&:source).join(', ')})" + + lambda do |corrector| + corrector.replace(node.source_range, replacement) + end + end + + private + + def receiver(node) + node.children[0] + end + + def method_name(node) + node.children[1] + end + + def arguments(node) + node.children[2..-1] + end + end + end +end diff --git a/rubocop/rubocop.rb b/rubocop/rubocop.rb index 55d7708fa8c..69b4b29507c 100644 --- a/rubocop/rubocop.rb +++ b/rubocop/rubocop.rb @@ -3,6 +3,7 @@ require_relative 'cop/gem_fetcher' require_relative 'cop/activerecord_serialize' require_relative 'cop/redirect_with_status' require_relative 'cop/polymorphic_associations' +require_relative 'cop/project_path_helper' require_relative 'cop/migration/add_column' require_relative 'cop/migration/add_column_with_default_to_large_table' require_relative 'cop/migration/add_concurrent_foreign_key' diff --git a/spec/rubocop/cop/project_path_helper_spec.rb b/spec/rubocop/cop/project_path_helper_spec.rb new file mode 100644 index 00000000000..bc47b45cad7 --- /dev/null +++ b/spec/rubocop/cop/project_path_helper_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +require 'rubocop' +require 'rubocop/rspec/support' + +require_relative '../../../rubocop/cop/project_path_helper' + +describe RuboCop::Cop::ProjectPathHelper do + include CopHelper + + subject(:cop) { described_class.new } + + context "when using namespace_project with the project's namespace" do + let(:source) { 'edit_namespace_project_issue_path(@issue.project.namespace, @issue.project, @issue)' } + let(:correct_source) { 'edit_project_issue_path(@issue.project, @issue)' } + + it 'registers an offense' do + inspect_source(cop, source) + + aggregate_failures do + expect(cop.offenses.size).to eq(1) + expect(cop.offenses.map(&:line)).to eq([1]) + expect(cop.highlights).to eq(['edit_namespace_project_issue_path']) + end + end + + it 'autocorrects to the right version' do + autocorrected = autocorrect_source(cop, source) + + expect(autocorrected).to eq(correct_source) + end + end + + context 'when using namespace_project with a different namespace' do + it 'registers no offense' do + inspect_source(cop, 'edit_namespace_project_issue_path(namespace, project)') + + expect(cop.offenses.size).to eq(0) + end + end +end |