skeptic 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +4 -1
- data/VERSION +1 -1
- data/bin/skeptic +22 -12
- data/features/skeptic.feature +25 -0
- data/lib/skeptic/critic.rb +10 -14
- data/lib/skeptic/rule_table.rb +40 -0
- data/lib/skeptic/rules/check_syntax.rb +27 -0
- data/lib/skeptic/rules/line_length.rb +31 -0
- data/lib/skeptic/rules/lines_per_method.rb +3 -1
- data/lib/skeptic/rules/max_nesting_depth.rb +11 -1
- data/lib/skeptic/rules/methods_per_class.rb +3 -1
- data/lib/skeptic/rules/no_semicolons.rb +3 -1
- data/lib/skeptic/rules.rb +14 -0
- data/lib/skeptic.rb +4 -0
- metadata +24 -29
- data/features/step_definitions/skeptic_steps.rb +0 -0
- data/features/support/aruba.rb +0 -1
- data/features/support/env.rb +0 -13
- data/spec/skeptic/critic_spec.rb +0 -66
- data/spec/skeptic/environment_spec.rb +0 -48
- data/spec/skeptic/rules/lines_per_method_spec.rb +0 -67
- data/spec/skeptic/rules/max_nesting_depth_spec.rb +0 -134
- data/spec/skeptic/rules/methods_per_class_spec.rb +0 -90
- data/spec/skeptic/rules/no_semicolons_spec.rb +0 -49
- data/spec/skeptic/scope_spec.rb +0 -32
- data/spec/spec_helper.rb +0 -12
data/Rakefile
CHANGED
@@ -22,6 +22,9 @@ Jeweler::Tasks.new do |gem|
|
|
22
22
|
gem.email = "stefan.kanev@gmail.com"
|
23
23
|
gem.authors = ["Stefan Kanev"]
|
24
24
|
# dependencies defined in Gemfile
|
25
|
+
gem.files.exclude 'spec/**/*.rb'
|
26
|
+
gem.files.exclude 'features/**/*.rb'
|
27
|
+
gem.files.exclude 'tmp'
|
25
28
|
end
|
26
29
|
Jeweler::RubygemsDotOrgTasks.new
|
27
30
|
|
@@ -41,7 +44,7 @@ Cucumber::Rake::Task.new(:features)
|
|
41
44
|
|
42
45
|
task :default => :spec
|
43
46
|
|
44
|
-
require '
|
47
|
+
require 'rdoc/task'
|
45
48
|
Rake::RDocTask.new do |rdoc|
|
46
49
|
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
47
50
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.1
|
data/bin/skeptic
CHANGED
@@ -3,21 +3,31 @@ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
|
3
3
|
require 'skeptic'
|
4
4
|
require 'trollop'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
opt :methods_per_class, 'Maximum number of methods per class', type: :int
|
10
|
-
opt :no_semicolons, 'Complain about semicolons', type: :boolean
|
11
|
-
end
|
12
|
-
code = File.read ARGV[0]
|
6
|
+
parser = Trollop::Parser.new do
|
7
|
+
banner <<BANNER
|
8
|
+
Points out annoying things in your Ruby code. Just run with:
|
13
9
|
|
14
|
-
|
10
|
+
skeptic [options] <path_to_file>
|
15
11
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
12
|
+
where [options] are:
|
13
|
+
BANNER
|
14
|
+
Skeptic::Rules.table.each_rule do |klass, slug, option_type, description|
|
15
|
+
opt slug, description, type: option_type
|
16
|
+
end
|
17
|
+
opt :file, 'Path to file to analyze', type: :string
|
18
|
+
end
|
19
|
+
|
20
|
+
opts = Trollop::with_standard_exception_handling parser do
|
21
|
+
parsed = parser.parse ARGV
|
22
|
+
parsed[:file] ||= ARGV.shift unless parsed[:file_given]
|
23
|
+
raise Trollop::HelpNeeded unless parsed[:file]
|
24
|
+
raise Trollop::CommandlineError, "excessive arguments: #{ARGV.join(' ')}" unless ARGV.empty?
|
25
|
+
raise Trollop::CommandlineError, "file does not exist: #{parsed[:file]}" unless File.exist? parsed[:file]
|
26
|
+
parsed
|
27
|
+
end
|
20
28
|
|
29
|
+
code = File.read opts[:file]
|
30
|
+
critic = Skeptic::Critic.new opts.select { |key, value| Skeptic::Rules.table.slugs.include? key }
|
21
31
|
critic.criticize code
|
22
32
|
|
23
33
|
if critic.criticism.empty?
|
data/features/skeptic.feature
CHANGED
@@ -74,3 +74,28 @@ Feature: Running skeptic
|
|
74
74
|
Number of methods per class (1)
|
75
75
|
* Foo has 2 methods: #bar, #baz
|
76
76
|
"""
|
77
|
+
|
78
|
+
Scenario: Limiting line length
|
79
|
+
Given a file named "input.rb" with:
|
80
|
+
"""
|
81
|
+
short line
|
82
|
+
longer line
|
83
|
+
"""
|
84
|
+
When I run `skeptic --line-length 10 input.rb`
|
85
|
+
Then it should fail with:
|
86
|
+
"""
|
87
|
+
Line length (10)
|
88
|
+
* Line 2 is too long: 11 columns
|
89
|
+
"""
|
90
|
+
|
91
|
+
Scenario: Checking the syntax
|
92
|
+
Given a file named "input.rb" with:
|
93
|
+
"""
|
94
|
+
unexpected end
|
95
|
+
"""
|
96
|
+
When I run `skeptic --check-syntax input.rb`
|
97
|
+
Then it should fail with:
|
98
|
+
"""
|
99
|
+
Syntax check
|
100
|
+
* Invalid syntax:
|
101
|
+
"""
|
data/lib/skeptic/critic.rb
CHANGED
@@ -1,14 +1,15 @@
|
|
1
1
|
module Skeptic
|
2
2
|
class Critic
|
3
|
-
attr_accessor
|
4
|
-
attr_accessor :max_nesting_depth
|
5
|
-
attr_accessor :methods_per_class
|
6
|
-
attr_accessor :no_semicolons
|
3
|
+
attr_accessor *Rules.table.slugs
|
7
4
|
|
8
5
|
attr_reader :criticism
|
9
6
|
|
10
|
-
def initialize
|
7
|
+
def initialize(options = {})
|
11
8
|
@criticism = []
|
9
|
+
|
10
|
+
options.each do |key, value|
|
11
|
+
send "#{key}=", value
|
12
|
+
end
|
12
13
|
end
|
13
14
|
|
14
15
|
def criticize(code)
|
@@ -16,16 +17,11 @@ module Skeptic
|
|
16
17
|
@tokens = Ripper.lex(code)
|
17
18
|
@sexp = Ripper.sexp(code)
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
Rules::MaxNestingDepth => max_nesting_depth,
|
22
|
-
Rules::MethodsPerClass => methods_per_class,
|
23
|
-
Rules::NoSemicolons => no_semicolons,
|
24
|
-
}
|
20
|
+
Rules.table.each_rule do |rule_class, slug, option|
|
21
|
+
next unless send(slug)
|
25
22
|
|
26
|
-
|
27
|
-
rule
|
28
|
-
rule.apply_to @tokens, @sexp
|
23
|
+
rule = rule_class.new send(slug)
|
24
|
+
rule.apply_to @code, @tokens, @sexp
|
29
25
|
|
30
26
|
rule.violations.each do |violation|
|
31
27
|
@criticism << [violation, rule.name]
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Skeptic
|
2
|
+
class RuleTable
|
3
|
+
def initialize
|
4
|
+
@rules = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def rules
|
8
|
+
@rules.keys
|
9
|
+
end
|
10
|
+
|
11
|
+
def register(klass, option_type)
|
12
|
+
@rules[klass] = option_type
|
13
|
+
end
|
14
|
+
|
15
|
+
def slugs
|
16
|
+
@rules.keys.map { |klass| slug_for klass }
|
17
|
+
end
|
18
|
+
|
19
|
+
def each_rule
|
20
|
+
@rules.each do |klass, option_type|
|
21
|
+
yield klass, slug_for(klass), option_type, description_for(klass)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def description_for(klass)
|
28
|
+
klass.const_get(:DESCRIPTION)
|
29
|
+
end
|
30
|
+
|
31
|
+
def slug_for(klass)
|
32
|
+
klass
|
33
|
+
.name
|
34
|
+
.gsub(/.*::/, '')
|
35
|
+
.gsub(/([a-z])([A-Z])/, '\1_\2')
|
36
|
+
.downcase
|
37
|
+
.to_sym
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'open3'
|
2
|
+
|
3
|
+
module Skeptic
|
4
|
+
module Rules
|
5
|
+
class CheckSyntax
|
6
|
+
DESCRIPTION = 'Check the syntax'
|
7
|
+
|
8
|
+
def initialize(enabled = false)
|
9
|
+
@errors = []
|
10
|
+
end
|
11
|
+
|
12
|
+
def apply_to(code, tokens, sexp)
|
13
|
+
output, error, status = Open3.capture3 'ruby -c', stdin_data: code
|
14
|
+
@errors << "Invalid syntax:\n#{error.gsub(/^/m, ' ')}" unless output.chomp == 'Syntax OK'
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def violations
|
19
|
+
@errors
|
20
|
+
end
|
21
|
+
|
22
|
+
def name
|
23
|
+
'Syntax check'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Skeptic
|
2
|
+
module Rules
|
3
|
+
class LineLength
|
4
|
+
DESCRIPTION = 'Limit the line length'
|
5
|
+
|
6
|
+
attr_reader :line_lengths
|
7
|
+
|
8
|
+
def initialize(limit)
|
9
|
+
@limit = limit
|
10
|
+
@line_lengths = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def apply_to(code, tokens, sexp)
|
14
|
+
code.lines.each_with_index do |line, index|
|
15
|
+
@line_lengths[index + 1] = line.chomp.length
|
16
|
+
end
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def violations
|
21
|
+
@line_lengths.select { |line, length| length > @limit }.map do |line, length|
|
22
|
+
"Line #{line} is too long: #{length} columns"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def name
|
27
|
+
"Line length (#@limit)"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module Skeptic
|
2
2
|
module Rules
|
3
3
|
class LinesPerMethod
|
4
|
+
DESCRIPTION = 'Limit the number of lines in each method'
|
5
|
+
|
4
6
|
include SexpVisitor
|
5
7
|
|
6
8
|
def initialize(limit = nil)
|
@@ -9,7 +11,7 @@ module Skeptic
|
|
9
11
|
@limit = limit
|
10
12
|
end
|
11
13
|
|
12
|
-
def apply_to(tokens, sexp)
|
14
|
+
def apply_to(code, tokens, sexp)
|
13
15
|
visit sexp
|
14
16
|
self
|
15
17
|
end
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module Skeptic
|
2
2
|
module Rules
|
3
3
|
class MaxNestingDepth
|
4
|
+
DESCRIPTION = 'Limit the maximum nesting depth'
|
5
|
+
|
4
6
|
include SexpVisitor
|
5
7
|
|
6
8
|
def initialize(limit = nil)
|
@@ -9,7 +11,7 @@ module Skeptic
|
|
9
11
|
@limit = limit
|
10
12
|
end
|
11
13
|
|
12
|
-
def apply_to(tokens, sexp)
|
14
|
+
def apply_to(code, tokens, sexp)
|
13
15
|
visit sexp
|
14
16
|
self
|
15
17
|
end
|
@@ -62,6 +64,14 @@ module Skeptic
|
|
62
64
|
end
|
63
65
|
end
|
64
66
|
|
67
|
+
on :ifop do |condition, true_branch, false_branch|
|
68
|
+
with scope.push(:ternary_if) do
|
69
|
+
visit condition
|
70
|
+
visit true_branch
|
71
|
+
visit false_branch
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
65
75
|
on :while, :while_mod, :until, :until_mod do |condition, body|
|
66
76
|
key = sexp_type.to_s.gsub(/_mod$/, '').to_sym
|
67
77
|
|
@@ -1,6 +1,8 @@
|
|
1
1
|
module Skeptic
|
2
2
|
module Rules
|
3
3
|
class MethodsPerClass
|
4
|
+
DESCRIPTION = 'Limit the number of methods per class'
|
5
|
+
|
4
6
|
include SexpVisitor
|
5
7
|
|
6
8
|
def initialize(limit)
|
@@ -8,7 +10,7 @@ module Skeptic
|
|
8
10
|
@limit = limit
|
9
11
|
end
|
10
12
|
|
11
|
-
def apply_to(tokens, tree)
|
13
|
+
def apply_to(code, tokens, tree)
|
12
14
|
visit tree
|
13
15
|
self
|
14
16
|
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
module Skeptic
|
2
2
|
module Rules
|
3
3
|
class NoSemicolons
|
4
|
+
DESCRIPTION = 'Do not allow semicolons as statement separators'
|
5
|
+
|
4
6
|
def initialize(enabled = false)
|
5
7
|
@enabled = enabled
|
6
8
|
end
|
7
9
|
|
8
|
-
def apply_to(tokens, sexp)
|
10
|
+
def apply_to(code, tokens, sexp)
|
9
11
|
@locations = tokens.
|
10
12
|
select { |location, type, token| token == ';' and type == :on_semicolon }.
|
11
13
|
map { |location, type, token| location }
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Skeptic
|
2
|
+
module Rules
|
3
|
+
def self.table
|
4
|
+
@rule_table ||= RuleTable.new
|
5
|
+
end
|
6
|
+
|
7
|
+
table.register CheckSyntax, :boolean
|
8
|
+
table.register LineLength, :int
|
9
|
+
table.register LinesPerMethod, :int
|
10
|
+
table.register MaxNestingDepth, :int
|
11
|
+
table.register MethodsPerClass, :int
|
12
|
+
table.register NoSemicolons, :boolean
|
13
|
+
end
|
14
|
+
end
|
data/lib/skeptic.rb
CHANGED
@@ -4,9 +4,13 @@ require 'skeptic/environment'
|
|
4
4
|
require 'skeptic/scope'
|
5
5
|
require 'skeptic/sexp_visitor'
|
6
6
|
|
7
|
+
require 'skeptic/rules/check_syntax'
|
7
8
|
require 'skeptic/rules/max_nesting_depth'
|
8
9
|
require 'skeptic/rules/methods_per_class'
|
9
10
|
require 'skeptic/rules/lines_per_method'
|
10
11
|
require 'skeptic/rules/no_semicolons'
|
12
|
+
require 'skeptic/rules/line_length'
|
11
13
|
|
14
|
+
require 'skeptic/rule_table'
|
15
|
+
require 'skeptic/rules'
|
12
16
|
require 'skeptic/critic'
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: skeptic
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,12 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-10-
|
12
|
+
date: 2011-10-28 00:00:00.000000000 +03:00
|
13
|
+
default_executable: skeptic
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: trollop
|
16
|
-
requirement: &
|
17
|
+
requirement: &2157956920 !ruby/object:Gem::Requirement
|
17
18
|
none: false
|
18
19
|
requirements:
|
19
20
|
- - ! '>='
|
@@ -21,10 +22,10 @@ dependencies:
|
|
21
22
|
version: 1.16.2
|
22
23
|
type: :runtime
|
23
24
|
prerelease: false
|
24
|
-
version_requirements: *
|
25
|
+
version_requirements: *2157956920
|
25
26
|
- !ruby/object:Gem::Dependency
|
26
27
|
name: rspec
|
27
|
-
requirement: &
|
28
|
+
requirement: &2157956340 !ruby/object:Gem::Requirement
|
28
29
|
none: false
|
29
30
|
requirements:
|
30
31
|
- - ! '>='
|
@@ -32,10 +33,10 @@ dependencies:
|
|
32
33
|
version: '0'
|
33
34
|
type: :development
|
34
35
|
prerelease: false
|
35
|
-
version_requirements: *
|
36
|
+
version_requirements: *2157956340
|
36
37
|
- !ruby/object:Gem::Dependency
|
37
38
|
name: cucumber
|
38
|
-
requirement: &
|
39
|
+
requirement: &2157955800 !ruby/object:Gem::Requirement
|
39
40
|
none: false
|
40
41
|
requirements:
|
41
42
|
- - ! '>='
|
@@ -43,10 +44,10 @@ dependencies:
|
|
43
44
|
version: '0'
|
44
45
|
type: :development
|
45
46
|
prerelease: false
|
46
|
-
version_requirements: *
|
47
|
+
version_requirements: *2157955800
|
47
48
|
- !ruby/object:Gem::Dependency
|
48
49
|
name: aruba
|
49
|
-
requirement: &
|
50
|
+
requirement: &2157955200 !ruby/object:Gem::Requirement
|
50
51
|
none: false
|
51
52
|
requirements:
|
52
53
|
- - ! '>='
|
@@ -54,10 +55,10 @@ dependencies:
|
|
54
55
|
version: '0'
|
55
56
|
type: :development
|
56
57
|
prerelease: false
|
57
|
-
version_requirements: *
|
58
|
+
version_requirements: *2157955200
|
58
59
|
- !ruby/object:Gem::Dependency
|
59
60
|
name: bundler
|
60
|
-
requirement: &
|
61
|
+
requirement: &2157954600 !ruby/object:Gem::Requirement
|
61
62
|
none: false
|
62
63
|
requirements:
|
63
64
|
- - ! '>='
|
@@ -65,10 +66,10 @@ dependencies:
|
|
65
66
|
version: '0'
|
66
67
|
type: :development
|
67
68
|
prerelease: false
|
68
|
-
version_requirements: *
|
69
|
+
version_requirements: *2157954600
|
69
70
|
- !ruby/object:Gem::Dependency
|
70
71
|
name: jeweler
|
71
|
-
requirement: &
|
72
|
+
requirement: &2157949640 !ruby/object:Gem::Requirement
|
72
73
|
none: false
|
73
74
|
requirements:
|
74
75
|
- - ! '>='
|
@@ -76,10 +77,10 @@ dependencies:
|
|
76
77
|
version: '0'
|
77
78
|
type: :development
|
78
79
|
prerelease: false
|
79
|
-
version_requirements: *
|
80
|
+
version_requirements: *2157949640
|
80
81
|
- !ruby/object:Gem::Dependency
|
81
82
|
name: rcov
|
82
|
-
requirement: &
|
83
|
+
requirement: &2157949120 !ruby/object:Gem::Requirement
|
83
84
|
none: false
|
84
85
|
requirements:
|
85
86
|
- - ! '>='
|
@@ -87,7 +88,7 @@ dependencies:
|
|
87
88
|
version: '0'
|
88
89
|
type: :development
|
89
90
|
prerelease: false
|
90
|
-
version_requirements: *
|
91
|
+
version_requirements: *2157949120
|
91
92
|
description: An experimental, half-assed, bug-ridden and highly opinionated code analyzer.
|
92
93
|
email: stefan.kanev@gmail.com
|
93
94
|
executables:
|
@@ -107,26 +108,20 @@ files:
|
|
107
108
|
- VERSION
|
108
109
|
- bin/skeptic
|
109
110
|
- features/skeptic.feature
|
110
|
-
- features/step_definitions/skeptic_steps.rb
|
111
|
-
- features/support/aruba.rb
|
112
|
-
- features/support/env.rb
|
113
111
|
- lib/skeptic.rb
|
114
112
|
- lib/skeptic/critic.rb
|
115
113
|
- lib/skeptic/environment.rb
|
114
|
+
- lib/skeptic/rule_table.rb
|
115
|
+
- lib/skeptic/rules.rb
|
116
|
+
- lib/skeptic/rules/check_syntax.rb
|
117
|
+
- lib/skeptic/rules/line_length.rb
|
116
118
|
- lib/skeptic/rules/lines_per_method.rb
|
117
119
|
- lib/skeptic/rules/max_nesting_depth.rb
|
118
120
|
- lib/skeptic/rules/methods_per_class.rb
|
119
121
|
- lib/skeptic/rules/no_semicolons.rb
|
120
122
|
- lib/skeptic/scope.rb
|
121
123
|
- lib/skeptic/sexp_visitor.rb
|
122
|
-
|
123
|
-
- spec/skeptic/environment_spec.rb
|
124
|
-
- spec/skeptic/rules/lines_per_method_spec.rb
|
125
|
-
- spec/skeptic/rules/max_nesting_depth_spec.rb
|
126
|
-
- spec/skeptic/rules/methods_per_class_spec.rb
|
127
|
-
- spec/skeptic/rules/no_semicolons_spec.rb
|
128
|
-
- spec/skeptic/scope_spec.rb
|
129
|
-
- spec/spec_helper.rb
|
124
|
+
has_rdoc: true
|
130
125
|
homepage: http://github.com/skanev/skeptic
|
131
126
|
licenses:
|
132
127
|
- MIT
|
@@ -142,7 +137,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
142
137
|
version: '0'
|
143
138
|
segments:
|
144
139
|
- 0
|
145
|
-
hash: -
|
140
|
+
hash: -794478687402394833
|
146
141
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
147
142
|
none: false
|
148
143
|
requirements:
|
@@ -151,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
151
146
|
version: '0'
|
152
147
|
requirements: []
|
153
148
|
rubyforge_project:
|
154
|
-
rubygems_version: 1.
|
149
|
+
rubygems_version: 1.6.2
|
155
150
|
signing_key:
|
156
151
|
specification_version: 3
|
157
152
|
summary: Skeptic points out annoying things in your code
|
File without changes
|
data/features/support/aruba.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require 'aruba/cucumber'
|
data/features/support/env.rb
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
require 'bundler'
|
2
|
-
begin
|
3
|
-
Bundler.setup(:default, :development)
|
4
|
-
rescue Bundler::BundlerError => e
|
5
|
-
$stderr.puts e.message
|
6
|
-
$stderr.puts "Run `bundle install` to install missing gems"
|
7
|
-
exit e.status_code
|
8
|
-
end
|
9
|
-
|
10
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
|
11
|
-
require 'skeptic'
|
12
|
-
|
13
|
-
require 'rspec/expectations'
|
data/spec/skeptic/critic_spec.rb
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Skeptic
|
4
|
-
describe Critic do
|
5
|
-
let(:critic) { Critic.new }
|
6
|
-
|
7
|
-
it "can locate semicolons in the code" do
|
8
|
-
criticize 'foo; bar', no_semicolons: true
|
9
|
-
|
10
|
-
expect_criticism 'You have a semicolon at line 1, column 3', 'No semicolons as expression separators'
|
11
|
-
end
|
12
|
-
|
13
|
-
it "can locate deep levels of nesting" do
|
14
|
-
criticize <<-RUBY, max_nesting_depth: 1
|
15
|
-
class Foo
|
16
|
-
def bar
|
17
|
-
while true
|
18
|
-
if false
|
19
|
-
really?
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
RUBY
|
25
|
-
|
26
|
-
expect_criticism 'Foo#bar has 2 levels of nesting: while > if', 'Maximum nesting depth (1)'
|
27
|
-
end
|
28
|
-
|
29
|
-
it "can locate classes with too many methods" do
|
30
|
-
criticize <<-RUBY, methods_per_class: 1
|
31
|
-
class Foo
|
32
|
-
def bar; end
|
33
|
-
def baz; end
|
34
|
-
end
|
35
|
-
RUBY
|
36
|
-
|
37
|
-
expect_criticism 'Foo has 2 methods: #bar, #baz', 'Number of methods per class (1)'
|
38
|
-
end
|
39
|
-
|
40
|
-
it "can locate methods that are too long" do
|
41
|
-
criticize <<-RUBY, lines_per_method: 1
|
42
|
-
class Foo
|
43
|
-
def bar
|
44
|
-
one
|
45
|
-
two
|
46
|
-
three
|
47
|
-
end
|
48
|
-
end
|
49
|
-
RUBY
|
50
|
-
|
51
|
-
expect_criticism 'Foo#bar is 3 lines long', 'Number of lines per method (1)'
|
52
|
-
end
|
53
|
-
|
54
|
-
def criticize(code, options)
|
55
|
-
options.each do |key, value|
|
56
|
-
critic.send "#{key}=", value
|
57
|
-
end
|
58
|
-
|
59
|
-
critic.criticize code
|
60
|
-
end
|
61
|
-
|
62
|
-
def expect_criticism(message, type)
|
63
|
-
critic.criticism.should include [message, type]
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Skeptic
|
4
|
-
describe Environment do
|
5
|
-
let(:env) { Environment.new }
|
6
|
-
|
7
|
-
it "maps names to objects" do
|
8
|
-
env[:foo] = 42
|
9
|
-
env[:foo].should eq 42
|
10
|
-
end
|
11
|
-
|
12
|
-
it "returns nil for names that are not set" do
|
13
|
-
env[:foo].should be_nil
|
14
|
-
end
|
15
|
-
|
16
|
-
it "allows environments to be extender" do
|
17
|
-
env.push foo: 2
|
18
|
-
env[:foo].should eq 2
|
19
|
-
end
|
20
|
-
|
21
|
-
it "allows environments to be unextended" do
|
22
|
-
env[:foo] = 1
|
23
|
-
env.push foo: 2
|
24
|
-
env.pop
|
25
|
-
env[:foo].should eq 1
|
26
|
-
end
|
27
|
-
|
28
|
-
it "looks up undefined names in the closure" do
|
29
|
-
env[:foo] = 1
|
30
|
-
env.push
|
31
|
-
env[:foo].should eq 1
|
32
|
-
end
|
33
|
-
|
34
|
-
it "can be extended for a block" do
|
35
|
-
executed_block = false
|
36
|
-
|
37
|
-
env[:foo] = 1
|
38
|
-
env.scoped do
|
39
|
-
env[:foo] = 2
|
40
|
-
env[:foo].should eq 2
|
41
|
-
executed_block = true
|
42
|
-
end
|
43
|
-
|
44
|
-
executed_block.should be_true
|
45
|
-
env[:foo].should eq 1
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
@@ -1,67 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Skeptic
|
4
|
-
module Rules
|
5
|
-
describe LinesPerMethod do
|
6
|
-
describe "calculating method size" do
|
7
|
-
it "can count the size of a method" do
|
8
|
-
code = <<-RUBY
|
9
|
-
class Foo
|
10
|
-
def bar
|
11
|
-
first
|
12
|
-
second
|
13
|
-
end
|
14
|
-
end
|
15
|
-
RUBY
|
16
|
-
|
17
|
-
analyze(code).size_of('Foo#bar').should eq 2
|
18
|
-
end
|
19
|
-
|
20
|
-
it "does not count empty lines" do
|
21
|
-
expect_line_count 2, <<-RUBY
|
22
|
-
foo
|
23
|
-
|
24
|
-
bar
|
25
|
-
RUBY
|
26
|
-
end
|
27
|
-
|
28
|
-
it "does not count lines containing one end" do
|
29
|
-
expect_line_count 2, <<-RUBY
|
30
|
-
if foo
|
31
|
-
bar
|
32
|
-
end
|
33
|
-
RUBY
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
describe "reporting" do
|
38
|
-
it "can tell which methods are too long" do
|
39
|
-
analyzer = analyze 1, <<-RUBY
|
40
|
-
class Foo
|
41
|
-
def bar
|
42
|
-
one
|
43
|
-
two
|
44
|
-
three
|
45
|
-
end
|
46
|
-
end
|
47
|
-
RUBY
|
48
|
-
|
49
|
-
analyzer.violations.should include 'Foo#bar is 3 lines long'
|
50
|
-
end
|
51
|
-
|
52
|
-
it "reports under 'Number of lines per method'" do
|
53
|
-
LinesPerMethod.new(2).name.should eq 'Number of lines per method (2)'
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
def expect_line_count(count, code)
|
58
|
-
code = "class Foo\ndef bar\n#{code}\nend\nend"
|
59
|
-
analyze(code).size_of('Foo#bar').should eq count
|
60
|
-
end
|
61
|
-
|
62
|
-
def analyze(limit = nil, code)
|
63
|
-
LinesPerMethod.new(limit).apply_to nil, Ripper.sexp(code)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
@@ -1,134 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Skeptic
|
4
|
-
module Rules
|
5
|
-
describe MaxNestingDepth do
|
6
|
-
describe "structure analysis" do
|
7
|
-
it "counts all conditional forms as a level of nesting" do
|
8
|
-
expect_deepest_nesting :if, 'if condition?; action; end'
|
9
|
-
expect_deepest_nesting :if, 'if condition? then action end'
|
10
|
-
expect_deepest_nesting :if, 'action if condition?'
|
11
|
-
expect_deepest_nesting :unless, 'action unless condition?'
|
12
|
-
expect_deepest_nesting :unless, 'unless condition?; action; end'
|
13
|
-
expect_deepest_nesting :unless, 'unless condition? then action end'
|
14
|
-
expect_deepest_nesting :if, :if, 'a if b? if c?'
|
15
|
-
end
|
16
|
-
|
17
|
-
it "counts else blocks as a level of nesting" do
|
18
|
-
expect_deepest_nesting :if, :unless, 'if a?; else; foo unless b? end'
|
19
|
-
expect_deepest_nesting :if, :unless, 'if a?; elsif? b?; else; foo unless c? end'
|
20
|
-
expect_deepest_nesting :unless, :if, 'unless a?; else; foo if b? end'
|
21
|
-
end
|
22
|
-
|
23
|
-
it "counts elsif blocks as a level of nesting" do
|
24
|
-
expect_deepest_nesting :if, :unless, 'if a?; elsif b?; foo unless c?; end'
|
25
|
-
expect_deepest_nesting :if, :unless, 'if a?; elsif b?; foo unless c?; else; end'
|
26
|
-
end
|
27
|
-
|
28
|
-
it "counts unbound loops as a level of nesting" do
|
29
|
-
expect_deepest_nesting :while, :if, 'while a?; b if c? end'
|
30
|
-
expect_deepest_nesting :while, :if, '(a if b?) while c?'
|
31
|
-
expect_deepest_nesting :until, :if, 'until a?; b if c? end'
|
32
|
-
expect_deepest_nesting :until, :if, '(a if b?) until c?'
|
33
|
-
end
|
34
|
-
|
35
|
-
it "counts blocks as a level of nesting" do
|
36
|
-
expect_deepest_nesting :iter, :if, 'a { b if c? }'
|
37
|
-
expect_deepest_nesting :iter, :if, 'a(1) { b if c? }'
|
38
|
-
expect_deepest_nesting :iter, :if, 'a do; b if c?; end'
|
39
|
-
expect_deepest_nesting :iter, :if, 'a(1) do; b if c?; end'
|
40
|
-
|
41
|
-
expect_deepest_nesting :iter, :if, 'loop { a if b }'
|
42
|
-
end
|
43
|
-
|
44
|
-
it "counts lambdas as a level of nesting" do
|
45
|
-
expect_deepest_nesting :iter, :if, 'lambda { a if b }'
|
46
|
-
expect_deepest_nesting :iter, :if, 'Proc.new { a if b }'
|
47
|
-
expect_deepest_nesting :lambda, :if, '-> { a if b }'
|
48
|
-
end
|
49
|
-
|
50
|
-
it "counts for loops as a level of nesting" do
|
51
|
-
expect_deepest_nesting :for, :if, 'for a in b; c if d; end'
|
52
|
-
end
|
53
|
-
|
54
|
-
it "counts case statements as a level of nesting" do
|
55
|
-
expect_deepest_nesting :case, :if, 'case a; when b; c if d?; end'
|
56
|
-
expect_deepest_nesting :case, :if, 'case a; when b; when c; d if e?; end'
|
57
|
-
expect_deepest_nesting :case, :if, 'case a; when b; else; d if e?; end'
|
58
|
-
end
|
59
|
-
|
60
|
-
it "counts begin blocks as a level of nesting" do
|
61
|
-
expect_deepest_nesting :begin, :if, 'begin; a if b; end'
|
62
|
-
end
|
63
|
-
|
64
|
-
it "does not count the method invocation as a block" do
|
65
|
-
expect_a_nesting :if, 'a((b if c)) { d }'
|
66
|
-
expect_a_nesting :iter, :if, 'a.b { c if d? }.e { g }'
|
67
|
-
expect_a_nesting :iter, :unless, 'a.b { c unless d? }.c { }'
|
68
|
-
end
|
69
|
-
|
70
|
-
it "does not count the if condition as a level of nesting" do
|
71
|
-
expect_a_nesting :iter, 'a if b { c }'
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
describe "nesting location" do
|
76
|
-
it "recognizes top-level code" do
|
77
|
-
expect_scope nil, nil, :if, 'a if b'
|
78
|
-
end
|
79
|
-
|
80
|
-
it "recognizes class definitions" do
|
81
|
-
expect_scope 'A', nil, :if, 'class A; a if b; end'
|
82
|
-
expect_scope 'A::B', nil, :if, 'class A::B; a if b; end'
|
83
|
-
end
|
84
|
-
|
85
|
-
it "recognizes method definitions" do
|
86
|
-
expect_scope nil, 'a', :if, 'def a; b if c; end'
|
87
|
-
expect_scope 'A', 'b', :if, 'class A; def b; c if d; end; end'
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
describe "reporting" do
|
92
|
-
it "can report methods that contain a deep level of nesting" do
|
93
|
-
analyzer = analyze 1, <<-RUBY
|
94
|
-
class Foo
|
95
|
-
def bar
|
96
|
-
while true
|
97
|
-
if false
|
98
|
-
really?
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
RUBY
|
104
|
-
|
105
|
-
analyzer.violations.should include 'Foo#bar has 2 levels of nesting: while > if'
|
106
|
-
end
|
107
|
-
|
108
|
-
it "reports under 'Maximum nesting depth'" do
|
109
|
-
MaxNestingDepth.new(2).name.should eq 'Maximum nesting depth (2)'
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def nestings(code)
|
114
|
-
analyze(code).nestings
|
115
|
-
end
|
116
|
-
|
117
|
-
def expect_scope(class_name, method_name, *levels, code)
|
118
|
-
analyze(code).nestings.should include Scope.new(class_name, method_name, levels)
|
119
|
-
end
|
120
|
-
|
121
|
-
def expect_a_nesting(*levels, code)
|
122
|
-
analyze(code).nestings.should include Scope.new(nil, nil, levels)
|
123
|
-
end
|
124
|
-
|
125
|
-
def expect_deepest_nesting(*levels, code)
|
126
|
-
analyze(code).nestings.max_by(&:depth).should eq Scope.new(nil, nil, levels)
|
127
|
-
end
|
128
|
-
|
129
|
-
def analyze(limit = nil, code)
|
130
|
-
MaxNestingDepth.new(limit).apply_to nil, Ripper.sexp(code)
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
@@ -1,90 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Skeptic
|
4
|
-
module Rules
|
5
|
-
describe MethodsPerClass do
|
6
|
-
describe "counting methods" do
|
7
|
-
it "counts methods defined in classes" do
|
8
|
-
expect_method_count 'Foo', 2, <<-RUBY
|
9
|
-
class Foo
|
10
|
-
def bar; end
|
11
|
-
def baz; end
|
12
|
-
end
|
13
|
-
RUBY
|
14
|
-
end
|
15
|
-
|
16
|
-
it "counts methods defined in modules" do
|
17
|
-
expect_method_count 'Foo', 2, <<-RUBY
|
18
|
-
module Foo
|
19
|
-
def bar; end
|
20
|
-
def baz; end
|
21
|
-
end
|
22
|
-
RUBY
|
23
|
-
end
|
24
|
-
|
25
|
-
it "counts method defined when the class is reopened" do
|
26
|
-
expect_method_count 'Foo', 2, <<-RUBY
|
27
|
-
class Foo; def bar; end; end
|
28
|
-
class Foo; def baz; end; end
|
29
|
-
RUBY
|
30
|
-
end
|
31
|
-
|
32
|
-
it "counts redefining the method as a new method" do
|
33
|
-
expect_method_count 'Foo', 2, <<-RUBY
|
34
|
-
class Foo
|
35
|
-
def bar; end
|
36
|
-
def bar; end
|
37
|
-
end
|
38
|
-
RUBY
|
39
|
-
end
|
40
|
-
|
41
|
-
it "works with multiple classes" do
|
42
|
-
counter = analyze <<-RUBY
|
43
|
-
class Foo; def name; end; end
|
44
|
-
class Bar; def name; end; end
|
45
|
-
RUBY
|
46
|
-
|
47
|
-
counter.methods_in('Foo').should eq 1
|
48
|
-
counter.methods_in('Bar').should eq 1
|
49
|
-
end
|
50
|
-
|
51
|
-
it "recognizes qualified module names" do
|
52
|
-
expect_method_count 'Foo::Bar', 1, <<-RUBY
|
53
|
-
class Foo::Bar; def baz; end; end
|
54
|
-
RUBY
|
55
|
-
end
|
56
|
-
|
57
|
-
it "recognizes modules nested under other modules" do
|
58
|
-
expect_method_count 'Foo::Bar', 1, <<-RUBY
|
59
|
-
class Foo; module Bar; def baz; end; end; end
|
60
|
-
RUBY
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
describe "reporting" do
|
65
|
-
it "reports classes, violating the rule" do
|
66
|
-
analyzer = analyze 1, <<-RUBY
|
67
|
-
class Foo
|
68
|
-
def bar; end
|
69
|
-
def baz; end
|
70
|
-
end
|
71
|
-
RUBY
|
72
|
-
|
73
|
-
analyzer.violations.should include 'Foo has 2 methods: #bar, #baz'
|
74
|
-
end
|
75
|
-
|
76
|
-
it "reports under 'Number of methods per class'" do
|
77
|
-
MethodsPerClass.new(42).name.should eq 'Number of methods per class (42)'
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def analyze(limit = nil, code)
|
82
|
-
MethodsPerClass.new(limit).apply_to nil, Ripper.sexp(code)
|
83
|
-
end
|
84
|
-
|
85
|
-
def expect_method_count(class_name, count, code)
|
86
|
-
analyze(code).methods_in(class_name).should eq count
|
87
|
-
end
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Skeptic
|
4
|
-
module Rules
|
5
|
-
describe NoSemicolons do
|
6
|
-
describe "detecting semicolons" do
|
7
|
-
it "complains if it finds a semicolon in the code" do
|
8
|
-
expect_complaint 'foo; bar'
|
9
|
-
expect_complaint 'this; that; other'
|
10
|
-
expect_complaint '"#{foo;bar}"'
|
11
|
-
end
|
12
|
-
|
13
|
-
it "does not complain for semicolons in literals" do
|
14
|
-
expect_fine_and_dandy '"foo;"'
|
15
|
-
expect_fine_and_dandy '";"'
|
16
|
-
expect_fine_and_dandy '/;/'
|
17
|
-
end
|
18
|
-
|
19
|
-
it "can tell the locations of the semicolons" do
|
20
|
-
analyze("foo;\n;bar").semicolon_locations.should =~ [[1, 3], [2, 0]]
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
describe "reporting" do
|
25
|
-
it "points out file locations with semicolons" do
|
26
|
-
analyzer = analyze 'foo; bar'
|
27
|
-
|
28
|
-
analyzer.violations.should include 'You have a semicolon at line 1, column 3'
|
29
|
-
end
|
30
|
-
|
31
|
-
it "reports under 'No semicolons'" do
|
32
|
-
NoSemicolons.new(true).name.should eq 'No semicolons as expression separators'
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def expect_fine_and_dandy(code)
|
37
|
-
analyze(code).semicolon_locations.should be_empty
|
38
|
-
end
|
39
|
-
|
40
|
-
def expect_complaint(code)
|
41
|
-
analyze(code).semicolon_locations.should_not be_empty
|
42
|
-
end
|
43
|
-
|
44
|
-
def analyze(code)
|
45
|
-
NoSemicolons.new(true).apply_to Ripper.lex(code), nil
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
data/spec/skeptic/scope_spec.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
module Skeptic
|
4
|
-
describe Scope do
|
5
|
-
it "contains levels of nesting" do
|
6
|
-
Scope.new(nil, nil, [:for, :if]).levels.should eq [:for, :if]
|
7
|
-
Scope.new(nil, nil, []).levels.should eq []
|
8
|
-
Scope.new.levels.should eq []
|
9
|
-
end
|
10
|
-
|
11
|
-
it "can be compared to another scope" do
|
12
|
-
Scope.new(nil, nil, [:for, :if]).should eq Scope.new(nil, nil, [:for, :if])
|
13
|
-
Scope.new(nil, nil, []).should_not eq Scope.new(nil, nil, [:if])
|
14
|
-
Scope.new('Bar', nil).should_not eq Scope.new('Foo', nil)
|
15
|
-
Scope.new(nil, 'bar').should_not eq Scope.new(nil, 'foo')
|
16
|
-
end
|
17
|
-
|
18
|
-
it "can be extended and unextended" do
|
19
|
-
Scope.new.push(:if).should eq Scope.new(nil, nil, [:if])
|
20
|
-
Scope.new(nil, nil, [:for, :if]).pop.should eq Scope.new(nil, nil, [:for])
|
21
|
-
|
22
|
-
Scope.new.in_class('Foo').should eq Scope.new('Foo')
|
23
|
-
Scope.new.in_method('bar').should eq Scope.new(nil, 'bar')
|
24
|
-
end
|
25
|
-
|
26
|
-
it "can tell its depth" do
|
27
|
-
Scope.new(nil, nil, [:for]).depth.should eq 1
|
28
|
-
Scope.new(nil, nil, [:for, :if]).depth.should eq 2
|
29
|
-
Scope.new(nil, nil, [:for, :if, :if]).depth.should eq 3
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
data/spec/spec_helper.rb
DELETED
@@ -1,12 +0,0 @@
|
|
1
|
-
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
-
require 'rspec'
|
4
|
-
require 'skeptic'
|
5
|
-
|
6
|
-
# Requires supporting files with custom matchers and macros, etc,
|
7
|
-
# in ./support/ and its subdirectories.
|
8
|
-
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
|
9
|
-
|
10
|
-
RSpec.configure do |config|
|
11
|
-
|
12
|
-
end
|