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

Don't use #send etc. to include or prepend modules #7274

Closed
bquorning opened this issue Aug 9, 2019 · 1 comment · Fixed by #7279
Closed

Don't use #send etc. to include or prepend modules #7274

bquorning opened this issue Aug 9, 2019 · 1 comment · Fixed by #7279

Comments

@bquorning
Copy link
Contributor

bquorning commented Aug 9, 2019

Back in the day (before Ruby 2.1), Module.include and Module.prepend were private methods. So when people authored gems / patched monkeys, they wrote e.g. Foo.send(:include, MyPatch). In our day and age, I think we should encourage everybody to just write Foo.include(MyPatch).

Describe the solution you'd like

Recommend calling Foo.include(Bar) over Foo.send(:include, Bar), Foo.public_send(:include, M), etc.

Recommend calling Foo.prepend(Bar) over Foo.send(:prepend, Bar), Foo.public_send(:prepend, M), etc.

Describe alternatives you've considered

None

Additional context

None

@koic
Copy link
Member

koic commented Aug 11, 2019

I think it makes sense because it can be unified with a new mix-in style using public include and prepend (and extend) methods. I opened a PR #7279.

koic added a commit to koic/rubocop that referenced this issue Aug 12, 2019
Resolves rubocop#7274.

This PR adds new `Lint/SendWithMixinArgument` cop.

This cop checks for `send`, `public_send`, and `__send__` methods
when using mix-in.

```ruby
# bad
Foo.send(:include, Bar)
Foo.send(:prepend, Bar)
Foo.send(:extend, Bar)

# bad
Foo.public_send(:include, Bar)
Foo.public_send(:prepend, Bar)
Foo.public_send(:extend, Bar)

# bad
Foo.__send__(:include, Bar)
Foo.__send__(:prepend, Bar)
Foo.__send__(:extend, Bar)

# good
Foo.include Bar
Foo.prepend Bar
Foo.extend Bar
```

`include` and `prepend` methods were private methods until Ruby 2.0,
they were mixed-in via `send` method. This cop uses Ruby 2.1 or higher
style that can be called by public methods.

```console
% ruby -v
ruby 2.0.0p648 (2015-12-16 revision 53162) [x86_64-darwin13.0.2]
% ruby -e 'module M end; Object.include(M)'
-e:1:in `<main>': private method `include' called for Object:Class (NoMethodError)
% ruby -e 'module M end; Object.prepend(M)'
-e:1:in `<main>': private method `prepend' called for Object:Class (NoMethodError)

% ruby -v
ruby 2.1.10p492 (2016-04-01 revision 54464) [x86_64-darwin13.0]
% ruby -e 'module M end; Object.include(M)' # no error
% ruby -e 'module M end; Object.prepend(M)' # no error
```

And `extend` method that was originally a public method is also targeted
for style unification.

```console
% ruby -v
ruby 2.0.0p648 (2015-12-16 revision 53162) [x86_64-darwin13.0.2]
% ruby -e 'module M end; Object.extend(M)' # no error

% ruby -v
ruby 2.1.10p492 (2016-04-01 revision 54464) [x86_64-darwin13.0]
% ruby -e 'module M end; Object.extend(M)' # no error
```
bbatsov pushed a commit that referenced this issue Aug 13, 2019
Resolves #7274.

This PR adds new `Lint/SendWithMixinArgument` cop.

This cop checks for `send`, `public_send`, and `__send__` methods
when using mix-in.

```ruby
# bad
Foo.send(:include, Bar)
Foo.send(:prepend, Bar)
Foo.send(:extend, Bar)

# bad
Foo.public_send(:include, Bar)
Foo.public_send(:prepend, Bar)
Foo.public_send(:extend, Bar)

# bad
Foo.__send__(:include, Bar)
Foo.__send__(:prepend, Bar)
Foo.__send__(:extend, Bar)

# good
Foo.include Bar
Foo.prepend Bar
Foo.extend Bar
```

`include` and `prepend` methods were private methods until Ruby 2.0,
they were mixed-in via `send` method. This cop uses Ruby 2.1 or higher
style that can be called by public methods.

```console
% ruby -v
ruby 2.0.0p648 (2015-12-16 revision 53162) [x86_64-darwin13.0.2]
% ruby -e 'module M end; Object.include(M)'
-e:1:in `<main>': private method `include' called for Object:Class (NoMethodError)
% ruby -e 'module M end; Object.prepend(M)'
-e:1:in `<main>': private method `prepend' called for Object:Class (NoMethodError)

% ruby -v
ruby 2.1.10p492 (2016-04-01 revision 54464) [x86_64-darwin13.0]
% ruby -e 'module M end; Object.include(M)' # no error
% ruby -e 'module M end; Object.prepend(M)' # no error
```

And `extend` method that was originally a public method is also targeted
for style unification.

```console
% ruby -v
ruby 2.0.0p648 (2015-12-16 revision 53162) [x86_64-darwin13.0.2]
% ruby -e 'module M end; Object.extend(M)' # no error

% ruby -v
ruby 2.1.10p492 (2016-04-01 revision 54464) [x86_64-darwin13.0]
% ruby -e 'module M end; Object.extend(M)' # no error
```
tijmenb added a commit to tijmenb/mail-notify that referenced this issue Oct 21, 2019
This trips over a Rubocop rule on CI:

```
lib/mail/notify/railtie.rb:12:36: W: Lint/SendWithMixinArgument: Use
prepend Mail::Notify::MailersController instead of send(:prepend,
Mail::Notify::MailersController).
          Rails::MailersController.send(:prepend,
Mail::Notify::MailersController)

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
```

As rubocop/rubocop#7274 explains:

> Back in the day (before Ruby 2.1), Module.include and Module.prepend
were private methods. So when people authored gems / patched monkeys,
they wrote e.g. Foo.send(:include, MyPatch). In our day and age, I
think we should encourage everybody to just write Foo.include(MyPatch).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants