reek 1.2.6 → 1.2.7
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +10 -0
- data/History.txt +20 -0
- data/README.md +90 -0
- data/bin/reek +2 -2
- data/config/defaults.reek +34 -4
- data/features/masking_smells.feature +35 -15
- data/features/options.feature +2 -0
- data/features/rake_task.feature +11 -18
- data/features/reports.feature +13 -15
- data/features/samples.feature +90 -105
- data/features/stdin.feature +3 -6
- data/features/step_definitions/reek_steps.rb +8 -4
- data/features/support/env.rb +2 -3
- data/features/yaml.feature +124 -0
- data/lib/reek.rb +8 -4
- data/lib/reek/cli/application.rb +46 -0
- data/lib/reek/cli/command_line.rb +106 -0
- data/lib/reek/cli/help_command.rb +18 -0
- data/lib/reek/cli/reek_command.rb +37 -0
- data/lib/reek/cli/report.rb +91 -0
- data/lib/reek/cli/version_command.rb +19 -0
- data/lib/reek/cli/yaml_command.rb +32 -0
- data/lib/reek/core/block_context.rb +18 -0
- data/lib/reek/core/class_context.rb +23 -0
- data/lib/reek/core/code_context.rb +72 -0
- data/lib/reek/core/code_parser.rb +192 -0
- data/lib/reek/core/detector_stack.rb +29 -0
- data/lib/reek/core/masking_collection.rb +46 -0
- data/lib/reek/core/method_context.rb +132 -0
- data/lib/reek/core/module_context.rb +64 -0
- data/lib/reek/{object_refs.rb → core/object_refs.rb} +8 -6
- data/lib/reek/{singleton_method_context.rb → core/singleton_method_context.rb} +10 -5
- data/lib/reek/core/smell_configuration.rb +66 -0
- data/lib/reek/core/sniffer.rb +110 -0
- data/lib/reek/core/stop_context.rb +26 -0
- data/lib/reek/examiner.rb +88 -0
- data/lib/reek/rake/task.rb +124 -0
- data/lib/reek/smell_warning.rb +69 -13
- data/lib/reek/smells.rb +29 -0
- data/lib/reek/smells/attribute.rb +13 -14
- data/lib/reek/smells/boolean_parameter.rb +33 -0
- data/lib/reek/smells/class_variable.rb +8 -6
- data/lib/reek/smells/control_couple.rb +33 -17
- data/lib/reek/smells/data_clump.rb +10 -6
- data/lib/reek/smells/duplication.rb +24 -14
- data/lib/reek/smells/feature_envy.rb +11 -6
- data/lib/reek/smells/irresponsible_module.rb +28 -0
- data/lib/reek/smells/large_class.rb +9 -7
- data/lib/reek/smells/long_method.rb +6 -5
- data/lib/reek/smells/long_parameter_list.rb +11 -9
- data/lib/reek/smells/long_yield_list.rb +37 -7
- data/lib/reek/smells/nested_iterators.rb +34 -9
- data/lib/reek/smells/simulated_polymorphism.rb +15 -11
- data/lib/reek/smells/smell_detector.rb +24 -12
- data/lib/reek/smells/uncommunicative_method_name.rb +76 -0
- data/lib/reek/smells/uncommunicative_module_name.rb +76 -0
- data/lib/reek/smells/{uncommunicative_name.rb → uncommunicative_parameter_name.rb} +14 -26
- data/lib/reek/smells/uncommunicative_variable_name.rb +90 -0
- data/lib/reek/smells/utility_function.rb +33 -9
- data/lib/reek/source.rb +18 -0
- data/lib/reek/source/code_comment.rb +19 -0
- data/lib/reek/source/config_file.rb +72 -0
- data/lib/reek/source/core_extras.rb +46 -0
- data/lib/reek/source/sexp_formatter.rb +16 -0
- data/lib/reek/source/source_code.rb +44 -0
- data/lib/reek/source/source_file.rb +32 -0
- data/lib/reek/source/source_locator.rb +36 -0
- data/lib/reek/source/tree_dresser.rb +128 -0
- data/lib/reek/spec.rb +51 -0
- data/lib/reek/spec/should_reek.rb +34 -0
- data/lib/reek/spec/should_reek_of.rb +37 -0
- data/lib/reek/spec/should_reek_only_of.rb +36 -0
- data/reek.gemspec +5 -5
- data/spec/reek/{help_command_spec.rb → cli/help_command_spec.rb} +3 -4
- data/spec/reek/{reek_command_spec.rb → cli/reek_command_spec.rb} +8 -7
- data/spec/reek/cli/report_spec.rb +26 -0
- data/spec/reek/{version_command_spec.rb → cli/version_command_spec.rb} +3 -3
- data/spec/reek/cli/yaml_command_spec.rb +47 -0
- data/spec/reek/core/block_context_spec.rb +26 -0
- data/spec/reek/core/class_context_spec.rb +53 -0
- data/spec/reek/{code_context_spec.rb → core/code_context_spec.rb} +15 -37
- data/spec/reek/{code_parser_spec.rb → core/code_parser_spec.rb} +5 -5
- data/spec/reek/{config_spec.rb → core/config_spec.rb} +2 -6
- data/spec/reek/{masking_collection_spec.rb → core/masking_collection_spec.rb} +3 -4
- data/spec/reek/{method_context_spec.rb → core/method_context_spec.rb} +6 -7
- data/spec/reek/core/module_context_spec.rb +42 -0
- data/spec/reek/{object_refs_spec.rb → core/object_refs_spec.rb} +5 -6
- data/spec/reek/core/singleton_method_context_spec.rb +15 -0
- data/spec/reek/core/smell_configuration_spec.rb +11 -0
- data/spec/reek/core/stop_context_spec.rb +17 -0
- data/spec/reek/examiner_spec.rb +42 -0
- data/spec/reek/smell_warning_spec.rb +82 -33
- data/spec/reek/smells/attribute_spec.rb +33 -7
- data/spec/reek/smells/boolean_parameter_spec.rb +76 -0
- data/spec/reek/smells/class_variable_spec.rb +15 -6
- data/spec/reek/smells/control_couple_spec.rb +40 -29
- data/spec/reek/smells/data_clump_spec.rb +28 -7
- data/spec/reek/smells/duplication_spec.rb +47 -41
- data/spec/reek/smells/feature_envy_spec.rb +76 -18
- data/spec/reek/smells/irresponsible_module_spec.rb +37 -0
- data/spec/reek/smells/large_class_spec.rb +91 -56
- data/spec/reek/smells/long_method_spec.rb +32 -7
- data/spec/reek/smells/long_parameter_list_spec.rb +42 -13
- data/spec/reek/smells/long_yield_list_spec.rb +65 -0
- data/spec/reek/smells/nested_iterators_spec.rb +94 -3
- data/spec/reek/smells/simulated_polymorphism_spec.rb +48 -20
- data/spec/reek/smells/smell_detector_shared.rb +28 -0
- data/spec/reek/smells/uncommunicative_method_name_spec.rb +57 -0
- data/spec/reek/smells/uncommunicative_module_name_spec.rb +67 -0
- data/spec/reek/smells/uncommunicative_parameter_name_spec.rb +61 -0
- data/spec/reek/smells/uncommunicative_variable_name_spec.rb +124 -0
- data/spec/reek/smells/utility_function_spec.rb +45 -3
- data/spec/reek/source/code_comment_spec.rb +24 -0
- data/spec/reek/source/object_source_spec.rb +20 -0
- data/spec/reek/{adapters/source_spec.rb → source/source_code_spec.rb} +7 -8
- data/spec/reek/source/tree_dresser_spec.rb +165 -0
- data/spec/reek/spec/should_reek_of_spec.rb +76 -0
- data/spec/reek/spec/should_reek_only_of_spec.rb +89 -0
- data/spec/reek/{adapters → spec}/should_reek_spec.rb +8 -32
- data/spec/samples/all_but_one_masked/clean_one.rb +1 -0
- data/spec/samples/all_but_one_masked/dirty.rb +1 -0
- data/spec/samples/all_but_one_masked/masked.reek +5 -1
- data/spec/samples/clean_due_to_masking/clean_one.rb +1 -0
- data/spec/samples/clean_due_to_masking/clean_three.rb +1 -0
- data/spec/samples/clean_due_to_masking/clean_two.rb +1 -0
- data/spec/samples/clean_due_to_masking/dirty_one.rb +1 -1
- data/spec/samples/clean_due_to_masking/dirty_two.rb +1 -1
- data/spec/samples/clean_due_to_masking/masked.reek +5 -1
- data/spec/samples/corrupt_config_file/dirty.rb +1 -1
- data/spec/samples/empty_config_file/dirty.rb +2 -1
- data/spec/samples/exceptions.reek +1 -1
- data/spec/samples/masked/dirty.rb +2 -1
- data/spec/samples/masked/masked.reek +3 -1
- data/spec/samples/mixed_results/clean_one.rb +1 -0
- data/spec/samples/mixed_results/clean_three.rb +1 -0
- data/spec/samples/mixed_results/clean_two.rb +1 -0
- data/spec/samples/mixed_results/dirty_one.rb +1 -0
- data/spec/samples/mixed_results/dirty_two.rb +1 -0
- data/spec/samples/not_quite_masked/dirty.rb +2 -1
- data/spec/samples/not_quite_masked/masked.reek +1 -1
- data/spec/samples/overrides/masked/dirty.rb +2 -1
- data/spec/samples/overrides/masked/lower.reek +3 -1
- data/spec/samples/three_clean_files/clean_one.rb +1 -0
- data/spec/samples/three_clean_files/clean_three.rb +1 -0
- data/spec/samples/three_clean_files/clean_two.rb +1 -0
- data/spec/samples/two_smelly_files/dirty_one.rb +2 -1
- data/spec/samples/two_smelly_files/dirty_two.rb +2 -1
- data/spec/spec_helper.rb +1 -2
- data/tasks/reek.rake +2 -2
- data/tasks/test.rake +12 -3
- metadata +81 -62
- data/README.rdoc +0 -84
- data/lib/reek/adapters/application.rb +0 -46
- data/lib/reek/adapters/command_line.rb +0 -77
- data/lib/reek/adapters/config_file.rb +0 -31
- data/lib/reek/adapters/core_extras.rb +0 -64
- data/lib/reek/adapters/rake_task.rb +0 -121
- data/lib/reek/adapters/report.rb +0 -86
- data/lib/reek/adapters/source.rb +0 -72
- data/lib/reek/adapters/spec.rb +0 -133
- data/lib/reek/block_context.rb +0 -62
- data/lib/reek/class_context.rb +0 -41
- data/lib/reek/code_context.rb +0 -68
- data/lib/reek/code_parser.rb +0 -203
- data/lib/reek/configuration.rb +0 -57
- data/lib/reek/detector_stack.rb +0 -37
- data/lib/reek/help_command.rb +0 -14
- data/lib/reek/if_context.rb +0 -18
- data/lib/reek/masking_collection.rb +0 -33
- data/lib/reek/method_context.rb +0 -138
- data/lib/reek/module_context.rb +0 -49
- data/lib/reek/name.rb +0 -57
- data/lib/reek/reek_command.rb +0 -28
- data/lib/reek/sexp_formatter.rb +0 -10
- data/lib/reek/sniffer.rb +0 -177
- data/lib/reek/stop_context.rb +0 -35
- data/lib/reek/tree_dresser.rb +0 -82
- data/lib/reek/version_command.rb +0 -14
- data/lib/reek/yield_call_context.rb +0 -12
- data/spec/reek/adapters/report_spec.rb +0 -31
- data/spec/reek/adapters/should_reek_of_spec.rb +0 -138
- data/spec/reek/adapters/should_reek_only_of_spec.rb +0 -87
- data/spec/reek/block_context_spec.rb +0 -65
- data/spec/reek/class_context_spec.rb +0 -161
- data/spec/reek/configuration_spec.rb +0 -12
- data/spec/reek/if_context_spec.rb +0 -17
- data/spec/reek/module_context_spec.rb +0 -46
- data/spec/reek/name_spec.rb +0 -37
- data/spec/reek/object_source_spec.rb +0 -23
- data/spec/reek/singleton_method_context_spec.rb +0 -16
- data/spec/reek/smells/smell_detector_spec.rb +0 -36
- data/spec/reek/smells/uncommunicative_name_spec.rb +0 -146
- data/spec/reek/sniffer_spec.rb +0 -11
- data/spec/reek/stop_context_spec.rb +0 -33
- data/spec/reek/tree_dresser_spec.rb +0 -20
@@ -1,10 +1,14 @@
|
|
1
1
|
module Reek
|
2
|
+
module Core
|
2
3
|
|
4
|
+
#
|
5
|
+
# Manages and counts the references out of a method to other objects.
|
6
|
+
#
|
3
7
|
class ObjectRefs # :nodoc:
|
4
8
|
def initialize
|
5
9
|
@refs = Hash.new(0)
|
6
10
|
end
|
7
|
-
|
11
|
+
|
8
12
|
def record_reference_to_self
|
9
13
|
record_ref(SELF_REF)
|
10
14
|
end
|
@@ -29,12 +33,9 @@ module Reek
|
|
29
33
|
@refs.values.max or 0
|
30
34
|
end
|
31
35
|
|
32
|
-
# TODO
|
33
|
-
# Should be moved to Hash
|
34
|
-
#
|
35
36
|
def max_keys
|
36
37
|
max = max_refs
|
37
|
-
@refs.reject {|key,val| val != max}
|
38
|
+
@refs.reject {|key,val| val != max}
|
38
39
|
end
|
39
40
|
|
40
41
|
def self_is_max?
|
@@ -42,8 +43,9 @@ module Reek
|
|
42
43
|
end
|
43
44
|
|
44
45
|
private
|
45
|
-
|
46
|
+
|
46
47
|
SELF_REF = Sexp.from_array([:lit, :self])
|
47
48
|
|
48
49
|
end
|
50
|
+
end
|
49
51
|
end
|
@@ -1,14 +1,18 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
3
|
-
require 'reek/sexp_formatter'
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), 'method_context')
|
2
|
+
require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'source')
|
4
3
|
|
5
4
|
module Reek
|
5
|
+
module Core
|
6
|
+
|
7
|
+
#
|
8
|
+
# A context wrapper for any singleton method definition found in a syntax tree.
|
9
|
+
#
|
6
10
|
class SingletonMethodContext < MethodContext
|
7
11
|
|
8
12
|
def initialize(outer, exp)
|
9
13
|
super(outer, exp)
|
10
|
-
@name =
|
11
|
-
@receiver = SexpFormatter.format(exp[1])
|
14
|
+
@name = exp[2].to_s
|
15
|
+
@receiver = Source::SexpFormatter.format(exp[1])
|
12
16
|
@scope_connector = ""
|
13
17
|
record_depends_on_self
|
14
18
|
end
|
@@ -23,4 +27,5 @@ module Reek
|
|
23
27
|
"#{prefix}#{@receiver}.#{@name}"
|
24
28
|
end
|
25
29
|
end
|
30
|
+
end
|
26
31
|
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Reek
|
2
|
+
module Core
|
3
|
+
|
4
|
+
#
|
5
|
+
# Represents a single set of configuration options for a smell detector
|
6
|
+
#
|
7
|
+
class SmellConfiguration
|
8
|
+
|
9
|
+
# The name of the config field that specifies whether a smell is
|
10
|
+
# enabled. Set to +true+ or +false+.
|
11
|
+
ENABLED_KEY = 'enabled'
|
12
|
+
|
13
|
+
# The name of the config field that sets scope-specific overrides
|
14
|
+
# for other values in the current smell detector's configuration.
|
15
|
+
OVERRIDES_KEY = 'overrides'
|
16
|
+
|
17
|
+
def initialize(hash)
|
18
|
+
@options = hash
|
19
|
+
end
|
20
|
+
|
21
|
+
def adopt!(options)
|
22
|
+
@options.adopt!(options)
|
23
|
+
end
|
24
|
+
|
25
|
+
def deep_copy
|
26
|
+
@options.deep_copy # SMELL: Open Secret -- returns a Hash
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# Is this smell detector active?
|
31
|
+
#--
|
32
|
+
# SMELL: Getter
|
33
|
+
def enabled?
|
34
|
+
@options[ENABLED_KEY]
|
35
|
+
end
|
36
|
+
|
37
|
+
def overrides_for(context)
|
38
|
+
Overrides.new(@options.fetch(OVERRIDES_KEY, {})).for_context(context)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Retrieves the value, if any, for the given +key+.
|
42
|
+
#
|
43
|
+
# Returns +fall_back+ if this config has no value for the key.
|
44
|
+
#
|
45
|
+
def value(key, context, fall_back)
|
46
|
+
overrides_for(context).each { |conf| return conf[key] if conf.has_key?(key) }
|
47
|
+
return @options.fetch(key, fall_back)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# A set of context-specific overrides for smell detectors.
|
53
|
+
#
|
54
|
+
class Overrides
|
55
|
+
def initialize(hash)
|
56
|
+
@hash = hash
|
57
|
+
end
|
58
|
+
|
59
|
+
# Find any overrides that match the supplied context
|
60
|
+
def for_context(context)
|
61
|
+
contexts = @hash.keys.select {|ckey| context.matches?([ckey])}
|
62
|
+
contexts.map { |exc| @hash[exc] }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), 'detector_stack')
|
2
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), 'code_parser')
|
3
|
+
require File.join(File.dirname(File.dirname(File.expand_path(__FILE__))), 'smells')
|
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
|
34
|
+
|
35
|
+
module Reek
|
36
|
+
module Core
|
37
|
+
|
38
|
+
#
|
39
|
+
# Configures all available smell detectors and applies them to a source.
|
40
|
+
#
|
41
|
+
class Sniffer
|
42
|
+
|
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)
|
69
|
+
@already_checked_for_smells = false
|
70
|
+
@typed_detectors = nil
|
71
|
+
@detectors = Hash.new
|
72
|
+
Sniffer.smell_classes.each do |klass|
|
73
|
+
@detectors[klass] = DetectorStack.new(klass.new(src.desc))
|
74
|
+
end
|
75
|
+
@source = src
|
76
|
+
src.configure(self)
|
77
|
+
end
|
78
|
+
|
79
|
+
def check_for_smells
|
80
|
+
return if @already_checked_for_smells
|
81
|
+
CodeParser.new(self).process(@source.syntax_tree)
|
82
|
+
@already_checked_for_smells = true
|
83
|
+
end
|
84
|
+
|
85
|
+
def configure(klass, config)
|
86
|
+
@detectors[klass].push(config)
|
87
|
+
end
|
88
|
+
|
89
|
+
def report_on(report)
|
90
|
+
check_for_smells
|
91
|
+
@detectors.each_value { |stack| stack.report_on(report) }
|
92
|
+
end
|
93
|
+
|
94
|
+
def examine(scope, type)
|
95
|
+
listeners = smell_listeners[type]
|
96
|
+
listeners.each {|smell| smell.examine(scope) } if listeners
|
97
|
+
end
|
98
|
+
|
99
|
+
private
|
100
|
+
|
101
|
+
def smell_listeners()
|
102
|
+
unless @typed_detectors
|
103
|
+
@typed_detectors = Hash.new {|hash,key| hash[key] = [] }
|
104
|
+
@detectors.each_value { |stack| stack.listen_to(@typed_detectors) }
|
105
|
+
end
|
106
|
+
@typed_detectors
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Reek
|
2
|
+
module Core
|
3
|
+
|
4
|
+
#
|
5
|
+
# A context wrapper representing the root of an abstract syntax tree.
|
6
|
+
#
|
7
|
+
class StopContext
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@name = ''
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(method, *args)
|
14
|
+
nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def count_statements(num)
|
18
|
+
0
|
19
|
+
end
|
20
|
+
|
21
|
+
def full_name
|
22
|
+
''
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), 'core', 'masking_collection')
|
2
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), 'core', 'sniffer')
|
3
|
+
require File.join(File.dirname(File.expand_path(__FILE__)), 'source')
|
4
|
+
|
5
|
+
module Reek
|
6
|
+
|
7
|
+
#
|
8
|
+
# Finds the code smells in Ruby source code.
|
9
|
+
#
|
10
|
+
class Examiner
|
11
|
+
|
12
|
+
#
|
13
|
+
# A simple description of the source being analysed for smells.
|
14
|
+
# If the source is a single File, this will be the file's path.
|
15
|
+
#
|
16
|
+
attr_accessor :description
|
17
|
+
|
18
|
+
#
|
19
|
+
# Creates an Examiner which scans the given +source+ for code smells.
|
20
|
+
#
|
21
|
+
# The smells reported against any source file can be "masked" by
|
22
|
+
# creating *.reek files. See TBS for details.
|
23
|
+
#
|
24
|
+
# @param [Source::SourceCode, Array<String>, #to_reek_source]
|
25
|
+
# If +source+ is a String it is assumed to be Ruby source code;
|
26
|
+
# if it is a File, the file is opened and parsed for Ruby source code;
|
27
|
+
# and if it is an Array, it is assumed to be a list of file paths,
|
28
|
+
# each of which is opened and parsed for source code.
|
29
|
+
#
|
30
|
+
def initialize(source)
|
31
|
+
sniffers = case source
|
32
|
+
when Array
|
33
|
+
@description = 'dir'
|
34
|
+
Source::SourceLocator.new(source).all_sources.map {|src| Core::Sniffer.new(src)}
|
35
|
+
when Source::SourceCode
|
36
|
+
@description = source.desc
|
37
|
+
[Core::Sniffer.new(source)]
|
38
|
+
else
|
39
|
+
src = source.to_reek_source
|
40
|
+
@description = src.desc
|
41
|
+
[Core::Sniffer.new(src)]
|
42
|
+
end
|
43
|
+
@warnings = Core::MaskingCollection.new
|
44
|
+
sniffers.each {|sniffer| sniffer.report_on(@warnings)}
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
# Returns an Array of SmellWarning objects, one for each non-masked smell
|
49
|
+
# in the source.
|
50
|
+
#
|
51
|
+
# @return [Array<SmellWarning>]
|
52
|
+
#
|
53
|
+
def all_active_smells
|
54
|
+
@warnings.all_active_items.to_a
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# Returns an Array of SmellWarning objects, one for each smell
|
59
|
+
# in the source; includes active smells and masked smells.
|
60
|
+
#
|
61
|
+
# @return [Array<SmellWarning>]
|
62
|
+
#
|
63
|
+
def all_smells
|
64
|
+
@warnings.all_items
|
65
|
+
end
|
66
|
+
|
67
|
+
#
|
68
|
+
# Returns the number of non-masked smells in the source.
|
69
|
+
#
|
70
|
+
def num_active_smells
|
71
|
+
@warnings.num_visible_items
|
72
|
+
end
|
73
|
+
|
74
|
+
#
|
75
|
+
# Returns the number of masked smells in the source.
|
76
|
+
#
|
77
|
+
def num_masked_smells
|
78
|
+
@warnings.num_masked_items
|
79
|
+
end
|
80
|
+
|
81
|
+
#
|
82
|
+
# True if and only if there are non-masked code smells in the given source.
|
83
|
+
#
|
84
|
+
def smelly?
|
85
|
+
not all_active_smells.empty?
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rake'
|
4
|
+
require 'rake/tasklib'
|
5
|
+
|
6
|
+
module Reek
|
7
|
+
|
8
|
+
#
|
9
|
+
# Defines a task library for running reek.
|
10
|
+
#
|
11
|
+
module Rake
|
12
|
+
|
13
|
+
# A Rake task that runs reek on a set of source files.
|
14
|
+
#
|
15
|
+
# Example:
|
16
|
+
#
|
17
|
+
# Reek::Rake::Task.new do |t|
|
18
|
+
# t.fail_on_error = false
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# This will create a task that can be run with:
|
22
|
+
#
|
23
|
+
# rake reek
|
24
|
+
#
|
25
|
+
# Examples:
|
26
|
+
#
|
27
|
+
# rake reek # checks lib/**/*.rb
|
28
|
+
# rake reek REEK_SRC=just_one_file.rb # checks a single source file
|
29
|
+
# rake reek REEK_OPTS=-s # sorts the report by smell
|
30
|
+
#
|
31
|
+
class Task < ::Rake::TaskLib
|
32
|
+
|
33
|
+
# Name of reek task.
|
34
|
+
# Defaults to :reek.
|
35
|
+
attr_accessor :name
|
36
|
+
|
37
|
+
# Array of directories to be added to $LOAD_PATH before running reek.
|
38
|
+
# Defaults to ['<the absolute path to reek's lib directory>']
|
39
|
+
attr_accessor :libs
|
40
|
+
|
41
|
+
# Glob pattern to match source files.
|
42
|
+
# Setting the REEK_SRC environment variable overrides this.
|
43
|
+
# Defaults to 'lib/**/*.rb'.
|
44
|
+
attr_accessor :source_files
|
45
|
+
|
46
|
+
# String containing commandline options to be passed to Reek.
|
47
|
+
# Setting the REEK_OPTS environment variable overrides this value.
|
48
|
+
# Defaults to ''.
|
49
|
+
attr_accessor :reek_opts
|
50
|
+
|
51
|
+
# Array of commandline options to pass to ruby. Defaults to [].
|
52
|
+
attr_accessor :ruby_opts
|
53
|
+
|
54
|
+
# Whether or not to fail Rake when an error occurs (typically when smells are found).
|
55
|
+
# Defaults to true.
|
56
|
+
attr_accessor :fail_on_error
|
57
|
+
|
58
|
+
# Use verbose output. If this is set to true, the task will print
|
59
|
+
# the reek command to stdout. Defaults to false.
|
60
|
+
attr_accessor :verbose
|
61
|
+
|
62
|
+
# Defines a new task, using the name +name+.
|
63
|
+
def initialize(name = :reek)
|
64
|
+
@name = name
|
65
|
+
@libs = [File.expand_path(File.dirname(__FILE__) + '/../../../lib')]
|
66
|
+
@source_files = nil
|
67
|
+
@ruby_opts = []
|
68
|
+
@reek_opts = ''
|
69
|
+
@fail_on_error = true
|
70
|
+
@sort = nil
|
71
|
+
|
72
|
+
yield self if block_given?
|
73
|
+
@source_files ||= 'lib/**/*.rb'
|
74
|
+
define
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def define # :nodoc:
|
80
|
+
desc 'Check for code smells' unless ::Rake.application.last_comment
|
81
|
+
task(name) { run_task }
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
def run_task
|
86
|
+
return if source_file_list.empty?
|
87
|
+
cmd = cmd_words.join(' ')
|
88
|
+
puts cmd if @verbose
|
89
|
+
raise('Smells found!') if !system(cmd) and fail_on_error
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.reek_script
|
93
|
+
File.expand_path(File.dirname(__FILE__) + '/../../../bin/reek')
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.ruby_exe
|
97
|
+
File.join(Config::CONFIG['bindir'], Config::CONFIG['ruby_install_name'])
|
98
|
+
end
|
99
|
+
|
100
|
+
def cmd_words
|
101
|
+
[Task.ruby_exe] +
|
102
|
+
ruby_options +
|
103
|
+
[ %Q|"#{Task.reek_script}"| ] +
|
104
|
+
[sort_option] +
|
105
|
+
source_file_list.collect { |fn| %["#{fn}"] }
|
106
|
+
end
|
107
|
+
|
108
|
+
def ruby_options
|
109
|
+
lib_path = @libs.join(File::PATH_SEPARATOR)
|
110
|
+
@ruby_opts.clone << "-I\"#{lib_path}\""
|
111
|
+
end
|
112
|
+
|
113
|
+
def sort_option
|
114
|
+
ENV['REEK_OPTS'] || @reek_opts
|
115
|
+
end
|
116
|
+
|
117
|
+
def source_file_list # :nodoc:
|
118
|
+
files = ENV['REEK_SRC'] || @source_files
|
119
|
+
return [] unless files
|
120
|
+
return FileList[files]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|