reek 1.2.8 → 1.2.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/History.txt +4 -0
  2. data/README.md +40 -25
  3. data/Rakefile +2 -15
  4. data/bin/reek +1 -1
  5. data/features/{options.feature → command_line_interface/options.feature} +1 -3
  6. data/features/{stdin.feature → command_line_interface/stdin.feature} +2 -2
  7. data/features/{masking_smells.feature → configuration_files/masking_smells.feature} +23 -23
  8. data/features/{rake_task.feature → rake_task/rake_task.feature} +9 -9
  9. data/features/{reports.feature → reports/reports.feature} +10 -10
  10. data/features/{yaml.feature → reports/yaml.feature} +2 -2
  11. data/features/{api.feature → ruby_api/api.feature} +6 -6
  12. data/features/samples.feature +239 -247
  13. data/features/step_definitions/reek_steps.rb +15 -1
  14. data/features/support/env.rb +0 -1
  15. data/lib/reek.rb +1 -4
  16. data/lib/reek/cli/command_line.rb +6 -5
  17. data/lib/reek/cli/report.rb +1 -1
  18. data/lib/reek/core/hash_extensions.rb +29 -0
  19. data/lib/reek/core/method_context.rb +3 -16
  20. data/lib/reek/core/object_refs.rb +5 -22
  21. data/lib/reek/core/smell_repository.rb +65 -0
  22. data/lib/reek/core/sniffer.rb +7 -76
  23. data/lib/reek/core/warning_collector.rb +1 -5
  24. data/lib/reek/examiner.rb +3 -13
  25. data/lib/reek/rake/task.rb +10 -2
  26. data/lib/reek/smell_warning.rb +1 -0
  27. data/lib/reek/smells/smell_detector.rb +0 -3
  28. data/lib/reek/smells/uncommunicative_module_name.rb +6 -3
  29. data/lib/reek/smells/uncommunicative_variable_name.rb +1 -1
  30. data/lib/reek/source/config_file.rb +8 -2
  31. data/lib/reek/source/sexp_formatter.rb +1 -1
  32. data/lib/reek/source/source_repository.rb +31 -0
  33. data/lib/reek/source/tree_dresser.rb +1 -1
  34. data/lib/reek/spec/should_reek.rb +5 -2
  35. data/lib/reek/version.rb +3 -0
  36. data/lib/xp.reek +63 -0
  37. data/reek.gemspec +16 -28
  38. data/spec/gem/manifest_spec.rb +22 -0
  39. data/spec/gem/updates_spec.rb +26 -0
  40. data/spec/gem/yard_spec.rb +15 -0
  41. data/spec/matchers/smell_of_matcher.rb +0 -1
  42. data/spec/reek/cli/reek_command_spec.rb +1 -1
  43. data/spec/reek/core/method_context_spec.rb +2 -2
  44. data/spec/reek/core/object_refs_spec.rb +115 -118
  45. data/spec/reek/smell_warning_spec.rb +2 -2
  46. data/spec/reek/smells/uncommunicative_variable_name_spec.rb +3 -0
  47. data/spec/reek/source/sexp_formatter_spec.rb +19 -0
  48. data/spec/reek/spec/should_reek_spec.rb +21 -3
  49. data/tasks/deployment.rake +69 -0
  50. data/tasks/develop.rake +29 -0
  51. data/tasks/test.rake +1 -6
  52. metadata +200 -138
@@ -31,13 +31,27 @@ Then /^the exit status indicates smells$/ do
31
31
  end
32
32
 
33
33
  Then /^it reports:$/ do |report|
34
- @last_stdout.should == report
34
+ @last_stdout.chomp.should == report.chomp
35
+ end
36
+
37
+ Then /^it reports this yaml:$/ do |expected_yaml|
38
+ expected_warnings = YAML.load(expected_yaml.chomp)
39
+ actual_warnings = YAML.load(@last_stdout)
40
+ actual_warnings.should == expected_warnings
41
+ end
42
+
43
+ Then /^it reports something like: (.*)$/ do |line|
44
+ @last_stdout.chomp.should match Regexp.new(Regexp.escape(line))
35
45
  end
36
46
 
37
47
  Then /^stderr reports:$/ do |report|
38
48
  @last_stderr.should == report
39
49
  end
40
50
 
51
+ Then /^it reports an error$/ do
52
+ @last_stderr.chomp.should_not be_empty
53
+ end
54
+
41
55
  Then /^it reports the error ['"](.*)['"]$/ do |string|
42
56
  @last_stderr.chomp.should == string
43
57
  end
@@ -2,7 +2,6 @@ $:.unshift 'lib'
2
2
 
3
3
  require 'rubygems'
4
4
  require 'tempfile'
5
- require 'spec/expectations'
6
5
  require 'fileutils'
7
6
  require 'reek/cli/application'
8
7
 
data/lib/reek.rb CHANGED
@@ -1,9 +1,6 @@
1
1
  #
2
2
  # Reek's core functionality
3
3
  #
4
- module Reek
5
- VERSION = '1.2.8'
6
- end
7
-
4
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'reek', 'version')
8
5
  require File.join(File.dirname(File.expand_path(__FILE__)), 'reek', 'examiner')
9
6
  require File.join(File.dirname(File.expand_path(__FILE__)), 'reek', 'smell_warning')
@@ -49,7 +49,7 @@ Examples:
49
49
  #{progname} -q lib
50
50
  cat my_class.rb | #{progname}
51
51
 
52
- See http://wiki.github.com/kevinrutherford/reek for detailed help.
52
+ See http://wiki.github.com/troessner/reek for detailed help.
53
53
 
54
54
  EOB
55
55
  end
@@ -87,12 +87,13 @@ EOB
87
87
  HelpCommand.new(@parser)
88
88
  elsif @command_class == VersionCommand
89
89
  VersionCommand.new(@parser.program_name)
90
- elsif @command_class == YamlCommand
91
- sources = get_sources
92
- YamlCommand.create(sources, @config_files)
93
90
  else
94
91
  sources = get_sources
95
- ReekCommand.create(sources, @report_class, @config_files)
92
+ if @command_class == YamlCommand
93
+ YamlCommand.create(sources, @config_files)
94
+ else
95
+ ReekCommand.create(sources, @report_class, @config_files)
96
+ end
96
97
  end
97
98
  end
98
99
 
@@ -10,7 +10,7 @@ module Reek
10
10
 
11
11
  def format_list(warnings)
12
12
  warnings.map do |warning|
13
- " #{warning.context} #{warning.message} (#{warning.smell_class})"
13
+ " #{warning.context} #{warning.message} (#{warning.subclass})"
14
14
  end.join("\n")
15
15
  end
16
16
 
@@ -0,0 +1,29 @@
1
+ #
2
+ # Extensions to +Hash+ needed by Reek.
3
+ #
4
+ class Hash
5
+ def push_keys(hash)
6
+ keys.each {|key| hash[key].adopt!(self[key]) }
7
+ end
8
+
9
+ def adopt!(other)
10
+ other.keys.each do |key|
11
+ ov = other[key]
12
+ if Array === ov and has_key?(key)
13
+ self[key] += ov
14
+ else
15
+ self[key] = ov
16
+ end
17
+ end
18
+ self
19
+ end
20
+
21
+ def adopt(other)
22
+ self.deep_copy.adopt!(other)
23
+ end
24
+
25
+ def deep_copy
26
+ YAML::load(YAML::dump(self))
27
+ end
28
+ end
29
+
@@ -24,19 +24,6 @@ module Reek
24
24
  def is_assignment_block?(param)
25
25
  Array === param and param[0] == :block
26
26
  end
27
-
28
- def names
29
- return @names if @names
30
- @names = self[1..-1].select {|arg| is_arg?(arg)}.map {|arg| arg.to_s }
31
- end
32
-
33
- def length
34
- names.length
35
- end
36
-
37
- def include?(name)
38
- names.include?(name)
39
- end
40
27
  end
41
28
 
42
29
  #
@@ -65,14 +52,14 @@ module Reek
65
52
  receiver ||= [:self]
66
53
  case receiver[0]
67
54
  when :lvar
68
- @refs.record_ref(receiver) unless meth == :new
55
+ @refs.record_reference_to(receiver) unless meth == :new
69
56
  when :self
70
- record_use_of_self
57
+ @refs.record_reference_to(:self)
71
58
  end
72
59
  end
73
60
 
74
61
  def record_use_of_self
75
- @refs.record_reference_to_self
62
+ @refs.record_reference_to(:self)
76
63
  end
77
64
 
78
65
  def envious_receivers
@@ -9,24 +9,12 @@ module Reek
9
9
  @refs = Hash.new(0)
10
10
  end
11
11
 
12
- def record_reference_to_self
13
- record_ref(SELF_REF)
12
+ def record_reference_to(exp)
13
+ @refs[exp] += 1
14
14
  end
15
15
 
16
- def record_ref(exp)
17
- type = exp[0]
18
- case type
19
- when :gvar
20
- return
21
- when :self
22
- record_reference_to_self
23
- else
24
- @refs[exp] += 1
25
- end
26
- end
27
-
28
- def refs_to_self
29
- @refs[SELF_REF]
16
+ def references_to(exp)
17
+ @refs[exp]
30
18
  end
31
19
 
32
20
  def max_refs
@@ -39,13 +27,8 @@ module Reek
39
27
  end
40
28
 
41
29
  def self_is_max?
42
- max_keys.length == 0 || @refs[SELF_REF] == max_refs
30
+ max_keys.length == 0 || @refs[:self] == max_refs
43
31
  end
44
-
45
- private
46
-
47
- SELF_REF = Sexp.from_array([:lit, :self])
48
-
49
32
  end
50
33
  end
51
34
  end
@@ -0,0 +1,65 @@
1
+ require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'smells')
2
+
3
+ module Reek
4
+ module Core
5
+ class SmellRepository
6
+
7
+ def self.smell_classes
8
+ # SMELL: Duplication -- these should be loaded by listing the files
9
+ [
10
+ Smells::Attribute,
11
+ Smells::BooleanParameter,
12
+ Smells::ClassVariable,
13
+ Smells::ControlCouple,
14
+ Smells::DataClump,
15
+ Smells::Duplication,
16
+ Smells::FeatureEnvy,
17
+ Smells::IrresponsibleModule,
18
+ Smells::LargeClass,
19
+ Smells::LongMethod,
20
+ Smells::LongParameterList,
21
+ Smells::LongYieldList,
22
+ Smells::NestedIterators,
23
+ Smells::SimulatedPolymorphism,
24
+ Smells::UncommunicativeMethodName,
25
+ Smells::UncommunicativeModuleName,
26
+ Smells::UncommunicativeParameterName,
27
+ Smells::UncommunicativeVariableName,
28
+ Smells::UtilityFunction,
29
+ ]
30
+ end
31
+
32
+ def initialize source_description
33
+ @typed_detectors = nil
34
+ @detectors = Hash.new
35
+ SmellRepository.smell_classes.each do |klass|
36
+ @detectors[klass] = klass.new(source_description)
37
+ end
38
+ end
39
+
40
+ def configure(klass, config)
41
+ @detectors[klass].configure_with(config)
42
+ end
43
+
44
+ def report_on listener
45
+ @detectors.each_value { |detector| detector.report_on(listener) }
46
+ end
47
+
48
+ def examine(scope, node_type)
49
+ smell_listeners[node_type].each do |detector|
50
+ detector.examine(scope)
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def smell_listeners()
57
+ unless @typed_detectors
58
+ @typed_detectors = Hash.new {|hash,key| hash[key] = [] }
59
+ @detectors.each_value { |detector| detector.register(@typed_detectors) }
60
+ end
61
+ @typed_detectors
62
+ end
63
+ end
64
+ end
65
+ end
@@ -1,36 +1,8 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), 'code_parser')
2
- require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'smells')
2
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'smell_repository')
3
3
  require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'source', 'config_file')
4
4
  require 'yaml'
5
-
6
- #
7
- # Extensions to +Hash+ needed by Reek.
8
- #
9
- class Hash
10
- def push_keys(hash)
11
- keys.each {|key| hash[key].adopt!(self[key]) }
12
- end
13
-
14
- def adopt!(other)
15
- other.keys.each do |key|
16
- ov = other[key]
17
- if Array === ov and has_key?(key)
18
- self[key] += ov
19
- else
20
- self[key] = ov
21
- end
22
- end
23
- self
24
- end
25
-
26
- def adopt(other)
27
- self.deep_copy.adopt!(other)
28
- end
29
-
30
- def deep_copy
31
- YAML::load(YAML::dump(self))
32
- end
33
- end
5
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'hash_extensions')
34
6
 
35
7
  module Reek
36
8
  module Core
@@ -40,65 +12,24 @@ module Reek
40
12
  #
41
13
  class Sniffer
42
14
 
43
- def self.smell_classes
44
- # SMELL: Duplication -- these should be loaded by listing the files
45
- [
46
- Smells::Attribute,
47
- Smells::BooleanParameter,
48
- Smells::ClassVariable,
49
- Smells::ControlCouple,
50
- Smells::DataClump,
51
- Smells::Duplication,
52
- Smells::FeatureEnvy,
53
- Smells::IrresponsibleModule,
54
- Smells::LargeClass,
55
- Smells::LongMethod,
56
- Smells::LongParameterList,
57
- Smells::LongYieldList,
58
- Smells::NestedIterators,
59
- Smells::SimulatedPolymorphism,
60
- Smells::UncommunicativeMethodName,
61
- Smells::UncommunicativeModuleName,
62
- Smells::UncommunicativeParameterName,
63
- Smells::UncommunicativeVariableName,
64
- Smells::UtilityFunction,
65
- ]
66
- end
67
-
68
- def initialize(src, config_files = [])
69
- @typed_detectors = nil
70
- @detectors = Hash.new
71
- Sniffer.smell_classes.each do |klass|
72
- @detectors[klass] = klass.new(src.desc)
73
- end
15
+ def initialize(src, config_files = [], smell_repository=Core::SmellRepository.new(src.desc))
16
+ @smell_repository = smell_repository
74
17
  config_files.each{ |cf| Reek::Source::ConfigFile.new(cf).configure(self) }
75
18
  @source = src
76
19
  src.configure(self)
77
20
  end
78
21
 
79
22
  def configure(klass, config)
80
- @detectors[klass].configure_with(config)
23
+ @smell_repository.configure klass, config
81
24
  end
82
25
 
83
26
  def report_on(listener)
84
27
  CodeParser.new(self).process(@source.syntax_tree)
85
- @detectors.each_value { |detector| detector.report_on(listener) }
28
+ @smell_repository.report_on(listener)
86
29
  end
87
30
 
88
31
  def examine(scope, node_type)
89
- smell_listeners[node_type].each do |detector|
90
- detector.examine(scope)
91
- end
92
- end
93
-
94
- private
95
-
96
- def smell_listeners()
97
- unless @typed_detectors
98
- @typed_detectors = Hash.new {|hash,key| hash[key] = [] }
99
- @detectors.each_value { |detector| detector.register(@typed_detectors) }
100
- end
101
- @typed_detectors
32
+ @smell_repository.examine scope, node_type
102
33
  end
103
34
  end
104
35
  end
@@ -16,11 +16,7 @@ module Reek
16
16
  end
17
17
 
18
18
  def warnings
19
- @warnings.to_a.sort do |first,second|
20
- first_sig = [first.context, first.message, first.smell_class]
21
- second_sig = [second.context, second.message, second.smell_class]
22
- first_sig <=> second_sig
23
- end
19
+ @warnings.to_a.sort
24
20
  end
25
21
  end
26
22
  end
data/lib/reek/examiner.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  require File.join(File.dirname(File.expand_path(__FILE__)), 'core', 'sniffer')
2
2
  require File.join(File.dirname(File.expand_path(__FILE__)), 'core', 'warning_collector')
3
- require File.join(File.dirname(File.expand_path(__FILE__)), 'source')
3
+ require File.join(File.dirname(File.expand_path(__FILE__)), 'source', 'source_repository')
4
4
 
5
5
  module Reek
6
6
 
@@ -28,18 +28,8 @@ module Reek
28
28
  # each of which is opened and parsed for source code.
29
29
  #
30
30
  def initialize(source, config_files = [])
31
- sources = case source
32
- when Array
33
- @description = 'dir'
34
- Source::SourceLocator.new(source).all_sources
35
- when Source::SourceCode
36
- @description = source.desc
37
- [source]
38
- else
39
- src = source.to_reek_source
40
- @description = src.desc
41
- [src]
42
- end
31
+ sources = Source::SourceRepository.parse(source)
32
+ @description = sources.description
43
33
  collector = Core::WarningCollector.new
44
34
  sources.each { |src| Core::Sniffer.new(src, config_files).report_on(collector) }
45
35
  @smells = collector.warnings
@@ -124,8 +124,16 @@ module Reek
124
124
  end
125
125
 
126
126
  def ruby_options
127
- lib_path = @libs.join(File::PATH_SEPARATOR)
128
- @ruby_opts.clone << "-I\"#{lib_path}\""
127
+ if bundler?
128
+ %w(-S bundle exec)
129
+ else
130
+ lib_path = @libs.join(File::PATH_SEPARATOR)
131
+ @ruby_opts.clone << "-I\"#{lib_path}\""
132
+ end
133
+ end
134
+
135
+ def bundler?
136
+ File.exist?('./Gemfile')
129
137
  end
130
138
 
131
139
  def sort_option
@@ -1,6 +1,7 @@
1
1
 
2
2
  module Reek
3
3
 
4
+
4
5
  #
5
6
  # Reports a warning that a smell has been found.
6
7
  # This object is essentially a DTO, and therefore contains a :reek:attribute or two.