-
Notifications
You must be signed in to change notification settings - Fork 280
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
UtilityFunction should not complain method that works inside an interator #681
Comments
I’d argue that |
It is, but instance state doesn't apply here. Actually it's huge NO. |
I'm open to changing this because I agree that the current set up of UtilityFunction is too strict. |
I think here there is some confusion in the description reek uses: Reek is not trying to suggest changing the method to use an instance variable. In fact, the smell description doesn't try to suggest anything, but one way to resolve this is to move the implementation of process to
I'm not too sure: I think UtilityFuntions are in fact often private and not flagging them means we miss out on opportunities to move methods to classes where they belong more naturally. |
@mvz makes a good point. I'd like to see some real-world examples of where this warning would be considered invalid. |
Well let's say that def process(x_variable)
if x_variable.code == :foo
"foo"
elsif x_variable.code == :bar
"bar"
else
raise "something went wrong"
end
end In that case I think the better action would be to extract this out into a method defined in Trying to automatically determine if the method is 'complex enough' to justify this would no doubt be difficult, but I feel that by disabling the warning for private methods, we'd be discarding something which acts as a helpful prompt to refactor in many cases. |
Agreed - that's why I'd make it configurable ;) |
From the example provided, I'd say this is a misuse of |
Hmmm. In general moving it to the We could argue that you shouldn’t use core types too much (as that’s primitive obsession), which I agree with in general, but in practice I do remember factoring out private utility functions to process some data. Factoring out a
|
Let's put it the other way, please tell me how the code suggested by reek is acceptable. # Just for Fun
class Fun
attr_accessor :arr
def bar
arr.each do |a_variable|
@x_variable = avariable
b_variable = process
puts b_variable
end
end
private
def process
@x_variable + 1
end
end I saw this code in real code base, and it's there basically to conform reek. |
i agree that's a bad approach, but Reek isn't suggesting you do that. There are a number of actions that could have been taken in response to this warning. Choosing the good one requires experience and understanding of OOP – satisfying Reek won't magically turn bad code into good code. Perhaps the warning could be improved by linking to a deeper discussion of why extracting the behaviour into another object could make sense in some situations. |
Yes, for experienced developers this is not a problem, but for new developers who take the warnings as principle, it does them more harm than good. Maybe besides suggesting turn the parameter passing into instance variable, there could be other suggestions. |
Couldn't agree more. Unfortunately, this is exactly what happens in quite a few projects.
and
Love the idea! Do you guys have suggestions for something we can link to? If not, we should come up with something on our own. However I still believe that we should make it configurable if we enable / disable UtilityFunction for private methods - @mvz, @chastell what are your thoughts on this? |
I think this may be a problem with reek in general: It detects code smells but it doesn't try to tell you what the solution should be. As it stands, it is not really suitable for beginners who don't have a more experienced developer at hand to help guide them toward good OO design. We can probably do a better job emphasising that the goal should not be to make your code smell free, but more to be aware of problems that may hinder further development and that you may want to tackle if and when you're touching that particular piece of code. Apart from that, we can provide some sample solutions for the smells in the documentation.
I'm still not sure where reek suggests this. Can you point that out to me please?
I think I'd like to see some more real-world examples before deciding on this. I think most cases of UitilityFunction may be private methods, but I don't know what the conclusion should be. Some threshold on method length may be more appropriate. I'll go over smells in my own code as well and see what the situation is. |
Preliminary results of that: One real bug in UtilityFunction found (it does not consider It now seems to me that the primary point of UtilityFunction is to ask "Why is this code here and not elsewhere?" |
For the docs: I think in general reek would benefit with ‘possible ways to adress this’ section for most (each?) smells. For UtilityFunction: I’m tentatively for making it configurable to whitelist private methods; at the same time I’ll probably keep them smelly in my pet projects, as fixing FeatureEnvy and UtilityFunction smells was an OOP eye-opener for me. |
I assume because of the warning message:
which implies to use instance variables. The problem is that it doesn't say "btw, dawg, with I think we can improve this a LOT by just adjusting our warning message accordingly. I just can't think of anything concise 😆 Ideas? |
@chastell The problem remains, is the style that pass intermediate state by instance variable a smell to reek? If it is should we warn that as well? If you actually think that's a good style, it is a REAL eyes opener for me. |
No. :)
I personally don’t like mutable objects at all, so in my personal projects ivars are set in the constructor and ~never change. I really don’t like setting ivars outside of constructors. That said I doubt we could easily distinguish ‘this ivar is set only to make a private UtilityFunction depend on state’ from typical Ruby object mutations.
😆 No worries – as I wrote, IMHO good style in general would be to move the method to the object that it gets as a param, but there are a lot of edge cases:
|
Jeez, don't be such a grumpy cat! Nobody think that is good style ;)
Yes, exactly. Like we already discussed above, the problem is that most people naturally don't immediately jump to the conclusion that this method should exist on another object.
Agree with all your edge cases. We should probably tweak UtilityFunction ;) |
What I suggested to Timo about a month ago - is to whitelist private and protected methods for UtilityFunction. My point is that class provides a public interface which should be checked, but in implementation details classes sometimes need such helper methods which were extracted from another methods to make them more readable. |
I agree with @hwo411. A lot of people complain about this rightfully and I believe we are too rigid in this regard. Lets make this configurable and give people the chance to opt out if it. People who want to stay strict can just ignore this setting. I'd come up with a pull request for this in the next days unless somebody in this thread would like to take over ;) |
IMO it's worth to add @chastell's hints to https://github.com/troessner/reek/blob/master/docs/Utility-Function.md For me private utility functions should be highlighted by default with configure to turn it off. |
@hwo411 wrote:
Basically, all of Reek's smell warnings are of that nature. We really need to stress this more in the docs.
I agree. If the alternative is that people turn of UtilityFunction entirely this is a good middle ground.
Agreed. |
@mvz usually, when a developer, especially junior developer, see a warning, he/she either treats it as an error or ignores it. If you say that it's a hint, he'll take it as advice and will think about it, rather than trying to ignore or remove it. It's just a psychological difference, not essential one, however hint is a light version of warning as for me. |
@hwo411 developers should consider all of reek's messages as hints. Maybe we should just say 'smells' instead of 'warnings'. |
General fyi: I'll come up with a PR the next days.
Agreed. I guess we should hire somebody from marketing to fix our wording ;) |
@troessner the problem is that we have travis configured to make a build red if it sees warnings from reek(and the same problem will arise if you follow reek-driven development) and we need either to put this warnings to ignore list or to fix, but some hints can be useful without making build red and some warnings are actually the things which indicate that something is wrong with the code. So there are cases where we need a message but don't need to treat is an error automatically. Probably ignoring is the right way to handle such cases but I see 2 problems in ignoring:
|
@hwo411 I think ignoring smells in code comments is what you should use for your case. Be sure to specify which smell you're ignoring, so new smells will still show up, like so (example from https://github.com/troessner/reek/blob/master/docs/Smell-Suppression.md):
If another smell starts occuring in this method, it will still be reported by reek. We just added ingoring comments to all of Reek precisely to be able to use it in Travis. You can see how we do this in reek's code. |
The
UtilityFunction
smell could be misleading, e.g.It is useful most of the time, but for method that process an intermediate state, create a instance variable is not the right way.
The text was updated successfully, but these errors were encountered: