skeptic 0.0.0 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|