reek 1.2.7.3 → 1.2.8

Sign up to get free protection for your applications and to get access to all the features.
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