reek 1.2.7.3 → 1.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. data/History.txt +17 -0
  2. data/README.md +32 -48
  3. data/config/defaults.reek +7 -1
  4. data/features/api.feature +20 -0
  5. data/features/masking_smells.feature +41 -0
  6. data/features/options.feature +4 -0
  7. data/features/rake_task.feature +14 -0
  8. data/features/yaml.feature +8 -8
  9. data/lib/reek.rb +1 -1
  10. data/lib/reek/cli/command_line.rb +9 -2
  11. data/lib/reek/cli/reek_command.rb +5 -4
  12. data/lib/reek/cli/yaml_command.rb +2 -2
  13. data/lib/reek/core/code_context.rb +10 -1
  14. data/lib/reek/core/method_context.rb +2 -2
  15. data/lib/reek/core/sniffer.rb +3 -1
  16. data/lib/reek/core/stop_context.rb +4 -0
  17. data/lib/reek/examiner.rb +2 -2
  18. data/lib/reek/rake/task.rb +16 -0
  19. data/lib/reek/smell_warning.rb +3 -2
  20. data/lib/reek/smells/attribute.rb +13 -9
  21. data/lib/reek/smells/boolean_parameter.rb +14 -9
  22. data/lib/reek/smells/class_variable.rb +16 -5
  23. data/lib/reek/smells/control_couple.rb +11 -6
  24. data/lib/reek/smells/data_clump.rb +33 -30
  25. data/lib/reek/smells/duplication.rb +39 -8
  26. data/lib/reek/smells/feature_envy.rb +7 -8
  27. data/lib/reek/smells/irresponsible_module.rb +12 -3
  28. data/lib/reek/smells/large_class.rb +31 -15
  29. data/lib/reek/smells/long_method.rb +15 -5
  30. data/lib/reek/smells/long_parameter_list.rb +14 -7
  31. data/lib/reek/smells/long_yield_list.rb +12 -9
  32. data/lib/reek/smells/nested_iterators.rb +46 -11
  33. data/lib/reek/smells/simulated_polymorphism.rb +16 -8
  34. data/lib/reek/smells/smell_detector.rb +13 -13
  35. data/lib/reek/smells/uncommunicative_method_name.rb +12 -20
  36. data/lib/reek/smells/uncommunicative_module_name.rb +17 -19
  37. data/lib/reek/smells/uncommunicative_parameter_name.rb +22 -15
  38. data/lib/reek/smells/uncommunicative_variable_name.rb +24 -18
  39. data/lib/reek/smells/utility_function.rb +6 -6
  40. data/lib/reek/source/code_comment.rb +19 -1
  41. data/lib/reek/source/tree_dresser.rb +40 -22
  42. data/reek.gemspec +6 -4
  43. data/spec/matchers/smell_of_matcher.rb +58 -0
  44. data/spec/reek/core/code_context_spec.rb +4 -2
  45. data/spec/reek/core/code_parser_spec.rb +2 -1
  46. data/spec/reek/core/method_context_spec.rb +5 -5
  47. data/spec/reek/smells/attribute_spec.rb +2 -4
  48. data/spec/reek/smells/boolean_parameter_spec.rb +32 -42
  49. data/spec/reek/smells/class_variable_spec.rb +22 -6
  50. data/spec/reek/smells/control_couple_spec.rb +15 -14
  51. data/spec/reek/smells/data_clump_spec.rb +29 -111
  52. data/spec/reek/smells/duplication_spec.rb +79 -49
  53. data/spec/reek/smells/feature_envy_spec.rb +1 -2
  54. data/spec/reek/smells/irresponsible_module_spec.rb +43 -22
  55. data/spec/reek/smells/large_class_spec.rb +34 -59
  56. data/spec/reek/smells/long_method_spec.rb +15 -10
  57. data/spec/reek/smells/long_parameter_list_spec.rb +24 -24
  58. data/spec/reek/smells/long_yield_list_spec.rb +13 -14
  59. data/spec/reek/smells/nested_iterators_spec.rb +93 -76
  60. data/spec/reek/smells/smell_detector_shared.rb +4 -2
  61. data/spec/reek/smells/uncommunicative_method_name_spec.rb +10 -27
  62. data/spec/reek/smells/uncommunicative_module_name_spec.rb +22 -23
  63. data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +36 -26
  64. data/spec/reek/smells/uncommunicative_variable_name_spec.rb +45 -48
  65. data/spec/reek/smells/utility_function_spec.rb +14 -13
  66. data/spec/reek/source/code_comment_spec.rb +61 -3
  67. data/spec/reek/source/tree_dresser_spec.rb +96 -1
  68. data/spec/samples/config/allow_duplication.reek +3 -0
  69. data/spec/samples/config/deeper_nested_iterators.reek +3 -0
  70. data/spec/samples/demo/demo.rb +8 -0
  71. data/spec/samples/inline_config/dirty.rb +16 -0
  72. data/spec/samples/inline_config/masked.reek +7 -0
  73. data/spec/samples/mask_some/dirty.rb +8 -0
  74. data/spec/samples/mask_some/some.reek +8 -0
  75. data/spec/spec_helper.rb +2 -0
  76. metadata +15 -5
@@ -1,3 +1,20 @@
1
+ == 1.2.8 (2010-04-26)
2
+
3
+ === Major Changes
4
+ * Smell detectors can be configured or disabled in code comments
5
+ ** Comment with :reek:smell_name disables the named smell for a class, module or method
6
+ ** Comment with :reek:smell_name:{...} for more detailed configuration
7
+ * Additional config file(s) can be specified:
8
+ ** on the command-line using -c
9
+ ** via Reek::Rake::Task in the rakefile
10
+
11
+ === Minor Changes
12
+ * Duplication can be configured to ignore specific calls
13
+ * IrresponsibleModule now reports scoped module names correctly (#66)
14
+ * NestedIterators is now more configurable:
15
+ ** Option to specify permitted nesting depth (#14)
16
+ ** Option to ignore certain iterator methods
17
+
1
18
  == 1.2.7.3 (2010-03-29)
2
19
 
3
20
  === Minor Changes
data/README.md CHANGED
@@ -1,5 +1,4 @@
1
- Reek -- code smell detection for Ruby
2
- =====================================
1
+ # Reek -- code smell detection for Ruby
3
2
 
4
3
  Reek is a tool that examines Ruby classes, modules and methods and
5
4
  reports any code smells it finds. Install it like this:
@@ -16,63 +15,47 @@ or run
16
15
 
17
16
  $ reek --help
18
17
 
19
- Example
20
- -------
21
-
22
- Imagine a source file <tt>csv_writer.rb</tt> containing:
23
-
24
- class CsvWriter
25
- def write_line(fields)
26
- if (fields.length == 0)
27
- puts
28
- else
29
- write_field(fields[0])
30
- 1.upto(fields.length-1) do |i|
31
- print ","
32
- write_field(fields[i])
33
- end
34
- puts
35
- end
36
- end
18
+ ## Example
19
+
20
+ Imagine a source file <tt>demo.rb</tt> containing:
37
21
 
38
- #...
22
+ class Dirty
23
+ # This method smells of :reek:NestedIterators but ignores them
24
+ def awful(x, y, offset = 0, log = false)
25
+ puts @screen.title
26
+ @screen = widgets.map {|w| w.each {|key| key += 3}}
27
+ puts @screen.contents
28
+ end
39
29
  end
40
30
 
41
31
  Reek will report the following code smells in this file:
42
32
 
43
- $ reek csv_writer.rb
44
- CsvWriter#write_line calls fields.length multiple times (Duplication)
45
- CsvWriter#write_line has approx 6 statements (Long Method)
46
- CsvWriter#write_line/block has the variable name 'i' (Uncommunicative Name)
47
-
48
- Features
49
- --------
50
-
51
- Reek currently includes checks for some aspects of the following smells:
52
-
53
- * [Control Couple](http://wiki.github.com/kevinrutherford/reek/control-couple)
54
- * [Data Clump](http://wiki.github.com/kevinrutherford/reek/data-clump)
55
- * [Feature Envy](http://wiki.github.com/kevinrutherford/reek/feature-envy)
56
- * [Large Class](http://wiki.github.com/kevinrutherford/reek/large-class)
57
- * [Long Method](http://wiki.github.com/kevinrutherford/reek/long-method)
58
- * [Long Parameter List](http://wiki.github.com/kevinrutherford/reek/long-parameter-list)
59
- * [Simulated Polymorphism](http://wiki.github.com/kevinrutherford/reek/simulated-polymorphism)
60
- * [Uncommunicative Name](http://wiki.github.com/kevinrutherford/reek/uncommunicative-name)
61
-
62
- ...and more. See the [Reek wiki](http://wiki.github.com/kevinrutherford/reek/code-smells)
33
+ $ reek demo.rb
34
+ spec/samples/demo/demo.rb -- 6 warnings:
35
+ Dirty has no descriptive comment (IrresponsibleModule)
36
+ Dirty#awful has 4 parameters (LongParameterList)
37
+ Dirty#awful has boolean parameter 'log' (ControlCouple)
38
+ Dirty#awful has the parameter name 'x' (UncommunicativeName)
39
+ Dirty#awful has the parameter name 'y' (UncommunicativeName)
40
+ Dirty#awful has the variable name 'w' (UncommunicativeName)
41
+
42
+ ## Features
43
+
44
+ Reek currently includes checks for some aspects of Control Couple,
45
+ Data Clump, Feature Envy, Large Class, Long Method, Long Parameter List,
46
+ Simulated Polymorphism, Uncommunicative Name and more.
47
+ See the [Reek wiki](http://wiki.github.com/kevinrutherford/reek/code-smells)
63
48
  for up to date details of exactly what Reek will check in your code.
64
49
 
65
- Tool Integration
66
- ----------------
50
+ ### Tool Integration
67
51
 
68
52
  Reek integrates with many of your favourite tools:
69
53
 
70
- * Use `Reek::Rake::Task` to easily add Reek to your Rakefile
71
- * Use `Reek::Spec` to add the `should_not reek` custom matcher to your Rspec examples
54
+ * `require 'reek/rake/task'` to easily add Reek to your Rakefile
55
+ * `require 'reek/spec'` to add the `should_not reek` custom matcher to your Rspec examples
72
56
  * Reek is fully compliant with Ruby 1.8.6, 1.8.7 and 1.9.1
73
57
 
74
- Dependencies
75
- ------------
58
+ ### Dependencies
76
59
 
77
60
  Reek makes use of the following other gems:
78
61
 
@@ -86,5 +69,6 @@ Learn More
86
69
  Find out more about Reek from any of the following sources:
87
70
 
88
71
  * Browse the Reek documentation at [http://wiki.github.com/kevinrutherford/reek](http://wiki.github.com/kevinrutherford/reek)
89
- * Browse the code or install the latest cutting-edge beta version from [http://github.com/kevinrutherford/reek/tree](http://github.com/kevinrutherford/reek/tree)
72
+ * Browse the code or install the latest development version from [http://github.com/kevinrutherford/reek/tree](http://github.com/kevinrutherford/reek/tree)
90
73
  * Read the code API at [http://rdoc.info/projects/kevinrutherford/reek](http://rdoc.info/projects/kevinrutherford/reek)
74
+ * Follow [@rubyreek](http://twitter.com/rubyreek) on twitter!
@@ -56,14 +56,20 @@ UncommunicativeModuleName:
56
56
  - !ruby/regexp /^.$/
57
57
  - !ruby/regexp /[0-9]$/
58
58
  NestedIterators:
59
- exclude: *id001
59
+ ignore_iterators: []
60
+
61
+ exclude: []
62
+
60
63
  enabled: true
64
+ max_allowed_nesting: 1
61
65
  LongMethod:
62
66
  max_statements: 5
63
67
  exclude:
64
68
  - initialize
65
69
  enabled: true
66
70
  Duplication:
71
+ allow_calls: []
72
+
67
73
  exclude: []
68
74
 
69
75
  enabled: true
@@ -0,0 +1,20 @@
1
+ @masking
2
+ Feature: The Reek API maintains backwards compatibility
3
+ In order to use Reek witthout fuss
4
+ As a developer
5
+ I want to mave a stable API
6
+
7
+ Scenario: the demo example reports as expected
8
+ When I run reek spec/samples/demo
9
+ Then the exit status indicates smells
10
+ And it reports:
11
+ """
12
+ spec/samples/demo/demo.rb -- 6 warnings:
13
+ Dirty has no descriptive comment (IrresponsibleModule)
14
+ Dirty#awful has 4 parameters (LongParameterList)
15
+ Dirty#awful has boolean parameter 'log' (ControlCouple)
16
+ Dirty#awful has the parameter name 'x' (UncommunicativeName)
17
+ Dirty#awful has the parameter name 'y' (UncommunicativeName)
18
+ Dirty#awful has the variable name 'w' (UncommunicativeName)
19
+
20
+ """
@@ -86,3 +86,44 @@ Feature: Masking smells using config files
86
86
  Dirty#a calls puts(@s.title) twice (Duplication)
87
87
 
88
88
  """
89
+
90
+ Scenario: allow masking some calls for duplication smell
91
+ When I run reek spec/samples/mask_some/dirty.rb
92
+ Then the exit status indicates smells
93
+ And it reports:
94
+ """
95
+ spec/samples/mask_some/dirty.rb -- 2 warnings:
96
+ Dirty#a calls @s.title twice (Duplication)
97
+ Dirty#a contains iterators nested 2 deep (NestedIterators)
98
+
99
+ """
100
+
101
+ @comments
102
+ Scenario: provide extra masking inline in comments
103
+ When I run reek spec/samples/inline_config
104
+ Then the exit status indicates smells
105
+ And it reports:
106
+ """
107
+ spec/samples/inline_config/dirty.rb -- 1 warning:
108
+ Dirty#a calls @s.title twice (Duplication)
109
+
110
+ """
111
+
112
+ Scenario: supports a config file
113
+ When I run reek -c spec/samples/config/allow_duplication.reek spec/samples/masked/dirty.rb
114
+ Then the exit status indicates smells
115
+ And it reports:
116
+ """
117
+ spec/samples/masked/dirty.rb -- 1 warning:
118
+ Dirty#a contains iterators nested 2 deep (NestedIterators)
119
+
120
+ """
121
+
122
+ Scenario: supports multiple config files
123
+ When I run reek -c spec/samples/config/allow_duplication.reek -c spec/samples/config/deeper_nested_iterators.reek spec/samples/masked/dirty.rb
124
+ Then it succeeds
125
+ And it reports:
126
+ """
127
+ spec/samples/masked/dirty.rb -- 0 warnings
128
+
129
+ """
@@ -35,6 +35,10 @@ Feature: Reek can be controlled using command-line options
35
35
  -v, --version Show version
36
36
 
37
37
 
38
+ Configuration:
39
+ -c, --config FILE Read configuration options from FILE
40
+
41
+
38
42
  Report formatting:
39
43
  -q, --[no-]quiet Suppress headings for smell-free source files
40
44
  -y, --yaml Report smells in YAML format
@@ -65,3 +65,17 @@ Feature: Reek can be driven through its Task
65
65
  Dirty#a has the name 'a' (UncommunicativeName)
66
66
  Dirty#a has the variable name 'x' (UncommunicativeName)
67
67
  """
68
+
69
+ Scenario: can be configured with config_files
70
+ When I run rake reek with:
71
+ """
72
+ Reek::Rake::Task.new do |t|
73
+ t.config_files = 'spec/samples/config/**/*.reek'
74
+ t.source_files = 'spec/samples/masked/dirty.rb'
75
+ end
76
+ """
77
+ Then it succeeds
78
+ And it reports:
79
+ """
80
+ spec/samples/masked/dirty.rb -- 0 warnings
81
+ """
@@ -54,7 +54,7 @@ Feature: Report smells using simple YAML layout
54
54
  smell:
55
55
  class: NestedIterators
56
56
  depth: 2
57
- subclass: ""
57
+ subclass: NestedIterators
58
58
  message: contains iterators nested 2 deep
59
59
  status:
60
60
  is_active: true
@@ -63,7 +63,7 @@ Feature: Report smells using simple YAML layout
63
63
 
64
64
  @stdin
65
65
  Scenario: return non-zero status when there are smells
66
- When I pass "# test class\nclass Turn; def fred(arg = true) end end" to reek --yaml
66
+ When I pass "class Turn; end" to reek --yaml
67
67
  Then the exit status indicates smells
68
68
  And it reports:
69
69
  """
@@ -71,14 +71,14 @@ Feature: Report smells using simple YAML layout
71
71
  - !ruby/object:Reek::SmellWarning
72
72
  location:
73
73
  lines:
74
- - 2
75
- context: Turn#fred
74
+ - 1
75
+ context: Turn
76
76
  source: $stdin
77
77
  smell:
78
- parameter: arg
79
- class: ControlCouple
80
- subclass: BooleanParameter
81
- message: has boolean parameter 'arg'
78
+ class: IrresponsibleModule
79
+ subclass: IrresponsibleModule
80
+ module_name: Turn
81
+ message: has no descriptive comment
82
82
  status:
83
83
  is_active: true
84
84
 
@@ -2,7 +2,7 @@
2
2
  # Reek's core functionality
3
3
  #
4
4
  module Reek
5
- VERSION = '1.2.7.3'
5
+ VERSION = '1.2.8'
6
6
  end
7
7
 
8
8
  require File.join(File.dirname(File.expand_path(__FILE__)), 'reek', 'examiner')
@@ -19,6 +19,7 @@ module Reek
19
19
  @parser = OptionParser.new
20
20
  @report_class = VerboseReport
21
21
  @command_class = ReekCommand
22
+ @config_files = []
22
23
  set_options
23
24
  end
24
25
 
@@ -35,6 +36,7 @@ module Reek
35
36
  # reek -v|--version Output the tool's version number
36
37
  #
37
38
  # reek [options] files List the smells in the given files
39
+ # -c|--config file Specify file(s) with config options
38
40
  # -q|-[no-]quiet Only list files that have smells
39
41
  # files Names of files or dirs to be checked
40
42
  #
@@ -62,6 +64,11 @@ EOB
62
64
  @command_class = VersionCommand
63
65
  end
64
66
 
67
+ @parser.separator "\nConfiguration:"
68
+ @parser.on("-c", "--config FILE", "Read configuration options from FILE") do |file|
69
+ @config_files << file
70
+ end
71
+
65
72
  @parser.separator "\nReport formatting:"
66
73
  @parser.on("-q", "--[no-]quiet", "Suppress headings for smell-free source files") do |opt|
67
74
  @report_class = opt ? QuietReport : VerboseReport
@@ -82,10 +89,10 @@ EOB
82
89
  VersionCommand.new(@parser.program_name)
83
90
  elsif @command_class == YamlCommand
84
91
  sources = get_sources
85
- YamlCommand.create(sources)
92
+ YamlCommand.create(sources, @config_files)
86
93
  else
87
94
  sources = get_sources
88
- ReekCommand.create(sources, @report_class)
95
+ ReekCommand.create(sources, @report_class, @config_files)
89
96
  end
90
97
  end
91
98
 
@@ -8,19 +8,20 @@ module Reek
8
8
  # text report format.
9
9
  #
10
10
  class ReekCommand
11
- def self.create(sources, report_class)
12
- new(report_class, sources)
11
+ def self.create(sources, report_class, config_files = [])
12
+ new(report_class, sources, config_files)
13
13
  end
14
14
 
15
- def initialize(report_class, sources)
15
+ def initialize(report_class, sources, config_files = [])
16
16
  @sources = sources
17
17
  @report_class = report_class
18
+ @config_files = config_files
18
19
  end
19
20
 
20
21
  def execute(view)
21
22
  had_smells = false
22
23
  @sources.each do |source|
23
- examiner = Examiner.new(source)
24
+ examiner = Examiner.new(source, @config_files)
24
25
  rpt = @report_class.new(examiner)
25
26
  had_smells ||= examiner.smelly?
26
27
  view.output(rpt.report)
@@ -8,8 +8,8 @@ module Reek
8
8
  # YAML format.
9
9
  #
10
10
  class YamlCommand
11
- def self.create(sources)
12
- examiners = sources.map {|src| Examiner.new(src) }
11
+ def self.create(sources, config_files)
12
+ examiners = sources.map {|src| Examiner.new(src, config_files) }
13
13
  new(examiners)
14
14
  end
15
15
 
@@ -10,11 +10,12 @@ module Reek
10
10
  #
11
11
  class CodeContext
12
12
 
13
- attr_reader :exp
13
+ attr_reader :exp, :config
14
14
 
15
15
  def initialize(outer, exp)
16
16
  @outer = outer
17
17
  @exp = exp
18
+ @config = local_config
18
19
  end
19
20
 
20
21
  def name
@@ -50,6 +51,14 @@ module Reek
50
51
  outer = @outer ? @outer.full_name : ''
51
52
  exp.full_name(outer)
52
53
  end
54
+
55
+ def local_config
56
+ return Hash.new if @exp.nil?
57
+ config = Source::CodeComment.new(@exp.comments || '').config
58
+ return config unless @outer
59
+ @outer.config.deep_copy.adopt!(config)
60
+ # no tests for this -----^
61
+ end
53
62
  end
54
63
  end
55
64
  end
@@ -10,10 +10,10 @@ module Reek
10
10
  module MethodParameters
11
11
  def default_assignments
12
12
  assignments = self[-1]
13
- result = {}
13
+ result = []
14
14
  return result unless is_assignment_block?(assignments)
15
15
  assignments[1..-1].each do |exp|
16
- result[exp[1]] = exp[2] if exp[0] == :lasgn
16
+ result << exp[1..2] if exp[0] == :lasgn
17
17
  end
18
18
  result
19
19
  end
@@ -1,5 +1,6 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), 'code_parser')
2
2
  require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'smells')
3
+ require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'source', 'config_file')
3
4
  require 'yaml'
4
5
 
5
6
  #
@@ -64,12 +65,13 @@ module Reek
64
65
  ]
65
66
  end
66
67
 
67
- def initialize(src)
68
+ def initialize(src, config_files = [])
68
69
  @typed_detectors = nil
69
70
  @detectors = Hash.new
70
71
  Sniffer.smell_classes.each do |klass|
71
72
  @detectors[klass] = klass.new(src.desc)
72
73
  end
74
+ config_files.each{ |cf| Reek::Source::ConfigFile.new(cf).configure(self) }
73
75
  @source = src
74
76
  src.configure(self)
75
77
  end
@@ -14,6 +14,10 @@ module Reek
14
14
  nil
15
15
  end
16
16
 
17
+ def config
18
+ {}
19
+ end
20
+
17
21
  def count_statements(num)
18
22
  0
19
23
  end