Skip to content

Commit

Permalink
[Fix #1545] Add Regex config parameter to Style/FileName
Browse files Browse the repository at this point in the history
  • Loading branch information
alexdowad committed Dec 30, 2015
1 parent 1274b8c commit 308f206
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
* `Style/SymbolArray` has both `percent` and `brackets` (which enforces the user of bracketed arrays for symbols) styles. ([@alexdowad][])
* [#2343](https://github.com/bbatsov/rubocop/issues/2343): Entire cop types (or "departments") can be disabled using in .rubocop.yml using config like `Style: Enabled: false`. ([@alexdowad][])
* [#2399](https://github.com/bbatsov/rubocop/issues/2399): New `start_of_line` style for `Lint/EndAlignment` aligns a closing `end` keyword with the start of the line where the opening keyword appears. ([@alexdowad][])
* [#1545](https://github.com/bbatsov/rubocop/issues/1545): New `Regex` config parameter for `Style/FileName` allows user to provide their own regex for validating file names. ([@alexdowad][])

### Bug Fixes

Expand Down
6 changes: 5 additions & 1 deletion config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,11 @@ Style/FileName:
# It further expects it to be nested inside modules which match the names
# of subdirectories in its path.
ExpectMatchingDefinition: false

# If non-nil, expect all source file names to match the following regex.
# Only the file name itself is matched, not the entire file path.
# Use anchors as necessary if you want to match the entire name rather than
# just a part of it.
Regex: ~
# With `IgnoreExecutableScripts` set to `true`, this cop does not
# report offending filenames for executable scripts (i.e. source
# files with a shebang in the first line).
Expand Down
21 changes: 16 additions & 5 deletions lib/rubocop/cop/style/file_name.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,31 +11,33 @@ module Style
class FileName < Cop
MSG_SNAKE_CASE = 'Use snake_case for source file names.'
MSG_NO_DEFINITION = '%s should define a class or module called `%s`.'
MSG_REGEX = '`%s` should match `%s`.'

SNAKE_CASE = /^[\da-z_]+$/

def investigate(processed_source)
file_path = processed_source.buffer.name
return if config.file_to_include?(file_path)

basename = File.basename(file_path).sub(/\.[^\.]+$/, '')
if snake_case?(basename)
basename = File.basename(file_path)
if filename_good?(basename)
return unless expect_matching_definition?
return if find_class_or_module(processed_source.ast,
to_namespace(file_path))
range = source_range(processed_source.buffer, 1, 0)
msg = format(MSG_NO_DEFINITION,
File.basename(file_path),
basename,
to_namespace(file_path).join('::'))
add_offense(nil, range, msg)
else
first_line = processed_source.lines.first
return if cop_config['IgnoreExecutableScripts'] &&
shebang?(first_line)

range = source_range(processed_source.buffer, 1, 0)
add_offense(nil, range, MSG_SNAKE_CASE)
msg = regex ? format(MSG_REGEX, basename, regex) : MSG_SNAKE_CASE
end

add_offense(nil, range, msg)
end

private
Expand All @@ -52,6 +54,15 @@ def expect_matching_definition?
cop_config['ExpectMatchingDefinition']
end

def regex
cop_config['Regex']
end

def filename_good?(basename)
basename = basename.sub(/\.[^\.]+$/, '')
regex ? basename =~ regex : snake_case?(basename)
end

def find_class_or_module(node, namespace)
name = namespace.pop

Expand Down
28 changes: 27 additions & 1 deletion spec/rubocop/cop/style/file_name_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@
)
end
let(:cop_config) do
{ 'IgnoreExecutableScripts' => true, 'ExpectMatchingDefinition' => false }
{
'IgnoreExecutableScripts' => true,
'ExpectMatchingDefinition' => false,
'Regex' => nil
}
end

let(:includes) { [] }
Expand Down Expand Up @@ -220,4 +224,26 @@
include_examples 'matching module or class'
end
end

context 'when Regex is set' do
let(:cop_config) { { 'Regex' => /\A[aeiou]\z/i } }

context 'with a matching name' do
let(:filename) { 'a.rb' }

it 'does not register an offense' do
expect(cop.offenses).to be_empty
end
end

context 'with a non-matching name' do
let(:filename) { 'z.rb' }

it 'registers an offense' do
expect(cop.offenses.size).to eq(1)
expect(cop.messages).to eq(
['`z.rb` should match `(?i-mx:\\A[aeiou]\\z)`.'])
end
end
end
end

0 comments on commit 308f206

Please sign in to comment.