ruumba 0.1.10 → 0.1.12

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: fc25f940621b5d2f205b48a164660f2fc445c38b
4
- data.tar.gz: 787c2558ba96d2bcef7b20ef62b6c4cf315215f5
3
+ metadata.gz: be806019ab1e49249c6c93f96fc6c3b0cc64133e
4
+ data.tar.gz: 23f5f794ec43878ffa895fca296c3635c765affc
5
5
  SHA512:
6
- metadata.gz: bc95b256427ae32527bb9a1e08bbe0df50b2e499481d1719d47eda8bbe27e0c4a24e7afaf86dc8a537ba107be64e3794cfaa5f7268c2383f613caa0a221ba46e
7
- data.tar.gz: 11c864be6842cbc0a119fee5b1d84bb0fdd6e58b8a0d6696a4be3a834a1761b886ac9a74597f276a485899ba79db271fa3f21121029a21e1925ef56fa5e6bbe9
6
+ metadata.gz: 98d98ef80a4604c9c4eeb61fc2e950ec029b6cc54f36af699f9051cb6c526bc20f6d6d6ee78e2946db3a358a30552c9928064445399584bf84d468136838958a
7
+ data.tar.gz: 1029541aa945f8611b1b8da61d7a4f9c53ccab0a6dc7b550e0de31ae4f24dce9f067a0c723ce341ebba77369ffd2e143bcbe1ac323b36fd1a938d92f3d1efbb4
data/bin/ruumba CHANGED
@@ -96,6 +96,11 @@ opts_parser = OptionParser.new do |opts|
96
96
  opts.on('-L', '--list-target-files', 'List all files Ruumba will inspect.') do
97
97
  options[:arguments] << '--list-target-files'
98
98
  end
99
+
100
+ opts.on('-a', '--auto-correct', 'Auto-correct offenses.') do
101
+ options[:arguments] << '--auto-correct'
102
+ options[:auto_correct] = true
103
+ end
99
104
  end
100
105
 
101
106
  begin
@@ -1,12 +1,14 @@
1
1
  # @author Eric Weinstein <eric.q.weinstein@gmail.com>
2
2
 
3
3
  require 'securerandom'
4
+ require 'digest'
4
5
  require 'pathname'
5
6
  require 'tmpdir'
6
7
  require 'open3'
7
8
  require 'English'
8
9
 
9
10
  require 'ruumba/iterators'
11
+ require 'ruumba/correctors'
10
12
  require 'ruumba/parser'
11
13
  require 'ruumba/rubocop_runner'
12
14
 
@@ -38,24 +40,43 @@ module Ruumba
38
40
  def analyze(temp_dir, files_or_dirs)
39
41
  temp_dir_path = Pathname.new(temp_dir)
40
42
 
41
- iterator =
43
+ iterator, corrector =
42
44
  if stdin?
43
- Iterators::StdinIterator.new(stdin_filename)
45
+ [Iterators::StdinIterator.new(File.expand_path(stdin_filename)), Correctors::StdinCorrector.new(digestor, parser)]
44
46
  else
45
- Iterators::DirectoryIterator.new(files_or_dirs)
47
+ [Iterators::DirectoryIterator.new(files_or_dirs), Correctors::FileCorrector.new(digestor, parser)]
46
48
  end
47
49
 
48
50
  iterator.each do |file, contents|
49
- copy_erb_file(file, contents, temp_dir_path)
51
+ code, new_file_name = copy_erb_file(file, contents, temp_dir_path)
52
+
53
+ if stdin?
54
+ @stdin_contents = code
55
+ @new_stdin_filename = new_file_name
56
+ end
50
57
  end
51
58
 
52
- RubocopRunner.new(arguments, pwd, temp_dir_path, !disable_rb_extension?).execute
59
+ stdout, stderr, exit_code = RubocopRunner.new(arguments, pwd, temp_dir_path, @stdin_contents, !disable_rb_extension?).execute
60
+
61
+ corrector.correct(stdout, stderr, file_mappings) if auto_correct?
62
+
63
+ [[STDOUT, stdout], [STDERR, stderr]].each do |output_stream, output|
64
+ next if output.nil? || output.empty?
65
+
66
+ output_stream.puts(output)
67
+ end
68
+
69
+ exit_code
53
70
  end
54
71
 
55
72
  def extension
56
73
  '.rb' unless disable_rb_extension?
57
74
  end
58
75
 
76
+ def auto_correct?
77
+ options[:auto_correct]
78
+ end
79
+
59
80
  def stdin?
60
81
  stdin_filename
61
82
  end
@@ -65,7 +86,11 @@ module Ruumba
65
86
  end
66
87
 
67
88
  def arguments
68
- options[:arguments]
89
+ if stdin?
90
+ options[:arguments] + ['--stdin', @new_stdin_filename]
91
+ else
92
+ options[:arguments]
93
+ end
69
94
  end
70
95
 
71
96
  def disable_rb_extension?
@@ -76,19 +101,52 @@ module Ruumba
76
101
  @pwd ||= Pathname.new(ENV['PWD'])
77
102
  end
78
103
 
104
+ def auto_correct_marker
105
+ return @auto_correct_marker if defined?(@auto_correct_marker)
106
+
107
+ @auto_correct_marker = auto_correct? ? 'marker_' + SecureRandom.uuid.tr('-', '_') : nil
108
+ end
109
+
79
110
  def parser
80
- @parser ||= Parser.new
111
+ @parser ||= Parser.new(auto_correct_marker)
112
+ end
113
+
114
+ def digestor
115
+ @digestor ||= ->(contents) { Digest::SHA256.base64digest(contents) }
116
+ end
117
+
118
+ def file_mappings
119
+ @file_mappings ||= {}
81
120
  end
82
121
 
83
122
  def copy_erb_file(file, contents, temp_dir)
84
123
  code = parser.extract(contents)
124
+ new_file = temp_filename_for(file, temp_dir)
85
125
 
86
- n = temp_filename_for(file, temp_dir)
87
- FileUtils.mkdir_p(File.dirname(n))
126
+ if auto_correct?
127
+ properties = []
128
+ properties << new_file
129
+ properties << digestor.call(code)
88
130
 
89
- File.open(n, 'w+') do |tmp_file|
90
- tmp_file.write(code)
131
+ properties <<
132
+ if stdin?
133
+ contents
134
+ else
135
+ -> { File.read(file) }
136
+ end
137
+
138
+ file_mappings[file] = properties
139
+ end
140
+
141
+ unless stdin?
142
+ FileUtils.mkdir_p(File.dirname(new_file))
143
+
144
+ File.open(new_file, 'w+') do |tmp_file|
145
+ tmp_file.write(code)
146
+ end
91
147
  end
148
+
149
+ [code, new_file]
92
150
  end
93
151
 
94
152
  def temp_filename_for(file, temp_dir)
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ruumba
4
+ # Responsible for extracted auto corrected code and updating the original ERBs files
5
+ module Correctors
6
+ # Module to help replace code
7
+ module Replacer
8
+ def handle_corrected_output(old_digest, new_contents, original_contents)
9
+ new_digest = digestor.call(new_contents)
10
+
11
+ return if old_digest == new_digest
12
+
13
+ original_contents = original_contents.call if original_contents.respond_to?(:call)
14
+
15
+ replaced_output = parser.replace(original_contents, new_contents)
16
+
17
+ yield(replaced_output) if replaced_output
18
+ end
19
+ end
20
+
21
+ # Corrector for when the checked file was passed through stdin.
22
+ class StdinCorrector
23
+ include Replacer
24
+
25
+ def initialize(digestor, parser)
26
+ @digestor = digestor
27
+ @parser = parser
28
+ end
29
+
30
+ def correct(stdout, stderr, file_mappings)
31
+ _, old_ruumba_digest, original_contents = *file_mappings.values.first
32
+
33
+ [stdout, stderr].each do |output|
34
+ next if output.nil? || output.empty?
35
+
36
+ matches = output.scan(/\A(.*^====================)?$(.*)\z/m)
37
+
38
+ next if matches.empty?
39
+
40
+ prefix, new_contents = *matches.first
41
+
42
+ handle_corrected_output(old_ruumba_digest, new_contents, original_contents) do |corrected_output|
43
+ output.clear
44
+ output.concat("#{prefix}\n#{corrected_output}")
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ attr_reader :digestor, :parser
52
+ end
53
+
54
+ # Corrector for when normal file checking
55
+ class FileCorrector
56
+ include Replacer
57
+
58
+ def initialize(digestor, parser)
59
+ @digestor = digestor
60
+ @parser = parser
61
+ end
62
+
63
+ def correct(_stdout, _stderr, file_mappings)
64
+ file_mappings.each do |original_file, (ruumba_file, old_ruumba_digest, original_contents)|
65
+ new_contents = File.read(ruumba_file)
66
+
67
+ handle_corrected_output(old_ruumba_digest, new_contents, original_contents) do |corrected_output|
68
+ File.open(original_file, 'w+') do |file_handle|
69
+ file_handle.write(corrected_output)
70
+ end
71
+ end
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ attr_reader :digestor, :parser
78
+ end
79
+ end
80
+ end
@@ -8,6 +8,10 @@ module Ruumba
8
8
  # The regular expression to capture interpolated Ruby.
9
9
  ERB_REGEX = /<%[-=]?(.*?)-?%>/m
10
10
 
11
+ def initialize(region_start_marker = nil)
12
+ @region_start_marker = region_start_marker
13
+ end
14
+
11
15
  # Extracts Ruby code from an ERB template.
12
16
  # @return [String] The extracted ruby code
13
17
  def extract(contents)
@@ -16,16 +20,16 @@ module Ruumba
16
20
  extracted_ruby = +''
17
21
 
18
22
  last_match = [0, 0]
19
- matches.each do |start_index, end_index|
23
+ matches.each_with_index do |(start_index, end_index), index|
20
24
  handle_region_before(start_index, last_match.last, file_text, extracted_ruby)
21
25
 
22
- extracted_ruby << extract_match(file_text, start_index, end_index)
26
+ match_marker = "#{region_start_marker}_#{format('%010d', index + 1)}" if region_start_marker
27
+ extracted_ruby << extract_match(file_text, start_index, end_index, match_marker)
23
28
 
24
29
  last_match = [start_index, end_index]
25
30
  end
26
31
 
27
32
  extracted_ruby << file_text[last_match.last..-1].gsub(/./, ' ')
28
- extracted_ruby.gsub!(/[^\S\r\n]+$/, '')
29
33
 
30
34
  # if we replaced <%== with <%= raw, try to shift the columns back to the
31
35
  # left so they match the original again
@@ -34,8 +38,44 @@ module Ruumba
34
38
  extracted_ruby
35
39
  end
36
40
 
41
+ def replace(old_contents, new_contents)
42
+ file_text, matches = parse(old_contents)
43
+
44
+ auto_corrected_erb = +''
45
+
46
+ last_match = [0, 0]
47
+ matches.each_with_index do |(start_index, end_index), index|
48
+ match_start = start_index
49
+ prev_end_index = last_match.last
50
+
51
+ if start_index > prev_end_index
52
+ region_before = file_text[prev_end_index..match_start - 1]
53
+
54
+ auto_corrected_erb << region_before
55
+ end
56
+
57
+ suffix = format('%010d', index + 1)
58
+ match_marker = "#{region_start_marker}_#{suffix}"
59
+
60
+ match_without_markers = new_contents[/\n#{match_marker}$\n(.*)\n#{match_marker}\n/m, 1]
61
+
62
+ # auto-correct is still experimental and can cause invalid ruby to be generated when extracting ruby from ERBs
63
+ return nil unless match_without_markers
64
+
65
+ auto_corrected_erb << match_without_markers
66
+
67
+ last_match = [start_index, end_index]
68
+ end
69
+
70
+ auto_corrected_erb << file_text[last_match.last..-1]
71
+
72
+ auto_corrected_erb
73
+ end
74
+
37
75
  private
38
76
 
77
+ attr_reader :region_start_marker
78
+
39
79
  def parse(contents)
40
80
  # http://edgeguides.rubyonrails.org/active_support_core_extensions.html#output-safety
41
81
  # replace '<%==' with '<%= raw' to avoid generating invalid ruby code
@@ -50,20 +90,24 @@ module Ruumba
50
90
  def handle_region_before(match_start, prev_end_index, file_text, extracted_ruby)
51
91
  return unless match_start > prev_end_index
52
92
 
93
+ last_position = extracted_ruby.length
94
+
53
95
  region_before = file_text[prev_end_index..match_start - 1]
54
96
 
55
- extracted_ruby << region_before.gsub(/./, ' ')
97
+ region_before.gsub!(/./, ' ')
56
98
 
57
99
  # if the last match was on the same line, we need to use a semicolon to
58
100
  # separate statements
59
- extracted_ruby[prev_end_index] = ';' if needs_stmt_delimiter?(prev_end_index, region_before)
101
+ extracted_ruby[last_position] = ';' if needs_stmt_delimiter?(prev_end_index, region_before)
102
+
103
+ extracted_ruby << region_before
60
104
  end
61
105
 
62
106
  def needs_stmt_delimiter?(last_match, region_before)
63
107
  last_match.positive? && region_before.index("\n").nil?
64
108
  end
65
109
 
66
- def extract_match(file_text, start_index, end_index)
110
+ def extract_match(file_text, start_index, end_index, match_marker)
67
111
  file_text[start_index...end_index].tap do |region|
68
112
  # if there is a ruby comment inside, replace the beginning of each line
69
113
  # with the '#' so we end up with valid ruby
@@ -71,6 +115,9 @@ module Ruumba
71
115
  if region[0] == '#'
72
116
  region.gsub!(/^ /, '#')
73
117
  region.gsub!(/^(?!#)/, '#')
118
+ elsif match_marker
119
+ region.prepend("\n", match_marker, "\n")
120
+ region.concat("\n", match_marker, "\n")
74
121
  end
75
122
  end
76
123
  end
@@ -5,11 +5,17 @@ require 'open3'
5
5
  module Ruumba
6
6
  # Runs rubocop on the files in the given target_directory
7
7
  class RubocopRunner
8
- def initialize(arguments, current_directory, target_directory, rb_extension_enabled)
8
+ def initialize(arguments, current_directory, target_directory, stdin, rb_extension_enabled)
9
9
  @arguments = Array(arguments)
10
10
  @current_directory = current_directory
11
11
  @rb_extension_enabled = rb_extension_enabled
12
+ @stdin = stdin
12
13
  @target_directory = target_directory
14
+ @replacements = []
15
+
16
+ # if adding the .rb extension is enabled, remove the extension again from
17
+ # any output so it matches the actual files names we are linting
18
+ @replacements << [/\.erb\.rb/, '.erb'] if rb_extension_enabled
13
19
  end
14
20
 
15
21
  # Executes rubocop, updating filenames in the output if needed.
@@ -18,41 +24,32 @@ module Ruumba
18
24
  args = ['rubocop'] + arguments
19
25
  todo = target_directory.join('.rubocop_todo.yml')
20
26
 
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
27
+ results = Dir.chdir(target_directory) do
28
28
  replacements.unshift([/^#{Regexp.quote(Dir.pwd)}/, current_directory.to_s])
29
- stdout, stderr, status = Open3.capture3(*args)
30
29
 
31
- munge_output(stdout, stderr, replacements)
30
+ stdout, stderr, status = Open3.capture3(*args, stdin_data: stdin)
32
31
 
33
- status.exitstatus
32
+ [munge_output(stdout), munge_output(stderr), status.exitstatus]
34
33
  end
35
34
 
36
35
  # copy the todo file back for the case where we've used --auto-gen-config
37
36
  FileUtils.cp(todo, current_directory) if todo.exist?
38
37
 
39
- result
38
+ results
40
39
  end
41
40
 
42
41
  private
43
42
 
44
- attr_reader :arguments, :current_directory, :rb_extension_enabled, :target_directory
43
+ attr_reader :arguments, :current_directory, :rb_extension_enabled, :replacements, :stdin, :target_directory
45
44
 
46
- def munge_output(stdout, stderr, replacements)
47
- [[STDOUT, stdout], [STDERR, stderr]].each do |output_stream, output|
48
- next if output.nil? || output.empty?
45
+ def munge_output(output)
46
+ return output if output.nil? || output.empty?
49
47
 
50
- replacements.each do |pattern, replacement|
51
- output.gsub!(pattern, replacement)
52
- end
53
-
54
- output_stream.puts(output)
48
+ replacements.each do |pattern, replacement|
49
+ output = output.gsub(pattern, replacement)
55
50
  end
51
+
52
+ output
56
53
  end
57
54
  end
58
55
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruumba
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.10
4
+ version: 0.1.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Weinstein
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2019-05-03 00:00:00.000000000 Z
14
+ date: 2019-08-30 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rubocop
@@ -39,6 +39,7 @@ files:
39
39
  - bin/ruumba
40
40
  - lib/ruumba.rb
41
41
  - lib/ruumba/analyzer.rb
42
+ - lib/ruumba/correctors.rb
42
43
  - lib/ruumba/iterators.rb
43
44
  - lib/ruumba/parser.rb
44
45
  - lib/ruumba/rake_task.rb
@@ -63,7 +64,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
63
64
  version: '0'
64
65
  requirements: []
65
66
  rubyforge_project:
66
- rubygems_version: 2.5.2.3
67
+ rubygems_version: 2.6.14.3
67
68
  signing_key:
68
69
  specification_version: 4
69
70
  summary: Allows users to lint Ruby code in ERB templates the same way they lint source