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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 499ac4d5c4a2e6ed257da7a16218a65fe5f48c8b
4
- data.tar.gz: 2068f31c26ad2011256ae53cbdbe978895898316
3
+ metadata.gz: 150e5c1981114be3f11e8f327ab80896264241e5
4
+ data.tar.gz: a043ba70111e3c987b6586f32424adac80ba0e26
5
5
  SHA512:
6
- metadata.gz: 7050b2578545ba9d0c393e3829d8a824c0b9e175aa22afb26ade2518af446bc11a9e9ab84afc2d29500ac1764c37cb2d1c49bdc58e95111dfd18e6687eb544fe
7
- data.tar.gz: 8ee48c0e20a55debd4cfce935ea843851fc5c4ee37ad496051b150af33eac51957839bc578a52ae22f963332bb66ab10584b83dbf9ef71788664b5c163190612
6
+ metadata.gz: 7b6657ec72b0e1c33625f39a890ff029281d1d20130ec8da9b346c19b8511ff0063d5eb37edb7040f1df67d13c64431ef11860b358b3b7917dabb71e8673746e
7
+ data.tar.gz: 27fa8094870b988a99a35409c55d0cc3fd2e20b60190a9aff43119d2d023b3f7d29d9b1b31fb3b148daec7ff704b04fef740cd2f70f164852225f81cee4a28a8
data/README.md CHANGED
@@ -11,7 +11,7 @@ Ruumba
11
11
  Ruumba is [RuboCop's](https://github.com/bbatsov/rubocop) sidekick, allowing you to lint your .erb Rubies as well as your regular-type ones.
12
12
 
13
13
  ## Dependencies
14
- * Ruby 2.2.2+
14
+ * Ruby 2.3+
15
15
 
16
16
  ## Installation
17
17
  ```bash
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
@@ -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
- files_or_dirs = ['.'] if files_or_dirs.empty?
25
- fq_files_or_dirs = files_or_dirs.map { |file_or_dir| Pathname.new(File.expand_path(file_or_dir)) }
26
- pwd = Pathname.new(ENV['PWD'])
27
- tmp = create_temp_dir
28
-
29
- copy_erb_files(fq_files_or_dirs, tmp, pwd)
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
- last_match = [start_index, end_index]
32
+ iterator.each do |file, contents|
33
+ copy_erb_file(file, contents)
50
34
  end
51
35
 
52
- extracted_ruby << file_text[last_match.last..-1].gsub(/./, ' ')
36
+ RubocopRunner.new(arguments, pwd, temp_dir, !disable_rb_extension?).execute
53
37
  end
54
38
 
55
39
  private
56
40
 
57
- def parse_file(filename)
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
- [file_text, matching_regions]
43
+ def extension
44
+ '.rb' unless disable_rb_extension?
66
45
  end
67
46
 
68
- def handled_region_before(match_start, prev_end_index,
69
- file_text, extracted_ruby)
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 needs_stmt_delimiter?(last_match, region_before)
82
- last_match.positive? && region_before.index("\n").nil?
51
+ def stdin_filename
52
+ options[:stdin]
83
53
  end
84
54
 
85
- def extract_match(file_text, start_index, end_index)
86
- file_text[start_index...end_index].tap do |region|
87
- region.gsub!(/./, ' ') if region[0] == '#'
88
- end
55
+ def arguments
56
+ options[:arguments]
89
57
  end
90
58
 
91
- def create_temp_dir
92
- if @options[:tmp_folder]
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 copy_erb_files(fq_files_or_dirs, tmp, pwd)
100
- extension = '.rb' unless @options[:disable_rb_extension]
63
+ def pwd
64
+ @pwd ||= Pathname.new(ENV['PWD'])
65
+ end
101
66
 
102
- fq_files_or_dirs.each do |fq_file_or_dir|
103
- if fq_file_or_dir.file?
104
- copy_erb_file(fq_file_or_dir, tmp, pwd, extension) if fq_file_or_dir.to_s.end_with?('.erb')
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["#{fq_file_or_dir}/**/*.erb"].each do |f|
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 copy_erb_file(file, tmp, pwd, extension)
114
- n = tmp + Pathname.new(file).relative_path_from(pwd)
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 run_rubocop(target, tmp)
124
- args = ['rubocop'] + (@options[:arguments] || []) + [target.to_s]
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
- stdout, stderr, status = Open3.capture3(*args)
139
-
140
- munge_output(stdout, stderr, replacements)
84
+ n = temp_filename_for(file)
85
+ FileUtils.mkdir_p(File.dirname(n))
141
86
 
142
- status.exitstatus.zero?
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 munge_output(stdout, stderr, replacements)
152
- [[STDOUT, stdout], [STDERR, stderr]].each do |output_stream, output|
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
- output_stream.puts(output)
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
@@ -36,7 +36,7 @@ module Ruumba
36
36
  analyzer = Ruumba::Analyzer.new(@options)
37
37
  puts 'Running Ruumba...'
38
38
 
39
- analyzer.run(@dir) || exit(1)
39
+ exit(analyzer.run(@dir))
40
40
  end
41
41
  end
42
42
  end
@@ -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.6
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-11 00:00:00.000000000 Z
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.6.11
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