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 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