reek 1.2.7.1 → 1.2.7.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/History.txt +13 -1
  2. data/config/defaults.reek +4 -1
  3. data/features/masking_smells.feature +7 -7
  4. data/features/rake_task.feature +2 -2
  5. data/features/reports.feature +3 -3
  6. data/features/samples.feature +5 -2
  7. data/features/yaml.feature +0 -39
  8. data/lib/reek.rb +1 -1
  9. data/lib/reek/cli/command_line.rb +3 -3
  10. data/lib/reek/cli/reek_command.rb +5 -6
  11. data/lib/reek/cli/report.rb +9 -20
  12. data/lib/reek/cli/yaml_command.rb +1 -1
  13. data/lib/reek/core/class_context.rb +1 -2
  14. data/lib/reek/core/code_context.rb +10 -27
  15. data/lib/reek/core/code_parser.rb +1 -18
  16. data/lib/reek/core/detector_stack.rb +4 -0
  17. data/lib/reek/core/masking_collection.rb +6 -0
  18. data/lib/reek/core/method_context.rb +8 -56
  19. data/lib/reek/core/module_context.rb +6 -32
  20. data/lib/reek/core/object_refs.rb +36 -36
  21. data/lib/reek/core/singleton_method_context.rb +10 -21
  22. data/lib/reek/core/sniffer.rb +3 -2
  23. data/lib/reek/examiner.rb +39 -31
  24. data/lib/reek/smell_warning.rb +8 -0
  25. data/lib/reek/smells/attribute.rb +4 -2
  26. data/lib/reek/smells/class_variable.rb +3 -3
  27. data/lib/reek/smells/control_couple.rb +1 -2
  28. data/lib/reek/smells/data_clump.rb +86 -9
  29. data/lib/reek/smells/duplication.rb +2 -3
  30. data/lib/reek/smells/feature_envy.rb +9 -4
  31. data/lib/reek/smells/simulated_polymorphism.rb +1 -2
  32. data/lib/reek/smells/smell_detector.rb +0 -6
  33. data/lib/reek/smells/uncommunicative_method_name.rb +8 -2
  34. data/lib/reek/smells/uncommunicative_parameter_name.rb +1 -1
  35. data/lib/reek/smells/uncommunicative_variable_name.rb +1 -1
  36. data/lib/reek/smells/utility_function.rb +17 -5
  37. data/lib/reek/source/reference_collector.rb +21 -0
  38. data/lib/reek/source/sexp_formatter.rb +1 -0
  39. data/lib/reek/source/tree_dresser.rb +67 -9
  40. data/lib/reek/spec/should_reek.rb +1 -1
  41. data/lib/reek/spec/should_reek_of.rb +1 -1
  42. data/lib/reek/spec/should_reek_only_of.rb +1 -1
  43. data/reek.gemspec +3 -3
  44. data/spec/reek/cli/reek_command_spec.rb +3 -2
  45. data/spec/reek/cli/report_spec.rb +2 -2
  46. data/spec/reek/cli/yaml_command_spec.rb +2 -2
  47. data/spec/reek/core/code_context_spec.rb +39 -54
  48. data/spec/reek/core/method_context_spec.rb +7 -26
  49. data/spec/reek/core/module_context_spec.rb +0 -15
  50. data/spec/reek/core/singleton_method_context_spec.rb +0 -6
  51. data/spec/reek/examiner_spec.rb +6 -6
  52. data/spec/reek/smells/attribute_spec.rb +30 -32
  53. data/spec/reek/smells/class_variable_spec.rb +15 -18
  54. data/spec/reek/smells/data_clump_spec.rb +22 -6
  55. data/spec/reek/smells/duplication_spec.rb +33 -19
  56. data/spec/reek/smells/feature_envy_spec.rb +82 -88
  57. data/spec/reek/smells/large_class_spec.rb +1 -1
  58. data/spec/reek/smells/smell_detector_shared.rb +1 -1
  59. data/spec/reek/smells/uncommunicative_method_name_spec.rb +37 -35
  60. data/spec/reek/smells/utility_function_spec.rb +7 -0
  61. data/spec/reek/source/reference_collector_spec.rb +53 -0
  62. data/spec/reek/source/tree_dresser_spec.rb +10 -0
  63. data/spec/reek/spec/should_reek_only_of_spec.rb +1 -1
  64. data/spec/spec_helper.rb +7 -0
  65. metadata +4 -5
  66. data/features/profile.feature +0 -34
  67. data/lib/reek/core/block_context.rb +0 -18
  68. data/spec/reek/core/block_context_spec.rb +0 -26
data/History.txt CHANGED
@@ -1,4 +1,16 @@
1
- == 1.2.7.1 (in development)
1
+ == 1.2.8 (in development)
2
+
3
+ == 1.2.7.2 (2010-03-05)
4
+
5
+ === Minor Changes
6
+ * Number of masked smells is no longer shown in report headers
7
+ * Masked smells are no longer listed in --yaml reports
8
+ * DataClump no longer needs infinite memory for large classes (#57)
9
+ * DataClump reports the names of the offending methods in the YAML report
10
+ * UncommunicativeMethodName now accepts operator names (+, -, ...)
11
+ * Uncommunicative Name now warns about uppercase letters in method & var names
12
+
13
+ == 1.2.7.1 (2010-02-03)
2
14
 
3
15
  === Minor Changes
4
16
  * Fixed crash on a case statement with no condition (#58)
data/config/defaults.reek CHANGED
@@ -8,6 +8,7 @@ UncommunicativeParameterName:
8
8
  reject:
9
9
  - !ruby/regexp /^.$/
10
10
  - !ruby/regexp /[0-9]$/
11
+ - !ruby/regexp /[A-Z]/
11
12
  LargeClass:
12
13
  max_methods: 25
13
14
  exclude: []
@@ -21,8 +22,9 @@ UncommunicativeMethodName:
21
22
 
22
23
  enabled: true
23
24
  reject:
24
- - !ruby/regexp /^.$/
25
+ - !ruby/regexp /^[a-z]$/
25
26
  - !ruby/regexp /[0-9]$/
27
+ - !ruby/regexp /[A-Z]/
26
28
  LongParameterList:
27
29
  max_params: 3
28
30
  exclude: []
@@ -84,6 +86,7 @@ UncommunicativeVariableName:
84
86
  reject:
85
87
  - !ruby/regexp /^.$/
86
88
  - !ruby/regexp /[0-9]$/
89
+ - !ruby/regexp /[A-Z]/
87
90
  SimulatedPolymorphism:
88
91
  exclude: []
89
92
 
@@ -41,7 +41,7 @@ Feature: Masking smells using config files
41
41
  Then the exit status indicates smells
42
42
  And it reports:
43
43
  """
44
- spec/samples/masked/dirty.rb -- 3 warnings (+3 masked):
44
+ spec/samples/masked/dirty.rb -- 3 warnings:
45
45
  Dirty#a calls @s.title twice (Duplication)
46
46
  Dirty#a calls puts(@s.title) twice (Duplication)
47
47
  Dirty#a contains iterators nested 2 deep (Nested Iterators)
@@ -54,7 +54,7 @@ Feature: Masking smells using config files
54
54
  Then the exit status indicates smells
55
55
  And it reports:
56
56
  """
57
- spec/samples/masked/dirty.rb -- 3 warnings (+3 masked):
57
+ spec/samples/masked/dirty.rb -- 3 warnings:
58
58
  Dirty#a calls @s.title twice (Duplication)
59
59
  Dirty#a calls puts(@s.title) twice (Duplication)
60
60
  Dirty#a contains iterators nested 2 deep (Nested Iterators)
@@ -66,7 +66,7 @@ Feature: Masking smells using config files
66
66
  Then the exit status indicates smells
67
67
  And it reports:
68
68
  """
69
- spec/samples/masked/dirty.rb -- 3 warnings (+3 masked):
69
+ spec/samples/masked/dirty.rb -- 6 warnings:
70
70
  (masked) Dirty has the variable name '@s' (Uncommunicative Name)
71
71
  Dirty#a calls @s.title twice (Duplication)
72
72
  Dirty#a calls puts(@s.title) twice (Duplication)
@@ -81,7 +81,7 @@ Feature: Masking smells using config files
81
81
  Then the exit status indicates smells
82
82
  And it reports:
83
83
  """
84
- spec/samples/masked/dirty.rb -- 3 warnings (+3 masked):
84
+ spec/samples/masked/dirty.rb -- 3 warnings:
85
85
  Dirty#a calls @s.title twice (Duplication)
86
86
  Dirty#a calls puts(@s.title) twice (Duplication)
87
87
  Dirty#a contains iterators nested 2 deep (Nested Iterators)
@@ -93,7 +93,7 @@ Feature: Masking smells using config files
93
93
  Then the exit status indicates smells
94
94
  And it reports:
95
95
  """
96
- spec/samples/not_quite_masked/dirty.rb -- 5 warnings (+1 masked):
96
+ spec/samples/not_quite_masked/dirty.rb -- 5 warnings:
97
97
  Dirty has the variable name '@s' (Uncommunicative Name)
98
98
  Dirty#a calls @s.title twice (Duplication)
99
99
  Dirty#a calls puts(@s.title) twice (Duplication)
@@ -108,7 +108,7 @@ Feature: Masking smells using config files
108
108
  Then the exit status indicates smells
109
109
  And it reports:
110
110
  """
111
- spec/samples/overrides/masked/dirty.rb -- 2 warnings (+4 masked):
111
+ spec/samples/overrides/masked/dirty.rb -- 2 warnings:
112
112
  Dirty#a calls @s.title twice (Duplication)
113
113
  Dirty#a calls puts(@s.title) twice (Duplication)
114
114
 
@@ -120,7 +120,7 @@ Feature: Masking smells using config files
120
120
  Then the exit status indicates smells
121
121
  And it reports:
122
122
  """
123
- spec/samples/overrides/masked/dirty.rb -- 2 warnings (+4 masked):
123
+ spec/samples/overrides/masked/dirty.rb -- 6 warnings:
124
124
  (masked) Dirty has the variable name '@s' (Uncommunicative Name)
125
125
  Dirty#a calls @s.title twice (Duplication)
126
126
  Dirty#a calls puts(@s.title) twice (Duplication)
@@ -13,7 +13,7 @@ Feature: Reek can be driven through its Task
13
13
  Then the exit status indicates an error
14
14
  And it reports:
15
15
  """
16
- spec/samples/masked/dirty.rb -- 3 warnings (+3 masked):
16
+ spec/samples/masked/dirty.rb -- 3 warnings:
17
17
  Dirty#a calls @s.title twice (Duplication)
18
18
  Dirty#a calls puts(@s.title) twice (Duplication)
19
19
  Dirty#a contains iterators nested 2 deep (Nested Iterators)
@@ -29,7 +29,7 @@ Feature: Reek can be driven through its Task
29
29
  Then the exit status indicates an error
30
30
  And it reports:
31
31
  """
32
- spec/samples/masked/dirty.rb -- 3 warnings (+3 masked):
32
+ spec/samples/masked/dirty.rb -- 3 warnings:
33
33
  Dirty#a calls @s.title twice (Duplication)
34
34
  Dirty#a calls puts(@s.title) twice (Duplication)
35
35
  Dirty#a contains iterators nested 2 deep (Nested Iterators)
@@ -60,10 +60,10 @@ Feature: Correctly formatted reports
60
60
 
61
61
  Scenario Outline: -a turns on details in presence of -q
62
62
  When I run reek <options> spec/samples/clean_due_to_masking/*.rb
63
- Then it succeeds
63
+ Then the exit status indicates smells
64
64
  And it reports:
65
65
  """
66
- spec/samples/clean_due_to_masking/dirty_one.rb -- 0 warnings (+7 masked):
66
+ spec/samples/clean_due_to_masking/dirty_one.rb -- 7 warnings:
67
67
  (masked) Dirty has no descriptive comment (Irresponsible Module)
68
68
  (masked) Dirty has the variable name '@s' (Uncommunicative Name)
69
69
  (masked) Dirty#a calls @s.title twice (Duplication)
@@ -71,7 +71,7 @@ Feature: Correctly formatted reports
71
71
  (masked) Dirty#a contains iterators nested 2 deep (Nested Iterators)
72
72
  (masked) Dirty#a has the name 'a' (Uncommunicative Name)
73
73
  (masked) Dirty#a has the variable name 'x' (Uncommunicative Name)
74
- spec/samples/clean_due_to_masking/dirty_two.rb -- 0 warnings (+7 masked):
74
+ spec/samples/clean_due_to_masking/dirty_two.rb -- 7 warnings:
75
75
  (masked) Dirty has no descriptive comment (Irresponsible Module)
76
76
  (masked) Dirty has the variable name '@s' (Uncommunicative Name)
77
77
  (masked) Dirty#a calls @s.title twice (Duplication)
@@ -10,7 +10,7 @@ Feature: Basic smell detection
10
10
  Then the exit status indicates smells
11
11
  And it reports:
12
12
  """
13
- spec/samples/inline.rb -- 41 warnings (+1 masked):
13
+ spec/samples/inline.rb -- 41 warnings:
14
14
  File has no descriptive comment (Irresponsible Module)
15
15
  Inline declares the class variable @@directory (Class Variable)
16
16
  Inline declares the class variable @@rootdir (Class Variable)
@@ -178,7 +178,7 @@ Feature: Basic smell detection
178
178
  Then the exit status indicates smells
179
179
  And it reports:
180
180
  """
181
- spec/samples/redcloth.rb -- 94 warnings:
181
+ spec/samples/redcloth.rb -- 97 warnings:
182
182
  RedCloth has at least 44 methods (Large Class)
183
183
  RedCloth has the variable name 'a' (Uncommunicative Name)
184
184
  RedCloth has the variable name 'b' (Uncommunicative Name)
@@ -245,6 +245,7 @@ Feature: Basic smell detection
245
245
  RedCloth#inline_textile_span contains iterators nested 2 deep (Nested Iterators)
246
246
  RedCloth#inline_textile_span has approx 8 statements (Long Method)
247
247
  RedCloth#inline_textile_span has the variable name 'm' (Uncommunicative Name)
248
+ RedCloth#lT has the name 'lT' (Uncommunicative Name)
248
249
  RedCloth#no_textile doesn't depend on instance state (Low Cohesion)
249
250
  RedCloth#no_textile refers to text more than self (Low Cohesion)
250
251
  RedCloth#pba calls $1.length twice (Duplication)
@@ -272,6 +273,8 @@ Feature: Basic smell detection
272
273
  RedCloth#textile_fn_ is controlled by argument atts (Control Couple)
273
274
  RedCloth#textile_p has 4 parameters (Long Parameter List)
274
275
  RedCloth#textile_p is controlled by argument atts (Control Couple)
276
+ RedCloth#textile_popup_help has the parameter name 'windowH' (Uncommunicative Name)
277
+ RedCloth#textile_popup_help has the parameter name 'windowW' (Uncommunicative Name)
275
278
  RedCloth#to_html has approx 24 statements (Long Method)
276
279
 
277
280
  """
@@ -15,19 +15,6 @@ Feature: Report smells using simple YAML layout
15
15
  And it reports:
16
16
  """
17
17
  ---
18
- - !ruby/object:Reek::SmellWarning
19
- location:
20
- lines:
21
- - 5
22
- context: Dirty
23
- source: spec/samples/masked/dirty.rb
24
- smell:
25
- class: UncommunicativeName
26
- subclass: UncommunicativeVariableName
27
- variable_name: "@s"
28
- message: has the variable name '@s'
29
- status:
30
- is_active: false
31
18
  - !ruby/object:Reek::SmellWarning
32
19
  location:
33
20
  lines:
@@ -71,32 +58,6 @@ Feature: Report smells using simple YAML layout
71
58
  message: contains iterators nested 2 deep
72
59
  status:
73
60
  is_active: true
74
- - !ruby/object:Reek::SmellWarning
75
- location:
76
- lines:
77
- - 3
78
- context: Dirty#a
79
- source: spec/samples/masked/dirty.rb
80
- smell:
81
- method_name: a
82
- class: UncommunicativeName
83
- subclass: UncommunicativeMethodName
84
- message: has the name 'a'
85
- status:
86
- is_active: false
87
- - !ruby/object:Reek::SmellWarning
88
- location:
89
- lines:
90
- - 5
91
- context: Dirty#a
92
- source: spec/samples/masked/dirty.rb
93
- smell:
94
- class: UncommunicativeName
95
- subclass: UncommunicativeVariableName
96
- variable_name: x
97
- message: has the variable name 'x'
98
- status:
99
- is_active: false
100
61
 
101
62
  """
102
63
 
data/lib/reek.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  # Reek's core functionality
3
3
  #
4
4
  module Reek
5
- VERSION = '1.2.7.1'
5
+ VERSION = '1.2.7.2'
6
6
  end
7
7
 
8
8
  require File.join(File.dirname(File.expand_path(__FILE__)), 'reek', 'examiner')
@@ -18,7 +18,7 @@ module Reek
18
18
  @argv = argv
19
19
  @parser = OptionParser.new
20
20
  @report_class = VerboseReport
21
- @show_all = false
21
+ @collection_strategy = ActiveSmellsOnly
22
22
  @command_class = ReekCommand
23
23
  set_options
24
24
  end
@@ -66,7 +66,7 @@ EOB
66
66
 
67
67
  @parser.separator "\nReport formatting:"
68
68
  @parser.on("-a", "--[no-]show-all", "Show all smells, including those masked by config settings") do |opt|
69
- @show_all = opt
69
+ @collection_strategy = opt ? ActiveAndMaskedSmells : ActiveSmellsOnly
70
70
  end
71
71
  @parser.on("-q", "--[no-]quiet", "Suppress headings for smell-free source files") do |opt|
72
72
  @report_class = opt ? QuietReport : VerboseReport
@@ -90,7 +90,7 @@ EOB
90
90
  YamlCommand.create(sources)
91
91
  else
92
92
  sources = get_sources
93
- ReekCommand.create(sources, @report_class, @show_all)
93
+ ReekCommand.create(sources, @report_class, @collection_strategy)
94
94
  end
95
95
  end
96
96
 
@@ -8,21 +8,20 @@ module Reek
8
8
  # text report format.
9
9
  #
10
10
  class ReekCommand
11
- def self.create(sources, report_class, show_all)
12
- examiners = sources.map {|src| Examiner.new(src) }
13
- new(examiners, report_class, show_all)
11
+ def self.create(sources, report_class, strategy = ActiveSmellsOnly)
12
+ examiners = sources.map { |src| Examiner.new(src, strategy.new) }
13
+ new(examiners, report_class)
14
14
  end
15
15
 
16
- def initialize(examiners, report_class, show_all)
16
+ def initialize(examiners, report_class)
17
17
  @examiners = examiners
18
18
  @report_class = report_class
19
- @show_all = show_all #SMELL: boolean parameter
20
19
  end
21
20
 
22
21
  def execute(view)
23
22
  had_smells = false
24
23
  @examiners.each do |examiner|
25
- rpt = @report_class.new(examiner, @show_all)
24
+ rpt = @report_class.new(examiner)
26
25
  had_smells ||= examiner.smelly?
27
26
  view.output(rpt.report)
28
27
  end
@@ -10,9 +10,8 @@ module Reek
10
10
 
11
11
  SMELL_FORMAT = '%m%c %w (%s)'
12
12
 
13
- def initialize(examiner, display_masked_warnings)
13
+ def initialize(examiner)
14
14
  @examiner = examiner
15
- @display_masked_warnings = display_masked_warnings # SMELL: Control Couple
16
15
  end
17
16
 
18
17
  # Creates a formatted report of all the +Smells::SmellWarning+ objects recorded in
@@ -31,45 +30,35 @@ module Reek
31
30
  end
32
31
 
33
32
  def header
34
- "#{@examiner.description} -- #{visible_header}#{masked_header}"
33
+ "#{@examiner.description} -- #{visible_header}"
35
34
  end
36
35
 
37
36
  # Creates a formatted report of all the +Smells::SmellWarning+ objects recorded in
38
37
  # this report.
39
38
  def smell_list
40
- if @display_masked_warnings
41
- result = @examiner.all_smells.map {|smell| " #{smell.report(SMELL_FORMAT)}"}
42
- else
43
- result = @examiner.all_active_smells.map {|smell| " #{smell.report(SMELL_FORMAT)}"}
44
- end
45
- result.join("\n")
39
+ @examiner.smells.map {|smell| " #{smell.report(SMELL_FORMAT)}"}.join("\n")
46
40
  end
47
41
 
48
42
  private
49
43
 
50
44
  def should_report
51
- @examiner.num_active_smells > 0 or (@display_masked_warnings and @examiner.num_masked_smells > 0)
45
+ @examiner.num_smells > 0
52
46
  end
53
47
 
54
48
  def visible_header
55
- num_smells = @examiner.all_active_smells.length
49
+ num_smells = @examiner.smells.length
56
50
  result = "#{num_smells} warning"
57
51
  result += 's' unless num_smells == 1
58
52
  result
59
53
  end
60
-
61
- def masked_header
62
- num_masked_warnings = @examiner.num_masked_smells
63
- num_masked_warnings == 0 ? '' : " (+#{num_masked_warnings} masked)"
64
- end
65
54
  end
66
55
 
67
56
  #
68
57
  # A report that lists every source, including those that have no smells.
69
58
  #
70
59
  class VerboseReport
71
- def initialize(examiner, display_masked_warnings)
72
- @reporter = ReportSection.new(examiner, display_masked_warnings)
60
+ def initialize(examiner)
61
+ @reporter = ReportSection.new(examiner)
73
62
  end
74
63
  def report
75
64
  @reporter.verbose_report
@@ -80,8 +69,8 @@ module Reek
80
69
  # A report that lists a section for each source that has smells.
81
70
  #
82
71
  class QuietReport
83
- def initialize(examiner, display_masked_warnings)
84
- @reporter = ReportSection.new(examiner, display_masked_warnings)
72
+ def initialize(examiner)
73
+ @reporter = ReportSection.new(examiner)
85
74
  end
86
75
  def report
87
76
  @reporter.quiet_report
@@ -19,7 +19,7 @@ module Reek
19
19
 
20
20
  def execute(view)
21
21
  smells = []
22
- @examiners.each {|examiner| smells += examiner.all_smells}
22
+ @examiners.each {|examiner| smells += examiner.smells}
23
23
  if smells.empty?
24
24
  view.report_success
25
25
  else
@@ -12,11 +12,10 @@ module Reek
12
12
 
13
13
  def initialize(outer, name, exp)
14
14
  super
15
- @superclass = exp[2]
16
15
  end
17
16
 
18
17
  def is_struct?
19
- @superclass == [:const, :Struct]
18
+ exp.superclass == [:const, :Struct]
20
19
  end
21
20
  end
22
21
  end
@@ -1,13 +1,4 @@
1
1
 
2
- #
3
- # Extensions to +Module+ needed by Reek.
4
- #
5
- class Module
6
- def const_or_nil(sym)
7
- const_defined?(sym) ? const_get(sym) : nil
8
- end
9
- end
10
-
11
2
  module Reek
12
3
  module Core
13
4
 
@@ -19,12 +10,15 @@ module Reek
19
10
  #
20
11
  class CodeContext
21
12
 
22
- attr_reader :name, :exp
13
+ attr_reader :exp
23
14
 
24
15
  def initialize(outer, exp)
25
16
  @outer = outer
26
17
  @exp = exp
27
- @scope_connector = ''
18
+ end
19
+
20
+ def name
21
+ @exp.name
28
22
  end
29
23
 
30
24
  def local_nodes(type, &blk)
@@ -32,22 +26,12 @@ module Reek
32
26
  end
33
27
 
34
28
  def each_node(type, ignoring, &blk)
35
- if block_given?
36
- @exp.look_for(type, ignoring, &blk)
37
- else
38
- result = []
39
- @exp.look_for(type, ignoring) {|exp| result << exp}
40
- result
41
- end
29
+ @exp.each_node(type, ignoring, &blk)
42
30
  end
43
31
 
44
- # SMELL: Temporary Field -- @name isn't always initialized
45
- def matches?(strings)
46
- me = @name.to_s
47
- strings.any? do |str|
48
- re = /#{str}/
49
- re === me or re === self.full_name
50
- end
32
+ def matches?(candidates)
33
+ my_fq_name = full_name
34
+ candidates.any? {|str| /#{str}/ === my_fq_name }
51
35
  end
52
36
 
53
37
  #
@@ -64,8 +48,7 @@ module Reek
64
48
 
65
49
  def full_name
66
50
  outer = @outer ? @outer.full_name : ''
67
- prefix = outer == '' ? '' : "#{outer}#{@scope_connector}"
68
- "#{prefix}#{@name}"
51
+ exp.full_name(outer)
69
52
  end
70
53
  end
71
54
  end