Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.
Reek implements a check for Duplicate Method Call.
Example
Here's a very much simplified and contrived example. The following method will report a warning:
defdouble_thing()
@other.thing +@other.thing
end
One quick approach to silence Reek would be to refactor the code thus:
defdouble_thing()
thing =@other.thing
thing + thing
end
A slightly different approach would be to replace all calls of double_thing by calls to @other.double_thing:
classOther
defdouble_thing()
thing + thing
end
end
The approach you take will depend on balancing other factors in your code.
Classes should not assume that instance variables are set or present outside of the current class definition.
Good:
classFoo
definitialize
@bar=:foo
end
deffoo?
@bar==:foo
end
end
Good as well:
classFoo
deffoo?
bar ==:foo
end
defbar
@bar||=:foo
end
end
Bad:
classFoo
defgo_foo!
@bar=:foo
end
deffoo?
@bar==:foo
end
end
Example
Running Reek on:
classDummy
deftest
@ivar
end
end
would report:
[1]:InstanceVariableAssumption: Dummy assumes too much for instance variable @ivar
Note that this example would trigger this smell warning as well:
classParent
definitialize(omg)
@omg= omg
end
end
classChild< Parent
deffoo
@omg
end
end
The way to address the smell warning is that you should create an attr_reader to use @omg in the subclass and not access @omg directly like this:
classParent
attr_reader :omg
definitialize(omg)
@omg= omg
end
end
classChild< Parent
deffoo
omg
end
end
Directly accessing instance variables is considered a smell because it breaks encapsulation and makes it harder to reason about code.
If you don't want to expose those methods as public API just make them private like this:
classParent
definitialize(omg)
@omg= omg
end
private
attr_reader :omg
end
classChild< Parent
deffoo
omg
end
end
Current Support in Reek
An instance variable must:
be set in the constructor
or be accessed through a method with lazy initialization / memoization.
If not, Instance Variable Assumption will be reported.
Classes should not assume that instance variables are set or present outside of the current class definition.
Good:
classFoo
definitialize
@bar=:foo
end
deffoo?
@bar==:foo
end
end
Good as well:
classFoo
deffoo?
bar ==:foo
end
defbar
@bar||=:foo
end
end
Bad:
classFoo
defgo_foo!
@bar=:foo
end
deffoo?
@bar==:foo
end
end
Example
Running Reek on:
classDummy
deftest
@ivar
end
end
would report:
[1]:InstanceVariableAssumption: Dummy assumes too much for instance variable @ivar
Note that this example would trigger this smell warning as well:
classParent
definitialize(omg)
@omg= omg
end
end
classChild< Parent
deffoo
@omg
end
end
The way to address the smell warning is that you should create an attr_reader to use @omg in the subclass and not access @omg directly like this:
classParent
attr_reader :omg
definitialize(omg)
@omg= omg
end
end
classChild< Parent
deffoo
omg
end
end
Directly accessing instance variables is considered a smell because it breaks encapsulation and makes it harder to reason about code.
If you don't want to expose those methods as public API just make them private like this:
classParent
definitialize(omg)
@omg= omg
end
private
attr_reader :omg
end
classChild< Parent
deffoo
omg
end
end
Current Support in Reek
An instance variable must:
be set in the constructor
or be accessed through a method with lazy initialization / memoization.
If not, Instance Variable Assumption will be reported.
Duplicated Code
Duplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:
Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.
When you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).
Tuning
This issue has a mass of 34.
We set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.
The threshold configuration represents the minimum mass a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.
If the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.
Duplicated code can lead to software that is hard to understand and difficult to change. The Don't Repeat Yourself (DRY) principle states:
Every piece of knowledge must have a single, unambiguous, authoritative representation within a system.
When you violate DRY, bugs and maintenance problems are sure to follow. Duplicated code has a tendency to both continue to replicate and also to diverge (leaving bugs as two similar implementations differ in subtle ways).
Tuning
This issue has a mass of 34.
We set useful threshold defaults for the languages we support but you may want to adjust these settings based on your project guidelines.
The threshold configuration represents the minimum mass a code block must have to be analyzed for duplication. The lower the threshold, the more fine-grained the comparison.
If the engine is too easily reporting duplication, try raising the threshold. If you suspect that the engine isn't catching enough duplication, try lowering the threshold. The best setting tends to differ from language to language.
In general, a Data Clump occurs when the same two or three items frequently appear together in classes and parameter lists, or when a group of instance variable names start or end with similar substrings.
The recurrence of the items often means there is duplicate code spread around to handle them. There may be an abstraction missing from the code, making the system harder to understand.
A possible way to fix this problem (quoting from Martin Fowler):
The first step is to replace data clumps with objects and use the objects whenever you see them. An immediate benefit is that you'll shrink some parameter lists. The interesting stuff happens as you begin to look for behavior to move into the new objects.
A Utility Function is any instance method that has no dependency on the state of the instance.
Flog calculates the ABC score for methods.
The ABC score is based on assignments, branches (method calls), and conditions.