wegolint 0.0.1 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +3 -0
- data/Gemfile.lock +1 -1
- data/Rakefile +6 -0
- data/bin/wegolint +6 -0
- data/lib/wegolint/dsl.rb +37 -0
- data/lib/wegolint/rule.rb +42 -0
- data/lib/wegolint/rule_registry.rb +57 -0
- data/lib/wegolint/rules/general.rb +15 -0
- data/lib/wegolint/rules/ruby.rb +27 -0
- data/lib/wegolint/wegolinter.rb +79 -0
- data/lib/wegolint.rb +4 -101
- data/spec/rule_registry_spec.rb +49 -0
- data/spec/rule_spec.rb +120 -0
- data/spec/spec_helper.rb +17 -0
- data/spec/wegolint_bin_spec.rb +77 -0
- data/spec/wegolinter_spec.rb +54 -0
- data/wegolint.gemspec +2 -2
- metadata +14 -4
- data/lib/init.rb +0 -1
- data/spec/wegolint_spec.rb +0 -199
data/.rspec
ADDED
data/Gemfile.lock
CHANGED
data/Rakefile
ADDED
data/bin/wegolint
CHANGED
data/lib/wegolint/dsl.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
module WegoLint
|
2
|
+
module DSL
|
3
|
+
module RuleDirectives
|
4
|
+
@@collapse_quotes = false
|
5
|
+
|
6
|
+
def included(klass)
|
7
|
+
@@pattern = nil
|
8
|
+
end
|
9
|
+
|
10
|
+
def pattern(pattern = nil)
|
11
|
+
@@pattern = pattern if pattern
|
12
|
+
@@pattern
|
13
|
+
end
|
14
|
+
|
15
|
+
def collapse_quotes
|
16
|
+
@@collapse_quotes = true
|
17
|
+
end
|
18
|
+
|
19
|
+
def collapse_quotes?
|
20
|
+
@@collapse_quotes
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def language(name, &block)
|
25
|
+
WegoLint::RuleRegistry.instance.language_context = name
|
26
|
+
yield
|
27
|
+
WegoLint::RuleRegistry.instance.language_context = :general
|
28
|
+
end
|
29
|
+
|
30
|
+
def rule(description, &rule_block)
|
31
|
+
rule = WegoLint::RuleRegistry.instance.add_rule(description, rule_block)
|
32
|
+
rule
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
extend WegoLint::DSL
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module WegoLint; end
|
2
|
+
|
3
|
+
class WegoLint::Rule
|
4
|
+
|
5
|
+
attr_accessor :errors, :language, :description
|
6
|
+
|
7
|
+
@@pattern = nil
|
8
|
+
|
9
|
+
def initialize(description, language = :general)
|
10
|
+
@errors = []
|
11
|
+
@language = language
|
12
|
+
@output = ""
|
13
|
+
@description = description
|
14
|
+
end
|
15
|
+
|
16
|
+
def run(text)
|
17
|
+
text.split("\n").each_with_index do |line, index|
|
18
|
+
line = collapse_quotes(line) if self.class.collapse_quotes?
|
19
|
+
if line =~ self.class.pattern
|
20
|
+
@errors << {message: description, line_number: index + 1 }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
##############################################################################
|
26
|
+
private
|
27
|
+
|
28
|
+
def self.block_not_padded
|
29
|
+
# This won't work with chained blocks on one line
|
30
|
+
no_space_beginning_block = /\{[\w\|'"].*\}/
|
31
|
+
no_space_end_block = /\{.*[\w'"]\}/
|
32
|
+
/(#{no_space_beginning_block}|#{no_space_end_block})/
|
33
|
+
end
|
34
|
+
|
35
|
+
def collapse_quotes(line)
|
36
|
+
newline = line
|
37
|
+
['"', "'"].each do |char|
|
38
|
+
newline.gsub!(/#{char}.+?#{char}/, "#{char}TEST#{char}")
|
39
|
+
end
|
40
|
+
newline
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module WegoLint; end
|
2
|
+
|
3
|
+
require 'singleton'
|
4
|
+
|
5
|
+
class WegoLint::RuleRegistry
|
6
|
+
include Singleton
|
7
|
+
|
8
|
+
@@rule_count = 0
|
9
|
+
|
10
|
+
def self.rule_count
|
11
|
+
@@rule_count
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.rule_count=(rule_count)
|
15
|
+
@@rule_count = rule_count
|
16
|
+
end
|
17
|
+
|
18
|
+
attr_accessor :language_context, :rules
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@rule_id = 0
|
22
|
+
@rules = {}
|
23
|
+
@language_context = :general
|
24
|
+
end
|
25
|
+
|
26
|
+
def find_by_description(description)
|
27
|
+
rules.keys.each do |key|
|
28
|
+
match = rules[key].find {|rule| rule.description == description }
|
29
|
+
return match if match
|
30
|
+
end
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def load_rules
|
35
|
+
Dir[File.dirname(__FILE__) + '/rules/*'].each { |rule| require rule }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Pattern stolen from rspec
|
39
|
+
def add_rule(description, block)
|
40
|
+
WegoLint::RuleRegistry::rule_count += 1
|
41
|
+
klass = WegoLint::RuleRegistry.const_set("Rule_#{WegoLint::RuleRegistry::rule_count}",
|
42
|
+
subclass(WegoLint::Rule, block))
|
43
|
+
@rules[@language_context] = [] unless @rules[@language_context]
|
44
|
+
rule = klass.new(description, @language_context)
|
45
|
+
@rules[@language_context] << rule
|
46
|
+
rule
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def subclass(parent, block)
|
52
|
+
subclass = Class.new(parent)
|
53
|
+
subclass.send(:extend, WegoLint::DSL::RuleDirectives.clone)
|
54
|
+
subclass.module_eval(&block) if block
|
55
|
+
subclass
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
rule "More than eighty characters" do
|
2
|
+
pattern /^.{80}.+$/
|
3
|
+
end
|
4
|
+
|
5
|
+
rule "Hard tabs (use soft tabs)" do
|
6
|
+
pattern /\t/
|
7
|
+
end
|
8
|
+
|
9
|
+
rule "Whitespace at end of line" do
|
10
|
+
pattern /^.*\w+.*\s+$/
|
11
|
+
end
|
12
|
+
|
13
|
+
rule "Opening curly brace on its own line" do
|
14
|
+
pattern /^\s*\{\s*$/
|
15
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
language :ruby do
|
2
|
+
rule 'Space should be around block { |param| foo }' do
|
3
|
+
pattern /[\.\s]#{block_not_padded}/
|
4
|
+
|
5
|
+
collapse_quotes
|
6
|
+
end
|
7
|
+
|
8
|
+
rule 'No spaces after (, [ or before ], )' do
|
9
|
+
collapse_quotes
|
10
|
+
|
11
|
+
def self.no_spaces_padding_parens
|
12
|
+
space = /[ ]/
|
13
|
+
left_parens = /[\[\(]/
|
14
|
+
right_parens = /[\]\)]/
|
15
|
+
space_after_left_parens = /#{left_parens}#{space}/
|
16
|
+
space_before_right_parens =
|
17
|
+
/^(?!\s+#{right_parens}).*#{space}#{right_parens}/
|
18
|
+
/(?:#{space_after_left_parens}|#{space_before_right_parens})/
|
19
|
+
end
|
20
|
+
|
21
|
+
pattern no_spaces_padding_parens
|
22
|
+
end
|
23
|
+
|
24
|
+
rule 'Parentheses should be around function parameters' do
|
25
|
+
pattern /def \w+ \w/
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module WegoLint; end
|
2
|
+
|
3
|
+
require 'stringio'
|
4
|
+
|
5
|
+
class WegoLint::WegoLinter
|
6
|
+
def initialize(filename)
|
7
|
+
@filename = filename
|
8
|
+
@filetype = File.extname(@filename)
|
9
|
+
@ruleset = @filetype == '.js' ? :javascript : :ruby
|
10
|
+
@output = ''
|
11
|
+
end
|
12
|
+
|
13
|
+
def lint!
|
14
|
+
@output = run_language_parser
|
15
|
+
parse_file
|
16
|
+
end
|
17
|
+
|
18
|
+
def rules
|
19
|
+
WegoLint::RuleRegistry.instance.rules
|
20
|
+
end
|
21
|
+
|
22
|
+
def output
|
23
|
+
@output
|
24
|
+
end
|
25
|
+
|
26
|
+
def load_rules
|
27
|
+
WegoLint::RuleRegistry.instance.load_rules
|
28
|
+
end
|
29
|
+
|
30
|
+
##############################################################################
|
31
|
+
private
|
32
|
+
|
33
|
+
def parse_file
|
34
|
+
File.open(@filename, 'r') do |file|
|
35
|
+
text = file.read
|
36
|
+
apply_rules(text, :general)
|
37
|
+
apply_rules(text, @ruleset)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def run_language_parser
|
42
|
+
if @filetype == ".js"
|
43
|
+
command = 'jsl -nologo -nofilelisting -nosummary -nocontext -process'
|
44
|
+
`#{command} #{@filename} 2>&1`
|
45
|
+
else
|
46
|
+
`ruby -w -T1 -c #{@filename} 2>&1`
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# The real algorithm for this has to find the first instance of any pair,
|
51
|
+
# strip it, start over, find the second instance, strip it, start over, etc.
|
52
|
+
def collapse_quotes(line)
|
53
|
+
newline = line
|
54
|
+
['"', "'"].each do |char|
|
55
|
+
newline.gsub!(/#{char}.+?#{char}/, "#{char}TEST#{char}")
|
56
|
+
end
|
57
|
+
newline
|
58
|
+
end
|
59
|
+
|
60
|
+
def apply_rules(text, ruleset = :general)
|
61
|
+
return unless rules[ruleset]
|
62
|
+
rules[ruleset].each do |rule|
|
63
|
+
rule.run(text)
|
64
|
+
@output += format_errors(rule.errors) + "\n" unless rule.errors.empty?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def format_errors(errors)
|
69
|
+
errors.collect { |error| format_error(error) }.join("\n")
|
70
|
+
end
|
71
|
+
|
72
|
+
def format_error(error)
|
73
|
+
if @ruleset == :ruby
|
74
|
+
"#{@filename}:#{error[:line_number]}: syntax error, #{error[:message]}"
|
75
|
+
elsif @ruleset == :javascript
|
76
|
+
"#{@filename}:#{error[:line_number]}: error: #{error[:message]}"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/wegolint.rb
CHANGED
@@ -1,102 +1,5 @@
|
|
1
1
|
module WegoLint; end
|
2
|
-
|
3
|
-
require '
|
4
|
-
|
5
|
-
|
6
|
-
def initialize(filename)
|
7
|
-
@filename = filename
|
8
|
-
@filetype = File.extname(@filename)
|
9
|
-
@ruleset = @filetype == '.js' ? :javascript : :ruby
|
10
|
-
@output = ''
|
11
|
-
end
|
12
|
-
|
13
|
-
def lint!
|
14
|
-
@output = run_language_parser
|
15
|
-
parse_file
|
16
|
-
end
|
17
|
-
|
18
|
-
def rules
|
19
|
-
@rules ||= {
|
20
|
-
general: [
|
21
|
-
{ name: 'More than eighty characters', pattern: /^.{80}.+$/ },
|
22
|
-
{ name: 'Curly brace on its own line', pattern: /^\s*(\{|\})\s*$/ },
|
23
|
-
{ name: "Hard tabs (use soft tabs)", pattern: /\t/ },
|
24
|
-
{ name: 'Whitespace at end of line', pattern: /^.*\w+.*\s+\n$/ }],
|
25
|
-
|
26
|
-
ruby: [
|
27
|
-
{ name: 'Space should be around block { |param| foo }',
|
28
|
-
pattern: /[\.\s]#{block_not_padded}/, collapse_quotes: true },
|
29
|
-
{ name: 'No spaces after (, [ or before ], )',
|
30
|
-
pattern: no_spaces_padding_parens, collapse_quotes: true },
|
31
|
-
{ name: 'No parentheses around function parameters',
|
32
|
-
pattern: /def \w+ \w/}]}
|
33
|
-
end
|
34
|
-
|
35
|
-
def output
|
36
|
-
@output
|
37
|
-
end
|
38
|
-
|
39
|
-
##############################################################################
|
40
|
-
private
|
41
|
-
|
42
|
-
def parse_file
|
43
|
-
File.open(@filename, 'r') do |file|
|
44
|
-
line_number = 0
|
45
|
-
file.each_line do |line|
|
46
|
-
line_number += 1
|
47
|
-
apply_rules(line, line_number)
|
48
|
-
apply_rules(line, line_number, @ruleset)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def run_language_parser
|
54
|
-
if @filetype == ".js"
|
55
|
-
`jsl -nologo -nofilelisting -nosummary -nocontext -process #{@filename} 2>&1`
|
56
|
-
else
|
57
|
-
`ruby -w -T1 -c #{@filename} 2>&1`
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
def block_not_padded
|
62
|
-
# This won't work with chained blocks on one line
|
63
|
-
no_space_beginning_block = /\{[\w\|'"].*\}[\.\s]$/
|
64
|
-
no_space_end_block = /\{.*[\w'"]\}[\.\s]$/
|
65
|
-
/(#{no_space_beginning_block}|#{no_space_end_block})/
|
66
|
-
end
|
67
|
-
|
68
|
-
def no_spaces_padding_parens
|
69
|
-
space = /[ ]/
|
70
|
-
left_parens = /[\[\(]/
|
71
|
-
right_parens = /[\]\)]/
|
72
|
-
space_after_left_parens = /#{left_parens}#{space}/
|
73
|
-
space_before_right_parens = /^(?!\s+#{right_parens}).*#{space}#{right_parens}/
|
74
|
-
/(?:#{space_after_left_parens}|#{space_before_right_parens})/
|
75
|
-
end
|
76
|
-
|
77
|
-
# The real algorithm for this has to find the first instance of any pair,
|
78
|
-
# strip it, start over, find the second instance, strip it, start over, etc.
|
79
|
-
def collapse_quotes(line)
|
80
|
-
newline = line
|
81
|
-
['"', "'"].each do |char|
|
82
|
-
newline.gsub!(/#{char}.+?#{char}/, "#{char}TEST#{char}")
|
83
|
-
end
|
84
|
-
newline
|
85
|
-
end
|
86
|
-
|
87
|
-
def apply_rules(line, line_number, ruleset = :general)
|
88
|
-
return unless rules[ruleset]
|
89
|
-
|
90
|
-
rules[ruleset].each do |rule|
|
91
|
-
line = collapse_quotes(line) if rule[:collapse_quotes]
|
92
|
-
if line =~ rule[:pattern]
|
93
|
-
if @filetype == ".js"
|
94
|
-
full_path = File.realpath(@filename)
|
95
|
-
@output += "#{full_path}(#{line_number}): SyntaxError: #{rule[:name]}\n"
|
96
|
-
else
|
97
|
-
@output += "#{@filename}:#{line_number}: syntax error, #{rule[:name]}\n"
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
2
|
+
require 'wegolint/rule_registry'
|
3
|
+
require 'wegolint/rule'
|
4
|
+
require 'wegolint/dsl'
|
5
|
+
require 'wegolint/wegolinter'
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
include WegoLint
|
4
|
+
|
5
|
+
describe RuleRegistry do
|
6
|
+
let(:description) { 'A new rule' }
|
7
|
+
let(:block) { proc do
|
8
|
+
pattern /foo/
|
9
|
+
end }
|
10
|
+
let(:rule_registry) { RuleRegistry.clone.instance }
|
11
|
+
|
12
|
+
describe ".add_rule" do
|
13
|
+
|
14
|
+
let!(:subject) { rule_registry.add_rule(description, block) }
|
15
|
+
|
16
|
+
it "creates a subclass of Rule" do
|
17
|
+
subject.should be_a Rule
|
18
|
+
end
|
19
|
+
|
20
|
+
it "creates a rule with the specified pattern" do
|
21
|
+
subject.class.pattern.should == /foo/
|
22
|
+
end
|
23
|
+
|
24
|
+
context "when there are more than two rules" do
|
25
|
+
before do
|
26
|
+
rule_registry.add_rule('Another rule', proc { pattern /bar/ } )
|
27
|
+
end
|
28
|
+
|
29
|
+
it "creates a rule with the specified pattern" do
|
30
|
+
subject.class.pattern.should == /foo/
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe ".rules" do
|
36
|
+
it "rule is added to the general rules list when language is not set" do
|
37
|
+
rule_registry.add_rule(description, block)
|
38
|
+
rule = rule_registry.rules[:general].first
|
39
|
+
rule.description.should == 'A new rule'
|
40
|
+
end
|
41
|
+
|
42
|
+
it "rule is added to the language rules list when language is set" do
|
43
|
+
rule_registry.language_context = :ruby
|
44
|
+
rule_registry.add_rule('Another rule', block)
|
45
|
+
rule = rule_registry.rules[:ruby].first
|
46
|
+
rule.description.should == 'Another rule'
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/spec/rule_spec.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
include WegoLint
|
4
|
+
|
5
|
+
describe Rule do
|
6
|
+
let(:rule_registry) { RuleRegistry.instance }
|
7
|
+
let(:subject) { rule_registry.find_by_description(description) }
|
8
|
+
|
9
|
+
before do
|
10
|
+
rule_registry.load_rules
|
11
|
+
subject.run(code)
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "General errors" do
|
15
|
+
context "hard tab" do
|
16
|
+
let(:description) { "Hard tabs (use soft tabs)" }
|
17
|
+
let(:code) { "\t" }
|
18
|
+
|
19
|
+
it { should_have_error description, 1, :ruby }
|
20
|
+
end
|
21
|
+
|
22
|
+
context "whitespace at end of line" do
|
23
|
+
let(:description) { "Whitespace at end of line" }
|
24
|
+
let(:code) { "'foo' \n" }
|
25
|
+
|
26
|
+
it { should_have_error description, 1, :ruby }
|
27
|
+
end
|
28
|
+
|
29
|
+
context "80 character limit" do
|
30
|
+
let(:description) { "More than eighty characters" }
|
31
|
+
let(:code) do
|
32
|
+
<<-EOF
|
33
|
+
a = 1
|
34
|
+
"kjkjkjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk"
|
35
|
+
b = 3
|
36
|
+
EOF
|
37
|
+
end
|
38
|
+
|
39
|
+
it { should_have_error description, 2, :ruby }
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "Ruby errors" do
|
45
|
+
context "curly brace on its own line" do
|
46
|
+
let(:description) { "Opening curly brace on its own line" }
|
47
|
+
let(:code) do
|
48
|
+
<<-EOF
|
49
|
+
q = 1
|
50
|
+
{
|
51
|
+
b = 1
|
52
|
+
}
|
53
|
+
c = a+b
|
54
|
+
EOF
|
55
|
+
end
|
56
|
+
|
57
|
+
it { should_have_error description, 2, :ruby }
|
58
|
+
it { should_not_have_error description, 4, :ruby }
|
59
|
+
end
|
60
|
+
|
61
|
+
context "no space on inside of block" do
|
62
|
+
let(:description) { 'Space should be around block { |param| foo }' }
|
63
|
+
let(:code) do
|
64
|
+
<<-EOF
|
65
|
+
a = 1
|
66
|
+
['a','b','c'].each {|item| puts item }
|
67
|
+
['a','b','c'].each { |item| puts item}
|
68
|
+
b = 2
|
69
|
+
EOF
|
70
|
+
end
|
71
|
+
|
72
|
+
it { should_have_error description, 2, :ruby }
|
73
|
+
it { should_have_error description, 3, :ruby }
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'No spaces after (, [ or before ], )' do
|
77
|
+
let(:description) { 'No spaces after (, [ or before ], )' }
|
78
|
+
let(:code) do
|
79
|
+
<<-EOF
|
80
|
+
b = [ 'a' ]
|
81
|
+
def something( foo )
|
82
|
+
c = " [ a ] "
|
83
|
+
c = ' [ ] '
|
84
|
+
[ 'a', 'b' ].each { |line| puts " [ ] " }
|
85
|
+
end
|
86
|
+
doSomething(bar: 'baz', baz: 'bar', foo: 'bar', fa: 'lalalalalalaalalalalalala'
|
87
|
+
)
|
88
|
+
EOF
|
89
|
+
end
|
90
|
+
|
91
|
+
it { should_have_error description, 1, :ruby }
|
92
|
+
it { should_have_error description, 2, :ruby }
|
93
|
+
|
94
|
+
context "when embedded in strings" do
|
95
|
+
it { should_not_have_error description, 3, :ruby }
|
96
|
+
it { should_not_have_error description, 4, :ruby }
|
97
|
+
it { should_have_error description, 5, :ruby }
|
98
|
+
it { should_not_have_error description, 8, :ruby }
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context "No parentheses around function parameters" do
|
103
|
+
let(:description) { 'Parentheses should be around function parameters' }
|
104
|
+
let(:code) do
|
105
|
+
<<-EOF
|
106
|
+
def something(foo)
|
107
|
+
end
|
108
|
+
def something foo, bar
|
109
|
+
end
|
110
|
+
def something
|
111
|
+
end
|
112
|
+
EOF
|
113
|
+
end
|
114
|
+
|
115
|
+
it { should_not_have_error description, 1, :ruby }
|
116
|
+
it { should_have_error description, 3, :ruby }
|
117
|
+
it { should_not_have_error description, 5, :ruby }
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -2,3 +2,20 @@ RSpec.configure do |config|
|
|
2
2
|
config.treat_symbols_as_metadata_keys_with_true_values = true
|
3
3
|
config.run_all_when_everything_filtered = true
|
4
4
|
end
|
5
|
+
|
6
|
+
require 'wegolint'
|
7
|
+
|
8
|
+
def should_have_error message, line_number, language
|
9
|
+
message = build_error_message message, line_number, language
|
10
|
+
subject.errors.should include message
|
11
|
+
end
|
12
|
+
|
13
|
+
def should_not_have_error message, line_number, language
|
14
|
+
message = build_error_message message, line_number, language
|
15
|
+
subject.errors.should_not include message
|
16
|
+
end
|
17
|
+
|
18
|
+
def build_error_message message, line_number, language
|
19
|
+
{ message: message, line_number: line_number }
|
20
|
+
end
|
21
|
+
|
@@ -0,0 +1,77 @@
|
|
1
|
+
describe "wegolint bin" do
|
2
|
+
let(:file) { '/tmp/wegolint_spec/foo.rb' }
|
3
|
+
let(:args) { file }
|
4
|
+
let(:subject) { `bundle exec wegolint #{args} 2>&1` }
|
5
|
+
let(:code) { '' }
|
6
|
+
|
7
|
+
before do
|
8
|
+
FileUtils.mkdir_p '/tmp/wegolint_spec/'
|
9
|
+
File.open(file, 'w+') do |f|
|
10
|
+
f.write(code)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
after do
|
15
|
+
File.unlink(file)
|
16
|
+
FileUtils.rmdir('/tmp/wegolint_spec')
|
17
|
+
end
|
18
|
+
|
19
|
+
context "without a file argument" do
|
20
|
+
let(:args) { '' }
|
21
|
+
it "without a file argument it provides usage instructions" do
|
22
|
+
subject.should =~ /USAGE:/
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "with a file that has no errors (ruby)" do
|
27
|
+
let(:code) do
|
28
|
+
<<-EOF
|
29
|
+
def something
|
30
|
+
puts "hello world"
|
31
|
+
end
|
32
|
+
EOF
|
33
|
+
end
|
34
|
+
|
35
|
+
it "outputs a success message" do
|
36
|
+
subject.should == "Syntax OK\n"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
context "with a file that has errors (ruby)" do
|
41
|
+
let(:code) do
|
42
|
+
<<-EOF
|
43
|
+
a = "Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong"
|
44
|
+
EOF
|
45
|
+
end
|
46
|
+
|
47
|
+
it "outputs ruby syntax parser format error message" do
|
48
|
+
subject.should == "Syntax OK\n#{file}:1: syntax error, More than eighty characters\n"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context "with a file that has no errors (javascript)" do
|
53
|
+
let(:file) { '/tmp/wegolint_spec/foo.js' }
|
54
|
+
let(:code) do
|
55
|
+
<<-EOF
|
56
|
+
var something ='foo';
|
57
|
+
EOF
|
58
|
+
end
|
59
|
+
|
60
|
+
it "outputs a success message" do
|
61
|
+
subject.should == "\n"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "with a file that has errors (javascript)" do
|
66
|
+
let(:file) { '/tmp/wegolint_spec/foo.js' }
|
67
|
+
let(:code) do
|
68
|
+
<<-EOF
|
69
|
+
var a = "Loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong";
|
70
|
+
EOF
|
71
|
+
end
|
72
|
+
|
73
|
+
it "outputs ruby syntax parser format error message" do
|
74
|
+
subject.should == "#{file}:1: error: More than eighty characters\n"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
include WegoLint
|
4
|
+
|
5
|
+
describe WegoLinter do
|
6
|
+
|
7
|
+
let(:wegolinter) { WegoLinter.new('/tmp/wegolint_spec/foo.rb') }
|
8
|
+
|
9
|
+
before do
|
10
|
+
wegolinter.load_rules
|
11
|
+
end
|
12
|
+
|
13
|
+
describe "#rules" do
|
14
|
+
let(:subject) { wegolinter.rules }
|
15
|
+
|
16
|
+
it "loads rules from rules/ directory" do
|
17
|
+
subject.should_not be_empty
|
18
|
+
end
|
19
|
+
|
20
|
+
it "sorts rules into language groups" do
|
21
|
+
subject[:general].should_not be_empty
|
22
|
+
subject[:ruby].should_not be_empty
|
23
|
+
subject[:general].first.should be_a Rule
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#lint!" do
|
28
|
+
context "ruby parser syntax errors" do
|
29
|
+
let(:subject) { wegolinter.output }
|
30
|
+
let(:code) do
|
31
|
+
<<-EOF
|
32
|
+
Foo
|
33
|
+
end
|
34
|
+
EOF
|
35
|
+
end
|
36
|
+
|
37
|
+
before do
|
38
|
+
FileUtils.mkdir_p '/tmp/wegolint_spec/'
|
39
|
+
File.open('/tmp/wegolint_spec/foo.rb', 'w+') do |file|
|
40
|
+
file.write(code)
|
41
|
+
end
|
42
|
+
wegolinter.lint!
|
43
|
+
end
|
44
|
+
|
45
|
+
after do
|
46
|
+
File.unlink('/tmp/wegolint_spec/foo.rb')
|
47
|
+
FileUtils.rmdir('/tmp/wegolint_spec')
|
48
|
+
end
|
49
|
+
|
50
|
+
it { should include
|
51
|
+
"foo.rb:2: syntax error, unexpected keyword_end, expecting $end" }
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/wegolint.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'wegolint'
|
3
|
-
s.version = '0.0.
|
4
|
-
s.date = '2012-09-
|
3
|
+
s.version = '0.0.3'
|
4
|
+
s.date = '2012-09-30'
|
5
5
|
s.summary = 'Parses code for style guide violations'
|
6
6
|
s.description = <<-EOF
|
7
7
|
Validates code according to style guide rules for an organization and outputs
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wegolint
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-09-
|
12
|
+
date: 2012-09-30 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: ! 'Validates code according to style guide rules for an organization
|
15
15
|
and outputs
|
@@ -24,14 +24,24 @@ extensions: []
|
|
24
24
|
extra_rdoc_files: []
|
25
25
|
files:
|
26
26
|
- .gitignore
|
27
|
+
- .rspec
|
27
28
|
- Gemfile
|
28
29
|
- Gemfile.lock
|
29
30
|
- README.md
|
31
|
+
- Rakefile
|
30
32
|
- bin/wegolint
|
31
|
-
- lib/init.rb
|
32
33
|
- lib/wegolint.rb
|
34
|
+
- lib/wegolint/dsl.rb
|
35
|
+
- lib/wegolint/rule.rb
|
36
|
+
- lib/wegolint/rule_registry.rb
|
37
|
+
- lib/wegolint/rules/general.rb
|
38
|
+
- lib/wegolint/rules/ruby.rb
|
39
|
+
- lib/wegolint/wegolinter.rb
|
40
|
+
- spec/rule_registry_spec.rb
|
41
|
+
- spec/rule_spec.rb
|
33
42
|
- spec/spec_helper.rb
|
34
|
-
- spec/
|
43
|
+
- spec/wegolint_bin_spec.rb
|
44
|
+
- spec/wegolinter_spec.rb
|
35
45
|
- wegolint.gemspec
|
36
46
|
homepage: ''
|
37
47
|
licenses: []
|
data/lib/init.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require 'wegolint'
|
data/spec/wegolint_spec.rb
DELETED
@@ -1,199 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require_relative '../lib/wegolint'
|
3
|
-
|
4
|
-
include WegoLint
|
5
|
-
|
6
|
-
describe WegoLinter do
|
7
|
-
let(:test_data_dir) { '/tmp/wegolint_spec'}
|
8
|
-
let(:wegolinter) { WegoLinter.new(file) }
|
9
|
-
|
10
|
-
subject { wegolinter.output }
|
11
|
-
|
12
|
-
before do
|
13
|
-
FileUtils.mkdir_p test_data_dir
|
14
|
-
File.open(file, 'w+') do |f|
|
15
|
-
f.write(code)
|
16
|
-
end
|
17
|
-
wegolinter.lint!
|
18
|
-
end
|
19
|
-
|
20
|
-
after do
|
21
|
-
File.unlink(file)
|
22
|
-
FileUtils.rmdir test_data_dir
|
23
|
-
end
|
24
|
-
|
25
|
-
describe "General errors" do
|
26
|
-
let(:file) { test_data_dir + '/foo.rb' }
|
27
|
-
|
28
|
-
context "hard tab" do
|
29
|
-
let(:code) { "\t" }
|
30
|
-
|
31
|
-
it { should_have_error "Hard tabs (use soft tabs)", 1, :ruby }
|
32
|
-
end
|
33
|
-
|
34
|
-
context "whitespace at end of line" do
|
35
|
-
let(:code) { "'foo' \n" }
|
36
|
-
|
37
|
-
it { should_have_error "Whitespace at end of line", 1, :ruby }
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
describe "Ruby errors" do
|
42
|
-
let(:file) { test_data_dir + '/foo.rb' }
|
43
|
-
|
44
|
-
context "ruby parser syntax errors" do
|
45
|
-
let(:code) do
|
46
|
-
<<-EOF
|
47
|
-
Foo
|
48
|
-
end
|
49
|
-
EOF
|
50
|
-
end
|
51
|
-
|
52
|
-
it { should_have_error "unexpected keyword_end, expecting $end",2, :ruby }
|
53
|
-
end
|
54
|
-
|
55
|
-
context "80 character limit" do
|
56
|
-
let(:code) do
|
57
|
-
<<-EOF
|
58
|
-
a = 1
|
59
|
-
"kjkjkjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk"
|
60
|
-
b = 3
|
61
|
-
EOF
|
62
|
-
end
|
63
|
-
|
64
|
-
it { should_have_error "More than eighty characters", 2, :ruby }
|
65
|
-
end
|
66
|
-
|
67
|
-
context "curly brace on its own line" do
|
68
|
-
let(:code) do
|
69
|
-
<<-EOF
|
70
|
-
q = 1
|
71
|
-
{
|
72
|
-
b = 1 }
|
73
|
-
c = a+b
|
74
|
-
EOF
|
75
|
-
end
|
76
|
-
|
77
|
-
it { should_have_error "Curly brace on its own line", 2, :ruby }
|
78
|
-
end
|
79
|
-
|
80
|
-
context "no space on inside of block" do
|
81
|
-
let(:code) do
|
82
|
-
<<-EOF
|
83
|
-
a = 1
|
84
|
-
['a','b','c'].each {|item| puts item }
|
85
|
-
['a','b','c'].each { |item| puts item}
|
86
|
-
b = 2
|
87
|
-
EOF
|
88
|
-
end
|
89
|
-
|
90
|
-
it { should_have_error "Space should be around block", 2, :ruby }
|
91
|
-
it { should_have_error "Space should be around block", 3, :ruby }
|
92
|
-
end
|
93
|
-
|
94
|
-
context "no spaces after (, [ or before ], )" do
|
95
|
-
let(:code) do
|
96
|
-
<<-EOF
|
97
|
-
b = [ 'a' ]
|
98
|
-
def something( foo )
|
99
|
-
c = " [ a ] "
|
100
|
-
c = ' [ ] '
|
101
|
-
[ 'a', 'b' ].each { |line| puts " [ ] " }
|
102
|
-
end
|
103
|
-
doSomething(bar: 'baz', baz: 'bar', foo: 'bar', fa: 'lalalalalalaalalalalalala'
|
104
|
-
)
|
105
|
-
EOF
|
106
|
-
end
|
107
|
-
|
108
|
-
it { should_have_error "No spaces after (", 1, :ruby }
|
109
|
-
it { should_have_error "No spaces after (", 2, :ruby }
|
110
|
-
|
111
|
-
context "when embedded in strings" do
|
112
|
-
it { should_not_have_error "No spaces after (", 3, :ruby }
|
113
|
-
it { should_not_have_error "No spaces after (", 4, :ruby }
|
114
|
-
it { should_have_error "No spaces after (", 5, :ruby }
|
115
|
-
it { should_not_have_error "No spaces after (", 8, :ruby }
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
context "No parentheses around function parameters" do
|
120
|
-
let(:code) do
|
121
|
-
<<-EOF
|
122
|
-
def something(foo)
|
123
|
-
end
|
124
|
-
def something foo, bar
|
125
|
-
end
|
126
|
-
def something
|
127
|
-
end
|
128
|
-
EOF
|
129
|
-
end
|
130
|
-
|
131
|
-
it { should_not_have_error "No parentheses around", 1, :ruby }
|
132
|
-
it { should_have_error "No parentheses around", 3, :ruby }
|
133
|
-
it { should_not_have_error "No parentheses around", 5, :ruby }
|
134
|
-
end
|
135
|
-
|
136
|
-
end
|
137
|
-
|
138
|
-
describe "Javascript errors" do
|
139
|
-
let(:file) { test_data_dir + '/foo.js' }
|
140
|
-
|
141
|
-
context "ruby parser syntax errors" do
|
142
|
-
let(:code) do
|
143
|
-
<<-EOF
|
144
|
-
fah {
|
145
|
-
}
|
146
|
-
EOF
|
147
|
-
end
|
148
|
-
|
149
|
-
it { should_have_error 'missing ; before statement', 1, :javascript }
|
150
|
-
end
|
151
|
-
|
152
|
-
context "80 character limit" do
|
153
|
-
let(:code) do
|
154
|
-
<<-EOF
|
155
|
-
var a = 1;
|
156
|
-
"kjkjkjkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk";
|
157
|
-
var b = 2;
|
158
|
-
EOF
|
159
|
-
end
|
160
|
-
|
161
|
-
it { should_have_error "More than eighty characters", 2, :javascript }
|
162
|
-
end
|
163
|
-
|
164
|
-
context "curly brace on its own line" do
|
165
|
-
let(:code) do
|
166
|
-
<<-EOF
|
167
|
-
var func = function()
|
168
|
-
{
|
169
|
-
var b = 1; }
|
170
|
-
EOF
|
171
|
-
end
|
172
|
-
|
173
|
-
it { should_have_error "Curly brace on its own line", 2, :javascript }
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
|
-
def should_have_error message, line_number, language
|
178
|
-
message = build_error_message message, line_number, language
|
179
|
-
subject.should include message
|
180
|
-
end
|
181
|
-
|
182
|
-
def should_not_have_error message, line_number, language
|
183
|
-
message = build_error_message message, line_number, language
|
184
|
-
subject.should_not include message
|
185
|
-
end
|
186
|
-
|
187
|
-
def build_error_message message, line_number, language
|
188
|
-
if language == :ruby
|
189
|
-
name = "foo.rb"
|
190
|
-
line_number = ":#{line_number}:"
|
191
|
-
prefix = " syntax error,"
|
192
|
-
else
|
193
|
-
name = "foo.js"
|
194
|
-
line_number = "(#{line_number}):"
|
195
|
-
prefix = " SyntaxError:"
|
196
|
-
end
|
197
|
-
"#{name}#{line_number}#{prefix} #{message}"
|
198
|
-
end
|
199
|
-
end
|