teksymmetry-reek 1.1.3.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +131 -0
- data/README.txt +36 -0
- data/Rakefile +17 -0
- data/bin/reek +27 -0
- data/config/defaults.reek +51 -0
- data/lib/reek/block_context.rb +59 -0
- data/lib/reek/class_context.rb +68 -0
- data/lib/reek/code_context.rb +54 -0
- data/lib/reek/code_parser.rb +221 -0
- data/lib/reek/exceptions.reek +13 -0
- data/lib/reek/if_context.rb +25 -0
- data/lib/reek/method_context.rb +91 -0
- data/lib/reek/module_context.rb +33 -0
- data/lib/reek/name.rb +49 -0
- data/lib/reek/object_refs.rb +52 -0
- data/lib/reek/object_source.rb +53 -0
- data/lib/reek/options.rb +100 -0
- data/lib/reek/rake_task.rb +121 -0
- data/lib/reek/report.rb +81 -0
- data/lib/reek/sexp_formatter.rb +10 -0
- data/lib/reek/singleton_method_context.rb +27 -0
- data/lib/reek/smell_warning.rb +49 -0
- data/lib/reek/smells/control_couple.rb +61 -0
- data/lib/reek/smells/duplication.rb +50 -0
- data/lib/reek/smells/feature_envy.rb +58 -0
- data/lib/reek/smells/large_class.rb +69 -0
- data/lib/reek/smells/long_method.rb +43 -0
- data/lib/reek/smells/long_parameter_list.rb +43 -0
- data/lib/reek/smells/long_yield_list.rb +18 -0
- data/lib/reek/smells/nested_iterators.rb +28 -0
- data/lib/reek/smells/smell_detector.rb +66 -0
- data/lib/reek/smells/smells.rb +81 -0
- data/lib/reek/smells/uncommunicative_name.rb +97 -0
- data/lib/reek/smells/utility_function.rb +34 -0
- data/lib/reek/source.rb +127 -0
- data/lib/reek/spec.rb +146 -0
- data/lib/reek/stop_context.rb +50 -0
- data/lib/reek/yield_call_context.rb +12 -0
- data/lib/reek.rb +7 -0
- data/reek.gemspec +44 -0
- data/spec/reek/block_context_spec.rb +40 -0
- data/spec/reek/class_context_spec.rb +169 -0
- data/spec/reek/code_context_spec.rb +93 -0
- data/spec/reek/code_parser_spec.rb +34 -0
- data/spec/reek/config_spec.rb +42 -0
- data/spec/reek/if_context_spec.rb +17 -0
- data/spec/reek/method_context_spec.rb +66 -0
- data/spec/reek/module_context_spec.rb +38 -0
- data/spec/reek/name_spec.rb +13 -0
- data/spec/reek/object_refs_spec.rb +131 -0
- data/spec/reek/options_spec.rb +13 -0
- data/spec/reek/report_spec.rb +48 -0
- data/spec/reek/singleton_method_context_spec.rb +17 -0
- data/spec/reek/smells/control_couple_spec.rb +23 -0
- data/spec/reek/smells/duplication_spec.rb +81 -0
- data/spec/reek/smells/feature_envy_spec.rb +221 -0
- data/spec/reek/smells/large_class_spec.rb +87 -0
- data/spec/reek/smells/long_method_spec.rb +195 -0
- data/spec/reek/smells/long_parameter_list_spec.rb +85 -0
- data/spec/reek/smells/nested_iterators_spec.rb +33 -0
- data/spec/reek/smells/smell_spec.rb +24 -0
- data/spec/reek/smells/uncommunicative_name_spec.rb +123 -0
- data/spec/reek/smells/utility_function_spec.rb +93 -0
- data/spec/slow/inline_spec.rb +40 -0
- data/spec/slow/optparse_spec.rb +109 -0
- data/spec/slow/redcloth_spec.rb +101 -0
- data/spec/slow/reek_source_spec.rb +20 -0
- data/spec/slow/samples/inline.rb +704 -0
- data/spec/slow/samples/optparse.rb +1788 -0
- data/spec/slow/samples/redcloth.rb +1130 -0
- data/spec/slow/script_spec.rb +55 -0
- data/spec/slow/source_list_spec.rb +40 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +13 -0
- data/tasks/reek.rake +7 -0
- data/tasks/rspec.rake +22 -0
- metadata +163 -0
data/lib/reek/source.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'reek/code_parser'
|
2
|
+
require 'reek/report'
|
3
|
+
require 'reek/smells/smells'
|
4
|
+
require 'ruby_parser'
|
5
|
+
|
6
|
+
module Reek
|
7
|
+
|
8
|
+
#
|
9
|
+
# A +Source+ object represents a chunk of Ruby source code.
|
10
|
+
#
|
11
|
+
# The various class methods are factories that will create +Source+
|
12
|
+
# instances from various types of input.
|
13
|
+
#
|
14
|
+
class Source
|
15
|
+
|
16
|
+
#
|
17
|
+
# Factory method: creates a +Source+ object by reading Ruby code from
|
18
|
+
# the +IO+ stream. The stream is consumed upto end-of-file, but the
|
19
|
+
# source code is not parsed until +report+ is called. +desc+ provides
|
20
|
+
# a string description to be used in the header of formatted reports.
|
21
|
+
#
|
22
|
+
def self.from_io(ios, desc)
|
23
|
+
code = ios.readlines.join
|
24
|
+
return new(code, desc)
|
25
|
+
end
|
26
|
+
|
27
|
+
#
|
28
|
+
# Factory method: creates a +Source+ object by reading Ruby code from
|
29
|
+
# the +code+ string. The code is not parsed until +report+ is called.
|
30
|
+
#
|
31
|
+
def self.from_s(code)
|
32
|
+
return new(code, 'string')
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# Factory method: creates a +Source+ object by reading Ruby code from
|
37
|
+
# File +file+. The source code is not parsed until +report+ is called.
|
38
|
+
#
|
39
|
+
def self.from_f(file)
|
40
|
+
from_path(file.path)
|
41
|
+
end
|
42
|
+
|
43
|
+
#
|
44
|
+
# Factory method: creates a +Source+ object by reading Ruby code from
|
45
|
+
# the named file. The source code is not parsed until +report+ is called.
|
46
|
+
#
|
47
|
+
def self.from_path(filename)
|
48
|
+
code = IO.readlines(filename).join
|
49
|
+
return new(code, filename, File.dirname(filename))
|
50
|
+
end
|
51
|
+
|
52
|
+
#
|
53
|
+
# Factory method: creates a +Source+ object from an array of file paths.
|
54
|
+
# No source code is actually parsed until the report is accessed.
|
55
|
+
#
|
56
|
+
def self.from_pathlist(paths)
|
57
|
+
sources = paths.map {|path| Source.from_path(path) }
|
58
|
+
SourceList.new(sources)
|
59
|
+
end
|
60
|
+
|
61
|
+
def initialize(code, desc, dir = nil) # :nodoc:
|
62
|
+
@source = code
|
63
|
+
@desc = desc
|
64
|
+
@cf = SmellConfig.new
|
65
|
+
@cf = @cf.load_local(dir) if dir
|
66
|
+
end
|
67
|
+
|
68
|
+
def generate_syntax_tree
|
69
|
+
RubyParser.new.parse(@source, @desc) || s()
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
# Returns a +Report+ listing the smells found in this source. The first
|
74
|
+
# call to +report+ parses the source code and constructs a list of
|
75
|
+
# +SmellWarning+s found; subsequent calls simply return this same list.
|
76
|
+
#
|
77
|
+
def report
|
78
|
+
unless @report
|
79
|
+
@report = Report.new
|
80
|
+
parser = CodeParser.new(@report, @cf.smell_listeners)
|
81
|
+
parser.process(generate_syntax_tree)
|
82
|
+
end
|
83
|
+
@report
|
84
|
+
end
|
85
|
+
|
86
|
+
def smelly?
|
87
|
+
report.length > 0
|
88
|
+
end
|
89
|
+
|
90
|
+
#
|
91
|
+
# Checks this source for instances of +smell_class+, and returns +true+
|
92
|
+
# only if one of them has a report string matching all of the +patterns+.
|
93
|
+
#
|
94
|
+
def has_smell?(smell_class, patterns)
|
95
|
+
report.any? { |smell| smell.matches?(smell_class, patterns) }
|
96
|
+
end
|
97
|
+
|
98
|
+
# Creates a formatted report of all the +Smells::SmellWarning+ objects recorded in
|
99
|
+
# this report, with a heading.
|
100
|
+
def full_report
|
101
|
+
report.full_report(@desc)
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_s
|
105
|
+
@desc
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
#
|
110
|
+
# Represents a list of Sources as if they were a single source.
|
111
|
+
#
|
112
|
+
class SourceList
|
113
|
+
def initialize(sources)
|
114
|
+
@sources = sources
|
115
|
+
end
|
116
|
+
|
117
|
+
def smelly?
|
118
|
+
@sources.any? {|source| source.smelly? }
|
119
|
+
end
|
120
|
+
|
121
|
+
def report
|
122
|
+
ReportList.new(@sources)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
require 'reek/object_source'
|
data/lib/reek/spec.rb
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
require 'reek/source'
|
2
|
+
|
3
|
+
module Reek
|
4
|
+
|
5
|
+
#
|
6
|
+
# Provides matchers for Rspec, making it easy to check code quality.
|
7
|
+
#
|
8
|
+
# If you require this module somewhere within your spec (or in your spec_helper),
|
9
|
+
# Reek will arrange to update Spec::Runner's config so that it knows about the
|
10
|
+
# matchers defined here.
|
11
|
+
#
|
12
|
+
# === Examples
|
13
|
+
#
|
14
|
+
# Here's a spec that ensures there are no smell warnings in the current project:
|
15
|
+
#
|
16
|
+
# describe 'source code quality' do
|
17
|
+
# Dir['lib/**/*.rb'].each do |path|
|
18
|
+
# it "reports no smells in #{path}" do
|
19
|
+
# File.new(path).should_not reek
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# And here's an even simpler way to do the same:
|
25
|
+
#
|
26
|
+
# it 'has no code smells' do
|
27
|
+
# Dir['lib/**/*.rb'].should_not reek
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# Here's a simple check of a code fragment:
|
31
|
+
#
|
32
|
+
# 'def equals(other) other.thing == self.thing end'.should_not reek
|
33
|
+
#
|
34
|
+
# And a more complex example, making use of one of the factory methods for
|
35
|
+
# +Source+ so that the code is parsed and analysed only once:
|
36
|
+
#
|
37
|
+
# ruby = 'def double_thing() @other.thing.foo + @other.thing.foo end'.to_source
|
38
|
+
# ruby.should reek_of(:Duplication, /@other.thing[^\.]/)
|
39
|
+
# ruby.should reek_of(:Duplication, /@other.thing.foo/)
|
40
|
+
# ruby.should_not reek_of(:FeatureEnvy)
|
41
|
+
#
|
42
|
+
module Spec
|
43
|
+
class ShouldReek # :nodoc:
|
44
|
+
def matches?(actual)
|
45
|
+
@source = actual.to_source
|
46
|
+
@source.smelly?
|
47
|
+
end
|
48
|
+
def failure_message_for_should
|
49
|
+
"Expected source to reek, but it didn't"
|
50
|
+
end
|
51
|
+
def failure_message_for_should_not
|
52
|
+
"Expected no smells, but got:\n#{@source.report}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
#
|
57
|
+
# Returns +true+ if and only if the target source code contains smells.
|
58
|
+
#
|
59
|
+
def reek
|
60
|
+
ShouldReek.new
|
61
|
+
end
|
62
|
+
|
63
|
+
class ShouldReekOf # :nodoc:
|
64
|
+
def initialize(klass, patterns)
|
65
|
+
@klass = klass
|
66
|
+
@patterns = patterns
|
67
|
+
end
|
68
|
+
def matches?(actual)
|
69
|
+
@source = actual.to_source
|
70
|
+
@source.has_smell?(@klass, @patterns)
|
71
|
+
end
|
72
|
+
def failure_message_for_should
|
73
|
+
"Expected #{@source} to reek of #{@klass}, but it didn't"
|
74
|
+
end
|
75
|
+
def failure_message_for_should_not
|
76
|
+
"Expected #{@source} not to reek of #{@klass}, but got:\n#{@source.report}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Checks the target source code for instances of +smell_class+,
|
82
|
+
# and returns +true+ only if one of them has a report string matching
|
83
|
+
# all of the +patterns+.
|
84
|
+
#
|
85
|
+
def reek_of(smell_class, *patterns)
|
86
|
+
ShouldReekOf.new(smell_class, patterns)
|
87
|
+
end
|
88
|
+
|
89
|
+
class ShouldReekOnlyOf # :nodoc:
|
90
|
+
def initialize(klass, patterns)
|
91
|
+
@klass = klass
|
92
|
+
@patterns = patterns
|
93
|
+
end
|
94
|
+
def matches?(actual)
|
95
|
+
@source = actual.to_source
|
96
|
+
@source.report.length == 1 and @source.has_smell?(@klass, @patterns)
|
97
|
+
end
|
98
|
+
def failure_message_for_should
|
99
|
+
"Expected source to reek only of #{@klass}, but got:\n#{@source.report}"
|
100
|
+
end
|
101
|
+
def failure_message_for_should_not
|
102
|
+
"Expected source not to reek only of #{@klass}, but it did"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
# As for reek_of, but the matched smell warning must be the only warning of
|
108
|
+
# any kind in the target source code's Reek report.
|
109
|
+
#
|
110
|
+
def reek_only_of(smell_class, *patterns)
|
111
|
+
ShouldReekOnlyOf.new(smell_class, patterns)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
class File
|
117
|
+
def to_source
|
118
|
+
Reek::Source.from_f(self)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
class String
|
123
|
+
def to_source
|
124
|
+
Reek::Source.from_s(self)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class Array
|
129
|
+
def to_source
|
130
|
+
Reek::Source.from_pathlist(self)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
module Reek
|
135
|
+
class Source
|
136
|
+
def to_source
|
137
|
+
self
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
if Object.const_defined?(:Spec)
|
143
|
+
Spec::Runner.configure do |config|
|
144
|
+
config.include(Reek::Spec)
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Reek
|
2
|
+
class StopContext
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
@refs = ObjectRefs.new
|
6
|
+
@myself = Object
|
7
|
+
end
|
8
|
+
def method_missing(method, *args)
|
9
|
+
nil
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
def count_statements(num)
|
14
|
+
0
|
15
|
+
end
|
16
|
+
|
17
|
+
def find_module(name)
|
18
|
+
sym = name.to_s
|
19
|
+
@myself.const_defined?(sym) ? @myself.const_get(sym) : nil
|
20
|
+
end
|
21
|
+
|
22
|
+
def has_parameter(sym)
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
def inside_a_block?
|
27
|
+
false
|
28
|
+
end
|
29
|
+
|
30
|
+
def is_overriding_method?(name)
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
def num_statements
|
35
|
+
0
|
36
|
+
end
|
37
|
+
|
38
|
+
def refs
|
39
|
+
@refs
|
40
|
+
end
|
41
|
+
|
42
|
+
def record_depends_on_self
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
def outer_name
|
47
|
+
''
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/lib/reek.rb
ADDED
data/reek.gemspec
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{reek}
|
5
|
+
s.version = "1.1.3.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Kevin Rutherford"]
|
9
|
+
s.date = %q{2009-06-04}
|
10
|
+
s.default_executable = %q{reek}
|
11
|
+
s.description = %q{Code smell detector for Ruby}
|
12
|
+
s.email = ["kevin@rutherford-software.com"]
|
13
|
+
s.executables = ["reek"]
|
14
|
+
s.extra_rdoc_files = ["History.txt", "README.txt"]
|
15
|
+
s.files = ["History.txt", "README.txt", "Rakefile", "bin/reek", "config/defaults.reek", "lib/reek.rb", "lib/reek/block_context.rb", "lib/reek/class_context.rb", "lib/reek/code_context.rb", "lib/reek/code_parser.rb", "lib/reek/exceptions.reek", "lib/reek/if_context.rb", "lib/reek/method_context.rb", "lib/reek/module_context.rb", "lib/reek/name.rb", "lib/reek/object_refs.rb", "lib/reek/object_source.rb", "lib/reek/options.rb", "lib/reek/rake_task.rb", "lib/reek/report.rb", "lib/reek/sexp_formatter.rb", "lib/reek/singleton_method_context.rb", "lib/reek/smell_warning.rb", "lib/reek/smells/control_couple.rb", "lib/reek/smells/duplication.rb", "lib/reek/smells/feature_envy.rb", "lib/reek/smells/large_class.rb", "lib/reek/smells/long_method.rb", "lib/reek/smells/long_parameter_list.rb", "lib/reek/smells/long_yield_list.rb", "lib/reek/smells/nested_iterators.rb", "lib/reek/smells/smell_detector.rb", "lib/reek/smells/smells.rb", "lib/reek/smells/uncommunicative_name.rb", "lib/reek/smells/utility_function.rb", "lib/reek/source.rb", "lib/reek/spec.rb", "lib/reek/stop_context.rb", "lib/reek/yield_call_context.rb", "reek.gemspec", "spec/reek/block_context_spec.rb", "spec/reek/class_context_spec.rb", "spec/reek/code_context_spec.rb", "spec/reek/code_parser_spec.rb", "spec/reek/config_spec.rb", "spec/reek/if_context_spec.rb", "spec/reek/method_context_spec.rb", "spec/reek/module_context_spec.rb", "spec/reek/name_spec.rb", "spec/reek/object_refs_spec.rb", "spec/reek/options_spec.rb", "spec/reek/report_spec.rb", "spec/reek/singleton_method_context_spec.rb", "spec/reek/smells/control_couple_spec.rb", "spec/reek/smells/duplication_spec.rb", "spec/reek/smells/feature_envy_spec.rb", "spec/reek/smells/large_class_spec.rb", "spec/reek/smells/long_method_spec.rb", "spec/reek/smells/long_parameter_list_spec.rb", "spec/reek/smells/nested_iterators_spec.rb", "spec/reek/smells/smell_spec.rb", "spec/reek/smells/uncommunicative_name_spec.rb", "spec/reek/smells/utility_function_spec.rb", "spec/slow/inline_spec.rb", "spec/slow/optparse_spec.rb", "spec/slow/redcloth_spec.rb", "spec/slow/reek_source_spec.rb", "spec/slow/samples/inline.rb", "spec/slow/samples/optparse.rb", "spec/slow/samples/redcloth.rb", "spec/slow/script_spec.rb", "spec/slow/source_list_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "tasks/reek.rake", "tasks/rspec.rake"]
|
16
|
+
s.homepage = %q{http://wiki.github.com/kevinrutherford/reek}
|
17
|
+
s.post_install_message = %q{
|
18
|
+
For more information on reek, see http://wiki.github.com/kevinrutherford/reek
|
19
|
+
}
|
20
|
+
s.rdoc_options = ["--main", "README.txt"]
|
21
|
+
s.require_paths = ["lib"]
|
22
|
+
s.rubyforge_project = %q{reek}
|
23
|
+
s.rubygems_version = %q{1.3.3}
|
24
|
+
s.summary = %q{Code smell detector for Ruby}
|
25
|
+
|
26
|
+
if s.respond_to? :specification_version then
|
27
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
28
|
+
s.specification_version = 3
|
29
|
+
|
30
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
31
|
+
s.add_runtime_dependency(%q<ruby_parser>, ["~> 2.0"])
|
32
|
+
s.add_runtime_dependency(%q<ruby2ruby>, ["~> 1.2"])
|
33
|
+
s.add_runtime_dependency(%q<sexp_processor>, ["~> 3.0"])
|
34
|
+
else
|
35
|
+
s.add_dependency(%q<ruby_parser>, ["~> 2.0"])
|
36
|
+
s.add_dependency(%q<ruby2ruby>, ["~> 1.2"])
|
37
|
+
s.add_dependency(%q<sexp_processor>, ["~> 3.0"])
|
38
|
+
end
|
39
|
+
else
|
40
|
+
s.add_dependency(%q<ruby_parser>, ["~> 2.0"])
|
41
|
+
s.add_dependency(%q<ruby2ruby>, ["~> 1.2"])
|
42
|
+
s.add_dependency(%q<sexp_processor>, ["~> 3.0"])
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
|
3
|
+
require 'reek/block_context'
|
4
|
+
require 'reek/method_context'
|
5
|
+
|
6
|
+
include Reek
|
7
|
+
|
8
|
+
describe BlockContext do
|
9
|
+
|
10
|
+
it "should record single parameter" do
|
11
|
+
element = StopContext.new
|
12
|
+
element = BlockContext.new(element, s(s(:lasgn, :x), nil))
|
13
|
+
element.variable_names.should == [Name.new(:x)]
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should record single parameter within a method" do
|
17
|
+
element = StopContext.new
|
18
|
+
element = MethodContext.new(element, s(:defn, :help))
|
19
|
+
element = BlockContext.new(element, s(s(:lasgn, :x), nil))
|
20
|
+
element.variable_names.should == [Name.new(:x)]
|
21
|
+
end
|
22
|
+
|
23
|
+
it "records multiple parameters" do
|
24
|
+
element = StopContext.new
|
25
|
+
element = BlockContext.new(element, s(s(:masgn, s(:array, s(:lasgn, :x), s(:lasgn, :y))), nil))
|
26
|
+
element.variable_names.should == [Name.new(:x), Name.new(:y)]
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should not pass parameters upward" do
|
30
|
+
mc = MethodContext.new(StopContext.new, s(:defn, :help))
|
31
|
+
element = BlockContext.new(mc, s(s(:lasgn, :x)))
|
32
|
+
mc.variable_names.should be_empty
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'records local variables' do
|
36
|
+
bctx = BlockContext.new(StopContext.new, nil)
|
37
|
+
bctx.record_local_variable(:q2)
|
38
|
+
bctx.variable_names.should include(Name.new(:q2))
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
|
3
|
+
require 'reek/class_context'
|
4
|
+
require 'reek/stop_context'
|
5
|
+
require 'reek/smells/feature_envy'
|
6
|
+
|
7
|
+
include Reek
|
8
|
+
include Reek::Smells
|
9
|
+
|
10
|
+
describe ClassContext do
|
11
|
+
it 'should report Long Parameter List' do
|
12
|
+
ruby = 'class Inner; def simple(arga, argb, argc, argd) f(3);true end end'
|
13
|
+
ruby.should reek_of(:LongParameterList, /Inner/, /simple/, /4 parameters/)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'should report two different methods' do
|
17
|
+
src = <<EOEX
|
18
|
+
class Fred
|
19
|
+
def simple(arga, argb, argc, argd) f(3);true end
|
20
|
+
def simply(arga, argb, argc, argd) f(3);false end
|
21
|
+
end
|
22
|
+
EOEX
|
23
|
+
src.should reek_of(:LongParameterList, /Fred/, /simple/)
|
24
|
+
src.should reek_of(:LongParameterList, /Fred/, /simply/)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should report many different methods' do
|
28
|
+
src = <<EOEX
|
29
|
+
class Fred
|
30
|
+
def textile_bq(tag, atts, cite, content) f(3);end
|
31
|
+
def textile_p(tag, atts, cite, content) f(3);end
|
32
|
+
def textile_fn_(tag, num, atts, cite, content) f(3);end
|
33
|
+
def textile_popup_help(name, windowW, windowH) f(3);end
|
34
|
+
end
|
35
|
+
EOEX
|
36
|
+
src.should reek_of(:LongParameterList, /Fred/, /textile_bq/)
|
37
|
+
src.should reek_of(:LongParameterList, /Fred/, /textile_fn_/)
|
38
|
+
src.should reek_of(:LongParameterList, /Fred/, /textile_p/)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe ClassContext, 'overridden methods' do
|
43
|
+
class Above
|
44
|
+
def above() end
|
45
|
+
def both() end
|
46
|
+
end
|
47
|
+
|
48
|
+
class Below < Above
|
49
|
+
def both() end
|
50
|
+
def below() end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'of loaded class' do
|
54
|
+
before :each do
|
55
|
+
@ctx = ClassContext.create(StopContext.new, [0, :Below])
|
56
|
+
end
|
57
|
+
|
58
|
+
it 'should recognise non-overridden method' do
|
59
|
+
@ctx.is_overriding_method?('below').should == false
|
60
|
+
@ctx.is_overriding_method?('above').should == false
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should recognise overridden method' do
|
64
|
+
@ctx.is_overriding_method?('both').should == true
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'should recognise methods in current codebase' do
|
68
|
+
ctx = ClassContext.create(StopContext.new, [0, :FeatureEnvy])
|
69
|
+
ctx.is_overriding_method?('examine_context').should == true
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe 'of non-loaded class' do
|
74
|
+
before :each do
|
75
|
+
@ctx = ClassContext.create(StopContext.new, [0, :Missing])
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should recognise non-overridden method' do
|
79
|
+
@ctx.is_overriding_method?('below').should == false
|
80
|
+
@ctx.is_overriding_method?('above').should == false
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'should recognise overridden method' do
|
84
|
+
@ctx.is_overriding_method?('both').should == false
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
describe 'Integration defect:' do
|
90
|
+
it 'should not report UtilityFunction for FeatureEnvy#examine_context' do
|
91
|
+
kelement = ClassContext.create(StopContext.new, [0, :FeatureEnvy, s(:const, :SmellDetector)])
|
92
|
+
meth = Name.new(:examine_context)
|
93
|
+
kelement.is_overriding_method?(meth).should == true
|
94
|
+
melement = MethodContext.new(kelement, [0, :examine_context])
|
95
|
+
melement.is_overriding_method?(meth).should == true
|
96
|
+
melement.depends_on_instance?.should == true
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe CodeContext, 'find class' do
|
101
|
+
module Mod1
|
102
|
+
class Klass1
|
103
|
+
module Mod2
|
104
|
+
class Klass2
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
before :each do
|
111
|
+
@stop = StopContext.new
|
112
|
+
@mod1 = ModuleContext.create(@stop, [0, :Mod1])
|
113
|
+
@klass1 = ClassContext.create(@mod1, [0, :Klass1])
|
114
|
+
@mod2 = ModuleContext.create(@klass1, [0, :Mod2])
|
115
|
+
@klass2 = ClassContext.create(@mod2, [0, :Klass2])
|
116
|
+
end
|
117
|
+
|
118
|
+
describe StopContext do
|
119
|
+
it 'should not find unqualified class' do
|
120
|
+
@stop.find_module('Klass2').should == nil
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'should find unqualified module' do
|
124
|
+
@stop.find_module('Mod1').name.should == 'Mod1'
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
describe ModuleContext do
|
129
|
+
it 'should find local name' do
|
130
|
+
@mod1.find_module('Klass1').name.should == 'Mod1::Klass1'
|
131
|
+
@mod2.find_module('Klass2').name.should == 'Mod1::Klass1::Mod2::Klass2'
|
132
|
+
end
|
133
|
+
|
134
|
+
it 'should not find deeper class' do
|
135
|
+
@mod1.find_module('Klass2').should == nil
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'should find own Module' do
|
139
|
+
@mod1.myself.name.should == 'Mod1'
|
140
|
+
@mod2.myself.name.should == 'Mod1::Klass1::Mod2'
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
describe ClassContext do
|
145
|
+
it 'should find local module' do
|
146
|
+
@klass1.find_module('Mod2').name.should == 'Mod1::Klass1::Mod2'
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'should not find deeper module' do
|
150
|
+
@klass1.find_module('Klass2').should == nil
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'should find own Class' do
|
154
|
+
@klass1.myself.name.should == 'Mod1::Klass1'
|
155
|
+
@klass2.myself.name.should == 'Mod1::Klass1::Mod2::Klass2'
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
describe ClassContext do
|
161
|
+
it 'should not report empty class in another module' do
|
162
|
+
'class Treetop::Runtime::SyntaxNode; end'.should_not reek
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'should deal with :: scoped names' do
|
166
|
+
element = ClassContext.create(StopContext.new, [:colon2, [:colon2, [:const, :Treetop], :Runtime], :SyntaxNode])
|
167
|
+
element.num_methods.should == 0
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper.rb'
|
2
|
+
|
3
|
+
require 'reek/block_context'
|
4
|
+
require 'reek/if_context'
|
5
|
+
require 'reek/class_context'
|
6
|
+
require 'reek/module_context'
|
7
|
+
require 'reek/method_context'
|
8
|
+
require 'reek/stop_context'
|
9
|
+
|
10
|
+
include Reek
|
11
|
+
|
12
|
+
describe CodeContext, 'to_s' do
|
13
|
+
|
14
|
+
it "should report full context" do
|
15
|
+
element = StopContext.new
|
16
|
+
element = ModuleContext.new(element, [0, :mod])
|
17
|
+
element = ClassContext.new(element, [0, :klass])
|
18
|
+
element = MethodContext.new(element, [0, :bad])
|
19
|
+
element = BlockContext.new(element, nil)
|
20
|
+
element.to_s.should match(/bad/)
|
21
|
+
element.to_s.should match(/klass/)
|
22
|
+
element.to_s.should match(/mod/)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should report method name via if context" do
|
26
|
+
element1 = StopContext.new
|
27
|
+
element2 = MethodContext.new(element1, [0, :bad])
|
28
|
+
element3 = IfContext.new(element2, [0,1])
|
29
|
+
BlockContext.new(element3, nil).to_s.should match(/bad/)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should report method name via nested blocks" do
|
33
|
+
element1 = StopContext.new
|
34
|
+
element2 = MethodContext.new(element1, [0, :bad])
|
35
|
+
element3 = BlockContext.new(element2, nil)
|
36
|
+
BlockContext.new(element3, nil).to_s.should match(/bad/)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe CodeContext, 'instance variables' do
|
41
|
+
it 'should pass instance variables down to the first class' do
|
42
|
+
element = StopContext.new
|
43
|
+
element = ModuleContext.new(element, [0, :mod])
|
44
|
+
class_element = ClassContext.new(element, [0, :klass])
|
45
|
+
element = MethodContext.new(class_element, [0, :bad])
|
46
|
+
element = BlockContext.new(element, nil)
|
47
|
+
element.record_instance_variable(:fred)
|
48
|
+
class_element.variable_names.size.should == 1
|
49
|
+
class_element.variable_names.should include(Name.new(:fred))
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe CodeContext, 'generics' do
|
54
|
+
it 'should pass unknown method calls down the stack' do
|
55
|
+
stop = StopContext.new
|
56
|
+
def stop.bananas(arg1, arg2) arg1 + arg2 + 43 end
|
57
|
+
element = ModuleContext.new(stop, [0, :mod])
|
58
|
+
class_element = ClassContext.new(element, [0, :klass])
|
59
|
+
element = MethodContext.new(class_element, [0, :bad])
|
60
|
+
element = BlockContext.new(element, nil)
|
61
|
+
element.bananas(17, -5).should == 55
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe CodeContext do
|
66
|
+
it 'should recognise itself in a collection of names' do
|
67
|
+
element = StopContext.new
|
68
|
+
element = ModuleContext.new(element, [0, :mod])
|
69
|
+
element.matches?(['banana', 'mod']).should == true
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should recognise itself in a collection of REs' do
|
73
|
+
element = StopContext.new
|
74
|
+
element = ModuleContext.new(element, [0, :mod])
|
75
|
+
element.matches?([/banana/, /mod/]).should == true
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should recognise its fq name in a collection of names' do
|
79
|
+
element = StopContext.new
|
80
|
+
element = ModuleContext.new(element, [0, :mod])
|
81
|
+
element = ClassContext.create(element, [0, :klass])
|
82
|
+
element.matches?(['banana', 'mod']).should == true
|
83
|
+
element.matches?(['banana', 'mod::klass']).should == true
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'should recognise its fq name in a collection of names' do
|
87
|
+
element = StopContext.new
|
88
|
+
element = ModuleContext.new(element, [0, :mod])
|
89
|
+
element = ClassContext.create(element, [0, :klass])
|
90
|
+
element.matches?([/banana/, /mod/]).should == true
|
91
|
+
element.matches?([/banana/, /mod::klass/]).should == true
|
92
|
+
end
|
93
|
+
end
|