/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

  1. ---
  2. Attribute:
  3. remediation_points: 250_000
  4. content: |
  5. 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.
  6. The same holds to a lesser extent for getters, but Reek doesn't flag those.
  7. ## Example
  8. Given:
  9. ```Ruby
  10. class Klass
  11. attr_accessor :dummy
  12. end
  13. ```
  14. Reek would emit the following warning:
  15. ```
  16. reek test.rb
  17. test.rb -- 1 warning:
  18. [2]:Klass declares the writable attribute dummy (Attribute)
  19. ```
  20. BooleanParameter:
  21. remediation_points: 500_000
  22. content: |
  23. `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.
  24. ## Example
  25. Given
  26. ```Ruby
  27. class Dummy
  28. def hit_the_switch(switch = true)
  29. if switch
  30. puts 'Hitting the switch'
  31. # do other things...
  32. else
  33. puts 'Not hitting the switch'
  34. # do other things...
  35. end
  36. end
  37. end
  38. ```
  39. Reek would emit the following warning:
  40. ```
  41. test.rb -- 3 warnings:
  42. [1]:Dummy#hit_the_switch has boolean parameter 'switch' (BooleanParameter)
  43. [2]:Dummy#hit_the_switch is controlled by argument switch (ControlParameter)
  44. ```
  45. Note that both smells are reported, `Boolean Parameter` and `Control Parameter`.
  46. ## Getting rid of the smell
  47. This is highly dependent on your exact architecture, but looking at the example above what you could do is:
  48. * Move everything in the `if` branch into a separate method
  49. * Move everything in the `else` branch into a separate method
  50. * Get rid of the `hit_the_switch` method alltogether
  51. * Make the decision what method to call in the initial caller of `hit_the_switch`
  52. ClassVariable:
  53. remediation_points: 350_000
  54. content: |
  55. 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).
  56. 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/)
  57. ## Example
  58. Given
  59. ```Ruby
  60. class Dummy
  61. @@class_variable = :whatever
  62. end
  63. ```
  64. Reek would emit the following warning:
  65. ```
  66. reek test.rb
  67. test.rb -- 1 warning:
  68. [2]:Dummy declares the class variable @@class_variable (ClassVariable)
  69. ```
  70. ## Getting rid of the smell
  71. You can use class-instance variable to mitigate the problem (as also suggested in the linked article above):
  72. ```Ruby
  73. class Dummy
  74. @class_variable = :whatever
  75. end
  76. ```
  77. ControlParameter:
  78. remediation_points: 500_000
  79. content: |
  80. `Control Parameter` is a special case of `Control Couple`
  81. ## Example
  82. A simple example would be the "quoted" parameter in the following method:
  83. ```Ruby
  84. def write(quoted)
  85. if quoted
  86. write_quoted @value
  87. else
  88. write_unquoted @value
  89. end
  90. end
  91. ```
  92. 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".
  93. DataClump:
  94. remediation_points: 250_000
  95. content: |
  96. 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.
  97. 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.
  98. ## Example
  99. Given
  100. ```Ruby
  101. class Dummy
  102. def x(y1,y2); end
  103. def y(y1,y2); end
  104. def z(y1,y2); end
  105. end
  106. ```
  107. Reek would emit the following warning:
  108. ```
  109. test.rb -- 1 warning:
  110. [2, 3, 4]:Dummy takes parameters [y1, y2] to 3 methods (DataClump)
  111. ```
  112. A possible way to fix this problem (quoting from [Martin Fowler](http://martinfowler.com/bliki/DataClump.html)):
  113. > 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.
  114. DuplicateMethodCall:
  115. remediation_points: 350_000
  116. content: |
  117. Duplication occurs when two fragments of code look nearly identical, or when two fragments of code have nearly identical effects at some conceptual level.
  118. Reek implements a check for _Duplicate Method Call_.
  119. ## Example
  120. Here's a very much simplified and contrived example. The following method will report a warning:
  121. ```Ruby
  122. def double_thing()
  123. @other.thing + @other.thing
  124. end
  125. ```
  126. One quick approach to silence Reek would be to refactor the code thus:
  127. ```Ruby
  128. def double_thing()
  129. thing = @other.thing
  130. thing + thing
  131. end
  132. ```
  133. A slightly different approach would be to replace all calls of `double_thing` by calls to `@other.double_thing`:
  134. ```Ruby
  135. class Other
  136. def double_thing()
  137. thing + thing
  138. end
  139. end
  140. ```
  141. The approach you take will depend on balancing other factors in your code.
  142. FeatureEnvy:
  143. remediation_points: 500_000
  144. content: |
  145. _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.
  146. _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.
  147. _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.
  148. _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.
  149. ## Example
  150. Running Reek on:
  151. ```Ruby
  152. class Warehouse
  153. def sale_price(item)
  154. (item.price - item.rebate) * @vat
  155. end
  156. end
  157. ```
  158. would report:
  159. ```Bash
  160. Warehouse#total_price refers to item more than self (FeatureEnvy)
  161. ```
  162. since this:
  163. ```Ruby
  164. (item.price - item.rebate)
  165. ```
  166. belongs to the Item class, not the Warehouse.
  167. InstanceVariableAssumption:
  168. remediation_points: 350_000
  169. content: |
  170. Classes should not assume that instance variables are set or present outside of the current class definition.
  171. Good:
  172. ```Ruby
  173. class Foo
  174. def initialize
  175. @bar = :foo
  176. end
  177. def foo?
  178. @bar == :foo
  179. end
  180. end
  181. ```
  182. Good as well:
  183. ```Ruby
  184. class Foo
  185. def foo?
  186. bar == :foo
  187. end
  188. def bar
  189. @bar ||= :foo
  190. end
  191. end
  192. ```
  193. Bad:
  194. ```Ruby
  195. class Foo
  196. def go_foo!
  197. @bar = :foo
  198. end
  199. def foo?
  200. @bar == :foo
  201. end
  202. end
  203. ```
  204. ## Example
  205. Running Reek on:
  206. ```Ruby
  207. class Dummy
  208. def test
  209. @ivar
  210. end
  211. end
  212. ```
  213. would report:
  214. ```Bash
  215. [1]:InstanceVariableAssumption: Dummy assumes too much for instance variable @ivar
  216. ```
  217. Note that this example would trigger this smell warning as well:
  218. ```Ruby
  219. class Parent
  220. def initialize(omg)
  221. @omg = omg
  222. end
  223. end
  224. class Child < Parent
  225. def foo
  226. @omg
  227. end
  228. end
  229. ```
  230. 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:
  231. ```Ruby
  232. class Parent
  233. attr_reader :omg
  234. def initialize(omg)
  235. @omg = omg
  236. end
  237. end
  238. class Child < Parent
  239. def foo
  240. omg
  241. end
  242. end
  243. ```
  244. 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.
  245. If you don't want to expose those methods as public API just make them private like this:
  246. ```Ruby
  247. class Parent
  248. def initialize(omg)
  249. @omg = omg
  250. end
  251. private
  252. attr_reader :omg
  253. end
  254. class Child < Parent
  255. def foo
  256. omg
  257. end
  258. end
  259. ```
  260. ## Current Support in Reek
  261. An instance variable must:
  262. * be set in the constructor
  263. * or be accessed through a method with lazy initialization / memoization.
  264. If not, _Instance Variable Assumption_ will be reported.
  265. IrresponsibleModule:
  266. remediation_points: 350_000
  267. content: |
  268. 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.
  269. ## Example
  270. Given
  271. ```Ruby
  272. class Dummy
  273. # Do things...
  274. end
  275. ```
  276. Reek would emit the following warning:
  277. ```
  278. test.rb -- 1 warning:
  279. [1]:Dummy has no descriptive comment (IrresponsibleModule)
  280. ```
  281. Fixing this is simple - just an explaining comment:
  282. ```Ruby
  283. # The Dummy class is responsible for ...
  284. class Dummy
  285. # Do things...
  286. end
  287. ```
  288. LongParameterList:
  289. remediation_points: 500_000
  290. content: |
  291. A `Long Parameter List` occurs when a method has a lot of parameters.
  292. ## Example
  293. Given
  294. ```Ruby
  295. class Dummy
  296. def long_list(foo,bar,baz,fling,flung)
  297. puts foo,bar,baz,fling,flung
  298. end
  299. end
  300. ```
  301. Reek would report the following warning:
  302. ```
  303. test.rb -- 1 warning:
  304. [2]:Dummy#long_list has 5 parameters (LongParameterList)
  305. ```
  306. A common solution to this problem would be the introduction of parameter objects.
  307. LongYieldList:
  308. remediation_points: 500_000
  309. content: |
  310. A _Long Yield List_ occurs when a method yields a lot of arguments to the block it gets passed.
  311. ## Example
  312. ```Ruby
  313. class Dummy
  314. def yields_a_lot(foo,bar,baz,fling,flung)
  315. yield foo,bar,baz,fling,flung
  316. end
  317. end
  318. ```
  319. Reek would report the following warning:
  320. ```
  321. test.rb -- 1 warning:
  322. [4]:Dummy#yields_a_lot yields 5 parameters (LongYieldList)
  323. ```
  324. A common solution to this problem would be the introduction of parameter objects.
  325. ManualDispatch:
  326. remediation_points: 350_000
  327. content: |
  328. 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.
  329. ## Example
  330. ```Ruby
  331. class MyManualDispatcher
  332. attr_reader :foo
  333. def initialize(foo)
  334. @foo = foo
  335. end
  336. def call
  337. foo.bar if foo.respond_to?(:bar)
  338. end
  339. end
  340. ```
  341. Reek would emit the following warning:
  342. ```
  343. test.rb -- 1 warning:
  344. [9]: MyManualDispatcher manually dispatches method call (ManualDispatch)
  345. ```
  346. ModuleInitialize:
  347. remediation_points: 350_000
  348. content: |
  349. A module is usually a mixin, so when an `#initialize` method is present it is
  350. hard to tell initialization order and parameters so having `#initialize`
  351. in a module is usually a bad idea.
  352. ## Example
  353. The `Foo` module below contains a method `initialize`. Although class `B` inherits from `A`, the inclusion of `Foo` stops `A#initialize` from being called.
  354. ```Ruby
  355. class A
  356. def initialize(a)
  357. @a = a
  358. end
  359. end
  360. module Foo
  361. def initialize(foo)
  362. @foo = foo
  363. end
  364. end
  365. class B < A
  366. include Foo
  367. def initialize(b)
  368. super('bar')
  369. @b = b
  370. end
  371. end
  372. ```
  373. A simple solution is to rename `Foo#initialize` and call that method by name:
  374. ```Ruby
  375. module Foo
  376. def setup_foo_module(foo)
  377. @foo = foo
  378. end
  379. end
  380. class B < A
  381. include Foo
  382. def initialize(b)
  383. super 'bar'
  384. setup_foo_module('foo')
  385. @b = b
  386. end
  387. end
  388. ```
  389. NestedIterators:
  390. remediation_points: 500_000
  391. content: |
  392. A `Nested Iterator` occurs when a block contains another block.
  393. ## Example
  394. Given
  395. ```Ruby
  396. class Duck
  397. class << self
  398. def duck_names
  399. %i!tick trick track!.each do |surname|
  400. %i!duck!.each do |last_name|
  401. puts "full name is #{surname} #{last_name}"
  402. end
  403. end
  404. end
  405. end
  406. end
  407. ```
  408. Reek would report the following warning:
  409. ```
  410. test.rb -- 1 warning:
  411. [5]:Duck#duck_names contains iterators nested 2 deep (NestedIterators)
  412. ```
  413. NilCheck:
  414. remediation_points: 250_000
  415. content: |
  416. A `NilCheck` is a type check. Failures of `NilCheck` violate the ["tell, don't ask"](http://robots.thoughtbot.com/tell-dont-ask) principle.
  417. Additionally, type checks often mask bigger problems in your source code like not using OOP and / or polymorphism when you should.
  418. ## Example
  419. Given
  420. ```Ruby
  421. class Klass
  422. def nil_checker(argument)
  423. if argument.nil?
  424. puts "argument isn't nil!"
  425. end
  426. end
  427. end
  428. ```
  429. Reek would emit the following warning:
  430. ```
  431. test.rb -- 1 warning:
  432. [3]:Klass#nil_checker performs a nil-check. (NilCheck)
  433. ```
  434. MissingSafeMethod:
  435. remediation_points: 250_000
  436. content: |
  437. A candidate method for the `Missing Safe Method` smell are methods whose names end with an exclamation mark.
  438. 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) ):
  439. >>
  440. The ! in method names that end with ! means, This method is dangerousor, more precisely, this method is the dangerous version of an otherwise equivalent method, with the same name minus the !. Danger is relative; the ! doesnt mean anything at all unless the method name its in corresponds to a similar but bang-less method name.
  441. 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.
  442. 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.
  443. ## Example
  444. Given
  445. ```Ruby
  446. class C
  447. def foo; end
  448. def foo!; end
  449. def bar!; end
  450. end
  451. ```
  452. Reek would report `bar!` as `Missing Safe Method` smell but not `foo!`.
  453. Reek reports this smell only in a class context, not in a module context in order to allow perfectly legit code like this:
  454. ```Ruby
  455. class Parent
  456. def foo; end
  457. end
  458. module Dangerous
  459. def foo!; end
  460. end
  461. class Son < Parent
  462. include Dangerous
  463. end
  464. class Daughter < Parent
  465. end
  466. ```
  467. In this example, Reek would not report the `Missing Safe Method` smell for the method `foo` of the `Dangerous` module.
  468. RepeatedConditional:
  469. remediation_points: 400_000
  470. content: |
  471. `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.
  472. ## Example
  473. Given
  474. ```Ruby
  475. class RepeatedConditionals
  476. attr_accessor :switch
  477. def repeat_1
  478. puts "Repeat 1!" if switch
  479. end
  480. def repeat_2
  481. puts "Repeat 2!" if switch
  482. end
  483. def repeat_3
  484. puts "Repeat 3!" if switch
  485. end
  486. end
  487. ```
  488. Reek would emit the following warning:
  489. ```
  490. test.rb -- 4 warnings:
  491. [5, 9, 13]:RepeatedConditionals tests switch at least 3 times (RepeatedConditional)
  492. ```
  493. If you get this warning then you are probably not using the right abstraction or even more probable, missing an additional abstraction.
  494. TooManyInstanceVariables:
  495. remediation_points: 500_000
  496. content: |
  497. `Too Many Instance Variables` is a special case of `LargeClass`.
  498. ## Example
  499. Given this configuration
  500. ```yaml
  501. TooManyInstanceVariables:
  502. max_instance_variables: 3
  503. ```
  504. and this code:
  505. ```Ruby
  506. class TooManyInstanceVariables
  507. def initialize
  508. @arg_1 = :dummy
  509. @arg_2 = :dummy
  510. @arg_3 = :dummy
  511. @arg_4 = :dummy
  512. end
  513. end
  514. ```
  515. Reek would emit the following warning:
  516. ```
  517. test.rb -- 5 warnings:
  518. [1]:TooManyInstanceVariables has at least 4 instance variables (TooManyInstanceVariables)
  519. ```
  520. TooManyConstants:
  521. remediation_points: 500_000
  522. content: |
  523. `Too Many Constants` is a special case of `LargeClass`.
  524. ## Example
  525. Given this configuration
  526. ```yaml
  527. TooManyConstants:
  528. max_constants: 3
  529. ```
  530. and this code:
  531. ```Ruby
  532. class TooManyConstants
  533. CONST_1 = :dummy
  534. CONST_2 = :dummy
  535. CONST_3 = :dummy
  536. CONST_4 = :dummy
  537. end
  538. ```
  539. Reek would emit the following warning:
  540. ```
  541. test.rb -- 1 warnings:
  542. [1]:TooManyConstants has 4 constants (TooManyConstants)
  543. ```
  544. TooManyMethods:
  545. remediation_points: 500_000
  546. content: |
  547. `Too Many Methods` is a special case of `LargeClass`.
  548. ## Example
  549. Given this configuration
  550. ```yaml
  551. TooManyMethods:
  552. max_methods: 3
  553. ```
  554. and this code:
  555. ```Ruby
  556. class TooManyMethods
  557. def one; end
  558. def two; end
  559. def three; end
  560. def four; end
  561. end
  562. ```
  563. Reek would emit the following warning:
  564. ```
  565. test.rb -- 1 warning:
  566. [1]:TooManyMethods has at least 4 methods (TooManyMethods)
  567. ```
  568. TooManyStatements:
  569. remediation_points: 500_000
  570. content: |
  571. A method with `Too Many Statements` is any method that has a large number of lines.
  572. `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.
  573. So the following method would score +6 in Reek's statement-counting algorithm:
  574. ```Ruby
  575. def parse(arg, argv, &error)
  576. if !(val = arg) and (argv.empty? or /\A-/ =~ (val = argv[0]))
  577. return nil, block, nil # +1
  578. end
  579. opt = (val = parse_arg(val, &error))[1] # +2
  580. val = conv_arg(*val) # +3
  581. if opt and !arg
  582. argv.shift # +4
  583. else
  584. val[0] = nil # +5
  585. end
  586. val # +6
  587. end
  588. ```
  589. (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.)
  590. UncommunicativeMethodName:
  591. remediation_points: 150_000
  592. content: |
  593. An `Uncommunicative Method Name` is a method name that doesn't communicate its intent well enough.
  594. 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.
  595. UncommunicativeModuleName:
  596. remediation_points: 150_000
  597. content: |
  598. An `Uncommunicative Module Name` is a module name that doesn't communicate its intent well enough.
  599. 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.
  600. UncommunicativeParameterName:
  601. remediation_points: 150_000
  602. content: |
  603. An `Uncommunicative Parameter Name` is a parameter name that doesn't communicate its intent well enough.
  604. 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.
  605. UncommunicativeVariableName:
  606. remediation_points: 150_000
  607. content: |
  608. An `Uncommunicative Variable Name` is a variable name that doesn't communicate its intent well enough.
  609. 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.
  610. UnusedParameters:
  611. remediation_points: 200_000
  612. content: |
  613. `Unused Parameter` refers to methods with parameters that are unused in scope of the method.
  614. 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.
  615. ## Example
  616. Given:
  617. ```Ruby
  618. class Klass
  619. def unused_parameters(x,y,z)
  620. puts x,y # but not z
  621. end
  622. end
  623. ```
  624. Reek would emit the following warning:
  625. ```
  626. [2]:Klass#unused_parameters has unused parameter 'z' (UnusedParameters)
  627. ```
  628. UnusedPrivateMethod:
  629. remediation_points: 200_000
  630. content: |
  631. Classes should use their private methods. Otherwise this is dead
  632. code which is confusing and bad for maintenance.
  633. The `Unused Private Method` detector reports unused private instance
  634. methods and instance methods only - class methods are ignored.
  635. ## Example
  636. Given:
  637. ```Ruby
  638. class Car
  639. private
  640. def drive; end
  641. def start; end
  642. end
  643. ```
  644. `Reek` would emit the following warning:
  645. ```
  646. 2 warnings:
  647. [3]:Car has the unused private instance method `drive` (UnusedPrivateMethod)
  648. [4]:Car has the unused private instance method `start` (UnusedPrivateMethod)
  649. ```
  650. UtilityFunction:
  651. remediation_points: 250_000
  652. content: |
  653. A _Utility Function_ is any instance method that has no dependency on the state of the instance.
  654. SubclassedFromCoreClass:
  655. remediation_points: 250_000
  656. content: |
  657. Subclassing core classes in Ruby can lead to unexpected side effects.
  658. Knowing that Ruby has a core library, which is written in C,
  659. and a standard library, which is written in Ruby,
  660. if you do not know exactly how these core classes operate at the C level,
  661. you are gonna have a bad time.
  662. Source: http://words.steveklabnik.com/beware-subclassing-ruby-core-classes