reek 2.2.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (157) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -1
  3. data/.travis.yml +9 -4
  4. data/CHANGELOG +8 -0
  5. data/Gemfile +6 -4
  6. data/README.md +6 -0
  7. data/docs/API.md +51 -22
  8. data/docs/Configuration-Files.md +12 -1
  9. data/docs/Feature-Envy.md +30 -10
  10. data/docs/How-reek-works-internally.md +109 -39
  11. data/docs/RSpec-matchers.md +26 -22
  12. data/docs/Reek-Driven-Development.md +0 -8
  13. data/docs/Utility-Function.md +8 -10
  14. data/features/{ruby_api/api.feature → command_line_interface/basic_usage.feature} +2 -2
  15. data/features/programmatic_access.feature +21 -2
  16. data/features/samples.feature +3 -1
  17. data/lib/reek.rb +2 -2
  18. data/lib/reek/{core → ast}/ast_node_class_map.rb +8 -8
  19. data/lib/reek/{sexp/sexp_node.rb → ast/node.rb} +47 -6
  20. data/lib/reek/{core → ast}/object_refs.rb +2 -1
  21. data/lib/reek/{core → ast}/reference_collector.rb +2 -1
  22. data/lib/reek/{sexp → ast}/sexp_extensions.rb +10 -5
  23. data/lib/reek/{sexp → ast}/sexp_formatter.rb +7 -5
  24. data/lib/reek/cli/application.rb +1 -0
  25. data/lib/reek/cli/command.rb +1 -0
  26. data/lib/reek/cli/input.rb +4 -1
  27. data/lib/reek/cli/option_interpreter.rb +6 -4
  28. data/lib/reek/cli/options.rb +2 -1
  29. data/lib/reek/cli/reek_command.rb +3 -2
  30. data/lib/reek/cli/silencer.rb +1 -0
  31. data/lib/reek/{core → cli}/warning_collector.rb +2 -1
  32. data/lib/reek/code_comment.rb +36 -0
  33. data/lib/reek/configuration/app_configuration.rb +17 -2
  34. data/lib/reek/configuration/configuration_file_finder.rb +1 -0
  35. data/lib/reek/{core → context}/code_context.rb +7 -5
  36. data/lib/reek/{core → context}/method_context.rb +5 -3
  37. data/lib/reek/{core → context}/module_context.rb +8 -3
  38. data/lib/reek/{core/stop_context.rb → context/root_context.rb} +4 -2
  39. data/lib/reek/{core → context}/singleton_method_context.rb +2 -1
  40. data/lib/reek/examiner.rb +82 -0
  41. data/lib/reek/report/formatter.rb +70 -0
  42. data/lib/reek/report/heading_formatter.rb +45 -0
  43. data/lib/reek/report/location_formatter.rb +35 -0
  44. data/lib/reek/report/report.rb +198 -0
  45. data/lib/reek/smells.rb +24 -13
  46. data/lib/reek/smells/attribute.rb +6 -4
  47. data/lib/reek/smells/boolean_parameter.rb +3 -1
  48. data/lib/reek/smells/class_variable.rb +3 -1
  49. data/lib/reek/smells/control_parameter.rb +3 -1
  50. data/lib/reek/smells/data_clump.rb +3 -1
  51. data/lib/reek/smells/duplicate_method_call.rb +3 -1
  52. data/lib/reek/smells/feature_envy.rb +3 -1
  53. data/lib/reek/smells/irresponsible_module.rb +12 -7
  54. data/lib/reek/smells/long_parameter_list.rb +5 -3
  55. data/lib/reek/smells/long_yield_list.rb +3 -1
  56. data/lib/reek/smells/module_initialize.rb +3 -1
  57. data/lib/reek/smells/nested_iterators.rb +3 -1
  58. data/lib/reek/smells/nil_check.rb +3 -1
  59. data/lib/reek/smells/prima_donna_method.rb +3 -1
  60. data/lib/reek/smells/repeated_conditional.rb +5 -3
  61. data/lib/reek/{core → smells}/smell_configuration.rb +3 -1
  62. data/lib/reek/smells/smell_detector.rb +9 -7
  63. data/lib/reek/{core → smells}/smell_repository.rb +3 -2
  64. data/lib/reek/smells/smell_warning.rb +6 -4
  65. data/lib/reek/smells/too_many_instance_variables.rb +3 -1
  66. data/lib/reek/smells/too_many_methods.rb +3 -1
  67. data/lib/reek/smells/too_many_statements.rb +3 -1
  68. data/lib/reek/smells/uncommunicative_method_name.rb +3 -1
  69. data/lib/reek/smells/uncommunicative_module_name.rb +3 -1
  70. data/lib/reek/smells/uncommunicative_parameter_name.rb +3 -1
  71. data/lib/reek/smells/uncommunicative_variable_name.rb +3 -1
  72. data/lib/reek/smells/unused_parameters.rb +3 -1
  73. data/lib/reek/smells/utility_function.rb +5 -2
  74. data/lib/reek/source/source_code.rb +40 -9
  75. data/lib/reek/source/source_locator.rb +30 -12
  76. data/lib/reek/spec/should_reek.rb +5 -4
  77. data/lib/reek/spec/should_reek_of.rb +3 -2
  78. data/lib/reek/spec/should_reek_only_of.rb +5 -4
  79. data/lib/reek/tree_dresser.rb +32 -0
  80. data/lib/reek/tree_walker.rb +182 -0
  81. data/lib/reek/version.rb +1 -1
  82. data/reek.gemspec +3 -3
  83. data/spec/factories/factories.rb +2 -0
  84. data/spec/reek/{sexp/sexp_node_spec.rb → ast/node_spec.rb} +2 -2
  85. data/spec/reek/{core → ast}/object_refs_spec.rb +3 -3
  86. data/spec/reek/{core → ast}/reference_collector_spec.rb +2 -2
  87. data/spec/reek/{sexp → ast}/sexp_extensions_spec.rb +6 -16
  88. data/spec/reek/{sexp → ast}/sexp_formatter_spec.rb +2 -2
  89. data/spec/reek/cli/option_interpreter_spec.rb +2 -1
  90. data/spec/reek/{core → cli}/warning_collector_spec.rb +3 -3
  91. data/spec/reek/{core/code_comment_spec.rb → code_comment_spec.rb} +3 -3
  92. data/spec/reek/configuration/app_configuration_spec.rb +31 -18
  93. data/spec/reek/{core → context}/code_context_spec.rb +14 -15
  94. data/spec/reek/{core → context}/method_context_spec.rb +8 -8
  95. data/spec/reek/{core → context}/module_context_spec.rb +4 -4
  96. data/spec/reek/context/root_context_spec.rb +14 -0
  97. data/spec/reek/{core → context}/singleton_method_context_spec.rb +4 -4
  98. data/spec/reek/{core/examiner_spec.rb → examiner_spec.rb} +3 -42
  99. data/spec/reek/{cli → report}/html_report_spec.rb +5 -5
  100. data/spec/reek/report/json_report_spec.rb +20 -0
  101. data/spec/reek/{cli → report}/text_report_spec.rb +14 -14
  102. data/spec/reek/{cli → report}/xml_report_spec.rb +7 -7
  103. data/spec/reek/report/yaml_report_spec.rb +20 -0
  104. data/spec/reek/smells/attribute_spec.rb +2 -1
  105. data/spec/reek/smells/boolean_parameter_spec.rb +1 -1
  106. data/spec/reek/smells/class_variable_spec.rb +5 -5
  107. data/spec/reek/smells/control_parameter_spec.rb +1 -1
  108. data/spec/reek/smells/data_clump_spec.rb +1 -1
  109. data/spec/reek/smells/duplicate_method_call_spec.rb +3 -3
  110. data/spec/reek/smells/feature_envy_spec.rb +1 -1
  111. data/spec/reek/smells/irresponsible_module_spec.rb +24 -28
  112. data/spec/reek/smells/long_parameter_list_spec.rb +2 -2
  113. data/spec/reek/smells/long_yield_list_spec.rb +2 -2
  114. data/spec/reek/smells/nested_iterators_spec.rb +1 -1
  115. data/spec/reek/smells/nil_check_spec.rb +2 -2
  116. data/spec/reek/smells/prima_donna_method_spec.rb +3 -3
  117. data/spec/reek/smells/repeated_conditional_spec.rb +6 -6
  118. data/spec/reek/{core → smells}/smell_configuration_spec.rb +4 -4
  119. data/spec/reek/smells/smell_detector_shared.rb +2 -2
  120. data/spec/reek/{core → smells}/smell_repository_spec.rb +5 -4
  121. data/spec/reek/smells/too_many_instance_variables_spec.rb +1 -1
  122. data/spec/reek/smells/too_many_methods_spec.rb +4 -4
  123. data/spec/reek/smells/too_many_statements_spec.rb +2 -2
  124. data/spec/reek/smells/uncommunicative_method_name_spec.rb +1 -1
  125. data/spec/reek/smells/uncommunicative_module_name_spec.rb +4 -4
  126. data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +2 -2
  127. data/spec/reek/smells/uncommunicative_variable_name_spec.rb +3 -3
  128. data/spec/reek/smells/utility_function_spec.rb +23 -1
  129. data/spec/reek/source/source_code_spec.rb +1 -1
  130. data/spec/reek/source/source_locator_spec.rb +30 -0
  131. data/spec/reek/spec/should_reek_of_spec.rb +0 -17
  132. data/spec/reek/spec/should_reek_spec.rb +0 -25
  133. data/spec/reek/tree_dresser_spec.rb +16 -0
  134. data/spec/reek/{core/tree_walker_spec.rb → tree_walker_spec.rb} +5 -5
  135. data/spec/samples/{simple_configuration.reek → configuration/simple_configuration.reek} +0 -0
  136. data/spec/samples/configuration/with_excluded_paths.reek +4 -0
  137. data/spec/samples/source_with_exclude_paths/ignore_me/uncommunicative_method_name.rb +5 -0
  138. data/spec/samples/source_with_exclude_paths/nested/ignore_me_as_well/irresponsible_module.rb +2 -0
  139. data/spec/samples/source_with_exclude_paths/nested/uncommunicative_parameter_name.rb +6 -0
  140. data/spec/spec_helper.rb +6 -7
  141. data/tasks/develop.rake +2 -2
  142. metadata +71 -69
  143. data/lib/reek/cli/report/formatter.rb +0 -69
  144. data/lib/reek/cli/report/heading_formatter.rb +0 -45
  145. data/lib/reek/cli/report/location_formatter.rb +0 -34
  146. data/lib/reek/cli/report/report.rb +0 -191
  147. data/lib/reek/core/ast_node.rb +0 -38
  148. data/lib/reek/core/code_comment.rb +0 -37
  149. data/lib/reek/core/examiner.rb +0 -85
  150. data/lib/reek/core/tree_dresser.rb +0 -24
  151. data/lib/reek/core/tree_walker.rb +0 -180
  152. data/lib/reek/source/source_repository.rb +0 -43
  153. data/spec/reek/cli/json_report_spec.rb +0 -20
  154. data/spec/reek/cli/yaml_report_spec.rb +0 -20
  155. data/spec/reek/core/object_source_spec.rb +0 -18
  156. data/spec/reek/core/stop_context_spec.rb +0 -14
  157. data/spec/reek/core/tree_dresser_spec.rb +0 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 988f551b421341be717b93689070ad4d8b809a2a
4
- data.tar.gz: 2089735509323d24ea39425e5aa3a67ba888cb40
3
+ metadata.gz: da371c20978d5a436025bc2ecddc2df4582a9c7e
4
+ data.tar.gz: 8aa3729d6814cba34e69ff9e1696ce93450221c6
5
5
  SHA512:
6
- metadata.gz: 38f11b564b3ae5f11d53849ece35b2ea544f83c4ced89e4e344a8a4b7a2d76470f614fd51890f2379faeac35860cc01fca62c643c8a864786ac15a3640c8aa16
7
- data.tar.gz: c24c9b966287c29db29073c5aad19e4890d1949d9d49c0d38478d4ed48f45818d4665b302704a98a58867f99156ee0865cccff71ebb18fbda90bd34eeca9e2c1
6
+ metadata.gz: d15340c5aa637820c280d28008579ecc97f9de7a56bf74e47f6f217a9f593d3a1416dfa6757b4beb3900032c505eed91aa8fa21de94374ac1e2d1537706cbabb
7
+ data.tar.gz: c49a1ae86f1265e493f6e5eff700e2d02c2849358a49db20d87d9c52498138da0ca38fd7bd83cb90d0c16cb7b19789aa43422b052910b145ac611d9421bc6bf8
@@ -7,7 +7,7 @@ AllCops:
7
7
  # FIXME: Make the class shorter
8
8
  Metrics/ClassLength:
9
9
  Exclude:
10
- - lib/reek/core/tree_walker.rb
10
+ - lib/reek/tree_walker.rb
11
11
  - lib/reek/cli/options.rb
12
12
 
13
13
  # FIXME: Lower the method length by fixing the biggest offenders
@@ -2,19 +2,24 @@ sudo: false
2
2
  cache: bundler
3
3
  language: ruby
4
4
  bundler_args: --without local_development
5
- script: bundle exec rake
5
+ script:
6
+ - bundle exec rake
7
+ - bundle exec ataru check
6
8
  rvm:
7
- - 1.9.3
8
9
  - 2.0.0
9
10
  - 2.1
10
11
  - 2.2
11
- - jruby-19mode
12
12
  - rbx-2
13
13
  - ruby-head
14
- - jruby-head
15
14
  matrix:
15
+ include:
16
+ - rvm: jruby-19mode
17
+ env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false'
18
+ - rvm: jruby-head
19
+ env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false'
16
20
  allow_failures:
17
21
  - rvm: jruby-head
22
+ env: JRUBY_OPTS='--server -Xcompile.invokedynamic=false'
18
23
  - rvm: ruby-head
19
24
  fast_finish: true
20
25
  notifications:
data/CHANGELOG CHANGED
@@ -1,3 +1,11 @@
1
+ == 3.0.0
2
+
3
+ * (troessner) Make directories excludable via configuration.
4
+ * (mvz) Define and document public API
5
+ * (troessner) Recognize methods defined with class << self syntax as singleton methods.
6
+ * (troessner) Use ruby22 instead of ruby21 for parsing.
7
+ * (nTraum) Drop support for Ruby 1.9
8
+
1
9
  == 2.2.1
2
10
 
3
11
  * (mvz) Support methods using array decomposition arguments
data/Gemfile CHANGED
@@ -5,10 +5,12 @@ gemspec
5
5
 
6
6
  group :local_development do
7
7
  gem 'pry'
8
+ gem 'yard', '~> 0.8.7'
9
+
8
10
  platforms :mri do
9
- if RUBY_VERSION >= '2.0.0'
10
- gem 'byebug'
11
- gem 'pry-byebug'
12
- end
11
+ gem 'pry-byebug'
12
+ gem 'pry-stack_explorer'
13
+
14
+ gem 'redcarpet', '~> 3.3.1'
13
15
  end
14
16
  end
data/README.md CHANGED
@@ -173,8 +173,14 @@ NestedIterators:
173
173
  DataClump:
174
174
  max_copies: 3
175
175
  min_clump_size: 3
176
+
177
+ exclude_paths:
178
+ - app/views
179
+ - app/controllers
176
180
  ```
177
181
 
182
+ Additionally to the smell configuration you can exclude whole directories from scans using `exclude_paths` as you can see at the end of the configuration above.
183
+
178
184
  ### Source code comments
179
185
 
180
186
  `reek` is not the police. In case you need to suppress a smell
@@ -1,6 +1,4 @@
1
- # API
2
-
3
- ## Using `reek` inside your Ruby application
1
+ # Using `reek` inside your Ruby application
4
2
 
5
3
  `reek` can be used inside another Ruby project.
6
4
 
@@ -8,37 +6,41 @@
8
6
  gem install reek
9
7
  ```
10
8
 
9
+ ## Using a reporter
10
+
11
11
  You can use reek inside your Ruby file `check_dirty.rb`
12
12
 
13
13
  ```ruby
14
14
  require 'reek'
15
- require 'reek/source/source_code'
16
- require 'reek/cli/report/report'
17
- require 'reek/core/examiner'
18
-
19
- source =<<END
20
- class Dirty
21
- # This method smells of :reek:NestedIterators but ignores them
22
- def awful(x, y, offset = 0, log = false)
23
- puts @screen.title
24
- @screen = widgets.map { |w| w.each { |key| key += 3 * x } }
25
- puts @screen.contents
26
- fail
15
+
16
+ source = <<-END
17
+ class Dirty
18
+ # This method smells of :reek:NestedIterators but ignores them
19
+ def awful(x, y, offset = 0, log = false)
20
+ puts @screen.title
21
+ @screen = widgets.map { |w| w.each { |key| key += 3 * x } }
22
+ puts @screen.contents
23
+ fail
24
+ end
27
25
  end
28
- end
29
26
  END
30
27
 
31
- source_code = Reek::Source::SourceCode.from(source)
32
- reporter = Reek::CLI::Report::TextReport.new
33
- reporter.add_examiner Reek::Core::Examiner.new(source_code)
34
- puts reporter.show
28
+ reporter = Reek::Report::TextReport.new
29
+ examiner = Reek::Examiner.new(source)
30
+ reporter.add_examiner examiner
31
+ reporter.show
35
32
  ```
36
33
 
37
34
  This will show the list of errors in variable `source`.
38
35
 
39
- `reek` can take `source` as `String`, `File` or `IO`.
36
+ `Reek::Examiner.new` can take `source` as `String`, `File` or `IO`.
37
+
38
+ ```
39
+ # Examine a file object
40
+ reporter.add_examiner Reek::Examiner.new(File.new('dirty.rb'))
41
+ ```
40
42
 
41
- Also, besides normal text output, `reek` can also generate output in YAML,
43
+ Also, besides normal text output, `reek` can generate output in YAML,
42
44
  JSON, HTML and XML by using the following Report types:
43
45
 
44
46
  ```
@@ -48,3 +50,30 @@ JSONReport
48
50
  HTMLReport
49
51
  XMLReport
50
52
  ```
53
+
54
+ ## Accessing the smell warnings directly
55
+
56
+ You can also access the smells detected by an examiner directly:
57
+
58
+ ```ruby
59
+ require 'reek'
60
+
61
+ source = <<-END
62
+ class Dirty
63
+ # This method smells of :reek:NestedIterators but ignores them
64
+ def awful(x, y, offset = 0, log = false)
65
+ puts @screen.title
66
+ @screen = widgets.map { |w| w.each { |key| key += 3 * x } }
67
+ puts @screen.contents
68
+ fail
69
+ end
70
+ end
71
+ END
72
+
73
+ examiner = Reek::Examiner.new(source)
74
+ examiner.smells.each do |smell|
75
+ puts smell.message
76
+ end
77
+ ```
78
+
79
+ `Examiner#smells` returns a list of `SmellWarning` objects.
@@ -14,7 +14,7 @@ The order in which `reek` tries to find such a configuration file is exactly lik
14
14
 
15
15
  As soon as `reek` detects a configuration file it stops searching immediately, meaning that from `reek`'s point of view there exists one configuration file and one configuration only regardless of how many ".reek" files you might have on your filesystem.
16
16
 
17
- ## Configuration options
17
+ ## Configuration options for smells
18
18
 
19
19
  The first thing you probably want to check out are the [Basic Smell Options](Basic-Smell-Options.md) which are supported by every smell type.
20
20
  Certain smell types offer a configuration that goes beyond that of the basic smell options - for instance [Data Clump](Data-Clump.md).
@@ -36,3 +36,14 @@ DataClump:
36
36
  max_copies: 3
37
37
  min_clump_size: 3
38
38
  ```
39
+
40
+ ## Excluding directories from scans
41
+
42
+ You can exclude whole directories from scans using `exclude_paths` in your configuration file:
43
+
44
+ ```yaml
45
+ ---
46
+ exclude_paths:
47
+ - app/views
48
+ - app/controllers
49
+ ```
@@ -2,28 +2,48 @@
2
2
 
3
3
  ## Introduction
4
4
 
5
- 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.
5
+ _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.
6
6
 
7
- A simple example would be the following method, which "belongs" on the Item class and not on the Cart class:
7
+ _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.
8
+
9
+ _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.
10
+
11
+ _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.
12
+
13
+ ## Example
14
+
15
+ Running reek on:
8
16
 
9
17
  ```Ruby
10
- class Cart
11
- def price
12
- @item.price + @item.tax
18
+ class Warehouse
19
+ def sale_price(item)
20
+ (item.price - item.rebate) * @vat
13
21
  end
14
22
  end
15
23
  ```
16
24
 
17
- `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.
25
+ would report:
18
26
 
19
- `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.
27
+ ```Bash
28
+ Warehouse#total_price refers to item more than self (FeatureEnvy)
29
+ ```
20
30
 
21
- `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.
31
+ since this:
32
+
33
+ ```Ruby
34
+ (item.price - item.rebate)
35
+ ```
36
+
37
+ belongs to the Item class, not the Warehouse.
22
38
 
23
39
  ## Current Support in reek
24
40
 
25
- `Feature Envy` reports any method that refers to self less often than it refers to (ie. send messages to) some other object.
41
+ _Feature Envy_ reports any method that refers to self less often than it refers to (ie. send messages to) some other object.
42
+
43
+ ## Differences to _Utility Function_
44
+
45
+ _Feature Envy_ is only triggered if there are some references to self and _[Utility Function](Utility Function.md)_ is triggered if there are no references to self.
26
46
 
27
47
  ## Configuration
28
48
 
29
- Feature Envy supports the [Basic Smell Options](Basic-Smell-Options.md).
49
+ _Feature Envy_ supports the [Basic Smell Options](Basic-Smell-Options.md).
@@ -1,44 +1,114 @@
1
1
  # How reek works internally
2
2
 
3
- **Using reek via bin/reek:**
3
+
4
+ ## The big picture
4
5
 
5
6
  ```
6
- [bin/reek]
7
- |
8
- |
9
- |
10
- Application (cli/application.rb) +
11
- Options (cli/options)
12
- |
13
- |
14
- |
15
- ReekCommand (cli/reek_command)
16
- with Reporter (cli/report/report)
17
- / | \
18
- / | \
19
- / | \
20
- Source Source Source (source/source_code)
21
- | | |
22
- | | |
23
- | | |
24
- Examiner Examiner Examiner (examiner)
25
- |
26
- |
27
- |
28
- Examiner sets up a:
29
- - SourceRepository (source/source_repository)
30
- - a WarningCollector (core/warning_collector)
31
-
32
- The Examiner then goes through each source:
33
- - Initializing a SmellRepository (core/smell_repository)
34
- - getting the AST from the source
35
- - applying a TreeWalker(core/tree_walker) to process this syntax tree given the SmellRepository
36
- - finally have that SmellRepository reporting back on the WarningCollector mentioned above
37
- |
38
- |
39
- |
40
- In the last step, the reporter from the ReekCommand:
41
- - gathers all the warnings from the collectors of all Examiners (as you can see here https://github.com/troessner/reek/blob/master/lib/reek/cli/report/report.rb#L30)
42
- - outputs them with whatever output format we have chose via the cli options
43
- ```
7
+ ["class C; end" | reek] [reek lib/*.rb] [expect(files).not_to reek_of(:LargeClass)]
8
+ \ | |
9
+ \ | |
10
+ \ | |
11
+ \ creates a | |
12
+ \ | |
13
+ \ | |
14
+ \ | |
15
+ \ | |
16
+ \---------- Application (cli/application.rb) + |
17
+ Options (cli/options) |
18
+ | |
19
+ | |
20
+ | |
21
+ | |
22
+ creates a | |
23
+ | |
24
+ | |
25
+ | |
26
+ | |
27
+ ReekCommand (cli/reek_command) |
28
+ * uses a reporter (report/report) |
29
+ * uses a SourceLocator (source/source_locator) |
30
+ / | \ |
31
+ / | \ |
32
+ / | \ |
33
+ Source Source Source (source/source_code) |
34
+ | | | |
35
+ | | | |
36
+ | | | |
37
+ Examiner | Examiner |
38
+ | |
39
+ | |
40
+ Examiner (core/examiner) --------------------------------------
41
+ * generates the AST out of the given source
42
+ * adorns the generated AST via a TreeDresser (core/tree_dresser)
43
+ * initializes a SmellRepository with all relevant smells (smells/smell_repository)
44
+ * initializes a WarningCollector (cli/warning_collector)
45
+ * runs all corresponding smell detectors via a Treewalker (core/tree_walker) for the SmellRepository above
46
+ / | \
47
+ / | \
48
+ / | \
49
+ UtilityFunction FeatureEnvy TooManyMethods
50
+ \ | /
51
+ \ | /
52
+ \ | /
53
+ WarningCollector
54
+ |
55
+ |
56
+ |
57
+ Application output
58
+
59
+ ## A closer look at how an Examiner works
60
+
61
+ The core foundation of reek and its API is the Examiner.
62
+ As you can see above, the Examiner is run for every source it gets passed and then runs the configured SmellDetectors.
63
+ The overall workflow is like this:
64
+
65
+ Examiner
66
+ |
67
+ |
68
+ |
69
+ Initialize SmellRepository only with eligible smells
70
+ |
71
+ |
72
+ |
73
+ Generate the AST out of the given source using SourceCode#syntax_tree, which works like this:
74
+
75
+ - We generate a "rough" AST using the "parser" gem
76
+ - We then obtain the comments from the source code separately
77
+ - We pass this unprocessed AST and the comment_map to TreeDresser#dress which
78
+ returns an instance of Reek::AST::SexpNode with type-dependent SexpExtensions mixed in.
79
+
80
+ An example should make this more palpable.
81
+ Given:
82
+
83
+ class C
84
+ def m
85
+ puts 'nada'
86
+ end
87
+ end
88
+
89
+ The AST generated by the parser gem (consisting of Parser::AST::Node) looks like this:
90
+
91
+ (class
92
+ (const nil :C)
93
+ nil
94
+ (def :m
95
+ (args)
96
+ (send nil :puts
97
+ (str "nada"))))
98
+
99
+ TreeDresser#dress would transform this into a very similar tree, but this time not consisting
100
+ of Parser::AST::Node but of Reek::AST::SexpNode and with node-dependent SexpExtensions
101
+ mixed in (noted in []):
44
102
 
103
+ (class [AST::SexpExtensions::ClassNode, AST::SexpExtensions::ModuleNode]
104
+ (const nil :C) [AST::SexpExtensions::ConstNode]
105
+ nil
106
+ (def :m [AST::SexpExtensions::DefNode, AST::SexpExtensions::MethodNodeBase]
107
+ (args) [AST::SexpExtensions::ArgsNode]
108
+ (send nil :puts [AST::SexpExtensions::SendNode]
109
+ (str "nada"))))
110
+ |
111
+ |
112
+ |
113
+ A TreeWalker then traverses this now adorned tree again and
114
+ runs all SmellDetectors from the SmellRepository above
@@ -37,7 +37,7 @@ end
37
37
  Running this via
38
38
 
39
39
  ```
40
- rspec reek-integration-spec.rb
40
+ rspec reek-integration-spec.rb
41
41
  ```
42
42
 
43
43
  would give you:
@@ -65,7 +65,8 @@ rspec ./reek-integration-spec.rb:6 # Reek Integration works with reek
65
65
 
66
66
  ### `reek`
67
67
 
68
- A very generic matcher that basically just tells you if something reeks, but not after what exactly.
68
+ A very generic matcher that basically just tells you if something reeks, but
69
+ not after what exactly.
69
70
  See the "Quickstart" example from above.
70
71
 
71
72
  ### `reek_of`
@@ -78,27 +79,29 @@ Remember that this includes our "smell types" as well. So it could be the
78
79
  in reek but it could also be "Duplication" which is a "smell categgory".
79
80
 
80
81
  In theory you could pass many different types of input here:
81
- - :UtilityFunction
82
- - "UtilityFunction"
83
- - UtilityFunction (this works in our specs because we tend to do "include Reek:Smells")
84
- - Reek::Smells::UtilityFunction (the right way if you really want to pass a class)
85
- - "Duplication" or :Duplication which is an abstract "smell category"
86
-
87
- It is recommended to pass this as a symbol like :UtilityFunction. However we don't
88
- enforce this.
89
-
90
- Additionally you can be more specific and pass in "smell_details" you
91
- want to check for as well e.g. "name" or "count" (see the examples below).
92
- The parameters you can check for are depending on the smell you are checking for.
93
- For instance "count" doesn't make sense everywhere whereas "name" does in most cases.
94
- If you pass in a parameter that doesn't exist (e.g. you make a typo like "namme") reek will
95
- raise an ArgumentError to give you a hint that you passed something that doesn't make
96
- much sense.
82
+ - `:UtilityFunction`
83
+ - `"UtilityFunction"`
84
+ - `UtilityFunction` (this works in our specs because we tend to do "include
85
+ Reek:Smells")
86
+ - `Reek::Smells::UtilityFunction` (the right way if you really want to pass a
87
+ class)
88
+ - `"Duplication"` or `:Duplication` which is an abstract "smell category"
89
+
90
+ It is recommended to pass this as a symbol like `:UtilityFunction`. However we
91
+ don't enforce this.
92
+
93
+ Additionally you can be more specific and pass in "smell_details" you want to
94
+ check for as well e.g. "name" or "count" (see the examples below). The
95
+ parameters you can check for are depending on the smell you are checking for.
96
+ For instance "count" doesn't make sense everywhere whereas "name" does in most
97
+ cases. If you pass in a parameter that doesn't exist (e.g. you make a typo like
98
+ "namme") reek will raise an ArgumentError to give you a hint that you passed
99
+ something that doesn't make much sense.
97
100
 
98
101
  So in a nutshell `reek_of` takes the following two arguments:
99
102
 
100
- - smell_category - The "smell category" or "smell_type" we check for.
101
- - smells_details - A hash containing "smell warning" parameters
103
+ - `smell_category` - The "smell category" or "smell_type" we check for.
104
+ - `smells_details` - A hash containing "smell warning" parameters
102
105
 
103
106
  **Examples**
104
107
 
@@ -128,6 +131,7 @@ See the documentaton for `reek_of`.
128
131
 
129
132
  **Notable differences to reek_of:**
130
133
 
131
- 1.) `reek_of` doesn't mind if there are other smells of a different category. "reek_only_of" will fail in that case.
134
+ 1. `reek_of` doesn't mind if there are other smells of a different category.
135
+ "reek_only_of" will fail in that case.
132
136
 
133
- 2.) `reek_only_of` doesn't support the additional smell_details hash.
137
+ 2. `reek_only_of` doesn't support the additional smell_details hash.