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

Add new Style/ClassMethodsDefinitions cop #8381

Merged
merged 1 commit into from Aug 22, 2020

Conversation

fatkodima
Copy link
Contributor

This cop enforces how class methods are defined:

# Either
def self.class_method
  # ...
end

# or
class << self
  def class_method
    # ...
  end
end

I have checked several existing style guides, and found that:

  1. shopify style guide

Use a class << self block over def self. when defining class methods, and group them together within a single block.

  1. airbnb style guide

Avoid class << self except when necessary, e.g. single accessors and aliased attributes.

  1. thoughtbot style guide

Use def self.method, not class << self.

So seems like people prefer to use either one of them. Personally I prefer def self.method style and noticed that rubocops codebase is also mostly based on this style. I disabled it for now, will enable if this is a useful for rubocops own codebase.

While implementing this cop (especially autocorrection), I noticed that it would be great to have the following cops: one that checks for empty class << self and another that checks for multiple class << self definitions and combines them. The second one will greatly simplify (not yet implemented) autocorrection for self_class style. Will implement both of them if sounds good.

@bbatsov
Copy link
Collaborator

bbatsov commented Jul 23, 2020

There's also a third option - using the class name, but we might have a cop about this already, as it's definitely bad style.

So seems like people prefer to use either one of them. Personally I prefer def self.method style and noticed that rubocops codebase is also mostly based on this style. I disabled it for now, will enable if this is a useful for rubocops own codebase.

Same here. It produces the easiest to read code. I'm surprised we didn't add anything to our style guide on the topic.

While implementing this cop (especially autocorrection), I noticed that it would be great to have the following cops: one that checks for empty class << self and another that checks for multiple class << self definitions and combines them. The second one will greatly simplify (not yet implemented) autocorrection for self_class style. Will implement both of them if sounds good.

Great ideas!

The first one should probably cover empty class/module definitions in general.

.rubocop.yml Outdated Show resolved Hide resolved
@marcandre
Copy link
Contributor

marcandre commented Jul 23, 2020

FWIW, I might write def self.foo if I feel lazy and think there's only going to be one, but I much prefer class << self otherwise. Reasons include:

  • I'm always grepping for def foo, and when it fails I have to think about def self.foo
  • I like that include / attr_reader, and al. are available to me.
  • easier refactoring into a module that I extend from.

Not directly related, but I love extend self for modules of pure functions and curse when authors prefer def self.foo in a module, which makes these methods unavailable other than through Module.foo

@bbatsov
Copy link
Collaborator

bbatsov commented Aug 5, 2020

@fatkodima ping :-)

@fatkodima
Copy link
Contributor Author

Working on this. Will update shortly.

@fatkodima fatkodima force-pushed the class_methods_definitions-cop branch from b7ad526 to 3f2c2d3 Compare August 5, 2020 13:13
@fatkodima
Copy link
Contributor Author

Enabled this cop on rubocop's codebase.

@bbatsov
Copy link
Collaborator

bbatsov commented Aug 5, 2020

Looking at the diff it seems we shouldn't report offenses when there's a mixture of method definitions and something else in class <<. Or maybe there should be some config flag for this. I'm on the fence about this.

@fatkodima
Copy link
Contributor Author

For def_self style, I would stick to this - https://github.com/airbnb/ruby#no-class-self and use class << self only for alias, include/extend and attr_* things. When there are private methods - do not report offenses for other methods within class << self and when only those mentioned things - report to rewrite methods with def self..

Report:

class << self
  def do_something
  end

  attr_reader :foo 
end

Do not report:

class << self
  def do_something
  end

  attr_reader :foo 

  private

  def do_something_else
  end
end

@marcandre
Copy link
Contributor

Report:

class << self
  def do_something
  end

  attr_reader :foo 
end

Doesn't this contradict the rest of what you wrote?

@fatkodima
Copy link
Contributor Author

Mmm, haven't seen any? I'm proposing a change that, when EnforcedStyle: def_self, then

class << self
  def do_something
  end

  attr_reader :foo 
end

should be rewritten as

def self.do_something
end

class << self
  attr_reader :foo 
end

And in the following case

class << self
  def do_something
  end

  attr_reader :foo 

  private

  def do_something_else
  end
end

no offense should be triggered.

Am I missing something?

@marcandre
Copy link
Contributor

Am I missing something?

No, my bad, sorry for the noise 🤦‍♂️

@koic
Copy link
Member

koic commented Aug 5, 2020

IMHO, I'm worried about enforcing to only either style. This is because it is doubt that decomposing the intentionally grouped class << self will get up to maintainability. If either one is the default, I think it is preferable to discuss it first in the style guide.
I think pending status is a candidate that can be enabled by default, next step. So, I think it's better to disable it by default at this point.

@marcandre
Copy link
Contributor

Is there already an issue about this in the style-guide? I can't seem to find it.

@bbatsov
Copy link
Collaborator

bbatsov commented Aug 5, 2020

Don't think so. Feel free to create one.

IMHO, I'm worried about enforcing to only either style. This is because it is doubt that decomposing the intentionally grouped class << self will get up to maintainability. If either one is the default, I think it is preferable to discuss it first in the style guide.
I think pending status is a candidate that can be enabled by default, next step. So, I think it's better to disable it by default at this point.

@koic I had suggested enabling this for RuboCop itself, not for the end users. But I agree we probably need to discuss it a bit more, as I didn't consider the cases where there something besides methods in the singleton class.

@fatkodima fatkodima force-pushed the class_methods_definitions-cop branch 2 times, most recently from 05488b9 to c6712b7 Compare August 6, 2020 09:03
@fatkodima
Copy link
Contributor Author

Updated:

  1. made cop disabled by default
  2. enabled for rubocops own codebase
  3. for def_self style made to not raise an offense when class << self contains non public methods

@koic
Copy link
Member

koic commented Aug 6, 2020

Thank you for your consideration. Even in RuboCop's codebase, I think it's preferable to disable it by default. This is because there are some cases where code before the change is preferable.

@bbatsov
Copy link
Collaborator

bbatsov commented Aug 22, 2020

Fair enough, let's keep it disabled for RuboCop as well and revert the changes related to it.

@fatkodima fatkodima force-pushed the class_methods_definitions-cop branch from c6712b7 to 62ef486 Compare August 22, 2020 10:58
@fatkodima
Copy link
Contributor Author

Made it disabled and reverted style changes.

@bbatsov bbatsov merged commit 265360c into rubocop:master Aug 22, 2020
@bbatsov
Copy link
Collaborator

bbatsov commented Aug 22, 2020

Thanks!

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.

None yet

4 participants