I just upgraded to the latest rubocop and received some peculiar offenses from Style/AccessModifierDeclarations offenses. I use a number of different tools that generate methods automatically, including anima, for example. But sometimes I apply a different access modifier to the generated method(s). Style/AccessModifierDeclarations flags this as "inlined", when it is not anything I would consider inline.
I expect the following example (and various similar cases) not to raise an offense with the default rubocop configuration.
class Foo
include Anima.new(:foo) # Defines constructor which takes the single keyword argument :foo and generates a public reader.
private :foo
end
If, for some reason, this is the intended behavior and I'm missing something here, I think the error message should be made clearer. I think this cop should ignore access modifier adjustments to methods it can't statically detect.
test.rb:4:3: C: Style/AccessModifierDeclarations: private should not be inlined in method definitions.
private :foo
^^^^^^^
Run rubocop with Style/AccessModifierDeclarations's style set to "group" (the default).
$ rubocop -V
0.57.0 (using Parser 2.5.1.0, running on ruby 2.5.1 x86_64-darwin17)
Same problem here:
def _run_before_callbacks(params)
end
private :_run_before_callbacks
Same is happening for me using module_function
Weird thing is if I try to disable the cop within source code around the method, I get a Lint/UnneededCopDisableDirective error!
I wonder if what @jodosha is doing should be a style option. It's logically similar to doing
private def _run_before_callbacks(params)
end
as if he were doing the inline style, but just choosing to put the private annotation elsewhere. It is different from the typical private region, anyway.
In my case I simply can't use the normal stateful / region modifier and I also cannot place it at the method definition site because there isn't one that's accessible to me.
@dgollahon There is a new cop released with 0.57.0 that discourage private def style: see https://github.com/rubocop-hq/rubocop/pull/5444
@jodosha I think perhaps we're talking past each other. I'm aware of #5444, as it's is the cop I'm opening the issue about. My understanding is the intent is to differentiate between having one private keyword per method v.s. one per class / group of methods.
I was suggesting that within what that cop is trying to support you might expect to find configuration for both
private def foo
end
as currently enforced and
def foo
end
private :foo
which are similar in intent (put the modifier right next to the method). I'm not sure anybody actually religiously does the latter though.
My main point in opening this issue is that my problem is none of the above--I simply don't have a method to put the modifier next to nor do I have a way to have a no-argument region style modifier apply to the generated method.
/cc @brandonweiss
Hmm, this is getting confusing as everyone seems to be talking about slightly different things, but Iāll try to respond to all of them.
@dgollahon Yeah, that probably shouldnāt be causing an offense as thereās no actual way to fix that since the method is defined elsewhere. I think there are two potential approachesāif you think this is relatively uncommon usage you could just disable the cop for that line, or if you think this is going to happen a lot, as you said, you could alter the cop to try and determine if the method is statically defined when configured with group. This isnāt an issue for me personally as Iāve never needed to control the visibility of methods that Iām not explicitly defining, so feel free to have at it if you feel compelled!
@jodosha @phillycheeze In your example that seems to be a valid offense. If you want to use that style you should configure Style/AccessModifierDeclarations with inline. Or you can fix the offense by using the āgroupā style.
@dgollahon Yes, the inline style is both of these:
private def foo
end
def foo
end
private :foo
The cop doesnāt discriminate because those are both technically inline, at least as far as I was concerned. You could totally create a third style to differentiate the two, but it didnāt seem useful to me.
As @phillycheeze mentioned, the following code generates a Style/AccessModifierDeclarations: module_function should not be inlined in method definitions. false positive:
module Kernel
def foo(cmd)
end
module_function :foo
end
or
module Kernel
module_function def foo(cmd)
end
end
@brandonweiss What if I have something like
def foo
end
whatever
something_else
def bar
end
private :foo
Will you think that this is inline too?
I don't know for sure but I think that people don't use your second "inline" style. But it's pretty common (I guess) to face issue from first post when method is defined elsewhere.
@Nondv There are three ways of using the private declaration.
# 1
Class.new do
private
def method; end
end
# 2
Class.new do
private def method; end
end
# 3
Class.new do
def method; end
private :method
end
#1 is the āgroupā style. #2 and #3 are the āinlineā style. Yes, #2 and #3 are not exactly the same, but they are _essentially_ the same. They are both explicit and require an individual private declaration for each method you want to make private and itās positioned inline, either before the method definition or before the symbol argument it takes.
I donāt really know what percentage of Rubyists use what style, but #3 is necessary because some methods can be defined in different ways. For example:
private
attr_reader :method
That would need to be come:
attr_reader :method
private :method
As for using the group style but also needing to declare private inline in order to privatize a method defined elsewhere, the solution depends on how common you think that might be. Iāve been a Rubyist about 12 years and have never needed to do that, so if I did Iād be inclined to just inline-disable it occasionally when I need to do it. However, if this something other people commonly do, then attempting to statically analyze if the method is defined in the class before throwing an offense might make sense, although I imagine doing that could be difficultāthere are a lot of ways to define a method in Rubyā¦
Here's another example where the current default settings introduced in 0.57 conflict.
In general, I tend to avoid using instance variables directly and rather create an attr_accessor, followed by making the writer private. This way I can help avoid that direct changes to instance variables pour into the code, and if I need to make adjustments to the setter / getter logic I can just drop the default attr methods and create my custom logic there, keeping i.e. my initializer untouched. See this code:
# frozen_string_literal: true
# Example for rubocop 0.57 complaint for https://github.com/rubocop-hq/rubocop/issues/5953
class Example
attr_accessor :foo
private :foo=
def initialize(foo)
self.foo = foo
end
end
With rubocop 0.57 this is getting blamed in the default settings:
rubocop-5953.rb:6:3: C: Style/AccessModifierDeclarations: private should not be inlined in method definitions.
private :foo=
^^^^^^^
I do like the general intention of introducing consistency via https://github.com/rubocop-hq/rubocop/pull/5444, but as it stands I have to disable this new cop on all my projects because it does not fit my needs (group style in the class body, inline style for attr_accessor and friends (i.e. this also applies to ActiveSupport delegate methods)
I am getting this and it doesn't make sense to me why.
# frozen_string_literal: true
module Foo
def bar
puts 'hi'
end
module_function :bar
end
with the result:
lib/tasks/foo.rake:7:3: C: Style/AccessModifierDeclarations: module_function should not be inlined in method definitions.
module_function :bar
^^^^^^^^^^^^^^^
Latest version of rubocop:
ā“ rubocop -v
0.57.2
@pboling I posted a comment on the other issue that should hopefully explain the reason!
Does happen for me, too although it is a really unfortunate case.
FactoryBot.define do
factory :hosting, class: Profiles::Hosting do
# mandatory properties
public true
private false
end
end
@brandonweiss Hi, Brandon.
From your earlier comment:
Yes, #2 and #3 are not exactly the same, but they are essentially the same. They are both explicit and require an individual private declaration for each method you want to make private and itās positioned inline, either before the method definition or before the symbol argument it takes.
I'm very confused by your assertion of "inline" describing both cases. Inserting the privacy modifier into the method declaration is what makes it "inline". Using a separate privacy declaration, on its own line, is by definition not inline. Yes, both forms have the same target, but that's beside the point: the method and privacy declarations do not share the same line.
It sounds to me like there's a missing word here, as conflating #2 and #3 is not helpful. "Matched" maybe? Because you match a privacy (or module_function, etc.) declaration with a separately-declared method?
@TrevorBramble The way I would explain it is, the access modifier is being inlined into _something_ (a thing indicating the method being modified). In one scenario itās being inlined into the method. In another scenario itās being inlined into a symbol. Some people seem to be confused by it, so perhaps it wasnāt the right name? An alternative was āargumentā, but I nixed that because although technically in both 2 and 3 an argument is being passed, a lot of people donāt understand that access modifiers are methods and not special keywords. I also considered āexplicitā, but when using the word āexplicitā there is an implicit (š) antonym of āimplicitā, and I didnāt think that was a good name for āgroupā. āinlineā was the best I could come up with. š¤·š¼āāļø
Regarding having three names, sure, it could be possible to come up with another name that distinguishes 2 and 3, but then that implies that there is some reason to choose 2 versus 3, which I donāt think there is. I think if you choose āinlineā you want to use 2. However, there are some scenarios in which you might _have_ to use 3.
Is there currently any way to actually write module_function without disabling the cop?
Without being able to use module_function the cop is not useable for me. Can we use an exclusion list like some other cops do?
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contribution and understanding!
This to me feels like a bug with the way the cop works, and perhaps should be a cause for auto-disabling the cop??
the following "should" not be raising issues. I know it does, by design; but I would argue it "shouldn't"
class Foo
attr_reader :baz
private :baz
def initialize(baz)
@baz = baz
end
private
# ten_other_methods
end
I use private as a keyword as seen above and then all private methods below. But I also put readers/writers at the top. I would argue this is the most common use-case.
I know a lot of conversation regarding several separate issues has happened since my original complaint and it went stale at some point, but my original problem is still unsolved and I think it could be improved. I'm unable to use the cop because of extensive use of concord and anima and similar tools.
What I think should happen is the cop should determine what was defined directly in that scope and ignore any method definitions it can't observe (ex: the #foo method in my example).
The module_function and attr_reader cases (possibly others) make sense to me, but I don't think they're the same problem I'm getting at which is that the cop has false positives when the method definition _cannot_ be moved below the group.
I would argue that module_function and attr_reader tripping this is also a false positive, since it is not code that should be flagged as outside of best practice, even if the reasons for the flagging may be different.
Yeah, wasn't trying to imply those weren't issues, just worried the original problem case got lost in the other discussion.
It seems like this cop should maybe just be checking for def defined methods only.
:point_up: does this seem a fair compromise rubocop devs?
Our case:
def cell(cell_name, &block)
define_method cell_name, block || instance_method(cell_name)
private cell_name
end
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contribution and understanding!
Could we maybe get the compromise mentioned above worked on?
If this cop would support an exclusion list I could turn it back on.
Without being able to use
module_functionthe cop is not useable for me. Can we use an exclusion list like some other cops do?
You can use module_function like so:
module Foo
def baz
end
module_function
def bar
puts 'hi'
end
end
````
irb(main):001:0> Foo.bar
hi
=> nil
```
Why does this keep getting marked as stale? The cop is wonky and nothing appears to have changed from 0.57 => 0.89 (current)
Bugs don't just fix themselves (usually).
</rant>
I'm going to go ahead and disable the cop in my configuration until someone posts a better solution.
Why does this keep getting marked as stale?


Sorry, what? If you're talking about regular stale labels ā it's just an automation, it doesn't count something else, like importance, except dates of the last comment.
I'm going to go ahead and disable the cop in my configuration until someone posts a better solution.
It's OK, especially inline, especially with the link to this issue.
Most helpful comment
I am getting this and it doesn't make sense to me why.
with the result:
Latest version of rubocop: