ruumba 0.1.6 → 0.1.7
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 +4 -4
- data/README.md +1 -1
- data/bin/ruumba +27 -2
- data/lib/ruumba/analyzer.rb +43 -108
- data/lib/ruumba/iterators.rb +70 -0
- data/lib/ruumba/parser.rb +73 -0
- data/lib/ruumba/rake_task.rb +1 -1
- data/lib/ruumba/rubocop_runner.rb +58 -0
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 150e5c1981114be3f11e8f327ab80896264241e5
|
4
|
+
data.tar.gz: a043ba70111e3c987b6586f32424adac80ba0e26
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b6657ec72b0e1c33625f39a890ff029281d1d20130ec8da9b346c19b8511ff0063d5eb37edb7040f1df67d13c64431ef11860b358b3b7917dabb71e8673746e
|
7
|
+
data.tar.gz: 27fa8094870b988a99a35409c55d0cc3fd2e20b60190a9aff43119d2d023b3f7d29d9b1b31fb3b148daec7ff704b04fef740cd2f70f164852225f81cee4a28a8
|
data/README.md
CHANGED
data/bin/ruumba
CHANGED
@@ -17,6 +17,15 @@ opts_parser = OptionParser.new do |opts|
|
|
17
17
|
options[:arguments] << '--auto-gen-config'
|
18
18
|
end
|
19
19
|
|
20
|
+
opts.on('--exclude-limit COUNT', 'Used together with --auto-gen-config to set the limit for how many Exclude properties to generate. Default is 15.') do |count|
|
21
|
+
options[:arguments] << '--exclude-limit'
|
22
|
+
options[:arguments] << count
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on('--no-offense-counts', 'Do not include offense counts in configuration file generated by --auto-gen-config.') do
|
26
|
+
options[:arguments] << '--no-offense-counts'
|
27
|
+
end
|
28
|
+
|
20
29
|
opts.on('-t', '--tmp-folder [TEMP_FOLDER]', 'Use this suffix for the temporary folder') do |f|
|
21
30
|
options[:tmp_folder] = f
|
22
31
|
end
|
@@ -31,11 +40,11 @@ opts_parser = OptionParser.new do |opts|
|
|
31
40
|
options[:arguments] << flag
|
32
41
|
end
|
33
42
|
|
34
|
-
opts.on('-d', '--debug', 'Display debug info.') do
|
43
|
+
opts.on('-d', '--debug', 'Display debug info.') do
|
35
44
|
options[:arguments] << '--debug'
|
36
45
|
end
|
37
46
|
|
38
|
-
opts.on('-D', '--display-cop-names', 'Display cop names') do
|
47
|
+
opts.on('-D', '--display-cop-names', 'Display cop names') do
|
39
48
|
options[:arguments] << '--display-cop-names'
|
40
49
|
end
|
41
50
|
|
@@ -71,6 +80,22 @@ opts_parser = OptionParser.new do |opts|
|
|
71
80
|
options[:arguments] << '--format'
|
72
81
|
options[:arguments] << format
|
73
82
|
end
|
83
|
+
|
84
|
+
opts.on('-s', '--stdin [FILE]', 'Pipe source from STDIN, using FILE in offense reports. This is useful for editor integration') do |file|
|
85
|
+
options[:stdin] = file
|
86
|
+
end
|
87
|
+
|
88
|
+
opts.on('--force-exclusion', 'Force excluding files specified in the configuration `Exclude` even if they are explicitly passed as arguments.') do
|
89
|
+
options[:arguments] << '--force-exclusion'
|
90
|
+
end
|
91
|
+
|
92
|
+
opts.on('-P', '--parallel', 'Use available CPUs to execute inspection in parallel.') do
|
93
|
+
options[:arguments] << '--parallel'
|
94
|
+
end
|
95
|
+
|
96
|
+
opts.on('-L', '--list-target-files', 'List all files Ruumba will inspect.') do
|
97
|
+
options[:arguments] << '--list-target-files'
|
98
|
+
end
|
74
99
|
end
|
75
100
|
|
76
101
|
begin
|
data/lib/ruumba/analyzer.rb
CHANGED
@@ -6,14 +6,15 @@ require 'tmpdir'
|
|
6
6
|
require 'open3'
|
7
7
|
require 'English'
|
8
8
|
|
9
|
+
require 'ruumba/iterators'
|
10
|
+
require 'ruumba/parser'
|
11
|
+
require 'ruumba/rubocop_runner'
|
12
|
+
|
9
13
|
# Ruumba: RuboCop's sidekick.
|
10
14
|
module Ruumba
|
11
15
|
# Generates analyzer objects that, when run, delegate
|
12
16
|
# to RuboCop for linting (style, correctness, &c).
|
13
17
|
class Analyzer
|
14
|
-
# The regular expression to capture interpolated Ruby.
|
15
|
-
ERB_REGEX = /<%[-=]?(.*?)-?%>/m
|
16
|
-
|
17
18
|
def initialize(opts = nil)
|
18
19
|
@options = opts || {}
|
19
20
|
end
|
@@ -21,143 +22,77 @@ module Ruumba
|
|
21
22
|
# Performs static analysis on the provided directory.
|
22
23
|
# @param [Array<String>] dir The directories / files to analyze.
|
23
24
|
def run(files_or_dirs = ARGV)
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
target = '.'
|
32
|
-
run_rubocop(target, tmp)
|
33
|
-
end
|
34
|
-
|
35
|
-
# Extracts Ruby code from an ERB template.
|
36
|
-
# @param [String] filename The filename.
|
37
|
-
# @return [String] The extracted Ruby code.
|
38
|
-
def extract(filename)
|
39
|
-
file_text, matches = parse_file(filename)
|
40
|
-
|
41
|
-
extracted_ruby = ''
|
42
|
-
|
43
|
-
last_match = [0, 0]
|
44
|
-
matches.each do |start_index, end_index|
|
45
|
-
handled_region_before(start_index, last_match.last, file_text, extracted_ruby)
|
46
|
-
|
47
|
-
extracted_ruby << extract_match(file_text, start_index, end_index)
|
25
|
+
iterator =
|
26
|
+
if stdin?
|
27
|
+
Iterators::StdinIterator.new(stdin_filename)
|
28
|
+
else
|
29
|
+
Iterators::DirectoryIterator.new(files_or_dirs)
|
30
|
+
end
|
48
31
|
|
49
|
-
|
32
|
+
iterator.each do |file, contents|
|
33
|
+
copy_erb_file(file, contents)
|
50
34
|
end
|
51
35
|
|
52
|
-
|
36
|
+
RubocopRunner.new(arguments, pwd, temp_dir, !disable_rb_extension?).execute
|
53
37
|
end
|
54
38
|
|
55
39
|
private
|
56
40
|
|
57
|
-
|
58
|
-
# http://edgeguides.rubyonrails.org/active_support_core_extensions.html#output-safety
|
59
|
-
# replace '<%==' with '<%= raw' to avoid generating invalid ruby code
|
60
|
-
file_text = File.read(filename).gsub(/<%==/, '<%= raw')
|
61
|
-
|
62
|
-
matching_regions = file_text.enum_for(:scan, ERB_REGEX)
|
63
|
-
.map { Regexp.last_match.offset(1) }
|
41
|
+
attr_reader :options
|
64
42
|
|
65
|
-
|
43
|
+
def extension
|
44
|
+
'.rb' unless disable_rb_extension?
|
66
45
|
end
|
67
46
|
|
68
|
-
def
|
69
|
-
|
70
|
-
return unless match_start > prev_end_index
|
71
|
-
|
72
|
-
region_before = file_text[prev_end_index..match_start - 1]
|
73
|
-
|
74
|
-
extracted_ruby << region_before.gsub(/./, ' ')
|
75
|
-
|
76
|
-
# if the last match was on the same line, we need to use a semicolon to
|
77
|
-
# separate statements
|
78
|
-
extracted_ruby[prev_end_index] = ';' if needs_stmt_delimiter?(prev_end_index, region_before)
|
47
|
+
def stdin?
|
48
|
+
stdin_filename
|
79
49
|
end
|
80
50
|
|
81
|
-
def
|
82
|
-
|
51
|
+
def stdin_filename
|
52
|
+
options[:stdin]
|
83
53
|
end
|
84
54
|
|
85
|
-
def
|
86
|
-
|
87
|
-
region.gsub!(/./, ' ') if region[0] == '#'
|
88
|
-
end
|
55
|
+
def arguments
|
56
|
+
options[:arguments]
|
89
57
|
end
|
90
58
|
|
91
|
-
def
|
92
|
-
|
93
|
-
Pathname.new(File.expand_path(@options[:tmp_folder]))
|
94
|
-
else
|
95
|
-
Pathname.new(Dir.mktmpdir)
|
96
|
-
end
|
59
|
+
def disable_rb_extension?
|
60
|
+
options[:disable_rb_extension]
|
97
61
|
end
|
98
62
|
|
99
|
-
def
|
100
|
-
|
63
|
+
def pwd
|
64
|
+
@pwd ||= Pathname.new(ENV['PWD'])
|
65
|
+
end
|
101
66
|
|
102
|
-
|
103
|
-
|
104
|
-
|
67
|
+
def temp_dir
|
68
|
+
@temp_dir ||= begin
|
69
|
+
if options[:tmp_folder]
|
70
|
+
Pathname.new(File.expand_path(options[:tmp_folder]))
|
105
71
|
else
|
106
|
-
Dir
|
107
|
-
copy_erb_file(f, tmp, pwd, extension)
|
108
|
-
end
|
72
|
+
Pathname.new(Dir.mktmpdir)
|
109
73
|
end
|
110
74
|
end
|
111
75
|
end
|
112
76
|
|
113
|
-
def
|
114
|
-
|
115
|
-
FileUtils.mkdir_p(File.dirname(n))
|
116
|
-
|
117
|
-
File.open("#{n}#{extension}", 'w+') do |tmp_file|
|
118
|
-
code = extract(file)
|
119
|
-
tmp_file.write(code)
|
120
|
-
end
|
77
|
+
def parser
|
78
|
+
@parser ||= Parser.new
|
121
79
|
end
|
122
80
|
|
123
|
-
def
|
124
|
-
|
125
|
-
todo = tmp + '.rubocop_todo.yml'
|
126
|
-
|
127
|
-
pwd = Dir.pwd
|
128
|
-
|
129
|
-
replacements = []
|
130
|
-
|
131
|
-
unless @options[:disable_rb_extension]
|
132
|
-
replacements << [/\.erb\.rb/, '.erb']
|
133
|
-
end
|
134
|
-
|
135
|
-
result = Dir.chdir(tmp) do
|
136
|
-
replacements.unshift([/^#{Regexp.quote(Dir.pwd)}/, pwd])
|
81
|
+
def copy_erb_file(file, contents)
|
82
|
+
code = parser.extract(contents)
|
137
83
|
|
138
|
-
|
139
|
-
|
140
|
-
munge_output(stdout, stderr, replacements)
|
84
|
+
n = temp_filename_for(file)
|
85
|
+
FileUtils.mkdir_p(File.dirname(n))
|
141
86
|
|
142
|
-
|
87
|
+
File.open(n, 'w+') do |tmp_file|
|
88
|
+
tmp_file.write(code)
|
143
89
|
end
|
144
|
-
|
145
|
-
FileUtils.cp(todo, pwd) if File.exist?(todo)
|
146
|
-
FileUtils.rm_rf(tmp) unless @options[:tmp_folder]
|
147
|
-
|
148
|
-
result
|
149
90
|
end
|
150
91
|
|
151
|
-
def
|
152
|
-
|
153
|
-
next if output.nil? || output.empty?
|
154
|
-
|
155
|
-
replacements.each do |pattern, replacement|
|
156
|
-
output.gsub!(pattern, replacement)
|
157
|
-
end
|
92
|
+
def temp_filename_for(file)
|
93
|
+
name = temp_dir.join(Pathname.new(file).relative_path_from(pwd))
|
158
94
|
|
159
|
-
|
160
|
-
end
|
95
|
+
"#{name}#{extension}"
|
161
96
|
end
|
162
97
|
end
|
163
98
|
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# @author Eric Weinstein <eric.q.weinstein@gmail.com>
|
2
|
+
|
3
|
+
# Ruumba: RuboCop's sidekick.
|
4
|
+
module Ruumba
|
5
|
+
# Generates analyzer objects that, when run, delegate
|
6
|
+
# to RuboCop for linting (style, correctness, &c).
|
7
|
+
module Iterators
|
8
|
+
# Iterator which returns the file passed in via stdin
|
9
|
+
class StdinIterator
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
def initialize(file)
|
13
|
+
@file = file
|
14
|
+
end
|
15
|
+
|
16
|
+
def each(&block)
|
17
|
+
[[file, STDIN.read]].each(&block)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :file
|
23
|
+
end
|
24
|
+
|
25
|
+
# Iterator which returns matching files from the given directory or file list
|
26
|
+
class DirectoryIterator
|
27
|
+
include Enumerable
|
28
|
+
|
29
|
+
def initialize(files_or_dirs)
|
30
|
+
@files_or_dirs = files_or_dirs
|
31
|
+
end
|
32
|
+
|
33
|
+
def each(&block)
|
34
|
+
files.map do |file|
|
35
|
+
[file, File.read(file)]
|
36
|
+
end.each(&block)
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
attr_reader :files_or_dirs
|
42
|
+
|
43
|
+
def files
|
44
|
+
full_list.flat_map do |file_or_dir|
|
45
|
+
if file_or_dir.file?
|
46
|
+
file_or_dir if file_or_dir.to_s.end_with?('.erb')
|
47
|
+
else
|
48
|
+
Dir["#{file_or_dir}/**/*.erb"].map do |file|
|
49
|
+
Pathname.new(file)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end.compact
|
53
|
+
end
|
54
|
+
|
55
|
+
def full_list
|
56
|
+
if files_or_dirs.nil? || files_or_dirs.empty?
|
57
|
+
[expand_path('.')]
|
58
|
+
else
|
59
|
+
files_or_dirs.map do |file_or_dir|
|
60
|
+
expand_path(file_or_dir)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def expand_path(file_or_dir)
|
66
|
+
Pathname.new(File.expand_path(file_or_dir))
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @author Eric Weinstein <eric.q.weinstein@gmail.com>
|
4
|
+
|
5
|
+
module Ruumba
|
6
|
+
# Responsible for extracting interpolated Ruby.
|
7
|
+
class Parser
|
8
|
+
# The regular expression to capture interpolated Ruby.
|
9
|
+
ERB_REGEX = /<%[-=]?(.*?)-?%>/m
|
10
|
+
|
11
|
+
# Extracts Ruby code from an ERB template.
|
12
|
+
# @return [String] The extracted ruby code
|
13
|
+
def extract(contents)
|
14
|
+
file_text, matches = parse(contents)
|
15
|
+
|
16
|
+
extracted_ruby = +''
|
17
|
+
|
18
|
+
last_match = [0, 0]
|
19
|
+
matches.each do |start_index, end_index|
|
20
|
+
handle_region_before(start_index, last_match.last, file_text, extracted_ruby)
|
21
|
+
|
22
|
+
extracted_ruby << extract_match(file_text, start_index, end_index)
|
23
|
+
|
24
|
+
last_match = [start_index, end_index]
|
25
|
+
end
|
26
|
+
|
27
|
+
extracted_ruby << file_text[last_match.last..-1].gsub(/./, ' ')
|
28
|
+
extracted_ruby.gsub!(/[^\S\r\n]+$/, '')
|
29
|
+
|
30
|
+
# if we replaced <%== with <%= raw, try to shift the columns back to the
|
31
|
+
# left so they match the original again
|
32
|
+
extracted_ruby.gsub!(/ raw/, 'raw')
|
33
|
+
|
34
|
+
extracted_ruby
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def parse(contents)
|
40
|
+
# http://edgeguides.rubyonrails.org/active_support_core_extensions.html#output-safety
|
41
|
+
# replace '<%==' with '<%= raw' to avoid generating invalid ruby code
|
42
|
+
file_text = contents.gsub(/<%==/, '<%= raw')
|
43
|
+
|
44
|
+
matching_regions = file_text.enum_for(:scan, ERB_REGEX)
|
45
|
+
.map { Regexp.last_match.offset(1) }
|
46
|
+
|
47
|
+
[file_text, matching_regions]
|
48
|
+
end
|
49
|
+
|
50
|
+
def handle_region_before(match_start, prev_end_index, file_text, extracted_ruby)
|
51
|
+
return unless match_start > prev_end_index
|
52
|
+
|
53
|
+
region_before = file_text[prev_end_index..match_start - 1]
|
54
|
+
|
55
|
+
extracted_ruby << region_before.gsub(/./, ' ')
|
56
|
+
|
57
|
+
# if the last match was on the same line, we need to use a semicolon to
|
58
|
+
# separate statements
|
59
|
+
extracted_ruby[prev_end_index] = ';' if needs_stmt_delimiter?(prev_end_index, region_before)
|
60
|
+
end
|
61
|
+
|
62
|
+
def needs_stmt_delimiter?(last_match, region_before)
|
63
|
+
last_match.positive? && region_before.index("\n").nil?
|
64
|
+
end
|
65
|
+
|
66
|
+
def extract_match(file_text, start_index, end_index)
|
67
|
+
file_text[start_index...end_index].tap do |region|
|
68
|
+
# if this is a ruby comment inside, replace the whole match with spaces
|
69
|
+
region.gsub!(/[^\r\n]/, ' ') if region[0] == '#'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/ruumba/rake_task.rb
CHANGED
@@ -0,0 +1,58 @@
|
|
1
|
+
# @author Eric Weinstein <eric.q.weinstein@gmail.com>
|
2
|
+
|
3
|
+
require 'open3'
|
4
|
+
|
5
|
+
module Ruumba
|
6
|
+
# Runs rubocop on the files in the given target_directory
|
7
|
+
class RubocopRunner
|
8
|
+
def initialize(arguments, current_directory, target_directory, rb_extension_enabled)
|
9
|
+
@arguments = Array(arguments)
|
10
|
+
@current_directory = current_directory
|
11
|
+
@rb_extension_enabled = rb_extension_enabled
|
12
|
+
@target_directory = target_directory
|
13
|
+
end
|
14
|
+
|
15
|
+
# Executes rubocop, updating filenames in the output if needed.
|
16
|
+
# @return the exit code of the rubocop process
|
17
|
+
def execute
|
18
|
+
args = ['rubocop'] + arguments
|
19
|
+
todo = target_directory.join('.rubocop_todo.yml')
|
20
|
+
|
21
|
+
replacements = []
|
22
|
+
|
23
|
+
# if adding the .rb extension is enabled, remove the extension again from
|
24
|
+
# any output so it matches the actual files names we are linting
|
25
|
+
replacements << [/\.erb\.rb/, '.erb'] if rb_extension_enabled
|
26
|
+
|
27
|
+
result = Dir.chdir(target_directory) do
|
28
|
+
replacements.unshift([/^#{Regexp.quote(Dir.pwd)}/, current_directory.to_s])
|
29
|
+
stdout, stderr, status = Open3.capture3(*args)
|
30
|
+
|
31
|
+
munge_output(stdout, stderr, replacements)
|
32
|
+
|
33
|
+
status.exitstatus
|
34
|
+
end
|
35
|
+
|
36
|
+
# copy the todo file back for the case where we've used --auto-gen-config
|
37
|
+
FileUtils.cp(todo, current_directory) if todo.exist?
|
38
|
+
|
39
|
+
result
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
attr_reader :arguments, :current_directory, :rb_extension_enabled, :target_directory
|
45
|
+
|
46
|
+
def munge_output(stdout, stderr, replacements)
|
47
|
+
[[STDOUT, stdout], [STDERR, stderr]].each do |output_stream, output|
|
48
|
+
next if output.nil? || output.empty?
|
49
|
+
|
50
|
+
replacements.each do |pattern, replacement|
|
51
|
+
output.gsub!(pattern, replacement)
|
52
|
+
end
|
53
|
+
|
54
|
+
output_stream.puts(output)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
metadata
CHANGED
@@ -1,16 +1,17 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruumba
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eric Weinstein
|
8
8
|
- Jan Biniok
|
9
9
|
- Yvan Barthélemy
|
10
|
+
- Andrew Clemons
|
10
11
|
autorequire:
|
11
12
|
bindir: bin
|
12
13
|
cert_chain: []
|
13
|
-
date: 2018-10-
|
14
|
+
date: 2018-10-30 00:00:00.000000000 Z
|
14
15
|
dependencies:
|
15
16
|
- !ruby/object:Gem::Dependency
|
16
17
|
name: rubocop
|
@@ -38,7 +39,10 @@ files:
|
|
38
39
|
- bin/ruumba
|
39
40
|
- lib/ruumba.rb
|
40
41
|
- lib/ruumba/analyzer.rb
|
42
|
+
- lib/ruumba/iterators.rb
|
43
|
+
- lib/ruumba/parser.rb
|
41
44
|
- lib/ruumba/rake_task.rb
|
45
|
+
- lib/ruumba/rubocop_runner.rb
|
42
46
|
homepage: https://github.com/ericqweinstein/ruumba
|
43
47
|
licenses:
|
44
48
|
- MIT
|
@@ -59,7 +63,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
59
63
|
version: '0'
|
60
64
|
requirements: []
|
61
65
|
rubyforge_project:
|
62
|
-
rubygems_version: 2.
|
66
|
+
rubygems_version: 2.5.2.3
|
63
67
|
signing_key:
|
64
68
|
specification_version: 4
|
65
69
|
summary: Allows users to lint Ruby code in ERB templates the same way they lint source
|