reek 1.2.1 → 1.2.3

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 (68) hide show
  1. data/History.txt +10 -0
  2. data/Rakefile +0 -1
  3. data/config/defaults.reek +8 -6
  4. data/features/masking_smells.feature +9 -9
  5. data/features/options.feature +2 -2
  6. data/features/profile.feature +34 -0
  7. data/features/rake_task.feature +74 -0
  8. data/features/reports.feature +1 -1
  9. data/features/samples.feature +4 -4
  10. data/features/stdin.feature +1 -1
  11. data/features/step_definitions/reek_steps.rb +11 -7
  12. data/features/support/env.rb +26 -18
  13. data/lib/reek/adapters/application.rb +9 -2
  14. data/lib/reek/adapters/command_line.rb +2 -2
  15. data/lib/reek/adapters/core_extras.rb +0 -8
  16. data/lib/reek/adapters/source.rb +4 -1
  17. data/lib/reek/adapters/spec.rb +1 -4
  18. data/lib/reek/block_context.rb +14 -8
  19. data/lib/reek/class_context.rb +6 -55
  20. data/lib/reek/code_context.rb +10 -0
  21. data/lib/reek/code_parser.rb +26 -53
  22. data/lib/reek/configuration.rb +12 -6
  23. data/lib/reek/if_context.rb +2 -3
  24. data/lib/reek/method_context.rb +8 -12
  25. data/lib/reek/module_context.rb +35 -16
  26. data/lib/reek/name.rb +2 -0
  27. data/lib/reek/object_refs.rb +0 -3
  28. data/lib/reek/sexp_formatter.rb +0 -2
  29. data/lib/reek/smells/attribute.rb +48 -0
  30. data/lib/reek/smells/class_variable.rb +17 -4
  31. data/lib/reek/smells/control_couple.rb +3 -10
  32. data/lib/reek/smells/data_clump.rb +10 -10
  33. data/lib/reek/smells/feature_envy.rb +1 -8
  34. data/lib/reek/smells/large_class.rb +3 -3
  35. data/lib/reek/smells/simulated_polymorphism.rb +17 -3
  36. data/lib/reek/smells/smell_detector.rb +11 -2
  37. data/lib/reek/smells/utility_function.rb +1 -1
  38. data/lib/reek/sniffer.rb +2 -8
  39. data/lib/reek/stop_context.rb +1 -1
  40. data/lib/reek/tree_dresser.rb +74 -0
  41. data/lib/reek.rb +1 -1
  42. data/reek.gemspec +3 -3
  43. data/spec/reek/adapters/should_reek_of_spec.rb +7 -1
  44. data/spec/reek/block_context_spec.rb +6 -6
  45. data/spec/reek/class_context_spec.rb +2 -23
  46. data/spec/reek/code_context_spec.rb +149 -67
  47. data/spec/reek/code_parser_spec.rb +35 -51
  48. data/spec/reek/method_context_spec.rb +4 -4
  49. data/spec/reek/singleton_method_context_spec.rb +1 -1
  50. data/spec/reek/smells/attribute_spec.rb +26 -0
  51. data/spec/reek/smells/behaves_like_variable_detector.rb +39 -0
  52. data/spec/reek/smells/class_variable_spec.rb +77 -43
  53. data/spec/reek/smells/control_couple_spec.rb +1 -1
  54. data/spec/reek/smells/data_clump_spec.rb +31 -13
  55. data/spec/reek/smells/feature_envy_spec.rb +1 -1
  56. data/spec/reek/smells/large_class_spec.rb +32 -69
  57. data/spec/reek/smells/long_parameter_list_spec.rb +0 -12
  58. data/spec/reek/smells/simulated_polymorphism_spec.rb +66 -18
  59. data/spec/reek/smells/utility_function_spec.rb +0 -21
  60. data/spec/reek/sniffer_spec.rb +1 -0
  61. data/spec/samples/not_quite_masked/dirty.rb +2 -0
  62. data/spec/spec_helper.rb +1 -1
  63. data/tasks/reek.rake +1 -1
  64. data/tasks/test.rake +3 -4
  65. metadata +8 -5
  66. data/lib/reek/adapters/object_source.rb +0 -52
  67. data/lib/reek/exceptions.reek +0 -20
  68. data/spec/quality/reek_source_spec.rb +0 -15
data/History.txt CHANGED
@@ -1,3 +1,13 @@
1
+ == 1.2.2 (under development in github)
2
+
3
+ === Minor Changes
4
+ * New smell: Attribute (disabled by default)
5
+ * Expanded DataClump to check modules (#9)
6
+ * Fixed LargeClass to ignore inner classes and modules
7
+ * Fixed LargeClass to ignore singleton methods
8
+ * Removed support for MyClass.should_not reek due to ParseTree EOL
9
+ * Removed internal requiring of 'rubygems'
10
+
1
11
  == 1.2.1 (2009-10-02)
2
12
 
3
13
  === Minor Changes
data/Rakefile CHANGED
@@ -1,4 +1,3 @@
1
- require 'rake'
2
1
  require 'rake/clean'
3
2
 
4
3
  $:.unshift File.dirname(__FILE__) + '/lib'
data/config/defaults.reek CHANGED
@@ -14,12 +14,11 @@ LongParameterList:
14
14
  initialize:
15
15
  max_params: 5
16
16
  FeatureEnvy:
17
- exclude:
18
- - initialize
19
- enabled: true
20
- ClassVariable:
21
17
  exclude: &id001 []
22
18
 
19
+ enabled: true
20
+ ClassVariable:
21
+ exclude: *id001
23
22
  enabled: true
24
23
  UncommunicativeName:
25
24
  accept:
@@ -48,6 +47,10 @@ UtilityFunction:
48
47
  exclude: []
49
48
 
50
49
  enabled: true
50
+ Attribute:
51
+ exclude: []
52
+
53
+ enabled: false
51
54
  SimulatedPolymorphism:
52
55
  exclude: []
53
56
 
@@ -60,8 +63,7 @@ DataClump:
60
63
  max_copies: 2
61
64
  min_clump_size: 2
62
65
  ControlCouple:
63
- exclude:
64
- - initialize
66
+ exclude: *id001
65
67
  enabled: true
66
68
  LongYieldList:
67
69
  max_params: 3
@@ -6,7 +6,7 @@ Feature: Masking smells using config files
6
6
 
7
7
  Scenario: empty config file is ignored
8
8
  When I run reek spec/samples/empty_config_file/dirty.rb
9
- Then it fails with exit status 2
9
+ Then the exit status indicates smells
10
10
  And it reports:
11
11
  """
12
12
  spec/samples/empty_config_file/dirty.rb -- 6 warnings:
@@ -21,17 +21,17 @@ Feature: Masking smells using config files
21
21
 
22
22
  Scenario: corrupt config file prevents normal output
23
23
  When I run reek spec/samples/corrupt_config_file/dirty.rb
24
- Then it fails with exit status 1
24
+ Then the exit status indicates an error
25
25
  And it reports the error 'Error: Invalid configuration file "corrupt.reek" -- not a Hash'
26
26
 
27
27
  Scenario: missing source file is an error
28
28
  When I run reek no_such_file.rb
29
- Then it fails with exit status 1
29
+ Then the exit status indicates an error
30
30
  And it reports the error "Error: No such file or directory - no_such_file.rb"
31
31
 
32
32
  Scenario: switch off one smell
33
33
  When I run reek spec/samples/masked/dirty.rb
34
- Then it fails with exit status 2
34
+ Then the exit status indicates smells
35
35
  And it reports:
36
36
  """
37
37
  spec/samples/masked/dirty.rb -- 3 warnings (+3 masked):
@@ -43,7 +43,7 @@ Feature: Masking smells using config files
43
43
 
44
44
  Scenario: switch off one smell but show all in the report
45
45
  When I run reek --show-all spec/samples/masked/dirty.rb
46
- Then it fails with exit status 2
46
+ Then the exit status indicates smells
47
47
  And it reports:
48
48
  """
49
49
  spec/samples/masked/dirty.rb -- 3 warnings (+3 masked):
@@ -58,7 +58,7 @@ Feature: Masking smells using config files
58
58
 
59
59
  Scenario: switch off one smell and hide them in the report
60
60
  When I run reek --no-show-all spec/samples/masked/dirty.rb
61
- Then it fails with exit status 2
61
+ Then the exit status indicates smells
62
62
  And it reports:
63
63
  """
64
64
  spec/samples/masked/dirty.rb -- 3 warnings (+3 masked):
@@ -70,7 +70,7 @@ Feature: Masking smells using config files
70
70
 
71
71
  Scenario: non-masked smells are only counted once
72
72
  When I run reek spec/samples/not_quite_masked/dirty.rb
73
- Then it fails with exit status 2
73
+ Then the exit status indicates smells
74
74
  And it reports:
75
75
  """
76
76
  spec/samples/not_quite_masked/dirty.rb -- 5 warnings (+1 masked):
@@ -85,7 +85,7 @@ Feature: Masking smells using config files
85
85
  @overrides
86
86
  Scenario: lower overrides upper
87
87
  When I run reek spec/samples/overrides
88
- Then it fails with exit status 2
88
+ Then the exit status indicates smells
89
89
  And it reports:
90
90
  """
91
91
  spec/samples/overrides/masked/dirty.rb -- 2 warnings (+4 masked):
@@ -97,7 +97,7 @@ Feature: Masking smells using config files
97
97
  @overrides
98
98
  Scenario: all show up masked even when overridden
99
99
  When I run reek --show-all spec/samples/overrides
100
- Then it fails with exit status 2
100
+ Then the exit status indicates smells
101
101
  And it reports:
102
102
  """
103
103
  spec/samples/overrides/masked/dirty.rb -- 2 warnings (+4 masked):
@@ -6,12 +6,12 @@ Feature: Reek can be controlled using command-line options
6
6
 
7
7
  Scenario: return non-zero status on bad option
8
8
  When I run reek --no-such-option
9
- Then it fails with exit status 1
9
+ Then the exit status indicates an error
10
10
  And it reports the error "Error: invalid option: --no-such-option"
11
11
 
12
12
  Scenario: return non-zero status on missing argument
13
13
  When I run reek -f
14
- Then it fails with exit status 1
14
+ Then the exit status indicates an error
15
15
  And it reports the error "Error: missing argument: -f"
16
16
 
17
17
  Scenario: display the current version number
@@ -0,0 +1,34 @@
1
+ @profile
2
+ Feature: Reek's configuration can be based on any number of canned profiles
3
+ The starting point for configuring the smell detectors can be
4
+ selected from a list of supplied config files.
5
+
6
+ # Scenario: XP profile reveals Attribute smells
7
+ # When I run reek --profile xp spec/samples/not_quite_masked/dirty.rb
8
+ # Then the exit status indicates smells
9
+ # And it reports:
10
+ # """
11
+ # spec/samples/not_quite_masked/dirty.rb -- 5 warnings (+2 masked):
12
+ # Dirty declares the attribute property (Attribute)
13
+ # Dirty has the variable name '@s' (Uncommunicative Name)
14
+ # Dirty#a calls @s.title twice (Duplication)
15
+ # Dirty#a calls puts(@s.title) twice (Duplication)
16
+ # Dirty#a has the name 'a' (Uncommunicative Name)
17
+ #
18
+ # """
19
+ #
20
+ # Scenario: XP profile works with unmasked smells
21
+ # When I run reek --show-all --profile xp spec/samples/not_quite_masked/dirty.rb
22
+ # Then the exit status indicates smells
23
+ # And it reports:
24
+ # """
25
+ # spec/samples/not_quite_masked/dirty.rb -- 5 warnings (+2 masked):
26
+ # Dirty declares the attribute property (Attribute)
27
+ # Dirty has the variable name '@s' (Uncommunicative Name)
28
+ # Dirty#a calls @s.title twice (Duplication)
29
+ # Dirty#a calls puts(@s.title) twice (Duplication)
30
+ # Dirty#a has the name 'a' (Uncommunicative Name)
31
+ # (masked) Dirty#a/block has the variable name 'x' (Uncommunicative Name)
32
+ # (masked) Dirty#a/block/block is nested (Nested Iterators)
33
+ #
34
+ # """
@@ -0,0 +1,74 @@
1
+ @rake
2
+ Feature: Reek can be driven through its RakeTask
3
+ Reek provides an easy way to integrate its use into Rakefiles,
4
+ via the RakeTask class. These scenarios test its various options.
5
+
6
+ Scenario: source_files points at the desired files
7
+ When I run rake reek with:
8
+ """
9
+ Reek::RakeTask.new do |t|
10
+ t.source_files = 'spec/samples/masked/dirty.rb'
11
+ end
12
+ """
13
+ Then the exit status indicates an error
14
+ And it reports:
15
+ """
16
+ spec/samples/masked/dirty.rb -- 3 warnings (+3 masked):
17
+ Dirty#a calls @s.title twice (Duplication)
18
+ Dirty#a calls puts(@s.title) twice (Duplication)
19
+ Dirty#a/block/block is nested (Nested Iterators)
20
+ """
21
+
22
+ Scenario: name changes the task name
23
+ When I run rake silky with:
24
+ """
25
+ Reek::RakeTask.new('silky') do |t|
26
+ t.source_files = 'spec/samples/masked/dirty.rb'
27
+ end
28
+ """
29
+ Then the exit status indicates an error
30
+ And it reports:
31
+ """
32
+ spec/samples/masked/dirty.rb -- 3 warnings (+3 masked):
33
+ Dirty#a calls @s.title twice (Duplication)
34
+ Dirty#a calls puts(@s.title) twice (Duplication)
35
+ Dirty#a/block/block is nested (Nested Iterators)
36
+ """
37
+
38
+ Scenario: verbose prints the reek command
39
+ When I run rake reek with:
40
+ """
41
+ Reek::RakeTask.new do |t|
42
+ t.source_files = 'spec/samples/masked/dirty.rb'
43
+ t.verbose = true
44
+ end
45
+ """
46
+ Then the exit status indicates an error
47
+ And it reports:
48
+ """
49
+ /usr/bin/ruby1.8 -I"/home/kevin/Working/git/reek/lib" "/home/kevin/Working/git/reek/bin/reek" "spec/samples/masked/dirty.rb"
50
+ spec/samples/masked/dirty.rb -- 3 warnings (+3 masked):
51
+ Dirty#a calls @s.title twice (Duplication)
52
+ Dirty#a calls puts(@s.title) twice (Duplication)
53
+ Dirty#a/block/block is nested (Nested Iterators)
54
+ """
55
+
56
+ Scenario: fail_on_error can hide the error status
57
+ When I run rake reek with:
58
+ """
59
+ Reek::RakeTask.new do |t|
60
+ t.fail_on_error = false
61
+ t.source_files = 'spec/samples/empty_config_file/dirty.rb'
62
+ end
63
+ """
64
+ Then it succeeds
65
+ And it reports:
66
+ """
67
+ spec/samples/empty_config_file/dirty.rb -- 6 warnings:
68
+ Dirty has the variable name '@s' (Uncommunicative Name)
69
+ Dirty#a calls @s.title twice (Duplication)
70
+ Dirty#a calls puts(@s.title) twice (Duplication)
71
+ Dirty#a has the name 'a' (Uncommunicative Name)
72
+ Dirty#a/block has the variable name 'x' (Uncommunicative Name)
73
+ Dirty#a/block/block is nested (Nested Iterators)
74
+ """
@@ -6,7 +6,7 @@ Feature: Correctly formatted reports
6
6
 
7
7
  Scenario Outline: two reports run together with indented smells
8
8
  When I run reek <args>
9
- Then it fails with exit status 2
9
+ Then the exit status indicates smells
10
10
  And it reports:
11
11
  """
12
12
  spec/samples/two_smelly_files/dirty_one.rb -- 6 warnings:
@@ -6,7 +6,7 @@ Feature: Basic smell detection
6
6
 
7
7
  Scenario: Correct smells from inline.rb
8
8
  When I run reek spec/samples/inline.rb
9
- Then it fails with exit status 2
9
+ Then the exit status indicates smells
10
10
  And it reports:
11
11
  """
12
12
  spec/samples/inline.rb -- 39 warnings (+1 masked):
@@ -54,11 +54,11 @@ Feature: Basic smell detection
54
54
 
55
55
  Scenario: Correct smells from optparse.rb
56
56
  When I run reek spec/samples/optparse.rb
57
- Then it fails with exit status 2
57
+ Then the exit status indicates smells
58
58
  And it reports:
59
59
  """
60
60
  spec/samples/optparse.rb -- 121 warnings:
61
- OptionParser has at least 59 methods (Large Class)
61
+ OptionParser has at least 42 methods (Large Class)
62
62
  OptionParser tests ((argv.size == 1) and Array.===(argv[0])) at least 3 times (Simulated Polymorphism)
63
63
  OptionParser tests a at least 7 times (Simulated Polymorphism)
64
64
  OptionParser tests default_pattern at least 7 times (Simulated Polymorphism)
@@ -184,7 +184,7 @@ Feature: Basic smell detection
184
184
 
185
185
  Scenario: Correct smells from redcloth.rb
186
186
  When I run reek spec/samples/redcloth.rb
187
- Then it fails with exit status 2
187
+ Then the exit status indicates smells
188
188
  And it reports:
189
189
  """
190
190
  spec/samples/redcloth.rb -- 95 warnings:
@@ -33,7 +33,7 @@ Feature: Reek reads from $stdin when no files are given
33
33
 
34
34
  Scenario: return non-zero status when there are smells
35
35
  When I pass "class Turn; def y() @x = 3; end end" to reek
36
- Then it fails with exit status 2
36
+ Then the exit status indicates smells
37
37
  And it reports:
38
38
  """
39
39
  $stdin -- 2 warnings:
@@ -1,13 +1,13 @@
1
1
  When /^I run reek (.*)$/ do |args|
2
- run args
2
+ reek(args)
3
3
  end
4
4
 
5
5
  When /^I pass "([^\"]*)" to reek *(.*)$/ do |stdin, args|
6
- run_with_pipe(stdin, args)
6
+ reek_with_pipe(stdin, args)
7
7
  end
8
8
 
9
- When /^I run rake reek$/ do
10
- rake
9
+ When /^I run rake (\w*) with:$/ do |name, task_def|
10
+ rake(name, task_def)
11
11
  end
12
12
 
13
13
  Then /^stdout equals "([^\"]*)"$/ do |report|
@@ -15,11 +15,15 @@ Then /^stdout equals "([^\"]*)"$/ do |report|
15
15
  end
16
16
 
17
17
  Then /^it succeeds$/ do
18
- @last_exit_status.should == 0
18
+ @last_exit_status.should == Reek::EXIT_STATUS[:success]
19
19
  end
20
20
 
21
- Then /^it fails with exit status (\d+)$/ do |status|
22
- @last_exit_status.should == status.to_i
21
+ Then /^the exit status indicates an error$/ do
22
+ @last_exit_status.should == Reek::EXIT_STATUS[:error]
23
+ end
24
+
25
+ Then /^the exit status indicates smells$/ do
26
+ @last_exit_status.should == Reek::EXIT_STATUS[:smells]
23
27
  end
24
28
 
25
29
  Then /^it reports:$/ do |report|
@@ -5,34 +5,42 @@ require 'tempfile'
5
5
  require 'spec/expectations'
6
6
  require 'fileutils'
7
7
  require 'reek'
8
+ require 'reek/adapters/application'
8
9
 
9
- class CucumberWorld
10
-
11
- def run(args)
12
- stderr_file = Tempfile.new('cucumber')
10
+ class ReekWorld
11
+ def run(cmd)
12
+ stderr_file = Tempfile.new('reek-world')
13
13
  stderr_file.close
14
- @last_stdout = `ruby -Ilib bin/reek #{args} 2> #{stderr_file.path}`
14
+ @last_stdout = `#{cmd} 2> #{stderr_file.path}`
15
15
  @last_exit_status = $?.exitstatus
16
16
  @last_stderr = IO.read(stderr_file.path)
17
17
  end
18
18
 
19
- def run_with_pipe(stdin, args)
20
- stderr_file = Tempfile.new('cucumber')
21
- stderr_file.close
22
- @last_stdout = `echo \"#{stdin}\" | ruby -Ilib bin/reek #{args} 2> #{stderr_file.path}`
23
- @last_exit_status = $?.exitstatus
24
- @last_stderr = IO.read(stderr_file.path)
19
+ def reek(args)
20
+ run("ruby -Ilib -rubygems bin/reek #{args}")
25
21
  end
26
22
 
27
- def rake
28
- stderr_file = Tempfile.new('cucumber')
29
- stderr_file.close
30
- @last_stdout = `rake reek 2> #{stderr_file.path}`
31
- @last_exit_status = $?.exitstatus
32
- @last_stderr = IO.read(stderr_file.path)
23
+ def reek_with_pipe(stdin, args)
24
+ run("echo \"#{stdin}\" | ruby -Ilib -rubygems bin/reek #{args}")
25
+ end
26
+
27
+ def rake(name, task_def)
28
+ header = <<EOS
29
+ $:.unshift('lib')
30
+ require 'reek/adapters/rake_task'
31
+
32
+ EOS
33
+ rakefile = Tempfile.new('rake_task', '.')
34
+ rakefile.puts(header + task_def)
35
+ rakefile.close
36
+ run("RUBYOPT=rubygems rake -f #{rakefile.path} #{name}")
37
+ lines = @last_stdout.split("\n")
38
+ if lines.length > 0 and lines[0] =~ /^\(/
39
+ @last_stdout = lines[1..-1].join("\n")
40
+ end
33
41
  end
34
42
  end
35
43
 
36
44
  World do
37
- CucumberWorld.new
45
+ ReekWorld.new
38
46
  end
@@ -3,6 +3,13 @@ require 'reek/adapters/source'
3
3
  require 'reek/adapters/core_extras'
4
4
 
5
5
  module Reek
6
+
7
+ EXIT_STATUS = {
8
+ :success => 0,
9
+ :error => 1,
10
+ :smells => 2
11
+ }
12
+
6
13
  #
7
14
  # Represents an instance of a Reek application.
8
15
  # This is the entry point for all invocations of Reek from the
@@ -27,7 +34,7 @@ module Reek
27
34
  def reek
28
35
  examine_sources
29
36
  puts @options.create_report(@sniffer.sniffers).report
30
- return @sniffer.smelly? ? 2 : 0
37
+ return EXIT_STATUS[@sniffer.smelly? ? :smells : :success]
31
38
  end
32
39
 
33
40
  def execute
@@ -37,7 +44,7 @@ module Reek
37
44
  return ex.status
38
45
  rescue Exception => error
39
46
  $stderr.puts "Error: #{error}"
40
- return 1
47
+ return EXIT_STATUS[:error]
41
48
  end
42
49
  end
43
50
  end
@@ -45,11 +45,11 @@ EOB
45
45
 
46
46
  @parser.on("-h", "--help", "Show this message") do
47
47
  puts @parser
48
- exit(0)
48
+ exit(EXIT_STATUS[:success])
49
49
  end
50
50
  @parser.on("-v", "--version", "Show version") do
51
51
  puts "#{@parser.program_name} #{Reek::VERSION}"
52
- exit(0)
52
+ exit(EXIT_STATUS[:success])
53
53
  end
54
54
 
55
55
  @parser.separator "\nReport formatting:"
@@ -10,14 +10,6 @@ class Object
10
10
  def sniff
11
11
  Reek::Sniffer.new(self.to_reek_source)
12
12
  end
13
-
14
- #
15
- # Constructs a Sniffer which examines this object for code smells.
16
- # (This feature is only enabled if you have the ParseTree gem installed.)
17
- #
18
- def to_reek_source
19
- ObjectSource.new(self, self.to_s)
20
- end
21
13
  end
22
14
 
23
15
  class File
@@ -1,4 +1,6 @@
1
+ require 'ruby_parser'
1
2
  require 'reek/adapters/config_file'
3
+ require 'reek/tree_dresser'
2
4
 
3
5
  module Reek
4
6
 
@@ -17,7 +19,8 @@ module Reek
17
19
  def configure(sniffer) end
18
20
 
19
21
  def syntax_tree
20
- RubyParser.new.parse(@source, @desc) || s()
22
+ ast = RubyParser.new.parse(@source, @desc) || s()
23
+ TreeDresser.new.dress(ast)
21
24
  end
22
25
  end
23
26
 
@@ -1,6 +1,5 @@
1
1
  require 'reek/sniffer'
2
2
  require 'reek/adapters/core_extras'
3
- require 'reek/adapters/object_source'
4
3
  require 'reek/adapters/report'
5
4
 
6
5
  module Reek
@@ -89,11 +88,9 @@ module Reek
89
88
  end
90
89
 
91
90
  class ShouldReekOnlyOf < ShouldReekOf # :nodoc:
92
- include ReekMatcher
93
-
94
91
  def matches?(actual)
95
92
  @sniffer = actual.sniff
96
- @sniffer.smells_only_of?(@klass, @patterns)
93
+ @sniffer.num_smells == 1 and @sniffer.has_smell?(@klass, @patterns)
97
94
  end
98
95
  def failure_message_for_should
99
96
  "Expected #{@sniffer.desc} to reek only of #{@klass}, but got:\n#{report}"
@@ -21,15 +21,25 @@ module Reek
21
21
  end
22
22
  end
23
23
 
24
- class BlockContext < CodeContext
24
+ class VariableContainer < CodeContext
25
+
26
+ def initialize(outer, exp)
27
+ super
28
+ @local_variables = Set.new
29
+ end
30
+
31
+ def record_local_variable(sym)
32
+ @local_variables << Name.new(sym)
33
+ end
34
+ end
35
+
36
+ class BlockContext < VariableContainer
25
37
 
26
38
  def initialize(outer, exp)
27
39
  super
28
40
  @name = Name.new('block')
29
- @parameters = exp[0] if exp
30
- @parameters ||= []
41
+ @parameters = exp[2] || []
31
42
  @parameters.extend(ParameterSet)
32
- @local_variables = Set.new
33
43
  end
34
44
 
35
45
  def inside_a_block?
@@ -43,10 +53,6 @@ module Reek
43
53
  def nested_block?
44
54
  @outer.inside_a_block?
45
55
  end
46
-
47
- def record_local_variable(sym)
48
- @local_variables << Name.new(sym)
49
- end
50
56
 
51
57
  def outer_name
52
58
  "#{@outer.outer_name}#{@name}/"
@@ -1,5 +1,5 @@
1
1
  require 'set'
2
- require 'reek/code_context'
2
+ require 'reek/module_context'
3
3
 
4
4
  class Class
5
5
  def is_overriding_method?(name)
@@ -11,39 +11,14 @@ class Class
11
11
  end
12
12
 
13
13
  module Reek
14
- class ClassContext < CodeContext
14
+ class ClassContext < ModuleContext
15
15
 
16
- def ClassContext.create(outer, exp)
17
- res = Name.resolve(exp[1], outer)
18
- ClassContext.new(res[0], res[1], exp[2])
19
- end
20
-
21
- def ClassContext.from_s(src)
22
- source = src.to_reek_source
23
- sniffer = Sniffer.new(source)
24
- CodeParser.new(sniffer).process_class(source.syntax_tree)
25
- end
16
+ attr_reader :parsed_methods
26
17
 
27
- attr_reader :conditionals, :parsed_methods, :class_variables
28
-
29
- # SMELL: inconsistent with other contexts (not linked to the sexp)
30
- def initialize(outer, name, superclass = nil)
31
- super(outer, nil)
32
- @name = name
33
- @superclass = superclass
34
- @parsed_methods = []
18
+ def initialize(outer, name, exp)
19
+ super
20
+ @superclass = exp[2]
35
21
  @instance_variables = Set.new
36
- @conditionals = []
37
- @class_variables = Set.new
38
- end
39
-
40
- def myself
41
- @myself ||= @outer.find_module(@name)
42
- end
43
-
44
- def find_module(modname)
45
- return nil unless myself
46
- @myself.const_or_nil(modname.to_s)
47
22
  end
48
23
 
49
24
  def is_overriding_method?(name)
@@ -55,40 +30,16 @@ module Reek
55
30
  @superclass == [:const, :Struct]
56
31
  end
57
32
 
58
- def num_methods
59
- @parsed_methods.length
60
- end
61
-
62
- def record_class_variable(cvar)
63
- @class_variables << Name.new(cvar)
64
- end
65
-
66
33
  def record_instance_variable(sym)
67
34
  @instance_variables << Name.new(sym)
68
35
  end
69
36
 
70
- def record_method(meth)
71
- @parsed_methods << meth
72
- end
73
-
74
37
  def outer_name
75
38
  "#{@outer.outer_name}#{@name}#"
76
39
  end
77
40
 
78
- def to_s
79
- "#{@outer.outer_name}#{@name}"
80
- end
81
-
82
41
  def variable_names
83
42
  @instance_variables
84
43
  end
85
-
86
- def record_conditional(exp)
87
- @conditionals << exp
88
- end
89
-
90
- def parameterized_methods(min_clump_size)
91
- parsed_methods.select {|meth| meth.parameters.length >= min_clump_size }
92
- end
93
44
  end
94
45
  end
@@ -23,6 +23,16 @@ module Reek
23
23
  @myself = nil
24
24
  end
25
25
 
26
+ def each(type, ignoring, &blk)
27
+ if block_given?
28
+ @exp.look_for(type, ignoring, &blk)
29
+ else
30
+ result = []
31
+ @exp.look_for(type, ignoring) {|exp| result << exp}
32
+ result
33
+ end
34
+ end
35
+
26
36
  # SMELL: Temporary Field -- @name isn't always initialized
27
37
  def matches?(strings)
28
38
  me = @name.to_s