/lib/reek/report/code_climate/code_climate_configuration.yml
https://github.com/troessner/reek · YAML · 862 lines · 632 code · 200 blank · 30 comment · 0 complexity · 8870f0c28c2828405244c51b4265fbf1 MD5 · raw file
- ---
- Attribute:
- remediation_points: 250_000
- content: |
- A class that publishes a setter for an instance variable invites client classes to become too intimate with its inner workings, and in particular with its representation of state.
- The same holds to a lesser extent for getters, but Reek doesn't flag those.
- ## Example
- Given:
- ```Ruby
- class Klass
- attr_accessor :dummy
- end
- ```
- Reek would emit the following warning:
- ```
- reek test.rb
- test.rb -- 1 warning:
- [2]:Klass declares the writable attribute dummy (Attribute)
- ```
- BooleanParameter:
- remediation_points: 500_000
- content: |
- `Boolean Parameter` is a special case of `Control Couple`, where a method parameter is defaulted to true or false. A _Boolean Parameter_ effectively permits a method's caller to decide which execution path to take. This is a case of bad cohesion. You're creating a dependency between methods that is not really necessary, thus increasing coupling.
- ## Example
- Given
- ```Ruby
- class Dummy
- def hit_the_switch(switch = true)
- if switch
- puts 'Hitting the switch'
- # do other things...
- else
- puts 'Not hitting the switch'
- # do other things...
- end
- end
- end
- ```
- Reek would emit the following warning:
- ```
- test.rb -- 3 warnings:
- [1]:Dummy#hit_the_switch has boolean parameter 'switch' (BooleanParameter)
- [2]:Dummy#hit_the_switch is controlled by argument switch (ControlParameter)
- ```
- Note that both smells are reported, `Boolean Parameter` and `Control Parameter`.
- ## Getting rid of the smell
- This is highly dependent on your exact architecture, but looking at the example above what you could do is:
- * Move everything in the `if` branch into a separate method
- * Move everything in the `else` branch into a separate method
- * Get rid of the `hit_the_switch` method alltogether
- * Make the decision what method to call in the initial caller of `hit_the_switch`
- ClassVariable:
- remediation_points: 350_000
- content: |
- Class variables form part of the global runtime state, and as such make it easy for one part of the system to accidentally or inadvertently depend on another part of the system. So the system becomes more prone to problems where changing something over here breaks something over there. In particular, class variables can make it hard to set up tests (because the context of the test includes all global state).
- For a detailed explanation, check out [this article](http://4thmouse.com/index.php/2011/03/20/why-class-variables-in-ruby-are-a-bad-idea/)
- ## Example
- Given
- ```Ruby
- class Dummy
- @@class_variable = :whatever
- end
- ```
- Reek would emit the following warning:
- ```
- reek test.rb
- test.rb -- 1 warning:
- [2]:Dummy declares the class variable @@class_variable (ClassVariable)
- ```
- ## Getting rid of the smell
- You can use class-instance variable to mitigate the problem (as also suggested in the linked article above):
- ```Ruby
- class Dummy
- @class_variable = :whatever
- end
- ```
- ControlParameter:
- remediation_points: 500_000
- content: |
- `Control Parameter` is a special case of `Control Couple`
- ## Example
- A simple example would be the "quoted" parameter in the following method:
- ```Ruby
- def write(quoted)
- if quoted
- write_quoted @value
- else
- write_unquoted @value
- end
- end
- ```
- Fixing those problems is out of the scope of this document but an easy solution could be to remove the "write" method alltogether and to move the calls to "write_quoted" / "write_unquoted" in the initial caller of "write".
- DataClump:
- remediation_points: 250_000
- content: |
- 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.
- ## Example
- Given
- ```Ruby
- class Dummy
- def x(y1,y2); end
- def y(y1,y2); end
- def z(y1,y2); end
- end
- ```
- Reek would emit the following warning:
- ```
- test.rb -- 1 warning:
- [2, 3, 4]:Dummy takes parameters [y1, y2] to 3 methods (DataClump)
- ```
- A possible way to fix this problem (quoting from [Martin Fowler](http://martinfowler.com/bliki/DataClump.html)):
- > 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.
- DuplicateMethodCall:
- remediation_points: 350_000
- content: |
- 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:
- ```Ruby
- def double_thing()
- @other.thing + @other.thing
- end
- ```
- One quick approach to silence Reek would be to refactor the code thus:
- ```Ruby
- def double_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`:
- ```Ruby
- class Other
- def double_thing()
- thing + thing
- end
- end
- ```
- The approach you take will depend on balancing other factors in your code.
- FeatureEnvy:
- remediation_points: 500_000
- content: |
- _Feature Envy_ occurs when a code fragment references another object more often than it references itself, or when several clients do the same series of manipulations on a particular type of object.
- _Feature Envy_ reduces the code's ability to communicate intent: code that "belongs" on one class but which is located in another can be hard to find, and may upset the "System of Names" in the host class.
- _Feature Envy_ also affects the design's flexibility: A code fragment that is in the wrong class creates couplings that may not be natural within the application's domain, and creates a loss of cohesion in the unwilling host class.
- _Feature Envy_ often arises because it must manipulate other objects (usually its arguments) to get them into a useful form, and one force preventing them (the arguments) doing this themselves is that the common knowledge lives outside the arguments, or the arguments are of too basic a type to justify extending that type. Therefore there must be something which 'knows' about the contents or purposes of the arguments. That thing would have to be more than just a basic type, because the basic types are either containers which don't know about their contents, or they are single objects which can't capture their relationship with their fellows of the same type. So, this thing with the extra knowledge should be reified into a class, and the utility method will most likely belong there.
- ## Example
- Running Reek on:
- ```Ruby
- class Warehouse
- def sale_price(item)
- (item.price - item.rebate) * @vat
- end
- end
- ```
- would report:
- ```Bash
- Warehouse#total_price refers to item more than self (FeatureEnvy)
- ```
- since this:
- ```Ruby
- (item.price - item.rebate)
- ```
- belongs to the Item class, not the Warehouse.
- InstanceVariableAssumption:
- remediation_points: 350_000
- content: |
- Classes should not assume that instance variables are set or present outside of the current class definition.
- Good:
- ```Ruby
- class Foo
- def initialize
- @bar = :foo
- end
- def foo?
- @bar == :foo
- end
- end
- ```
- Good as well:
- ```Ruby
- class Foo
- def foo?
- bar == :foo
- end
- def bar
- @bar ||= :foo
- end
- end
- ```
- Bad:
- ```Ruby
- class Foo
- def go_foo!
- @bar = :foo
- end
- def foo?
- @bar == :foo
- end
- end
- ```
- ## Example
- Running Reek on:
- ```Ruby
- class Dummy
- def test
- @ivar
- end
- end
- ```
- would report:
- ```Bash
- [1]:InstanceVariableAssumption: Dummy assumes too much for instance variable @ivar
- ```
- Note that this example would trigger this smell warning as well:
- ```Ruby
- class Parent
- def initialize(omg)
- @omg = omg
- end
- end
- class Child < Parent
- def foo
- @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:
- ```Ruby
- class Parent
- attr_reader :omg
- def initialize(omg)
- @omg = omg
- end
- end
- class Child < Parent
- def foo
- omg
- end
- end
- ```
- Directly accessing instance variables is considered a smell because it [breaks encapsulation](http://designisrefactoring.com/2015/03/29/organizing-data-self-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:
- ```Ruby
- class Parent
- def initialize(omg)
- @omg = omg
- end
- private
- attr_reader :omg
- end
- class Child < Parent
- def foo
- 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.
- IrresponsibleModule:
- remediation_points: 350_000
- content: |
- Classes and modules are the units of reuse and release. It is therefore considered good practice to annotate every class and module with a brief comment outlining its responsibilities.
- ## Example
- Given
- ```Ruby
- class Dummy
- # Do things...
- end
- ```
- Reek would emit the following warning:
- ```
- test.rb -- 1 warning:
- [1]:Dummy has no descriptive comment (IrresponsibleModule)
- ```
- Fixing this is simple - just an explaining comment:
- ```Ruby
- # The Dummy class is responsible for ...
- class Dummy
- # Do things...
- end
- ```
- LongParameterList:
- remediation_points: 500_000
- content: |
- A `Long Parameter List` occurs when a method has a lot of parameters.
- ## Example
- Given
- ```Ruby
- class Dummy
- def long_list(foo,bar,baz,fling,flung)
- puts foo,bar,baz,fling,flung
- end
- end
- ```
- Reek would report the following warning:
- ```
- test.rb -- 1 warning:
- [2]:Dummy#long_list has 5 parameters (LongParameterList)
- ```
- A common solution to this problem would be the introduction of parameter objects.
- LongYieldList:
- remediation_points: 500_000
- content: |
- A _Long Yield List_ occurs when a method yields a lot of arguments to the block it gets passed.
- ## Example
- ```Ruby
- class Dummy
- def yields_a_lot(foo,bar,baz,fling,flung)
- yield foo,bar,baz,fling,flung
- end
- end
- ```
- Reek would report the following warning:
- ```
- test.rb -- 1 warning:
- [4]:Dummy#yields_a_lot yields 5 parameters (LongYieldList)
- ```
- A common solution to this problem would be the introduction of parameter objects.
- ManualDispatch:
- remediation_points: 350_000
- content: |
- Reek reports a _Manual Dispatch_ smell if it finds source code that manually checks whether an object responds to a method before that method is called. Manual dispatch is a type of Simulated Polymorphism which leads to code that is harder to reason about, debug, and refactor.
- ## Example
- ```Ruby
- class MyManualDispatcher
- attr_reader :foo
- def initialize(foo)
- @foo = foo
- end
- def call
- foo.bar if foo.respond_to?(:bar)
- end
- end
- ```
- Reek would emit the following warning:
- ```
- test.rb -- 1 warning:
- [9]: MyManualDispatcher manually dispatches method call (ManualDispatch)
- ```
- ModuleInitialize:
- remediation_points: 350_000
- content: |
- A module is usually a mixin, so when an `#initialize` method is present it is
- hard to tell initialization order and parameters so having `#initialize`
- in a module is usually a bad idea.
- ## Example
- The `Foo` module below contains a method `initialize`. Although class `B` inherits from `A`, the inclusion of `Foo` stops `A#initialize` from being called.
- ```Ruby
- class A
- def initialize(a)
- @a = a
- end
- end
- module Foo
- def initialize(foo)
- @foo = foo
- end
- end
- class B < A
- include Foo
- def initialize(b)
- super('bar')
- @b = b
- end
- end
- ```
- A simple solution is to rename `Foo#initialize` and call that method by name:
- ```Ruby
- module Foo
- def setup_foo_module(foo)
- @foo = foo
- end
- end
- class B < A
- include Foo
- def initialize(b)
- super 'bar'
- setup_foo_module('foo')
- @b = b
- end
- end
- ```
- NestedIterators:
- remediation_points: 500_000
- content: |
- A `Nested Iterator` occurs when a block contains another block.
- ## Example
- Given
- ```Ruby
- class Duck
- class << self
- def duck_names
- %i!tick trick track!.each do |surname|
- %i!duck!.each do |last_name|
- puts "full name is #{surname} #{last_name}"
- end
- end
- end
- end
- end
- ```
- Reek would report the following warning:
- ```
- test.rb -- 1 warning:
- [5]:Duck#duck_names contains iterators nested 2 deep (NestedIterators)
- ```
- NilCheck:
- remediation_points: 250_000
- content: |
- A `NilCheck` is a type check. Failures of `NilCheck` violate the ["tell, don't ask"](http://robots.thoughtbot.com/tell-dont-ask) principle.
- Additionally, type checks often mask bigger problems in your source code like not using OOP and / or polymorphism when you should.
- ## Example
- Given
- ```Ruby
- class Klass
- def nil_checker(argument)
- if argument.nil?
- puts "argument isn't nil!"
- end
- end
- end
- ```
- Reek would emit the following warning:
- ```
- test.rb -- 1 warning:
- [3]:Klass#nil_checker performs a nil-check. (NilCheck)
- ```
- MissingSafeMethod:
- remediation_points: 250_000
- content: |
- A candidate method for the `Missing Safe Method` smell are methods whose names end with an exclamation mark.
- An exclamation mark in method names means (the explanation below is taken from [here](http://dablog.rubypal.com/2007/8/15/bang-methods-or-danger-will-rubyist) ):
- >>
- The ! in method names that end with ! means, “This method is dangerous”—or, more precisely, this method is the “dangerous” version of an otherwise equivalent method, with the same name minus the !. “Danger” is relative; the ! doesn’t mean anything at all unless the method name it’s in corresponds to a similar but bang-less method name.
- So, for example, gsub! is the dangerous version of gsub. exit! is the dangerous version of exit. flatten! is the dangerous version of flatten. And so forth.
- Such a method is called `Missing Safe Method` if and only if her non-bang version does not exist and this method is reported as a smell.
- ## Example
- Given
- ```Ruby
- class C
- def foo; end
- def foo!; end
- def bar!; end
- end
- ```
- Reek would report `bar!` as `Missing Safe Method` smell but not `foo!`.
- Reek reports this smell only in a class context, not in a module context in order to allow perfectly legit code like this:
- ```Ruby
- class Parent
- def foo; end
- end
- module Dangerous
- def foo!; end
- end
- class Son < Parent
- include Dangerous
- end
- class Daughter < Parent
- end
- ```
- In this example, Reek would not report the `Missing Safe Method` smell for the method `foo` of the `Dangerous` module.
- RepeatedConditional:
- remediation_points: 400_000
- content: |
- `Repeated Conditional` is a special case of `Simulated Polymorphism`. Basically it means you are checking the same value throughout a single class and take decisions based on this.
- ## Example
- Given
- ```Ruby
- class RepeatedConditionals
- attr_accessor :switch
- def repeat_1
- puts "Repeat 1!" if switch
- end
- def repeat_2
- puts "Repeat 2!" if switch
- end
- def repeat_3
- puts "Repeat 3!" if switch
- end
- end
- ```
- Reek would emit the following warning:
- ```
- test.rb -- 4 warnings:
- [5, 9, 13]:RepeatedConditionals tests switch at least 3 times (RepeatedConditional)
- ```
- If you get this warning then you are probably not using the right abstraction or even more probable, missing an additional abstraction.
- TooManyInstanceVariables:
- remediation_points: 500_000
- content: |
- `Too Many Instance Variables` is a special case of `LargeClass`.
- ## Example
- Given this configuration
- ```yaml
- TooManyInstanceVariables:
- max_instance_variables: 3
- ```
- and this code:
- ```Ruby
- class TooManyInstanceVariables
- def initialize
- @arg_1 = :dummy
- @arg_2 = :dummy
- @arg_3 = :dummy
- @arg_4 = :dummy
- end
- end
- ```
- Reek would emit the following warning:
- ```
- test.rb -- 5 warnings:
- [1]:TooManyInstanceVariables has at least 4 instance variables (TooManyInstanceVariables)
- ```
- TooManyConstants:
- remediation_points: 500_000
- content: |
- `Too Many Constants` is a special case of `LargeClass`.
- ## Example
- Given this configuration
- ```yaml
- TooManyConstants:
- max_constants: 3
- ```
- and this code:
- ```Ruby
- class TooManyConstants
- CONST_1 = :dummy
- CONST_2 = :dummy
- CONST_3 = :dummy
- CONST_4 = :dummy
- end
- ```
- Reek would emit the following warning:
- ```
- test.rb -- 1 warnings:
- [1]:TooManyConstants has 4 constants (TooManyConstants)
- ```
- TooManyMethods:
- remediation_points: 500_000
- content: |
- `Too Many Methods` is a special case of `LargeClass`.
- ## Example
- Given this configuration
- ```yaml
- TooManyMethods:
- max_methods: 3
- ```
- and this code:
- ```Ruby
- class TooManyMethods
- def one; end
- def two; end
- def three; end
- def four; end
- end
- ```
- Reek would emit the following warning:
- ```
- test.rb -- 1 warning:
- [1]:TooManyMethods has at least 4 methods (TooManyMethods)
- ```
- TooManyStatements:
- remediation_points: 500_000
- content: |
- A method with `Too Many Statements` is any method that has a large number of lines.
- `Too Many Statements` warns about any method that has more than 5 statements. Reek's smell detector for `Too Many Statements` counts +1 for every simple statement in a method and +1 for every statement within a control structure (`if`, `else`, `case`, `when`, `for`, `while`, `until`, `begin`, `rescue`) but it doesn't count the control structure itself.
- So the following method would score +6 in Reek's statement-counting algorithm:
- ```Ruby
- def parse(arg, argv, &error)
- if !(val = arg) and (argv.empty? or /\A-/ =~ (val = argv[0]))
- return nil, block, nil # +1
- end
- opt = (val = parse_arg(val, &error))[1] # +2
- val = conv_arg(*val) # +3
- if opt and !arg
- argv.shift # +4
- else
- val[0] = nil # +5
- end
- val # +6
- end
- ```
- (You might argue that the two assigments within the first @if@ should count as statements, and that perhaps the nested assignment should count as +2.)
- UncommunicativeMethodName:
- remediation_points: 150_000
- content: |
- An `Uncommunicative Method Name` is a method name that doesn't communicate its intent well enough.
- Poor names make it hard for the reader to build a mental picture of what's going on in the code. They can also be mis-interpreted; and they hurt the flow of reading, because the reader must slow down to interpret the names.
- UncommunicativeModuleName:
- remediation_points: 150_000
- content: |
- An `Uncommunicative Module Name` is a module name that doesn't communicate its intent well enough.
- Poor names make it hard for the reader to build a mental picture of what's going on in the code. They can also be mis-interpreted; and they hurt the flow of reading, because the reader must slow down to interpret the names.
- UncommunicativeParameterName:
- remediation_points: 150_000
- content: |
- An `Uncommunicative Parameter Name` is a parameter name that doesn't communicate its intent well enough.
- Poor names make it hard for the reader to build a mental picture of what's going on in the code. They can also be mis-interpreted; and they hurt the flow of reading, because the reader must slow down to interpret the names.
- UncommunicativeVariableName:
- remediation_points: 150_000
- content: |
- An `Uncommunicative Variable Name` is a variable name that doesn't communicate its intent well enough.
- Poor names make it hard for the reader to build a mental picture of what's going on in the code. They can also be mis-interpreted; and they hurt the flow of reading, because the reader must slow down to interpret the names.
- UnusedParameters:
- remediation_points: 200_000
- content: |
- `Unused Parameter` refers to methods with parameters that are unused in scope of the method.
- Having unused parameters in a method is code smell because leaving dead code in a method can never improve the method and it makes the code confusing to read.
- ## Example
- Given:
- ```Ruby
- class Klass
- def unused_parameters(x,y,z)
- puts x,y # but not z
- end
- end
- ```
- Reek would emit the following warning:
- ```
- [2]:Klass#unused_parameters has unused parameter 'z' (UnusedParameters)
- ```
- UnusedPrivateMethod:
- remediation_points: 200_000
- content: |
- Classes should use their private methods. Otherwise this is dead
- code which is confusing and bad for maintenance.
- The `Unused Private Method` detector reports unused private instance
- methods and instance methods only - class methods are ignored.
- ## Example
- Given:
- ```Ruby
- class Car
- private
- def drive; end
- def start; end
- end
- ```
- `Reek` would emit the following warning:
- ```
- 2 warnings:
- [3]:Car has the unused private instance method `drive` (UnusedPrivateMethod)
- [4]:Car has the unused private instance method `start` (UnusedPrivateMethod)
- ```
- UtilityFunction:
- remediation_points: 250_000
- content: |
- A _Utility Function_ is any instance method that has no dependency on the state of the instance.
- SubclassedFromCoreClass:
- remediation_points: 250_000
- content: |
- Subclassing core classes in Ruby can lead to unexpected side effects.
- Knowing that Ruby has a core library, which is written in C,
- and a standard library, which is written in Ruby,
- if you do not know exactly how these core classes operate at the C level,
- you are gonna have a bad time.
- Source: http://words.steveklabnik.com/beware-subclassing-ruby-core-classes