diff --git a/lib/annotate/annotate_models.rb b/lib/annotate/annotate_models.rb index dc2901a3..4406114b 100644 --- a/lib/annotate/annotate_models.rb +++ b/lib/annotate/annotate_models.rb @@ -39,7 +39,7 @@ module AnnotateModels } }.freeze - MAGIC_COMMENT_MATCHER = Regexp.new(/(^#\s*encoding:.*(?:\n|r\n))|(^# coding:.*(?:\n|\r\n))|(^# -\*- coding:.*(?:\n|\r\n))|(^# -\*- encoding\s?:.*(?:\n|\r\n))|(^#\s*frozen_string_literal:.+(?:\n|\r\n))|(^# -\*- frozen_string_literal\s*:.+-\*-(?:\n|\r\n))/).freeze + MAGIC_COMMENT_MATCHER = Regexp.new(/(^#\s*encoding:.*(?:\n|r\n))|(^# coding:.*(?:\n|\r\n))|(^# -\*- coding:.*(?:\n|\r\n))|(^# -\*- encoding\s?:.*(?:\n|\r\n))|(^#\s*frozen_string_literal:.+(?:\n|\r\n))|(^# -\*- frozen_string_literal\s*:.+-\*-(?:\n|\r\n))|(^#\s*typed:.+(?:\n|\r\n))/).freeze class << self def annotate_pattern(options = {}) @@ -208,6 +208,10 @@ def get_schema_info(klass, header, options = {}) # rubocop:disable Metrics/Metho end info << get_schema_footer_text(klass, options) + + info.chomp!("#\n") if options[:with_trailing_newline] + + info end def get_schema_header_text(klass, options = {}) @@ -459,6 +463,8 @@ def annotate_one_file(file_name, info_block, position, options = {}) magic_comments_block + (old_content.rstrip + "\n\n" + wrapped_info_block) elsif magic_comments_block.empty? magic_comments_block + wrapped_info_block + old_content.lstrip + elsif options[:with_trailing_newline] + magic_comments_block + "\n" + wrapped_info_block + old_content else magic_comments_block + "\n" + wrapped_info_block + old_content.lstrip end @@ -492,7 +498,8 @@ def remove_annotation_of_file(file_name, options = {}) return false if content =~ /#{SKIP_ANNOTATION_PREFIX}.*\n/ wrapper_open = options[:wrapper_open] ? "# #{options[:wrapper_open]}\n" : '' - content.sub!(/(#{wrapper_open})?#{annotate_pattern(options)}/, '') + content_end = options[:with_trailing_newline] ? "\n" : '' + content.sub!(/(#{wrapper_open})?#{annotate_pattern(options)}/, content_end) File.open(file_name, 'wb') { |f| f.puts content } diff --git a/lib/annotate/constants.rb b/lib/annotate/constants.rb index 0d322565..6909a446 100644 --- a/lib/annotate/constants.rb +++ b/lib/annotate/constants.rb @@ -25,7 +25,7 @@ module Constants OTHER_OPTIONS = [ :additional_file_patterns, :ignore_columns, :skip_on_db_migrate, :wrapper_open, :wrapper_close, :wrapper, :routes, :models, :hide_limit_column_types, :hide_default_column_types, - :ignore_routes, :active_admin + :ignore_routes, :active_admin, :with_trailing_newline ].freeze PATH_OPTIONS = [ diff --git a/lib/annotate/parser.rb b/lib/annotate/parser.rb index ad85caf5..0ae36d38 100644 --- a/lib/annotate/parser.rb +++ b/lib/annotate/parser.rb @@ -309,6 +309,11 @@ def add_options_to_parser(option_parser) # rubocop:disable Metrics/MethodLength, "include database comments in model annotations, as its own column, after all others") do env['with_comment_column'] = 'true' end + + option_parser.on('--with-trailing-newline', + "include a trailing newline in the annotation") do + env['with_trailing_newline'] = 'true' + end end end end diff --git a/spec/lib/annotate/annotate_models_spec.rb b/spec/lib/annotate/annotate_models_spec.rb index 09647461..4380384a 100644 --- a/spec/lib/annotate/annotate_models_spec.rb +++ b/spec/lib/annotate/annotate_models_spec.rb @@ -18,7 +18,11 @@ "# frozen_string_literal: true\n# encoding: utf-8", '# frozen_string_literal: true', '#frozen_string_literal: false', - '# -*- frozen_string_literal : true -*-' + '# -*- frozen_string_literal : true -*-', + '# typed: false', + '# typed: true', + '# typed: strict', + '# typed: strong', ].freeze unless const_defined?(:MAGIC_COMMENTS) def mock_index(name, params = {}) @@ -1449,6 +1453,35 @@ def mock_column(name, type, options = {}) end end end + + context 'when "with_trailing_newline" is specified in options' do + let :options do + { with_trailing_newline: true } + end + + let :columns do + [ + mock_column(:id, :integer, limit: 8), + mock_column(:name, :string, limit: 50) + ] + end + + let :expected_result do + <<~EOS + # Schema Info + # + # Table name: users + # + # id :integer not null, primary key + # name :string(50) not null + + EOS + end + + it 'works with option "with_trailing_newline"' do + is_expected.to eq expected_result + end + end end end end @@ -2527,7 +2560,7 @@ class Foo < ActiveRecord::Base describe '.remove_annotation_of_file' do subject do - AnnotateModels.remove_annotation_of_file(path) + AnnotateModels.remove_annotation_of_file(path, options) end after :each do @@ -2558,6 +2591,10 @@ class Foo < ActiveRecord::Base EOS end + let :options do + {} + end + context 'when annotation is before main content' do let :filename do 'before.rb' @@ -2759,6 +2796,43 @@ class Foo < ActiveRecord::Base expect(file_content_after_removal).to eq expected_result end end + + context 'when option "with_trailing_newline" is specified' do + let :options do + { with_trailing_newline: true } + end + + let :filename do + 'before.rb' + end + + let :file_content do + <<~EOS + # == Schema Information + # + # Table name: foo + # + # id :integer not null, primary key + # created_at :datetime + # updated_at :datetime + + class Foo < ActiveRecord::Base + end + EOS + end + + let :expected_result do + <<~EOS + + class Foo < ActiveRecord::Base + end + EOS + end + + it 'removes annotation and leaves newline before class declaration' do + expect(file_content_after_removal).to eq expected_result + end + end end describe '.resolve_filename' do diff --git a/spec/lib/annotate/annotate_routes_spec.rb b/spec/lib/annotate/annotate_routes_spec.rb index 2d27b745..b769cfe7 100644 --- a/spec/lib/annotate/annotate_routes_spec.rb +++ b/spec/lib/annotate/annotate_routes_spec.rb @@ -21,7 +21,7 @@ '# frozen_string_literal: true', '#frozen_string_literal: false', '# -*- frozen_string_literal : true -*-' - ].freeze unless const_defined?(:MAGIC_COMMENTS) + ].freeze let :stubs do {}