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 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 'rake/rdoctask'
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.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
- opts = Trollop::options do
7
- opt :lines_per_method, 'Maximum number of lines per method', type: :int
8
- opt :max_nesting_depth, 'Maximum nesting depth', type: :int
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
- critic = Skeptic::Critic.new
10
+ skeptic [options] <path_to_file>
15
11
 
16
- critic.no_semicolons = opts[:no_semicolons]
17
- critic.max_nesting_depth = opts[:max_nesting_depth]
18
- critic.methods_per_class = opts[:methods_per_class]
19
- critic.lines_per_method = opts[:lines_per_method]
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?
@@ -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
+ """
@@ -1,14 +1,15 @@
1
1
  module Skeptic
2
2
  class Critic
3
- attr_accessor :lines_per_method
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
- rules = {
20
- Rules::LinesPerMethod => lines_per_method,
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
- rules.reject { |rule_type, option| option.nil? }.each do |rule_type, option|
27
- rule = rule_type.new(option)
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.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-26 00:00:00.000000000Z
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: &25894860 !ruby/object:Gem::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: *25894860
25
+ version_requirements: *2157956920
25
26
  - !ruby/object:Gem::Dependency
26
27
  name: rspec
27
- requirement: &25893680 !ruby/object:Gem::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: *25893680
36
+ version_requirements: *2157956340
36
37
  - !ruby/object:Gem::Dependency
37
38
  name: cucumber
38
- requirement: &25892440 !ruby/object:Gem::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: *25892440
47
+ version_requirements: *2157955800
47
48
  - !ruby/object:Gem::Dependency
48
49
  name: aruba
49
- requirement: &25891280 !ruby/object:Gem::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: *25891280
58
+ version_requirements: *2157955200
58
59
  - !ruby/object:Gem::Dependency
59
60
  name: bundler
60
- requirement: &25890280 !ruby/object:Gem::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: *25890280
69
+ version_requirements: *2157954600
69
70
  - !ruby/object:Gem::Dependency
70
71
  name: jeweler
71
- requirement: &25889520 !ruby/object:Gem::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: *25889520
80
+ version_requirements: *2157949640
80
81
  - !ruby/object:Gem::Dependency
81
82
  name: rcov
82
- requirement: &25888560 !ruby/object:Gem::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: *25888560
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
- - spec/skeptic/critic_spec.rb
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: -2839916521790912268
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.8.10
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
@@ -1 +0,0 @@
1
- require 'aruba/cucumber'
@@ -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'
@@ -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
@@ -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