diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7f907097a..c75ffea20 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,11 +2,11 @@ name: CI on: pull_request: branches: - - '*' + - "*" push: branches: - - '*' + - "*" jobs: test: @@ -14,7 +14,7 @@ jobs: strategy: fail-fast: false matrix: - ruby: ['2.7'] + ruby: ["3.4.4"] steps: - name: Checkout diff --git a/.rubocop.yml b/.rubocop.yml index 4d06aa989..4e6507a8b 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -7,12 +7,18 @@ require: AllCops: Exclude: - - 'vendor/**/*' - - 'spec/fixtures/**/*' - - 'tmp/**/*' - - 'spec/integration/**/*' + - "vendor/**/*" + - "spec/fixtures/**/*" + - "tmp/**/*" + - "spec/integration/**/*" NewCops: enable Metrics/BlockLength: Exclude: - - 'spec/**/*.rb' + - "spec/**/*.rb" +Style/FetchEnvVar: + Enabled: false +Metrics/ClassLength: + Enabled: false +RSpec/RemoveConst: + Enabled: false diff --git a/.tool-versions b/.tool-versions index 9e83a384b..ca745c6d6 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1 +1 @@ -ruby 2.7.3 +ruby 3.4.4 diff --git a/Gemfile b/Gemfile index 0998ee0d0..86355de5f 100644 --- a/Gemfile +++ b/Gemfile @@ -1,14 +1,13 @@ source 'https://rubygems.org' -ruby '>= 2.4.0' +ruby '>= 3.0.0' -gem 'activerecord', '>= 4.2.5', '< 6', require: false +gem 'activerecord', '>= 4.2.5', '< 9', require: false gem 'rake', require: false group :development do gem 'bump' gem 'mg', require: false - gem 'travis', require: false platforms :mri, :mingw do gem 'yard', require: false end @@ -19,9 +18,9 @@ group :development, :test do gem 'guard-rspec', require: false gem 'rspec', require: false - gem 'rubocop', '~> 1.12.0', require: false + gem 'rubocop', '~> 1.76.1', require: false gem 'rubocop-rake', require: false - gem 'rubocop-rspec', '~> 2.2.0', require: false + gem 'rubocop-rspec', '~> 3.6.0', require: false gem 'simplecov', require: false gem 'terminal-notifier-guard', require: false @@ -29,7 +28,6 @@ group :development, :test do gem 'coveralls' gem 'overcommit' - gem 'ruby_dep', '1.5.0' platforms :mri, :mingw do gem 'pry', require: false diff --git a/annotate.gemspec b/annotate.gemspec index 43b2ac990..271625357 100644 --- a/annotate.gemspec +++ b/annotate.gemspec @@ -7,7 +7,7 @@ Gem::Specification.new do |s| s.name = 'annotate' s.version = Annotate.version - s.required_ruby_version = '>= 2.4.0' + s.required_ruby_version = '>= 3.0.0' s.required_rubygems_version = Gem::Requirement.new('>= 0') if s.respond_to? :required_rubygems_version= s.authors = ['Alex Chaffee', 'Cuong Tran', 'Marcos Piccinini', 'Turadg Aleahmad', 'Jon Frisby'] s.description = 'Annotates Rails/ActiveRecord Models, routes, fixtures, and others based on the database schema.' @@ -18,15 +18,14 @@ Gem::Specification.new do |s| s.homepage = 'https://github.com/ctran/annotate_models' s.licenses = ['Ruby'] s.require_paths = ['lib'] - s.rubygems_version = '2.1.11' s.summary = 'Annotates Rails Models, routes, fixtures, and others based on the database schema.' - s.specification_version = 4 if s.respond_to? :specification_version - s.add_runtime_dependency(%q, '>= 10.4', '< 14.0') - s.add_runtime_dependency(%q, ['>= 3.2', '< 8.0']) + s.add_dependency(%q, '>= 10.4', '< 14.0') + s.add_dependency(%q, ['>= 3.2', '< 9.0']) s.metadata = { "bug_tracker_uri" => "https://github.com/ctran/annotate_models/issues/", - "source_code_uri" => "https://github.com/ctran/annotate_models.git" + "source_code_uri" => "https://github.com/ctran/annotate_models.git", + 'rubygems_mfa_required' => 'true' } end diff --git a/lib/annotate.rb b/lib/annotate.rb index 7c54e9ea6..692f6c611 100644 --- a/lib/annotate.rb +++ b/lib/annotate.rb @@ -35,7 +35,7 @@ def self.set_defaults(options = {}) end default_value = ENV[key.to_s] unless ENV[key.to_s].blank? - ENV[key.to_s] = default_value.nil? ? nil : default_value.to_s + ENV[key.to_s] = default_value&.to_s end end @@ -89,7 +89,7 @@ def self.eager_load(options) if Rails.version.split('.').first.to_i < 3 Rails.configuration.eager_load_paths.each do |load_path| matcher = /\A#{Regexp.escape(load_path)}(.*)\.rb\Z/ - Dir.glob("#{load_path}/**/*.rb").sort.each do |file| + Dir.glob("#{load_path}/**/*.rb").each do |file| require_dependency file.sub(matcher, '\1') end end diff --git a/lib/annotate/annotate_models.rb b/lib/annotate/annotate_models.rb index dc2901a32..8bcd8bf14 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 = /(^#\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))/ class << self def annotate_pattern(options = {}) @@ -155,8 +155,8 @@ def get_schema_info(klass, header, options = {}) # rubocop:disable Metrics/Metho with_comments_column = with_comments_column?(klass, options) # Precalculate Values - cols_meta = cols.map do |col| - col_comment = with_comments || with_comments_column ? col.comment&.gsub(/\n/, "\\n") : nil + cols_meta = cols.to_h do |col| + col_comment = with_comments || with_comments_column ? col.comment&.gsub("\n", "\\n") : nil col_type = get_col_type(col) attrs = get_attributes(col, col_type, klass, options) col_name = if with_comments && col_comment @@ -166,7 +166,7 @@ def get_schema_info(klass, header, options = {}) # rubocop:disable Metrics/Metho end simple_formatted_attrs = attrs.join(", ") [col.name, { col_type: col_type, attrs: attrs, col_name: col_name, simple_formatted_attrs: simple_formatted_attrs, col_comment: col_comment }] - end.to_h + end # Output annotation bare_max_attrs_length = cols_meta.map { |_, m| m[:simple_formatted_attrs].length }.max @@ -179,15 +179,15 @@ def get_schema_info(klass, header, options = {}) # rubocop:disable Metrics/Metho col_comment = cols_meta[col.name][:col_comment] if options[:format_rdoc] - info << sprintf("# %-#{max_size}.#{max_size}s%s", "*#{col_name}*::", attrs.unshift(col_type).join(", ")).rstrip + "\n" + info << (sprintf("# %-#{max_size}.#{max_size}s%s", "*#{col_name}*::", attrs.unshift(col_type).join(", ")).rstrip + "\n") elsif options[:format_yard] - info << sprintf("# @!attribute #{col_name}") + "\n" + info << ("# @!attribute #{col_name}" + "\n") ruby_class = col.respond_to?(:array) && col.array ? "Array<#{map_col_type_to_ruby_classes(col_type)}>": map_col_type_to_ruby_classes(col_type) - info << sprintf("# @return [#{ruby_class}]") + "\n" + info << ("# @return [#{ruby_class}]" + "\n") elsif options[:format_markdown] name_remainder = max_size - col_name.length - non_ascii_length(col_name) type_remainder = (md_type_allowance - 2) - col_type.length - info << (sprintf("# **`%s`**%#{name_remainder}s | `%s`%#{type_remainder}s | `%s`", col_name, " ", col_type, " ", attrs.join(", ").rstrip)).gsub('``', ' ').rstrip + "\n" + info << ((sprintf("# **`%s`**%#{name_remainder}s | `%s`%#{type_remainder}s | `%s`", col_name, " ", col_type, " ", attrs.join(", ").rstrip)).gsub('``', ' ').rstrip + "\n") elsif with_comments_column info << format_default(col_name, max_size, col_type, bare_type_allowance, simple_formatted_attrs, bare_max_attrs_length, col_comment) else @@ -423,7 +423,7 @@ def get_check_constraint_info(klass, options = {}) # :position_in_*:: where to place the annotated section in fixture or model file, # :before, :top, :after or :bottom. Default is :before. # - def annotate_one_file(file_name, info_block, position, options = {}) + def annotate_one_file?(file_name, info_block, position, options = {}) return false unless File.exist?(file_name) old_content = File.read(file_name) return false if old_content =~ /#{SKIP_ANNOTATION_PREFIX}.*\n/ @@ -486,7 +486,7 @@ def magic_comments_as_string(content) end end - def remove_annotation_of_file(file_name, options = {}) + def remove_annotation_of_file?(file_name, options = {}) if File.exist?(file_name) content = File.read(file_name) return false if content =~ /#{SKIP_ANNOTATION_PREFIX}.*\n/ @@ -542,7 +542,7 @@ def annotate(klass, file, header, options = {}) model_file_name = File.join(file) annotated = [] - if annotate_one_file(model_file_name, info, :position_in_class, options_with_position(options, :position_in_class)) + if annotate_one_file?(model_file_name, info, :position_in_class, options_with_position(options, :position_in_class)) annotated << model_file_name end @@ -563,7 +563,7 @@ def annotate(klass, file, header, options = {}) .map { |f| expand_glob_into_files(f) } .flatten .each do |f| - if annotate_one_file(f, info, position_key, options_with_position(options, position_key)) + if annotate_one_file?(f, info, position_key, options_with_position(options, position_key)) annotated << f end end @@ -694,7 +694,7 @@ def parse_options(options = {}) end def split_model_dir(option_value) - option_value = option_value.is_a?(Array) ? option_value : option_value.split(',') + option_value = option_value.split(',') unless option_value.is_a?(Array) option_value.map(&:strip).reject(&:empty?) end @@ -762,13 +762,13 @@ def remove_annotations(options = {}) model_name = klass.name.underscore table_name = klass.table_name model_file_name = file - deannotated_klass = true if remove_annotation_of_file(model_file_name, options) + deannotated_klass = true if remove_annotation_of_file?(model_file_name, options) get_patterns(options, matched_types(options)) .map { |f| resolve_filename(f, model_name, table_name) } .each do |f| if File.exist?(f) - remove_annotation_of_file(f, options) + remove_annotation_of_file?(f, options) deannotated_klass = true end end diff --git a/lib/annotate/annotate_routes.rb b/lib/annotate/annotate_routes.rb index c9a2218ac..45b9e0c53 100644 --- a/lib/annotate/annotate_routes.rb +++ b/lib/annotate/annotate_routes.rb @@ -18,8 +18,8 @@ # Released under the same license as Ruby. No Support. No Warranty. # -require_relative './annotate_routes/helpers' -require_relative './annotate_routes/header_generator' +require_relative 'annotate_routes/helpers' +require_relative 'annotate_routes/header_generator' module AnnotateRoutes class << self @@ -95,7 +95,7 @@ def rewrite_contents(existing_text, new_text, frozen) def annotate_routes(header, content, header_position, options = {}) magic_comments_map, content = Helpers.extract_magic_comments_from_array(content) if %w(before top).include?(options[:position_in_routes]) - header = header << '' if content.first != '' + header <<= '' if content.first != '' magic_comments_map << '' if magic_comments_map.any? new_content = magic_comments_map + header + content else diff --git a/lib/annotate/annotate_routes/header_generator.rb b/lib/annotate/annotate_routes/header_generator.rb index b1c93acf7..e8c126f0f 100644 --- a/lib/annotate/annotate_routes/header_generator.rb +++ b/lib/annotate/annotate_routes/header_generator.rb @@ -1,4 +1,4 @@ -require_relative './helpers' +require_relative 'helpers' module AnnotateRoutes class HeaderGenerator @@ -16,7 +16,7 @@ def generate(options = {}) private def routes_map(options) - result = `rake routes`.chomp("\n").split(/\n/, -1) + result = `rake routes`.chomp("\n").split("\n", -1) # In old versions of Rake, the first line of output was the cwd. Not so # much in newer ones. We ditch that line if it exists, and if not, we @@ -29,7 +29,7 @@ def routes_map(options) # Skip routes which match given regex # Note: it matches the complete line (route_name, path, controller/action) if regexp_for_ignoring_routes - result.reject { |line| line =~ regexp_for_ignoring_routes } + result.grep_v(regexp_for_ignoring_routes) else result end @@ -44,18 +44,16 @@ def initialize(options, routes_map) def generate magic_comments_map, contents_without_magic_comments = Helpers.extract_magic_comments_from_array(routes_map) - out = [] - - magic_comments_map.each do |magic_comment| - out << magic_comment + out = magic_comments_map.map do |magic_comment| + magic_comment end out << '' if magic_comments_map.any? out << comment(options[:wrapper_open]) if options[:wrapper_open] - out << comment(markdown? ? PREFIX_MD : PREFIX) + timestamp_if_required + out << (comment(markdown? ? PREFIX_MD : PREFIX) + timestamp_if_required) out << comment - return out if contents_without_magic_comments.size.zero? + return out if contents_without_magic_comments.empty? maxs = [HEADER_ROW.map(&:size)] + contents_without_magic_comments[1..-1].map { |line| line.split.map(&:size) } diff --git a/lib/annotate/annotate_routes/helpers.rb b/lib/annotate/annotate_routes/helpers.rb index 1dba65bbe..91235dc50 100644 --- a/lib/annotate/annotate_routes/helpers.rb +++ b/lib/annotate/annotate_routes/helpers.rb @@ -1,6 +1,6 @@ module AnnotateRoutes module Helpers - MAGIC_COMMENT_MATCHER = Regexp.new(/(^#\s*encoding:.*)|(^# coding:.*)|(^# -\*- coding:.*)|(^# -\*- encoding\s?:.*)|(^#\s*frozen_string_literal:.+)|(^# -\*- frozen_string_literal\s*:.+-\*-)/).freeze + MAGIC_COMMENT_MATCHER = /(^#\s*encoding:.*)|(^# coding:.*)|(^# -\*- coding:.*)|(^# -\*- encoding\s?:.*)|(^#\s*frozen_string_literal:.+)|(^# -\*- frozen_string_literal\s*:.+-\*-)/ class << self # TODO: write the method doc using ruby rdoc formats @@ -15,7 +15,7 @@ def strip_annotations(content) mode = :content header_position = 0 - content.split(/\n/, -1).each_with_index do |line, line_number| + content.split("\n", -1).each_with_index do |line, line_number| if mode == :header && line !~ /\s*#/ mode = :content real_content << line unless line.blank? diff --git a/lib/annotate/constants.rb b/lib/annotate/constants.rb index 0d3225659..61176dd41 100644 --- a/lib/annotate/constants.rb +++ b/lib/annotate/constants.rb @@ -1,6 +1,6 @@ module Annotate module Constants - TRUE_RE = /^(true|t|yes|y|1)$/i.freeze + TRUE_RE = /^(true|t|yes|y|1)$/i ## # The set of available options to customize the behavior of Annotate. diff --git a/lib/annotate/parser.rb b/lib/annotate/parser.rb index ad85caf50..27597386a 100644 --- a/lib/annotate/parser.rb +++ b/lib/annotate/parser.rb @@ -2,7 +2,7 @@ module Annotate # Class for handling command line arguments - class Parser # rubocop:disable Metrics/ClassLength + class Parser def self.parse(args, env = {}) new(args, env).parse end diff --git a/spec/lib/annotate/annotate_models_spec.rb b/spec/lib/annotate/annotate_models_spec.rb index 096474610..db48de4de 100644 --- a/spec/lib/annotate/annotate_models_spec.rb +++ b/spec/lib/annotate/annotate_models_spec.rb @@ -206,7 +206,7 @@ def mock_column(name, type, options = {}) end it 'sets skip_subdirectory_model_load to true' do - is_expected.to eq(true) + is_expected.to be(true) end end @@ -220,7 +220,7 @@ def mock_column(name, type, options = {}) end it 'sets skip_subdirectory_model_load to false' do - is_expected.to eq(false) + is_expected.to be(false) end end end @@ -2525,9 +2525,9 @@ class Foo < ActiveRecord::Base end end - describe '.remove_annotation_of_file' do + describe '.remove_annotation_of_file?' do subject do - AnnotateModels.remove_annotation_of_file(path) + AnnotateModels.remove_annotation_of_file?(path) end after :each do @@ -2632,7 +2632,7 @@ class Foo < ActiveRecord::Base end subject do - AnnotateModels.remove_annotation_of_file(path, wrapper_open: 'wrapper') + AnnotateModels.remove_annotation_of_file?(path, wrapper_open: 'wrapper') end it 'removes annotation' do @@ -2662,7 +2662,7 @@ class Foo < ActiveRecord::Base end subject do - AnnotateModels.remove_annotation_of_file(path, wrapper_open: 'wrapper') + AnnotateModels.remove_annotation_of_file?(path, wrapper_open: 'wrapper') end it 'removes annotation' do @@ -2721,7 +2721,7 @@ class Foo < ActiveRecord::Base end subject do - AnnotateModels.remove_annotation_of_file(path, wrapper_close: 'wrapper') + AnnotateModels.remove_annotation_of_file?(path, wrapper_close: 'wrapper') end it 'removes annotation' do @@ -2842,15 +2842,15 @@ class User < ActiveRecord::Base def write_model(file_name, file_content) fname = File.join(@model_dir, file_name) FileUtils.mkdir_p(File.dirname(fname)) - File.open(fname, 'wb') { |f| f.write file_content } + File.binwrite(fname, file_content) [fname, file_content] end - def annotate_one_file(options = {}) + def annotate_one_file?(options = {}) Annotate.set_defaults(options) options = Annotate.setup_options(options) - AnnotateModels.annotate_one_file(@model_file_name, @schema_info, :position_in_class, options) + AnnotateModels.annotate_one_file?(@model_file_name, @schema_info, :position_in_class, options) ensure # Wipe settings so the next call will pick up new values... Annotate.instance_variable_set('@has_set_defaults', false) @@ -2861,7 +2861,7 @@ def annotate_one_file(options = {}) ['before', :before, 'top', :top].each do |position| it "should put annotation before class if :position == #{position}" do - annotate_one_file position: position + annotate_one_file? position: position expect(File.read(@model_file_name)) .to eq("#{@schema_info}#{@file_content}") end @@ -2869,14 +2869,14 @@ def annotate_one_file(options = {}) ['after', :after, 'bottom', :bottom].each do |position| it "should put annotation after class if position: #{position}" do - annotate_one_file position: position + annotate_one_file? position: position expect(File.read(@model_file_name)) .to eq("#{@file_content}\n#{@schema_info}") end end it 'should wrap annotation if wrapper is specified' do - annotate_one_file wrapper_open: 'START', wrapper_close: 'END' + annotate_one_file? wrapper_open: 'START', wrapper_close: 'END' expect(File.read(@model_file_name)) .to eq("# START\n#{@schema_info}# END\n#{@file_content}") end @@ -2899,7 +2899,7 @@ def annotate_one_file(options = {}) on_delete: :cascade) ]) @schema_info = AnnotateModels.get_schema_info(klass, '== Schema Info', show_foreign_keys: true) - annotate_one_file + annotate_one_file? end it 'should update foreign key constraint' do @@ -2918,7 +2918,7 @@ def annotate_one_file(options = {}) on_delete: :restrict) ]) @schema_info = AnnotateModels.get_schema_info(klass, '== Schema Info', show_foreign_keys: true) - annotate_one_file + annotate_one_file? expect(File.read(@model_file_name)).to eq("#{@schema_info}#{@file_content}") end end @@ -2926,46 +2926,46 @@ def annotate_one_file(options = {}) describe 'with existing annotation => :before' do before do - annotate_one_file position: :before + annotate_one_file? position: :before another_schema_info = AnnotateModels.get_schema_info(mock_class(:users, :id, [mock_column(:id, :integer)]), '== Schema Info') @schema_info = another_schema_info end it 'should retain current position' do - annotate_one_file + annotate_one_file? expect(File.read(@model_file_name)).to eq("#{@schema_info}#{@file_content}") end it 'should retain current position even when :position is changed to :after' do - annotate_one_file position: :after + annotate_one_file? position: :after expect(File.read(@model_file_name)).to eq("#{@schema_info}#{@file_content}") end it 'should change position to :after when force: true' do - annotate_one_file position: :after, force: true + annotate_one_file? position: :after, force: true expect(File.read(@model_file_name)).to eq("#{@file_content}\n#{@schema_info}") end end describe 'with existing annotation => :after' do before do - annotate_one_file position: :after + annotate_one_file? position: :after another_schema_info = AnnotateModels.get_schema_info(mock_class(:users, :id, [mock_column(:id, :integer)]), '== Schema Info') @schema_info = another_schema_info end it 'should retain current position' do - annotate_one_file + annotate_one_file? expect(File.read(@model_file_name)).to eq("#{@file_content}\n#{@schema_info}") end it 'should retain current position even when :position is changed to :before' do - annotate_one_file position: :before + annotate_one_file? position: :before expect(File.read(@model_file_name)).to eq("#{@file_content}\n#{@schema_info}") end it 'should change position to :before when force: true' do - annotate_one_file position: :before, force: true + annotate_one_file? position: :before, force: true expect(File.read(@model_file_name)).to eq("#{@schema_info}#{@file_content}") end end @@ -2989,7 +2989,7 @@ class Foo::User < ActiveRecord::Base mock_column(:name, :string, limit: 50) ]) schema_info = AnnotateModels.get_schema_info(klass, '== Schema Info') - AnnotateModels.annotate_one_file(model_file_name, schema_info, position: :before) + AnnotateModels.annotate_one_file?(model_file_name, schema_info, position: :before) expect(File.read(model_file_name)).to eq("#{schema_info}#{file_content}") end @@ -3001,7 +3001,7 @@ class User < ActiveRecord::Base end EOS - annotate_one_file position: :before + annotate_one_file? position: :before lines = magic_comment.split("\n") File.open @model_file_name do |file| @@ -3017,7 +3017,7 @@ class User < ActiveRecord::Base MAGIC_COMMENTS.each do |magic_comment| model_file_name, = write_model 'user.rb', "#{magic_comment}\n#{content}" - annotate_one_file position: :before + annotate_one_file? position: :before schema_info = AnnotateModels.get_schema_info(@klass, '== Schema Info') expect(File.read(model_file_name)).to eq("#{magic_comment}\n\n#{schema_info}#{content}") @@ -3030,7 +3030,7 @@ class User < ActiveRecord::Base schema_info = AnnotateModels.get_schema_info(@klass, '== Schema Info') model_file_name, = write_model 'user.rb', "#{magic_comment}\n\n\n\n#{content}" - annotate_one_file position: :before + annotate_one_file? position: :before expect(File.read(model_file_name)).to eq("#{magic_comment}\n\n#{schema_info}#{content}") end @@ -3041,7 +3041,7 @@ class User < ActiveRecord::Base MAGIC_COMMENTS.each do |magic_comment| model_file_name, = write_model 'user.rb', "#{magic_comment}\n#{content}" - annotate_one_file position: :after + annotate_one_file? position: :after schema_info = AnnotateModels.get_schema_info(@klass, '== Schema Info') expect(File.read(model_file_name)).to eq("#{magic_comment}\n#{content}\n#{schema_info}") @@ -3087,27 +3087,30 @@ class User < ActiveRecord::Base end it 'displays the error message and stacktrace with trace enabled' do - expect { AnnotateModels.remove_annotations model_dir: @model_dir, is_rake: true, trace: true }.to output(a_string_including("Unable to deannotate #{@model_dir}/user.rb: oops")).to_stderr - expect { AnnotateModels.remove_annotations model_dir: @model_dir, is_rake: true, trace: true }.to output(a_string_including("/user.rb:2:in `'")).to_stderr + output = capture_stderr do + AnnotateModels.remove_annotations model_dir: @model_dir, is_rake: true, trace: true + end + expect(output).to include("Unable to deannotate #{@model_dir}/user.rb: oops") + expect(output).to include('/user.rb:2:in') end end describe 'frozen option' do - it "should abort without existing annotation when frozen: true " do - expect { annotate_one_file frozen: true }.to raise_error SystemExit, /user.rb needs to be updated, but annotate was run with `--frozen`./ + it "should abort without existing annotation when frozen: true" do + expect { annotate_one_file? frozen: true }.to raise_error SystemExit, /user.rb needs to be updated, but annotate was run with `--frozen`./ end - it "should abort with different annotation when frozen: true " do - annotate_one_file + it "should abort with different annotation when frozen: true" do + annotate_one_file? another_schema_info = AnnotateModels.get_schema_info(mock_class(:users, :id, [mock_column(:id, :integer)]), '== Schema Info') @schema_info = another_schema_info - expect { annotate_one_file frozen: true }.to raise_error SystemExit, /user.rb needs to be updated, but annotate was run with `--frozen`./ + expect { annotate_one_file? frozen: true }.to raise_error SystemExit, /user.rb needs to be updated, but annotate was run with `--frozen`./ end - it "should NOT abort with same annotation when frozen: true " do - annotate_one_file - expect { annotate_one_file frozen: true }.not_to raise_error + it "should NOT abort with same annotation when frozen: true" do + annotate_one_file? + expect { annotate_one_file? frozen: true }.not_to raise_error end end end @@ -3126,7 +3129,7 @@ class Foo < ActiveRecord::Base; end after { Object.send :remove_const, 'Foo' } it 'skips attempt to annotate if no table exists for model' do - is_expected.to eq nil + is_expected.to be_nil end context 'with a non-class' do diff --git a/spec/lib/annotate/helpers_spec.rb b/spec/lib/annotate/helpers_spec.rb index b8de5df52..3e0a987c2 100644 --- a/spec/lib/annotate/helpers_spec.rb +++ b/spec/lib/annotate/helpers_spec.rb @@ -102,20 +102,20 @@ describe '.fallback' do subject { described_class.fallback(*args) } - let(:args) { [arg_1, arg_2] } + let(:args) { [blank_arg, boolean_arg] } - let(:arg_1) { '' } # is considered blank - let(:arg_2) { 'yes' } + let(:blank_arg) { '' } # is considered blank + let(:boolean_arg) { 'yes' } it 'returns the first non-blank argument' do - is_expected.to eq(arg_2) + is_expected.to eq(boolean_arg) end context 'when the first argument is non-blank' do - let(:arg_1) { 'yes' } - let(:arg_2) { 'no' } + let(:blank_arg) { 'yes' } + let(:boolean_arg) { 'no' } - it { is_expected.to eq(arg_1) } + it { is_expected.to eq(blank_arg) } end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e461e55bb..1e9a182b8 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -24,7 +24,24 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '../lib')) $LOAD_PATH.unshift(File.dirname(__FILE__)) +require 'logger' require 'active_support' + +# Ruby 3 removes File.exists? in favor of File.exist? +unless File.respond_to?(:exists?) + class << File + alias exists? exist? + end +end + +def capture_stderr + old = $stderr + $stderr = StringIO.new + yield + $stderr.string +ensure + $stderr = old +end require 'active_support/core_ext/object/blank' require 'active_support/core_ext/class/subclasses' require 'active_support/core_ext/string/inflections'