diff --git a/bin/magic_frozen_string_literal b/bin/magic_frozen_string_literal index d3f8504..a15c53c 100755 --- a/bin/magic_frozen_string_literal +++ b/bin/magic_frozen_string_literal @@ -3,5 +3,19 @@ # A simple tool to prepend magic '# frozen_string_literal: true' comments to multiple ".rb" files require_relative '../lib/add_magic_comment' +require "optparse" -AddMagicComment.process(ARGV) +process_files = false +OptionParser.new do |opts| + opts.banner = "Usage: magic_frozen_string_literal app/ lib/" + + opts.on("-f", "--files", "Apply to specific files instead of files in directories") do |v| + process_files = true + end +end.parse! + +if process_files + AddMagicComment.process_files_at(ARGV) +else + AddMagicComment.process(ARGV) +end diff --git a/lib/add_magic_comment.rb b/lib/add_magic_comment.rb index 713ad15..759b551 100644 --- a/lib/add_magic_comment.rb +++ b/lib/add_magic_comment.rb @@ -18,6 +18,7 @@ module AddMagicComment "*.gemspec" => "# #{MAGIC_COMMENT}\n\n", "*.rabl" => "# #{MAGIC_COMMENT}\n\n", "*.jbuilder" => "# #{MAGIC_COMMENT}\n\n", + "*.pbbuilder" => "# #{MAGIC_COMMENT}\n\n", "*.haml" => "-# #{MAGIC_COMMENT}\n", "*.slim" => "-# #{MAGIC_COMMENT}\n" } @@ -25,41 +26,59 @@ module AddMagicComment def self.process(argv) directory = argv.first || Dir.pwd - count = 0 + + touched_paths = [] EXTENSION_COMMENTS.each do |pattern, comment| - filename_pattern = File.join(directory, "**", "#{pattern}") - Dir.glob(filename_pattern).each do |filename| - File.open(filename, "rb+") do |file| - lines = file.readlines - newline = detect_newline(lines.first) - next unless lines.any? - count += 1 - - if lines.first =~ SHEBANG_PATTERN - shebang = lines.shift - end - - # remove current magic comment(s) - while lines.first && (lines.first.match(MAGIC_COMMENT_PATTERN) || lines.first.match(EMPTY_LINE_PATTERN)) - lines.shift - end - - # add magic comment as the first line - lines.unshift(comment.gsub("\n", newline)) - - # put shebang back - if shebang - lines.unshift(shebang) - end - - file.pos = 0 - file.print(*lines) - file.truncate(file.pos) - end + file_path_pattern = File.join(directory, "**", "#{pattern}") + Dir.glob(file_path_pattern).each do |file_path| + process_file_at(file_path, comment, touched_paths) + end + end + + puts "Magic comments added to #{touched_paths.size} source file(s)" + end + + def self.process_file_at(path, comment, touched_paths) + File.open(path, "rb+") do |file| + lines = file.readlines + newline = detect_newline(lines.first) + return unless lines.any? + + if lines.first =~ SHEBANG_PATTERN + shebang = lines.shift + end + + # remove current magic comment(s) + while lines.first && (lines.first.match(MAGIC_COMMENT_PATTERN) || lines.first.match(EMPTY_LINE_PATTERN)) + lines.shift + end + + # add magic comment as the first line + lines.unshift(comment.gsub("\n", newline)) + + # put shebang back + if shebang + lines.unshift(shebang) + end + + file.pos = 0 + file.print(*lines) + file.truncate(file.pos) + end + touched_paths << path + end + + def self.process_files_at(paths) + touched_paths = [] + paths.each do |path| + matching_pattern_and_comment = EXTENSION_COMMENTS.find do |(glob_pattern, _comment)| + File.fnmatch(glob_pattern, path) end + _, comment = matching_pattern_and_comment + process_file_at(path, comment, touched_paths) if comment end - puts "Magic comments added to #{count} source file(s)" + puts "Magic comments added to #{touched_paths.size} source file(s)" end def self.detect_newline(line) diff --git a/spec/acceptance_spec.rb b/spec/acceptance_spec.rb index 2bea2ab..df7f7ff 100644 --- a/spec/acceptance_spec.rb +++ b/spec/acceptance_spec.rb @@ -14,7 +14,7 @@ } end - it "inserts magic comments where expected", :aggregate_failures do + it "inserts magic comments across directories", :aggregate_failures do AddMagicComment.process([directories[:test]]) Dir["#{directories[:test]}/*"].each do |path| @@ -22,6 +22,18 @@ end end + it "inserts magic comments just into specific files", :aggregate_failures do + paths = Dir.glob(directories[:test] + "/*.rb") + AddMagicComment.process_files_at(paths) + + rb_paths = Dir["#{directories[:test]}/*.rb"] + rb_paths.each do |path| + assert_file_matches_expected(File.basename(path)) + end + + assert_file_not_touched("Rakefile") + end + def setup_test_files FileUtils.mkdir_p(File.dirname(directories[:test])) FileUtils.cp_r(directories[:input], directories[:test]) @@ -31,6 +43,12 @@ def teardown_test_files FileUtils.rm_rf(directories[:test]) end + def assert_file_not_touched(filename) + processed_path = File.join(directories[:test], filename) + expected_path = File.join(directories[:expected], filename) + expect(FileUtils).not_to be_identical(processed_path, expected_path) + end + def assert_file_matches_expected(filename) paths = { test: File.join(directories[:test], filename), diff --git a/spec/fixtures/expected/shebang_already_commented.rb b/spec/fixtures/expected/shebang_already_commented.rb new file mode 100755 index 0000000..3795ca2 --- /dev/null +++ b/spec/fixtures/expected/shebang_already_commented.rb @@ -0,0 +1,4 @@ +#!/usr/bin/ruby +# frozen_string_literal: true + +puts "hello" diff --git a/spec/fixtures/input/Gemfile b/spec/fixtures/input/Gemfile index 25115d0..c036588 100644 --- a/spec/fixtures/input/Gemfile +++ b/spec/fixtures/input/Gemfile @@ -1,5 +1,3 @@ -# frozen_string_literal: true - source 'https://rubygems.org' gem 'magic_frozen_string_literal' diff --git a/spec/fixtures/input/Rakefile b/spec/fixtures/input/Rakefile index f039a8b..1737b57 100644 --- a/spec/fixtures/input/Rakefile +++ b/spec/fixtures/input/Rakefile @@ -1,5 +1,3 @@ -# frozen_string_literal: true - require "bundler/gem_tasks" task :test do diff --git a/spec/fixtures/input/blank_line.rb b/spec/fixtures/input/blank_line.rb index ae917d5..bbad01b 100644 --- a/spec/fixtures/input/blank_line.rb +++ b/spec/fixtures/input/blank_line.rb @@ -1,3 +1 @@ -# frozen_string_literal: true - puts "hello" diff --git a/spec/fixtures/input/config.ru b/spec/fixtures/input/config.ru index 842bccc..f7ba0b5 100644 --- a/spec/fixtures/input/config.ru +++ b/spec/fixtures/input/config.ru @@ -1,5 +1,3 @@ -# frozen_string_literal: true - # This file is used by Rack-based servers to start the application. require_relative 'config/environment' diff --git a/spec/fixtures/input/shebang.rb b/spec/fixtures/input/shebang.rb old mode 100644 new mode 100755 index 3795ca2..e69af1e --- a/spec/fixtures/input/shebang.rb +++ b/spec/fixtures/input/shebang.rb @@ -1,4 +1,3 @@ #!/usr/bin/ruby -# frozen_string_literal: true puts "hello" diff --git a/spec/fixtures/input/shebang_already_commented.rb b/spec/fixtures/input/shebang_already_commented.rb new file mode 100755 index 0000000..3795ca2 --- /dev/null +++ b/spec/fixtures/input/shebang_already_commented.rb @@ -0,0 +1,4 @@ +#!/usr/bin/ruby +# frozen_string_literal: true + +puts "hello" diff --git a/spec/fixtures/input/shebang_blank_line.rb b/spec/fixtures/input/shebang_blank_line.rb old mode 100644 new mode 100755 index 3795ca2..e69af1e --- a/spec/fixtures/input/shebang_blank_line.rb +++ b/spec/fixtures/input/shebang_blank_line.rb @@ -1,4 +1,3 @@ #!/usr/bin/ruby -# frozen_string_literal: true puts "hello" diff --git a/spec/fixtures/input/t1.haml b/spec/fixtures/input/t1.haml index aeaeb94..d1ded0b 100644 --- a/spec/fixtures/input/t1.haml +++ b/spec/fixtures/input/t1.haml @@ -1,2 +1 @@ --# frozen_string_literal: true Hello! diff --git a/spec/fixtures/input/t1.rb b/spec/fixtures/input/t1.rb index ae917d5..bbad01b 100644 --- a/spec/fixtures/input/t1.rb +++ b/spec/fixtures/input/t1.rb @@ -1,3 +1 @@ -# frozen_string_literal: true - puts "hello" diff --git a/spec/fixtures/input/t1.slim b/spec/fixtures/input/t1.slim index aeaeb94..d1ded0b 100644 --- a/spec/fixtures/input/t1.slim +++ b/spec/fixtures/input/t1.slim @@ -1,2 +1 @@ --# frozen_string_literal: true Hello! diff --git a/spec/fixtures/input/utf8.rb b/spec/fixtures/input/utf8.rb index 904b2ba..0010968 100644 --- a/spec/fixtures/input/utf8.rb +++ b/spec/fixtures/input/utf8.rb @@ -1,3 +1 @@ -# frozen_string_literal: true - puts " "