-
-
Notifications
You must be signed in to change notification settings - Fork 278
Let RSpec/SpecFilePathFormat
leverage ActiveSupport inflections when configured
#2090
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -926,6 +926,8 @@ RSpec/SpecFilePathFormat: | |
IgnoreMethods: false | ||
IgnoreMetadata: | ||
type: routing | ||
InflectorPath: "./config/initializers/inflections.rb" | ||
UseActiveSupportInflections: false | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not saying that we should support more inflectors, but just in case: Should we rename this to e.g. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had tried to explain this choice in a PR comment:
Happy to change it if you want! But to reframe the above, I was on the "premature" side as I was implementing this. I thought it would be more user-friendly to use a boolean than have folks look up two "magic strings" to use this. They need both the key and the value if we abstract it, whereas they just need the key if it's a boolean. Just let me know if you want it though. |
||
VersionAdded: '2.24' | ||
Reference: https://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/SpecFilePathFormat | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,6 +32,12 @@ module RSpec | |
# # good | ||
# whatever_spec.rb # describe MyClass, type: :routing do; end | ||
# | ||
# @example `UseActiveSupportInflections: true` | ||
# # Enable to use ActiveSupport's inflector for custom acronyms | ||
# # like HTTP, etc. Set to false by default. | ||
# # The InflectorPath provides the path to the inflector file. | ||
# # The default is ./config/initializers/inflections.rb. | ||
# | ||
class SpecFilePathFormat < Base | ||
include TopLevelGroup | ||
include Namespace | ||
|
@@ -57,8 +63,67 @@ def on_top_level_example_group(node) | |
end | ||
end | ||
|
||
# For testing and debugging | ||
def self.reset_activesupport_cache! | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not crazy about this method. It feels like maybe we are memoizing on the wrong level somehow. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I kind of thought we should hoist this (the logic is in the module still), so we can use it more transparently. But open to alternatives... |
||
ActiveSupportInflector.reset_cache! | ||
end | ||
|
||
private | ||
|
||
# Inflector module that uses ActiveSupport for advanced inflection rules | ||
module ActiveSupportInflector | ||
def self.call(string) | ||
ActiveSupport::Inflector.underscore(string) | ||
end | ||
|
||
def self.available?(cop_config) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not sure if this method should memoize without considering There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I need the config, but I will see what I can do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean even if I separate the availability from the config check, I still need the config to look up the So config is somewhat coupled to availability There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Best I can think of would be something like this, but that seems worse to me: def active_support_available?
return false unless cop_config.fetch(
'UseActiveSupportInflections',
false
)
ActiveSupportInflector.available?(cop_config)
end
def inflector
@inflector ||= if active_support_available?
ActiveSupportInflector
else
DefaultInflector
end
end Let me know whart you're aiming for here There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thinking this though once more, I happened to ask myself this question: Do we expect people to give different values for So, should There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would guess the thinking here is that someone using ActiveSupport but not Rails might have a customized directory structure. And monkey patchers could still use this.
Maybe something else with nested Rails apps or nested Rails Engines? I do think this is mildly superfluous, but it was one of the comment requests on the original PR, and now that we have it, I'm hesitant to rip it out again since in theory we're making life easier for +1% of users. IMO, the heavyweight thing is we have to require 2 files and fail gracefully. Path configurability is minor compared that that. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you think its worth the sacrifice, we could take the path out of config and then make it a constant on the cop class. I'm imagining somewhere someone will eventually have to type |
||
return @available unless @available.nil? | ||
|
||
unless cop_config.fetch('UseActiveSupportInflections', false) | ||
return @available = false | ||
end | ||
|
||
unless File.exist?(inflector_path(cop_config)) | ||
return @available = false | ||
end | ||
|
||
@available = begin | ||
require 'active_support/inflector' | ||
require inflector_path(cop_config) | ||
true | ||
rescue LoadError, StandardError | ||
false | ||
end | ||
end | ||
|
||
def self.inflector_path(cop_config) | ||
cop_config.fetch('InflectorPath', | ||
'./config/initializers/inflections.rb') | ||
end | ||
|
||
def self.reset_cache! | ||
@available = nil | ||
end | ||
end | ||
|
||
# Inflector module that uses basic regex-based conversion | ||
module DefaultInflector | ||
def self.call(string) | ||
string | ||
.gsub(/([^A-Z])([A-Z]+)/, '\1_\2') | ||
.gsub(/([A-Z])([A-Z][^A-Z\d]+)/, '\1_\2') | ||
.downcase | ||
end | ||
end | ||
|
||
def inflector | ||
@inflector ||= if ActiveSupportInflector.available?(cop_config) | ||
ActiveSupportInflector | ||
else | ||
DefaultInflector | ||
end | ||
end | ||
|
||
def ensure_correct_file_path(send_node, class_name, arguments) | ||
pattern = correct_path_pattern(class_name, arguments) | ||
return if filename_ends_with?(pattern) | ||
|
@@ -106,10 +171,7 @@ def expected_path(constant) | |
end | ||
|
||
def camel_to_snake_case(string) | ||
string | ||
.gsub(/([^A-Z])([A-Z]+)/, '\1_\2') | ||
.gsub(/([A-Z])([A-Z][^A-Z\d]+)/, '\1_\2') | ||
.downcase | ||
inflector.call(string) | ||
end | ||
|
||
def custom_transform | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
most should never need to set this, but just in case