stylr 0.0.6
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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +37 -0
- data/LICENSE.txt +22 -0
- data/README.md +37 -0
- data/Rakefile +1 -0
- data/bin/stylr +33 -0
- data/lib/stylr/dir_loader.rb +16 -0
- data/lib/stylr/file_parser.rb +67 -0
- data/lib/stylr/lint.rb +129 -0
- data/lib/stylr/version.rb +3 -0
- data/lib/stylr.rb +8 -0
- data/spec/dir/one.rb +0 -0
- data/spec/dir/three.rb +0 -0
- data/spec/dir/two.rb +0 -0
- data/spec/dir_loader_spec.rb +26 -0
- data/spec/file_parser_spec.rb +83 -0
- data/spec/lint_spec.rb +313 -0
- data/spec/rdir/base.rb +0 -0
- data/spec/rdir/one/one.rb +0 -0
- data/spec/rdir/three/three.rb +0 -0
- data/spec/rdir/two/two.rb +0 -0
- data/spec/sdir/one_spec.rb +0 -0
- data/spec/txt/sample_1.rb +5 -0
- data/spec/txt/sample_fail.rb +5 -0
- data/spec/txt/sample_fail2.rb +9 -0
- data/spec/txt/sample_fail3.rb +9 -0
- data/spec/txt/sample_fail4.rb +15 -0
- data/spec/txt/sample_fail5.rb +5 -0
- data/spec/txt/sample_pass2.rb +9 -0
- data/stylr.gemspec +25 -0
- data/stylr.yml +21 -0
- metadata +151 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d24c550f19bdc73beb58ad28bc25ae1a10295d0b
|
4
|
+
data.tar.gz: c4eb18254875e21ac141c4fbaabf6488a82c9bbe
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: adc05ab742724d576381d07e498b8992fd4f0c34342d5e36ec5b0192e3c91bc61cf03975c07139000c38bb6d7d6fa0bf1b3e0f4fe76fb50813803b400c66eabb
|
7
|
+
data.tar.gz: e5502a2c101be36b9ecd344dec9da4ee4d4ba813f67e8374e1513f6e43988cc99305779a718236b4e207b41505be3fbf3ba704606c4463f9ffa1f34e0b088adf
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
stylr (0.0.4)
|
5
|
+
main (~> 5.2.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
arrayfields (4.9.0)
|
11
|
+
chronic (0.10.2)
|
12
|
+
diff-lcs (1.2.5)
|
13
|
+
fattr (2.2.1)
|
14
|
+
main (5.2.0)
|
15
|
+
arrayfields (>= 4.7.4)
|
16
|
+
chronic (>= 0.6.2)
|
17
|
+
fattr (>= 2.2.0)
|
18
|
+
map (>= 5.1.0)
|
19
|
+
map (6.5.1)
|
20
|
+
rake (10.1.1)
|
21
|
+
rspec (2.14.1)
|
22
|
+
rspec-core (~> 2.14.0)
|
23
|
+
rspec-expectations (~> 2.14.0)
|
24
|
+
rspec-mocks (~> 2.14.0)
|
25
|
+
rspec-core (2.14.7)
|
26
|
+
rspec-expectations (2.14.4)
|
27
|
+
diff-lcs (>= 1.1.3, < 2.0)
|
28
|
+
rspec-mocks (2.14.4)
|
29
|
+
|
30
|
+
PLATFORMS
|
31
|
+
ruby
|
32
|
+
|
33
|
+
DEPENDENCIES
|
34
|
+
bundler (~> 1.3)
|
35
|
+
rake
|
36
|
+
rspec
|
37
|
+
stylr!
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Mark Billie & ThoughtWorks, Inc.
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
<h1>stylr</h1>
|
2
|
+
|
3
|
+
Check if source code conforms to some elements of the Github Ruby Style Guidelines ( https://github.com/styleguide/ruby )
|
4
|
+
|
5
|
+
Kind of raw still. Currently supports checking against the following:
|
6
|
+
|
7
|
+
* Line length (user-configurable)
|
8
|
+
* Missing parens around method definitions with parameters (ie, "def foo bar" is disallowed)
|
9
|
+
* Trailing whitespace of any kind
|
10
|
+
* Use of the 'and' or 'or' operators (&& and || are preferred)
|
11
|
+
* Use of 'then' on a multiline if/then construct
|
12
|
+
* Spacing around parens
|
13
|
+
* Spacing around brackets (this is a little broken on multi-line arrays)
|
14
|
+
* Spacing around curly braces (this is also a little broken)
|
15
|
+
* Using the keyword 'for'
|
16
|
+
* Spacing around commas
|
17
|
+
* Using tab characters instead of soft tabs
|
18
|
+
* Spacing around math operators (+, *, etc)
|
19
|
+
|
20
|
+
Optionally checks for some metaprogramming, which you might not want in a large, enterprise codebase with varied levels of skill on your development team. This is not a condemnation of these practices - most of them are good, idiomatic Ruby. You might not, however, want your junior developers checking in lots of metaprogrammed code. Pass the '--meta' flag to enable these checks.
|
21
|
+
|
22
|
+
* eval
|
23
|
+
* class_eval
|
24
|
+
* instance_eval
|
25
|
+
* module_eval
|
26
|
+
* define_method
|
27
|
+
* send
|
28
|
+
|
29
|
+
All of these things are configurable via yml. See "stylr.yml" in the repo, and place it in "~/.stylr.yml"
|
30
|
+
|
31
|
+
Checks all *.rb files in the specified directory and subdirectories, excluding _spec.rb and _test.rb
|
32
|
+
|
33
|
+
<b>Usage</b>
|
34
|
+
|
35
|
+
./stylr /path/to/directory # normal checks
|
36
|
+
|
37
|
+
./stylr /path/to/directory --meta # also check for use of metaprogramming
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/stylr
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'main'
|
4
|
+
require 'stylr'
|
5
|
+
|
6
|
+
module Stylr
|
7
|
+
Main {
|
8
|
+
|
9
|
+
argument('directory') {
|
10
|
+
description "The directory to be parsed."
|
11
|
+
cast :string
|
12
|
+
}
|
13
|
+
|
14
|
+
option('meta') {
|
15
|
+
default false
|
16
|
+
description "Check for metaprogramming violations. Defaults to false."
|
17
|
+
}
|
18
|
+
|
19
|
+
def run
|
20
|
+
directory = params['directory'].values.first
|
21
|
+
meta = params['meta'].values.first ? true : false
|
22
|
+
dir_loader = DirLoader.new
|
23
|
+
dir_loader.load_dir(directory)
|
24
|
+
|
25
|
+
dir_loader.filenames.each do |filename|
|
26
|
+
puts "Checking #{filename}..."
|
27
|
+
FileParser.new(filename, Lint.new, true).violations?(meta)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
class DirLoader
|
2
|
+
attr_reader :filenames
|
3
|
+
|
4
|
+
DEPTH = "**/**/**/**/**/**/**/**/**/**/**/**" # lol wut
|
5
|
+
|
6
|
+
def load_dir(dir)
|
7
|
+
@filenames = Dir[File.expand_path(dir) + DEPTH].select do |f|
|
8
|
+
File.file?(f) &&
|
9
|
+
f =~ /\.rb$/
|
10
|
+
end.reject do |f|
|
11
|
+
f =~ /_spec\.rb$/ || f =~ /_test\.rb$/
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Stylr
|
2
|
+
class FileParser
|
3
|
+
attr_reader :lines
|
4
|
+
|
5
|
+
UniqueConstant = ";;;;;"
|
6
|
+
|
7
|
+
def initialize(file_path, lint, display = false)
|
8
|
+
@lines = []
|
9
|
+
@lint = lint
|
10
|
+
@display = display
|
11
|
+
@file_string = File.read(file_path)
|
12
|
+
end
|
13
|
+
|
14
|
+
def violations?(meta = false)
|
15
|
+
pre_multiline_string_removal_length_check!
|
16
|
+
remove_multiline_strings!
|
17
|
+
assign_lines_with_numbers!
|
18
|
+
|
19
|
+
@lines.each do |array|
|
20
|
+
line, number = array
|
21
|
+
@lint.violation?(line, number)
|
22
|
+
@lint.exception_violation?(line, number)
|
23
|
+
@lint.meta_violation?(line, number) if meta
|
24
|
+
end
|
25
|
+
|
26
|
+
if @lint.errors.any?
|
27
|
+
display_errors if @display
|
28
|
+
true
|
29
|
+
else
|
30
|
+
display_no_error_message if @display
|
31
|
+
false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def assign_lines_with_numbers!
|
36
|
+
@file_string.each_with_index do |line, number|
|
37
|
+
@lines << [line, number + 1]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def pre_multiline_string_removal_length_check!
|
42
|
+
@file_string.split(/\n/).each_with_index do |line, number|
|
43
|
+
@lint.line_too_long_violation?(line, number + 1)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def remove_multiline_strings!
|
48
|
+
@file_string = @file_string.gsub(/\n/, UniqueConstant)
|
49
|
+
@file_string = @lint.strip_multiline_strings(@file_string)
|
50
|
+
@file_string = @file_string.gsub(/#{UniqueConstant}/, "#{UniqueConstant}\n")
|
51
|
+
@file_string = @file_string.split(/#{UniqueConstant}/)
|
52
|
+
end
|
53
|
+
|
54
|
+
def display_no_error_message
|
55
|
+
puts "Your file is free of errors."
|
56
|
+
end
|
57
|
+
|
58
|
+
def display_errors
|
59
|
+
puts "You have the following errors:"
|
60
|
+
@lint.errors.each do |hash|
|
61
|
+
error = hash.keys.first
|
62
|
+
line_number = hash.values.first
|
63
|
+
puts "Line #{line_number}: #{@lint.send(error)}"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/stylr/lint.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
class Lint
|
4
|
+
attr_reader :errors, :violations, :exception_violations, :metaprogramming_violations, :messages
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@errors = []
|
8
|
+
@config = YAML.load_file(ENV["HOME"] + "/.stylr.yml")
|
9
|
+
setup_class_values
|
10
|
+
end
|
11
|
+
|
12
|
+
def line_too_long_violation?(line, number = 1)
|
13
|
+
abstract_violation?(@line_too_long_violations, line, number)
|
14
|
+
end
|
15
|
+
|
16
|
+
def violation?(line, number = 1)
|
17
|
+
line = strip_strings(line)
|
18
|
+
abstract_violation?(@violations, line, number)
|
19
|
+
end
|
20
|
+
|
21
|
+
def meta_violation?(line, number = 1)
|
22
|
+
abstract_violation?(@metaprogramming_violations, line, number)
|
23
|
+
end
|
24
|
+
|
25
|
+
def exception_violation?(line, number = 1)
|
26
|
+
abstract_violation?(@exception_violations, line, number)
|
27
|
+
end
|
28
|
+
|
29
|
+
def strip_multiline_strings(string)
|
30
|
+
string.tap do |str|
|
31
|
+
str.gsub!(/""".*"""/, '""')
|
32
|
+
start = /<<-?[A-Z]+/
|
33
|
+
finish = (str[start] || "")[/[A-Z]+/]
|
34
|
+
regexp = /#{start}.*\b#{finish}\b/
|
35
|
+
str.gsub!(/#{str[regexp]}/,'""') if str[regexp]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def strip_strings(line)
|
40
|
+
line.gsub(/".*"/, '""').gsub(/'.*'/, "''")
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def abstract_violation?(list, line, number)
|
46
|
+
list.each do |pattern, error|
|
47
|
+
@commented_line.each do |comment|
|
48
|
+
return false if line[comment]
|
49
|
+
end
|
50
|
+
|
51
|
+
if line[pattern]
|
52
|
+
@errors << { error => number }
|
53
|
+
return true
|
54
|
+
end
|
55
|
+
end
|
56
|
+
return false
|
57
|
+
end
|
58
|
+
|
59
|
+
def setup_class_values
|
60
|
+
@line_too_long_violations = {
|
61
|
+
/.{#{@config["line_length"]}}+/ => :line_too_long
|
62
|
+
}.delete_if { |_, v| !@config[v.to_s] }
|
63
|
+
|
64
|
+
@violations = {
|
65
|
+
/def (self\.)?\w+[!?]? .\w+/ => :missing_parens,
|
66
|
+
/( )+$/ => :trailing_whitespace,
|
67
|
+
/\band\b/ => :the_word_and,
|
68
|
+
/\bor\b/ => :the_word_or,
|
69
|
+
/\bfor\b/ => :the_word_for,
|
70
|
+
/\bif\b.*\bthen\b\n/ => :multiline_if_then,
|
71
|
+
/\(\s|\s\)/ => :paren_spacing,
|
72
|
+
/\[\s|\s\]/ => :bracket_spacing,
|
73
|
+
/[^\s][{}]|{[^\s]/ => :brace_spacing,
|
74
|
+
/,[^ \n]/ => :comma_spacing,
|
75
|
+
/\t/ => :no_soft_tabs,
|
76
|
+
/[^\s]\+/ => :no_operator_spaces,
|
77
|
+
/\+[^\s=]/ => :no_operator_spaces,
|
78
|
+
/[^\s]-/ => :no_operator_spaces
|
79
|
+
}.delete_if { |_, v| !@config[v.to_s] }
|
80
|
+
|
81
|
+
@exception_violations = {
|
82
|
+
/rescue\s*(Exception)?$/ => :rescue_class_exception
|
83
|
+
}.delete_if { |_, v| !@config[v.to_s] }
|
84
|
+
|
85
|
+
@metaprogramming_violations = {
|
86
|
+
/\beval\b/ => :used_eval,
|
87
|
+
/\bclass_eval\b/ => :used_class_eval,
|
88
|
+
/\bmodule_eval\b/ => :used_module_eval,
|
89
|
+
/\binstance_eval\b/ => :used_instance_eval,
|
90
|
+
/\bdefine_method\b/ => :used_define_method,
|
91
|
+
/\w+\.send.*".*#\{/ => :dynamic_invocation
|
92
|
+
}.delete_if { |_, v| !@config[v.to_s] }
|
93
|
+
|
94
|
+
@commented_line = [
|
95
|
+
/^\s*#/
|
96
|
+
]
|
97
|
+
|
98
|
+
@messages = {
|
99
|
+
:missing_parens => "You have omitted parentheses from a method definition with parameters.",
|
100
|
+
:line_too_long => "Line length of #{@config['line_length']} characters or more.",
|
101
|
+
:trailing_whitespace => "Trailing whitespace.",
|
102
|
+
:used_eval => "Used eval.",
|
103
|
+
:used_define_method => "Used define_method.",
|
104
|
+
:dynamic_invocation => "Dynamic invocation of a method.",
|
105
|
+
:rescue_class_exception => "Rescuing class Exception.",
|
106
|
+
:the_word_and => "Used 'and'; please use && instead.",
|
107
|
+
:the_word_or => "Used 'or'; please use || instead.",
|
108
|
+
:the_word_for => "Used 'for'; please use an enumerator, or else explain yourself adequately to the team.",
|
109
|
+
:multiline_if_then => "Used 'then' on a multiline if statement.",
|
110
|
+
:used_class_eval => "Used class_eval.",
|
111
|
+
:used_module_eval => "Used module_eval.",
|
112
|
+
:used_instance_eval => "Used instance_eval.",
|
113
|
+
:paren_spacing => "Space after ( or before ).",
|
114
|
+
:bracket_spacing => "Space after [ or before ].",
|
115
|
+
:brace_spacing => "No space around { or before }.",
|
116
|
+
:comma_spacing => "No space after a comma.",
|
117
|
+
:no_soft_tabs => "Used tab characters; please use soft tabs.",
|
118
|
+
:no_operator_spaces => "Please use spaces around operators."
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
def method_missing(method_name, *args, &block)
|
123
|
+
if @messages.keys.include?(method_name.to_sym)
|
124
|
+
@messages[method_name]
|
125
|
+
else
|
126
|
+
super(method_name, *args, &block)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
data/lib/stylr.rb
ADDED
data/spec/dir/one.rb
ADDED
File without changes
|
data/spec/dir/three.rb
ADDED
File without changes
|
data/spec/dir/two.rb
ADDED
File without changes
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "stylr"
|
2
|
+
|
3
|
+
module Stylr
|
4
|
+
describe DirLoader do
|
5
|
+
let(:d) { DirLoader.new }
|
6
|
+
|
7
|
+
it "loads all ruby files in a directory" do
|
8
|
+
dir = "/Users/Thoughtworker/stylr/spec/dir"
|
9
|
+
d.load_dir(dir)
|
10
|
+
d.filenames.should == ["#{dir}/one.rb", "#{dir}/two.rb", "#{dir}/three.rb"].sort
|
11
|
+
end
|
12
|
+
|
13
|
+
it "loads all files even in a recursive structure" do
|
14
|
+
dir = "/Users/Thoughtworker/stylr/spec/rdir"
|
15
|
+
d.load_dir(dir)
|
16
|
+
d.filenames.should == ["#{dir}/one/one.rb", "#{dir}/two/two.rb", "#{dir}/three/three.rb", "#{dir}/base.rb"].sort
|
17
|
+
end
|
18
|
+
|
19
|
+
it "ignores specs" do
|
20
|
+
dir = "/Users/Thoughtworker/stylr/spec/sdir"
|
21
|
+
d.load_dir(dir)
|
22
|
+
d.filenames.should == []
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require "stylr"
|
2
|
+
|
3
|
+
module Stylr
|
4
|
+
YAML_FILE_LOCATION = "/Users/Thoughtworker/stylr/stylr.yml"
|
5
|
+
describe FileParser do
|
6
|
+
context "sample_1" do
|
7
|
+
before(:each) do
|
8
|
+
@string = "/Users/Thoughtworker/stylr/spec/txt/sample_1.rb"
|
9
|
+
@f = FileParser.new(@string, Lint.new)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "checks the file for errors" do
|
13
|
+
@f.violations?.should be_false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context "sample_fail" do
|
18
|
+
before(:each) do
|
19
|
+
@string = "/Users/Thoughtworker/stylr/spec/txt/sample_fail.rb"
|
20
|
+
@f = FileParser.new(@string, Lint.new)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "knows when something has failed" do
|
24
|
+
@f.violations?.should be_true
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "sample_pass2" do
|
29
|
+
before(:each) do
|
30
|
+
@string = "/Users/Thoughtworker/stylr/spec/txt/sample_pass2.rb"
|
31
|
+
@f = FileParser.new(@string, Lint.new)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "doesn't break when shit appears in strings" do
|
35
|
+
@f.violations?.should be_false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "sample_fail2" do
|
40
|
+
before(:each) do
|
41
|
+
@string = "/Users/Thoughtworker/stylr/spec/txt/sample_fail2.rb"
|
42
|
+
@f = FileParser.new(@string, Lint.new)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "knows when something has failed" do
|
46
|
+
@f.violations?(meta = true).should be_true
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "sample_fail3" do
|
51
|
+
before(:each) do
|
52
|
+
@string = "/Users/Thoughtworker/stylr/spec/txt/sample_fail3.rb"
|
53
|
+
@f = FileParser.new(@string, Lint.new)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "knows when something has failed" do
|
57
|
+
@f.violations?.should be_true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
context "sample_fail4" do
|
62
|
+
before(:each) do
|
63
|
+
@string = "/Users/Thoughtworker/stylr/spec/txt/sample_fail4.rb"
|
64
|
+
@f = FileParser.new(@string, Lint.new)
|
65
|
+
end
|
66
|
+
|
67
|
+
it "knows when something has failed" do
|
68
|
+
@f.violations?.should be_false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
context "sample_fail5" do
|
73
|
+
before(:each) do
|
74
|
+
@string = "/Users/Thoughtworker/stylr/spec/txt/sample_fail5.rb"
|
75
|
+
@f = FileParser.new(@string, Lint.new)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "knows when something has failed" do
|
79
|
+
@f.violations?.should be_true
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/spec/lint_spec.rb
ADDED
@@ -0,0 +1,313 @@
|
|
1
|
+
require "stylr"
|
2
|
+
|
3
|
+
module Stylr
|
4
|
+
describe Lint do
|
5
|
+
let(:l) { Lint.new }
|
6
|
+
|
7
|
+
context "#violation?" do
|
8
|
+
context "missing_parens" do
|
9
|
+
it "wants parens around method definitions with args" do
|
10
|
+
l.violation?("def foo bar, baz").should be_true
|
11
|
+
end
|
12
|
+
|
13
|
+
it "does not need parens for methods with no args" do
|
14
|
+
l.violation?("def some_method").should be_false
|
15
|
+
end
|
16
|
+
|
17
|
+
it "using parens is good" do
|
18
|
+
l.violation?("def foo(bar, baz)").should be_false
|
19
|
+
end
|
20
|
+
|
21
|
+
it "using parens for methods with bang is the same" do
|
22
|
+
l.violation?("def foo!").should be_false
|
23
|
+
end
|
24
|
+
|
25
|
+
it "a bang method with args needs parens too" do
|
26
|
+
l.violation?("def foo! bar").should be_true
|
27
|
+
end
|
28
|
+
|
29
|
+
it "a question mark method without args is fine" do
|
30
|
+
l.violation?("def foo?").should be_false
|
31
|
+
end
|
32
|
+
|
33
|
+
it "a question mark method with args needs parens" do
|
34
|
+
l.violation?("def foo? bar").should be_true
|
35
|
+
end
|
36
|
+
|
37
|
+
it "class methods with bang+no args are fine" do
|
38
|
+
l.violation?("def self.foo!").should be_false
|
39
|
+
end
|
40
|
+
|
41
|
+
it "class methods with bang+args need parens" do
|
42
|
+
l.violation?("def self.foo! bar").should be_true
|
43
|
+
end
|
44
|
+
|
45
|
+
it "class methods with qmark+no args are fine" do
|
46
|
+
l.violation?("def self.foo?").should be_false
|
47
|
+
end
|
48
|
+
|
49
|
+
it "class methods with qmark+args need parens" do
|
50
|
+
l.violation?("def self.foo? bar").should be_true
|
51
|
+
end
|
52
|
+
|
53
|
+
it "positive case for bang" do
|
54
|
+
l.violation?("def foo!(bar)").should be_false
|
55
|
+
end
|
56
|
+
|
57
|
+
it "positive case for class method bang" do
|
58
|
+
l.violation?("def self.foo!(bar)").should be_false
|
59
|
+
end
|
60
|
+
|
61
|
+
it "positive case for qmark" do
|
62
|
+
l.violation?("def foo?(bar)").should be_false
|
63
|
+
end
|
64
|
+
|
65
|
+
it "positive case for class method qmark" do
|
66
|
+
l.violation?("def self.foo?(bar)").should be_false
|
67
|
+
end
|
68
|
+
|
69
|
+
it "acts the same for class methods" do
|
70
|
+
l.violation?("def self.something foo, bar").should be_true
|
71
|
+
end
|
72
|
+
|
73
|
+
it "no parens for class methods without args is fine too" do
|
74
|
+
l.violation?("def self.something").should be_false
|
75
|
+
end
|
76
|
+
|
77
|
+
it "arrays of args too need parens in the method def" do
|
78
|
+
l.violation?("def foo *args").should be_true
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context "operator spacing" do
|
83
|
+
it "spaces around + are good" do
|
84
|
+
l.violation?("2+2").should be_true
|
85
|
+
end
|
86
|
+
|
87
|
+
it "on both sides" do
|
88
|
+
l.violation?("2 +2").should be_true
|
89
|
+
end
|
90
|
+
|
91
|
+
it "but += is ok" do
|
92
|
+
l.violation?("x += 2").should be_false
|
93
|
+
end
|
94
|
+
|
95
|
+
it "same with -" do
|
96
|
+
l.violation?("2-2").should be_true
|
97
|
+
end
|
98
|
+
|
99
|
+
it "-= is fine" do
|
100
|
+
l.violation?("x -= 3").should be_false
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
context "soft tabs" do
|
105
|
+
it "don't use actual tabs" do
|
106
|
+
l.violation?("\t").should be_true
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
context "space after open paren/bracket or before close paren/bracket" do
|
111
|
+
it "space after open paren is bad" do
|
112
|
+
l.violation?("def foo( bar)").should be_true
|
113
|
+
end
|
114
|
+
|
115
|
+
it "space before close paren is also bad" do
|
116
|
+
l.violation?("def foo(bar )").should be_true
|
117
|
+
end
|
118
|
+
|
119
|
+
it "space after open bracket is bad" do
|
120
|
+
l.violation?("list = [ 1, 2, 3]").should be_true
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
context "space around { and } is good" do
|
125
|
+
it "no space before { is bad" do
|
126
|
+
l.violation?("lambda{puts :foo}").should be_true
|
127
|
+
end
|
128
|
+
|
129
|
+
it "no space before } is bad" do
|
130
|
+
l.violation?("lambda { puts :foo}").should be_true
|
131
|
+
end
|
132
|
+
|
133
|
+
it "no space after { is also bad" do
|
134
|
+
l.violation?("{:foo }").should be_true
|
135
|
+
end
|
136
|
+
|
137
|
+
it "no space after } is fine" do
|
138
|
+
l.violation?("lambda { puts :foo }.call").should be_false
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
context "space after commas" do
|
143
|
+
it "should have a space after a comma" do
|
144
|
+
l.violation?("[1,2,3]").should be_true
|
145
|
+
end
|
146
|
+
|
147
|
+
it "can also pass..." do
|
148
|
+
l.violation?("[1, 2, 3]").should be_false
|
149
|
+
end
|
150
|
+
|
151
|
+
it "newline after a comma is fine" do
|
152
|
+
l.violation?(":a => 1,\n").should be_false
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
context "line_too_long" do
|
157
|
+
it "dislikes lines of >= 80 chars" do
|
158
|
+
l.line_too_long_violation?("#{'a' * 80}").should be_true
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context "trailing_whitespace" do
|
163
|
+
it "whitespace at the end of the line is a no-no" do
|
164
|
+
l.violation?("def foo(bar) ").should be_true
|
165
|
+
end
|
166
|
+
|
167
|
+
it "a newline is ok by itself" do
|
168
|
+
l.violation?("\n").should be_false
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
context "and and or" do
|
173
|
+
it "the word 'and' is banned" do
|
174
|
+
l.violation?("foo and bar").should be_true
|
175
|
+
end
|
176
|
+
|
177
|
+
it "the word 'or' is also banned" do
|
178
|
+
l.violation?("1 or nil").should be_true
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
context "for loops" do
|
183
|
+
it "generally you should not use for loops" do
|
184
|
+
l.violation?("for x in list do").should be_true
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
context "multiline if then" do
|
189
|
+
it "should not use then in multiline if" do
|
190
|
+
l.violation?("if foo then\nbar\nend").should be_true
|
191
|
+
end
|
192
|
+
|
193
|
+
it "single line if then end is ok" do
|
194
|
+
l.violation?("if true then false end").should be_false
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
context "multiline strings" do
|
199
|
+
it "ignores multiline strings" do
|
200
|
+
l.strip_multiline_strings("sql = <<-SQL for and or SQL").should == "sql = \"\""
|
201
|
+
end
|
202
|
+
|
203
|
+
it "ignores the other kind too" do
|
204
|
+
l.strip_multiline_strings('"""foo bar baz"""').should == '""'
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
context "comments" do
|
209
|
+
it "ignores comments" do
|
210
|
+
l.violation?("# a comment").should be_false
|
211
|
+
end
|
212
|
+
|
213
|
+
it "ignores comments even when they otherwise violate the rules" do
|
214
|
+
l.violation?("# def foo bar").should be_false
|
215
|
+
end
|
216
|
+
|
217
|
+
it "does not care what violation the comment breaks" do
|
218
|
+
l.violation?("# and or def foo bar, baz").should be_false
|
219
|
+
end
|
220
|
+
|
221
|
+
it "ignores indented comments" do
|
222
|
+
l.violation?(" # def foo bar").should be_false
|
223
|
+
end
|
224
|
+
|
225
|
+
it "ignores comments indented with soft tabs" do
|
226
|
+
l.violation?(" # def foo bar").should be_false
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context "#meta_violation?" do
|
232
|
+
it "can optionally check for metaprogramming" do
|
233
|
+
l.meta_violation?("eval").should be_true
|
234
|
+
end
|
235
|
+
|
236
|
+
it "fucking HATES define_method" do
|
237
|
+
l.meta_violation?("define_method").should be_true
|
238
|
+
end
|
239
|
+
|
240
|
+
it "dynamic method invocation via send is bad, mmkay?" do
|
241
|
+
l.meta_violation?('foo.send "foo_#{:bar}"').should be_true
|
242
|
+
end
|
243
|
+
|
244
|
+
it "class eval, why not?" do
|
245
|
+
l.meta_violation?("class_eval &block").should be_true
|
246
|
+
end
|
247
|
+
|
248
|
+
it "module eval is also naughty" do
|
249
|
+
l.meta_violation?("module_eval foo").should be_true
|
250
|
+
end
|
251
|
+
|
252
|
+
it "and even instance eval!" do
|
253
|
+
l.meta_violation?("instance_eval &block").should be_true
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
context "#exception_violation?" do
|
258
|
+
it "rescuing Exception is bad" do
|
259
|
+
l.exception_violation?("rescue Exception").should be_true
|
260
|
+
end
|
261
|
+
|
262
|
+
it "rescuing nothing is the same as rescuing Exception" do
|
263
|
+
l.exception_violation?("rescue ").should be_true
|
264
|
+
end
|
265
|
+
|
266
|
+
it "rescuing an exception that ends with Exception is ok" do
|
267
|
+
l.violation?("rescue MyCustomException").should be_false
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
context "string stripping" do
|
272
|
+
it "removes strings so it doesn't break on them" do
|
273
|
+
l.strip_strings("def foo(\"bar\")").should == "def foo(\"\")"
|
274
|
+
end
|
275
|
+
|
276
|
+
it "does the same with single-quoted strings" do
|
277
|
+
l.strip_strings("def foo('bar')").should == "def foo('')"
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
context "error messages" do
|
282
|
+
it "keeps track of your mistakes and their lines, defaulting to line #1" do
|
283
|
+
l.violation?("def foo bar")
|
284
|
+
l.errors.first.values.should == [1]
|
285
|
+
end
|
286
|
+
|
287
|
+
it "knows what line number you fucked up on" do
|
288
|
+
l.violation?("def foo bar", 3)
|
289
|
+
l.errors.first.values.should == [3]
|
290
|
+
end
|
291
|
+
|
292
|
+
it "everything has an error message" do
|
293
|
+
expect do
|
294
|
+
l.violations.values.each do |violation|
|
295
|
+
l.messages.fetch(violation)
|
296
|
+
end
|
297
|
+
l.exception_violations.values.each do |exception_violation|
|
298
|
+
l.messages.fetch(exception_violation)
|
299
|
+
end
|
300
|
+
l.metaprogramming_violations.values.each do |metaprogramming_violation|
|
301
|
+
l.messages.fetch(metaprogramming_violation)
|
302
|
+
end
|
303
|
+
end.to_not raise_error
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
context "#method_missing" do
|
308
|
+
it "implements method_missing intelligently" do
|
309
|
+
expect { l.foo }.to raise_error
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
data/spec/rdir/base.rb
ADDED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/stylr.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'stylr/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "stylr"
|
8
|
+
spec.version = Stylr::VERSION
|
9
|
+
spec.authors = ["Mark Billie"]
|
10
|
+
spec.email = ["mbillie1@gmail.com"]
|
11
|
+
spec.description = %q{An attempt at enforcing https://github.com/styleguide/ruby}
|
12
|
+
spec.summary = %q{stylr - enforcing Ruby coding style standards}
|
13
|
+
spec.homepage = "https://github.com/yaaase/stylr.git"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency "main", "~> 5.2.0"
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
end
|
data/stylr.yml
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
line_length: 80
|
2
|
+
line_too_long: true
|
3
|
+
missing_parens: true
|
4
|
+
trailing_whitespace: true
|
5
|
+
the_word_and: true
|
6
|
+
the_word_or: true
|
7
|
+
the_word_for: true
|
8
|
+
multiline_if_then: true
|
9
|
+
paren_spacing: true
|
10
|
+
bracket_spacing: true
|
11
|
+
brace_spacing: true
|
12
|
+
comma_spacing: true
|
13
|
+
no_soft_tabs: true
|
14
|
+
no_operator_spaces: true
|
15
|
+
rescue_class_exception: true
|
16
|
+
used_eval: true
|
17
|
+
used_class_eval: true
|
18
|
+
used_module_eval: true
|
19
|
+
used_instance_eval: true
|
20
|
+
used_define_method: true
|
21
|
+
dynamic_invocation: true
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: stylr
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.6
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mark Billie
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-12-20 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: main
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.3'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: An attempt at enforcing https://github.com/styleguide/ruby
|
70
|
+
email:
|
71
|
+
- mbillie1@gmail.com
|
72
|
+
executables:
|
73
|
+
- stylr
|
74
|
+
extensions: []
|
75
|
+
extra_rdoc_files: []
|
76
|
+
files:
|
77
|
+
- .gitignore
|
78
|
+
- Gemfile
|
79
|
+
- Gemfile.lock
|
80
|
+
- LICENSE.txt
|
81
|
+
- README.md
|
82
|
+
- Rakefile
|
83
|
+
- bin/stylr
|
84
|
+
- lib/stylr.rb
|
85
|
+
- lib/stylr/dir_loader.rb
|
86
|
+
- lib/stylr/file_parser.rb
|
87
|
+
- lib/stylr/lint.rb
|
88
|
+
- lib/stylr/version.rb
|
89
|
+
- spec/dir/one.rb
|
90
|
+
- spec/dir/three.rb
|
91
|
+
- spec/dir/two.rb
|
92
|
+
- spec/dir_loader_spec.rb
|
93
|
+
- spec/file_parser_spec.rb
|
94
|
+
- spec/lint_spec.rb
|
95
|
+
- spec/rdir/base.rb
|
96
|
+
- spec/rdir/one/one.rb
|
97
|
+
- spec/rdir/three/three.rb
|
98
|
+
- spec/rdir/two/two.rb
|
99
|
+
- spec/sdir/one_spec.rb
|
100
|
+
- spec/txt/sample_1.rb
|
101
|
+
- spec/txt/sample_fail.rb
|
102
|
+
- spec/txt/sample_fail2.rb
|
103
|
+
- spec/txt/sample_fail3.rb
|
104
|
+
- spec/txt/sample_fail4.rb
|
105
|
+
- spec/txt/sample_fail5.rb
|
106
|
+
- spec/txt/sample_pass2.rb
|
107
|
+
- stylr.gemspec
|
108
|
+
- stylr.yml
|
109
|
+
homepage: https://github.com/yaaase/stylr.git
|
110
|
+
licenses:
|
111
|
+
- MIT
|
112
|
+
metadata: {}
|
113
|
+
post_install_message:
|
114
|
+
rdoc_options: []
|
115
|
+
require_paths:
|
116
|
+
- lib
|
117
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
118
|
+
requirements:
|
119
|
+
- - '>='
|
120
|
+
- !ruby/object:Gem::Version
|
121
|
+
version: '0'
|
122
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
123
|
+
requirements:
|
124
|
+
- - '>='
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
version: '0'
|
127
|
+
requirements: []
|
128
|
+
rubyforge_project:
|
129
|
+
rubygems_version: 2.0.6
|
130
|
+
signing_key:
|
131
|
+
specification_version: 4
|
132
|
+
summary: stylr - enforcing Ruby coding style standards
|
133
|
+
test_files:
|
134
|
+
- spec/dir/one.rb
|
135
|
+
- spec/dir/three.rb
|
136
|
+
- spec/dir/two.rb
|
137
|
+
- spec/dir_loader_spec.rb
|
138
|
+
- spec/file_parser_spec.rb
|
139
|
+
- spec/lint_spec.rb
|
140
|
+
- spec/rdir/base.rb
|
141
|
+
- spec/rdir/one/one.rb
|
142
|
+
- spec/rdir/three/three.rb
|
143
|
+
- spec/rdir/two/two.rb
|
144
|
+
- spec/sdir/one_spec.rb
|
145
|
+
- spec/txt/sample_1.rb
|
146
|
+
- spec/txt/sample_fail.rb
|
147
|
+
- spec/txt/sample_fail2.rb
|
148
|
+
- spec/txt/sample_fail3.rb
|
149
|
+
- spec/txt/sample_fail4.rb
|
150
|
+
- spec/txt/sample_fail5.rb
|
151
|
+
- spec/txt/sample_pass2.rb
|