Skip to content

Commit f0188e5

Browse files
committed
Let RSpec/SpecFilePathFormat leverage ActiveSupport inflections when defined and configured
Fix #740
1 parent b0fda47 commit f0188e5

File tree

5 files changed

+364
-4
lines changed

5 files changed

+364
-4
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
## Master (Unreleased)
44

5+
- Let `RSpec/SpecFilePathFormat` leverage ActiveSupport inflections when configured. ([@corsonknowles])
6+
57
## 3.6.0 (2025-04-18)
68

79
- Fix false positive in `RSpec/Pending`, where it would mark the default block `it` as an offense. ([@bquorning])

config/default.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -924,6 +924,8 @@ RSpec/SpecFilePathFormat:
924924
IgnoreMethods: false
925925
IgnoreMetadata:
926926
type: routing
927+
InflectorPath: "./config/initializers/inflections.rb"
928+
UseActiveSupportInflections: false
927929
VersionAdded: '2.24'
928930
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SpecFilePathFormat
929931

docs/modules/ROOT/pages/cops_rspec.adoc

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5897,6 +5897,17 @@ my_class_spec.rb # describe MyClass, '#method'
58975897
whatever_spec.rb # describe MyClass, type: :routing do; end
58985898
----
58995899
5900+
[#_useactivesupportinflections_-true_-rspecspecfilepathformat]
5901+
==== `UseActiveSupportInflections: true`
5902+
5903+
[source,ruby]
5904+
----
5905+
# Enable to use ActiveSupport's inflector for custom acronyms
5906+
# like HTTP, etc. Set to false by default.
5907+
# The InflectorPath provides the path to the inflector file.
5908+
# The default is ./config/initializers/inflections.rb.
5909+
----
5910+
59005911
[#configurable-attributes-rspecspecfilepathformat]
59015912
=== Configurable attributes
59025913
@@ -5922,6 +5933,14 @@ whatever_spec.rb # describe MyClass, type: :routing do; end
59225933
| IgnoreMetadata
59235934
| `{"type" => "routing"}`
59245935
|
5936+
5937+
| InflectorPath
5938+
| `./config/initializers/inflections.rb`
5939+
| String
5940+
5941+
| UseActiveSupportInflections
5942+
| `false`
5943+
| Boolean
59255944
|===
59265945
59275946
[#references-rspecspecfilepathformat]

lib/rubocop/cop/rspec/spec_file_path_format.rb

Lines changed: 66 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ module RSpec
3232
# # good
3333
# whatever_spec.rb # describe MyClass, type: :routing do; end
3434
#
35+
# @example `UseActiveSupportInflections: true`
36+
# # Enable to use ActiveSupport's inflector for custom acronyms
37+
# # like HTTP, etc. Set to false by default.
38+
# # The InflectorPath provides the path to the inflector file.
39+
# # The default is ./config/initializers/inflections.rb.
40+
#
3541
class SpecFilePathFormat < Base
3642
include TopLevelGroup
3743
include Namespace
@@ -57,8 +63,67 @@ def on_top_level_example_group(node)
5763
end
5864
end
5965

66+
# For testing and debugging
67+
def self.reset_activesupport_cache!
68+
ActiveSupportInflector.reset_cache!
69+
end
70+
6071
private
6172

73+
# Inflector module that uses ActiveSupport for advanced inflection rules
74+
module ActiveSupportInflector
75+
def self.call(string)
76+
ActiveSupport::Inflector.underscore(string)
77+
end
78+
79+
def self.available?(cop_config)
80+
return @available unless @available.nil?
81+
82+
unless cop_config.fetch('UseActiveSupportInflections', false)
83+
return @available = false
84+
end
85+
86+
unless File.exist?(inflector_path(cop_config))
87+
return @available = false
88+
end
89+
90+
@available = begin
91+
require 'active_support/inflector'
92+
require inflector_path(cop_config)
93+
true
94+
rescue LoadError, StandardError
95+
false
96+
end
97+
end
98+
99+
def self.inflector_path(cop_config)
100+
cop_config.fetch('InflectorPath',
101+
'./config/initializers/inflections.rb')
102+
end
103+
104+
def self.reset_cache!
105+
@available = nil
106+
end
107+
end
108+
109+
# Inflector module that uses basic regex-based conversion
110+
module DefaultInflector
111+
def self.call(string)
112+
string
113+
.gsub(/([^A-Z])([A-Z]+)/, '\1_\2')
114+
.gsub(/([A-Z])([A-Z][^A-Z\d]+)/, '\1_\2')
115+
.downcase
116+
end
117+
end
118+
119+
def inflector
120+
@inflector ||= if ActiveSupportInflector.available?(cop_config)
121+
ActiveSupportInflector
122+
else
123+
DefaultInflector
124+
end
125+
end
126+
62127
def ensure_correct_file_path(send_node, class_name, arguments)
63128
pattern = correct_path_pattern(class_name, arguments)
64129
return if filename_ends_with?(pattern)
@@ -106,10 +171,7 @@ def expected_path(constant)
106171
end
107172

108173
def camel_to_snake_case(string)
109-
string
110-
.gsub(/([^A-Z])([A-Z]+)/, '\1_\2')
111-
.gsub(/([A-Z])([A-Z][^A-Z\d]+)/, '\1_\2')
112-
.downcase
174+
inflector.call(string)
113175
end
114176

115177
def custom_transform

0 commit comments

Comments
 (0)