Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Support Bundler-like namespaced feature on require config
- Loading branch information
Showing
6 changed files
with
191 additions
and
5 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 @@ | ||
* [#10845](https://github.com/rubocop/rubocop/pull/10845): Support Bundler-like namespaced feature on require config. ([@r7kamura][]) |
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
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,88 @@ | ||
# frozen_string_literal: true | ||
|
||
module RuboCop | ||
# This class handles loading files (a.k.a. features in Ruby) specified | ||
# by `--require` command line option and `require` directive in the config. | ||
# | ||
# Normally, the given string is directly passed to `require`. If a string | ||
# beginning with `.` is given, it is assumed to be relative to the given | ||
# directory. | ||
# | ||
# If a string containing `-` is given, it will be used as is, but if we | ||
# cannot find the file to load, we will replace `-` with `/` and try it | ||
# again as when Bundler loads gems. | ||
# | ||
# @api private | ||
class FeatureLoader | ||
class << self | ||
# @param [String] config_directory_path | ||
# @param [String] feature | ||
def load(config_directory_path:, feature:) | ||
new(config_directory_path: config_directory_path, feature: feature).load | ||
end | ||
end | ||
|
||
# @param [String] config_directory_path | ||
# @param [String] feature | ||
def initialize(config_directory_path:, feature:) | ||
@config_directory_path = config_directory_path | ||
@feature = feature | ||
end | ||
|
||
def load | ||
::Kernel.require(target) | ||
rescue ::LoadError => e | ||
raise if e.path != target | ||
|
||
begin | ||
::Kernel.require(namespaced_target) | ||
rescue ::LoadError => error_for_namespaced_target | ||
raise e if error_for_namespaced_target.path == namespaced_target | ||
|
||
raise error_for_namespaced_target | ||
end | ||
end | ||
|
||
private | ||
|
||
# @return [String] | ||
def namespaced_feature | ||
@feature.tr('-', '/') | ||
end | ||
|
||
# @return [String] | ||
def namespaced_target | ||
if relative? | ||
relative(namespaced_feature) | ||
else | ||
namespaced_feature | ||
end | ||
end | ||
|
||
# @param [String] | ||
# @return [String] | ||
def relative(feature) | ||
::File.join(@config_directory_path, feature) | ||
end | ||
|
||
# @return [Boolean] | ||
def relative? | ||
@feature.start_with?('.') | ||
end | ||
|
||
# @param [LoadError] error | ||
# @return [Boolean] | ||
def seems_cannot_load_such_file_error?(error) | ||
error.path == target | ||
end | ||
|
||
# @return [String] | ||
def target | ||
if relative? | ||
relative(@feature) | ||
else | ||
@feature | ||
end | ||
end | ||
end | ||
end |
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,97 @@ | ||
# frozen_string_literal: true | ||
|
||
RSpec.describe RuboCop::FeatureLoader do | ||
describe '.load' do | ||
subject(:load) do | ||
described_class.load(config_directory_path: config_directory_path, feature: feature) | ||
end | ||
|
||
let(:config_directory_path) do | ||
'path-to-config' | ||
end | ||
|
||
let(:feature) do | ||
'feature' | ||
end | ||
|
||
context 'with normally lodable feature' do | ||
before do | ||
allow(Kernel).to receive(:require) | ||
end | ||
|
||
it 'loads it normally' do | ||
expect(Kernel).to receive(:require).with('feature') | ||
load | ||
end | ||
end | ||
|
||
context 'with dot-prefixed lodable feature' do | ||
before do | ||
allow(Kernel).to receive(:require) | ||
end | ||
|
||
let(:feature) do | ||
'./path/to/feature' | ||
end | ||
|
||
it 'loads it as relative path' do | ||
expect(Kernel).to receive(:require).with('path-to-config/./path/to/feature') | ||
load | ||
end | ||
end | ||
|
||
context 'with namespaced feature' do | ||
before do | ||
allow(Kernel).to receive(:require).with('feature-foo').and_call_original | ||
allow(Kernel).to receive(:require).with('feature/foo') | ||
end | ||
|
||
let(:feature) do | ||
'feature-foo' | ||
end | ||
|
||
it 'loads it as namespaced feature' do | ||
expect(Kernel).to receive(:require).with('feature/foo') | ||
load | ||
end | ||
end | ||
|
||
context 'with dot-prefixed namespaced feature' do | ||
before do | ||
allow(Kernel).to receive(:require).with('path-to-config/./feature-foo').and_call_original | ||
allow(Kernel).to receive(:require).with('path-to-config/./feature/foo') | ||
end | ||
|
||
let(:feature) do | ||
'./feature-foo' | ||
end | ||
|
||
it 'loads it as namespaced feature' do | ||
expect(Kernel).to receive(:require).with('path-to-config/./feature/foo') | ||
load | ||
end | ||
end | ||
|
||
context 'with unexpected LoadError from require' do | ||
before do | ||
allow(Kernel).to receive(:require).and_raise(LoadError) | ||
end | ||
|
||
it 'raises LoadError' do | ||
expect { load }.to raise_error(LoadError) | ||
end | ||
end | ||
|
||
context 'with unloadable namespaced feature' do | ||
let(:feature) do | ||
'feature-foo' | ||
end | ||
|
||
# In normal Ruby, the message starts with "cannot load such file", | ||
# but in JRuby it seems to start with "no such file to load". | ||
it 'raises LoadError with preferred message' do | ||
expect { load }.to raise_error(LoadError, /feature-foo/) | ||
end | ||
end | ||
end | ||
end |