Skip to content
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

Make RSpec/FilePath support ActiveSupport inflections, if defined #1266

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

jeromedalbert
Copy link

@jeromedalbert jeromedalbert commented Apr 23, 2022

Fixes #740. Implementation is based on the suggestion in this comment: #740 (comment).

This is especially useful for Rails applications (or any application using ActiveSupport inflections) that define mixed-case custom acronyms, for example:

# config/initializers/inflections.rb
ActiveSupport::Inflector.inflections do |inflect|
  inflect.acronym('PvP')
end

# app/models/pvp_class.rb instead of app/models/pv_p_class.rb, thanks to the custom acronym
class PvPClass
  ...
end

It is also great to be re-using ActiveSupport's inflection configuration instead of having to repeat it in Rubocop as mentioned in #740.

Other common custom mixed-case acronyms could be StatsD, OAuth, RESTful, etc.


Before submitting the PR make sure the following are checked:

  • Feature branch is up-to-date with master (if not - rebase it).
  • Squashed related commits together.
  • Added tests.
  • Updated documentation.
  • Added an entry to the CHANGELOG.md if the new code introduces user-observable changes.
  • The build (bundle exec rake) passes (be sure to run this locally, since it may produce updated documentation that you will need to commit).

If you have created a new cop:

  • Added the new cop to config/default.yml.
  • The cop is configured as Enabled: pending in config/default.yml.
  • The cop documents examples of good and bad code.
  • The tests assert both that bad code is reported and that good code is not reported.
  • Set VersionAdded in default/config.yml to the next minor version.

If you have modified an existing cop's configuration options:

  • Set VersionChanged in config/default.yml to the next major version.

@jeromedalbert jeromedalbert marked this pull request as draft April 23, 2022 20:52
@jeromedalbert jeromedalbert changed the title Compute RSpec/FilePath file paths with ActiveSupport Inflector, if defined Compute FilePath cop paths with ActiveSupport Inflector, if defined Apr 23, 2022
Comment on lines +134 to +136
if File.exist?('./config/initializers/inflections.rb')
require './config/initializers/inflections'
end
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inflection configuration must be loaded before ActiveSupport::Inflector.underscore is executed, to take acronyms and other inflection rules into account.

That configuration is typically found in the config/initializers/inflections.rb file that is generated when creating a Rails app.

@pirj
Copy link
Member

pirj commented Apr 24, 2022

Nice!
Do you mind throwing in a spec that would indicate how it behaves in action?

@jeromedalbert
Copy link
Author

@pirj Done

@jeromedalbert jeromedalbert force-pushed the activesupport-inflector branch 2 times, most recently from a6210f7 to 9bb2204 Compare April 26, 2022 16:18
@jeromedalbert jeromedalbert changed the title Compute FilePath cop paths with ActiveSupport Inflector, if defined Add ActiveSupport inflections support for FilePath cop Apr 26, 2022
@jeromedalbert jeromedalbert changed the title Add ActiveSupport inflections support for FilePath cop Make RSpec/FilePath support ActiveSupport inflections, if defined Apr 26, 2022
@jeromedalbert jeromedalbert marked this pull request as ready for review April 26, 2022 19:23
Copy link
Member

@pirj pirj left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@pirj pirj force-pushed the activesupport-inflector branch from 57d1e78 to 23cf2f3 Compare May 1, 2022 23:47
@pirj
Copy link
Member

pirj commented May 2, 2022

The failing example is flaky, due to order dependency. If the example that define the "PvP" acronym runs first, that the other one fails.
I suggest adding metadata to the example to work this around:

context 'when ActiveSupport Inflector is defined', order: :defined do

@pirj pirj requested review from bquorning and Darhazer May 2, 2022 00:01
@pirj pirj force-pushed the activesupport-inflector branch from 23cf2f3 to a60261d Compare May 2, 2022 00:02
Copy link
Collaborator

@bquorning bquorning left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am a bit on the fence about adding Rails specific logic into RuboCop-RSpec, so not giving a 👍🏼 right away. I’ll think about it some more.

Comment on lines +133 to +136
if defined?(ActiveSupport::Inflector)
if File.exist?('./config/initializers/inflections.rb')
require './config/initializers/inflections'
end
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I’m afraid this code is a bit slow. Could we do the defined?/File.exists?/require thing just once and store the result perhaps a class variable?

Comment on lines +134 to +135
if File.exist?('./config/initializers/inflections.rb')
require './config/initializers/inflections'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like this file path should be configurable.

Comment on lines +260 to +262
ActiveSupport::Inflector.inflections do |inflect|
inflect.acronym('PvP')
end
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we couldn’t do

ensure
  ActiveSupport::Inflector.inflections.clear(:acronyms)

instead of having the order :defined on the context block.

@jeromedalbert
Copy link
Author

jeromedalbert commented Jun 3, 2022

@bquorning

I’ll think about it some more.

Any update on your thoughts?

My guess is that Rails projects that use inflections are either probably just disabling this cop (my project does), so this PR is an attempt to fix that, and make the cop useful again.

I have seen many gems whose goal is to work without Rails, but still provide a Rails::Railtie file that gets executed if Rails is defined, as an optional enhancement so to speak. In that sense and as long as the Rails specific logic is more or less contained, one could argue that this PR is doing the same, just providing some optional enhancement while keeping the core of the gem intact (indeed, no runtime dependency to ActiveSupport is added in this PR, only a development dependency so my test case can use it).

@bquorning
Copy link
Collaborator

bquorning commented Jun 3, 2022

Any update on your thoughts?

Apologies for the silence on my end.

I do understand the use case, and it would be a neat addition for Rails projects, not having to configure rubocop-rspec with static map of inflections that the Rails application already knows how to generate. My problem is with putting Rails-specific logic into this gem. One might argue that we should then also put logic for other frameworks that might have similar inflection logic – an approach that wouldn’t scale.

It would be neat if we could configure this cop with an “inflector block”, i.e. ->(string) { ActiveSupport::Inflector.underscore(string) }, but I am not sure we can do that via YAML. @pirj what do you think?

@pirj
Copy link
Member

pirj commented Jun 3, 2022

@bquorning I was a secret admirer/dreamer of a plain Ruby RuboCop configuration, especially after our DSL configuration extension and some later problems with merging arrays in YAML.

Did you have something like:

RuboCop::Cop::RSpec::FilePath.add_custom_transform  { |string| ActiveSupport::Inflector.underscore(string) }

in mind? I'm all for it. 😈

cc @Darhazer

@@ -130,6 +130,13 @@ def expected_path(constant)
end

def camel_to_snake_case(string)
if defined?(ActiveSupport::Inflector)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are other libraries that can be used to set inflectors:

I would suggest providing a script field in the YAML files that could be run such that String#underscore could be defined if desired.

Rspec/FilePath:
  LoadInfector: |-
    require 'active_support/inflector'
    require './config/initializers/inflections'

Alternatively, we could add a field for the Inflector gem and/or definition file:

Rspec/FilePath:
  UseInflectorGem: ActiveSupport
  InflectorDefinition: ./config/initializers/inflections.rb

Doing either of these should then allow if string.respond_to?(:underscore) to be used instead of hard-coding a library and a file location.

Copy link

@cdhagmann cdhagmann Jun 29, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another alternative may simply be to make sure the file name matches the locations in which the class is defined, regardless of inflection.

Example:

app/services/my_html_module/xml_reader.rb
module MyHTMLModule
  class XMLReader
spec/services/my_html_module/xml_reader_spec.rb
RSpec.describe MyHTMLModule::XMLReader do
end

This could sidestep the need for inflection.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Proposal: Avoid repetitive definitions of CustomTransform in RSpec/FilePath cop
4 participants