diff --git a/.envrc b/.envrc
new file mode 100644
index 000000000..fe464f42a
--- /dev/null
+++ b/.envrc
@@ -0,0 +1,6 @@
+use_devbox() {
+    watch_file devbox.json
+    eval $(devbox shell --print-env)
+}
+
+# use devbox
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7f907097a..540e25d31 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -14,20 +14,26 @@ jobs:
     strategy:
       fail-fast: false
       matrix:
-        ruby: ['2.7']
+        ruby: ['2.7.6']
 
     steps:
       - name: Checkout
-        uses: actions/checkout@v3
+        uses: actions/checkout@v4
 
       - name: Setup Ruby
         uses: ruby/setup-ruby@v1
         with:
           ruby-version: ${{ matrix.ruby }}
-          bundler-cache: true
+          bundler-cache: false
+
+      - name: Update RubyGems and install dependencies
+        run: |
+          gem update --system 3.3.22
+          bundle config set --local force_ruby_platform true
+          bundle install --jobs 4 --retry 3
 
       - name: Run Tests
         run: bundle exec rspec
 
       - name: Rubocop
-        run: bundle exec rubocop
+        run: bundle exec rubocop
\ No newline at end of file
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 633796457..782237689 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -25,7 +25,7 @@ jobs:
 
     steps:
     - name: Checkout repository
-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
 
     # Initializes the CodeQL tools for scanning.
     - name: Initialize CodeQL
diff --git a/.gitignore b/.gitignore
index bd5cc0e0b..cee045635 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,13 +2,7 @@
 .DS_Store
 .bundle
 .gems
-.rbenv-version
-.ruby-*
-/.idea/
-/.rbx
-/.rvmrc
-/.yardoc/*
-/Gemfile.lock
+/out
 /coverage/*
 /dist
 /doc/*
diff --git a/.gitpod.Dockerfile b/.gitpod.Dockerfile
new file mode 100644
index 000000000..f556d4709
--- /dev/null
+++ b/.gitpod.Dockerfile
@@ -0,0 +1,11 @@
+FROM gitpod/workspace-full
+USER gitpod
+
+# Install Ruby version 2.7.6 and set it as default
+RUN _ruby_version=ruby-2.7.6 \
+    && printf "rvm_gems_path=/home/gitpod/.rvm\n" > ~/.rvmrc \
+    && bash -lc "rvm reinstall ${_ruby_version} && \
+                 rvm use ${_ruby_version} --default" \
+    && printf "rvm_gems_path=/workspace/.rvm" > ~/.rvmrc \
+    && printf "{ rvm use \$(rvm current); } >/dev/null 2>&1\n" >> "$HOME/.bashrc.d/70-ruby"
+
diff --git a/.gitpod.yml b/.gitpod.yml
new file mode 100644
index 000000000..76f9bdd96
--- /dev/null
+++ b/.gitpod.yml
@@ -0,0 +1,20 @@
+image:
+  file: .gitpod.Dockerfile
+
+github:
+  prebuilds:
+    develop: true
+    # enable for pull requests coming from this repo (defaults to true)
+    pullRequests: true
+
+    # add a "Review in Gitpod" button as a comment to pull requests (defaults to true)
+    addComment: true
+
+    # add a "Review in Gitpod" button to pull requests (defaults to false)
+    addBadge: true
+    
+    # add a label once the prebuild is ready to pull requests (defaults to false)
+    addLabel: prebuilt-in-gitpod
+    
+tasks:
+  - init: bundle install
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 000000000..19f20a0a4
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectRootManager" version="2" project-jdk-name="rbenv: 2.7.6" project-jdk-type="RUBY_SDK">
+    <output url="file://$PROJECT_DIR$/out" />
+  </component>
+</project>
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 000000000..be4c02422
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/annotate_models.iml" filepath="$PROJECT_DIR$/annotate_models2.iml" />
+    </modules>
+  </component>
+</project>
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 000000000..35eb1ddfb
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="" vcs="Git" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
new file mode 100644
index 000000000..90b77e517
--- /dev/null
+++ b/.idea/workspace.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="AutoImportSettings">
+    <option name="autoReloadType" value="SELECTIVE" />
+  </component>
+  <component name="ChangeListManager">
+    <list default="true" id="b64f608a-08cf-47e7-bb0a-48e62ee0ebe4" name="Changes" comment="" />
+    <option name="SHOW_DIALOG" value="false" />
+    <option name="HIGHLIGHT_CONFLICTS" value="true" />
+    <option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
+    <option name="LAST_RESOLUTION" value="IGNORE" />
+  </component>
+  <component name="Git.Settings">
+    <option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
+  </component>
+  <component name="MacroExpansionManager">
+    <option name="directoryName" value="w81fe55f" />
+  </component>
+  <component name="ProjectId" id="2JMR0RqCAUSI4iMuqzKXFYOHMrm" />
+  <component name="ProjectViewState">
+    <option name="autoscrollToSource" value="true" />
+    <option name="hideEmptyMiddlePackages" value="true" />
+    <option name="showExcludedFiles" value="false" />
+    <option name="showLibraryContents" value="true" />
+  </component>
+  <component name="PropertiesComponent">{
+  &quot;keyToString&quot;: {
+    &quot;RunOnceActivity.OpenProjectViewOnStart&quot;: &quot;true&quot;,
+    &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
+    &quot;WebServerToolWindowFactoryState&quot;: &quot;false&quot;,
+    &quot;node.js.detected.package.eslint&quot;: &quot;true&quot;,
+    &quot;node.js.detected.package.tslint&quot;: &quot;true&quot;,
+    &quot;node.js.selected.package.eslint&quot;: &quot;(autodetect)&quot;,
+    &quot;node.js.selected.package.tslint&quot;: &quot;(autodetect)&quot;,
+    &quot;project.structure.last.edited&quot;: &quot;Modules&quot;,
+    &quot;project.structure.proportion&quot;: &quot;0.0&quot;,
+    &quot;project.structure.side.proportion&quot;: &quot;0.2&quot;,
+    &quot;ruby.rails.projectView.checked&quot;: &quot;true&quot;,
+    &quot;vue.rearranger.settings.migration&quot;: &quot;true&quot;
+  }
+}</component>
+  <component name="RubyModuleManagerSettings">
+    <option name="blackListedRootsPaths">
+      <list>
+        <option value="$PROJECT_DIR$" />
+      </list>
+    </option>
+  </component>
+  <component name="RunManager">
+    <configuration name="annotate_models_spec" type="RubyRunConfigurationType" factoryName="Ruby" temporary="true">
+      <RUBY_RUN_CONFIG NAME="RUBY_ARGS" VALUE="" />
+      <RUBY_RUN_CONFIG NAME="WORK DIR" VALUE="$PROJECT_DIR$" />
+      <RUBY_RUN_CONFIG NAME="SHOULD_USE_SDK" VALUE="false" />
+      <RUBY_RUN_CONFIG NAME="ALTERN_SDK_NAME" VALUE="" />
+      <RUBY_RUN_CONFIG NAME="myPassParentEnvs" VALUE="true" />
+      <EXTENSION ID="BundlerRunConfigurationExtension" BUNDLE_MODE="AUTO" bundleExecEnabled="true" />
+      <EXTENSION ID="RubyCoverageRunConfigurationExtension" track_test_folders="true" runner="rcov" ENABLE_BRANCH_COVERAGE="true" ENABLE_FORKED_COVERAGE="true">
+        <COVERAGE_PATTERN ENABLED="true">
+          <PATTERN REGEXPS="/.rvm/" INCLUDED="false" />
+        </COVERAGE_PATTERN>
+      </EXTENSION>
+      <EXTENSION ID="org.jetbrains.plugins.ruby.rails.run.RailsRunConfigurationExtension" SCRATCH_USE_RAILS_RUNNER="false" />
+      <RUBY_RUN_CONFIG NAME="SCRIPT_PATH" VALUE="$PROJECT_DIR$/spec/lib/annotate/annotate_models_spec.rb" />
+      <RUBY_RUN_CONFIG NAME="SCRIPT_ARGS" VALUE="" />
+      <method v="2">
+        <option name="Make" enabled="true" />
+      </method>
+    </configuration>
+    <recent_temporary>
+      <list>
+        <item itemvalue="Ruby.annotate_models_spec" />
+      </list>
+    </recent_temporary>
+  </component>
+  <component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
+  <component name="SpringUtil" SPRING_PRE_LOADER_OPTION="false" RAKE_SPRING_PRE_LOADER_OPTION="false" RAILS_SPRING_PRE_LOADER_OPTION="false" />
+  <component name="TaskManager">
+    <task active="true" id="Default" summary="Default task">
+      <changelist id="b64f608a-08cf-47e7-bb0a-48e62ee0ebe4" name="Changes" comment="" />
+      <created>1671885743351</created>
+      <option name="number" value="Default" />
+      <option name="presentableId" value="Default" />
+      <updated>1671885743351</updated>
+      <workItem from="1671885744568" duration="21000" />
+      <workItem from="1671926398703" duration="724000" />
+    </task>
+    <servers />
+  </component>
+  <component name="TypeScriptGeneratedFilesManager">
+    <option name="version" value="3" />
+  </component>
+  <component name="com.intellij.coverage.CoverageDataManagerImpl">
+    <SUITE FILE_PATH="coverage/annotate_models@file_patterns_spec.rcov" NAME="file_patterns_spec Coverage Results" MODIFIED="1671926022651" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="rcov" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" MODULE_NAME="annotate_models" />
+    <SUITE FILE_PATH="coverage/annotate_models@annotate_models_spec.rcov" NAME="annotate_models_spec Coverage Results" MODIFIED="1671927602491" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="rcov" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
+    <SUITE FILE_PATH="coverage/annotate_models@annotate_routes_spec.rcov" NAME="annotate_routes_spec Coverage Results" MODIFIED="1671926030854" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="rcov" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" MODULE_NAME="annotate_models" />
+    <SUITE FILE_PATH="coverage/annotate_models@All_specs_in_spec__annotate_models.rcov" NAME="All specs in spec: annotate_models Coverage Results" MODIFIED="1671926004257" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="rcov" COVERAGE_BY_TEST_ENABLED="true" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" MODULE_NAME="annotate_models" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.rbenv-gemsets b/.rbenv-gemsets
deleted file mode 100644
index a9606b749..000000000
--- a/.rbenv-gemsets
+++ /dev/null
@@ -1 +0,0 @@
-.gems
diff --git a/.rubocop.yml b/.rubocop.yml
index 4d06aa989..c34d3beef 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -12,7 +12,15 @@ AllCops:
     - 'tmp/**/*'
     - 'spec/integration/**/*'
   NewCops: enable
+  TargetRubyVersion: 2.7
+
+Lint/FormatParameterMismatch:
+  Enabled: false
 
 Metrics/BlockLength:
   Exclude:
     - 'spec/**/*.rb'
+
+Metrics/ClassLength:
+  Exclude:
+    - 'lib/annotate/annotate_models.rb'
diff --git a/.ruby-version b/.ruby-version
new file mode 100644
index 000000000..49cdd668e
--- /dev/null
+++ b/.ruby-version
@@ -0,0 +1 @@
+2.7.6
diff --git a/.tool-versions b/.tool-versions
index 9e83a384b..33a8789fa 100644
--- a/.tool-versions
+++ b/.tool-versions
@@ -1 +1 @@
-ruby 2.7.3
+ruby 2.7.7
diff --git a/Gemfile b/Gemfile
index 0998ee0d0..4d7cf902e 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,14 +1,13 @@
 source 'https://rubygems.org'
 
-ruby '>= 2.4.0'
+ruby '>= 2.7.6'
 
-gem 'activerecord', '>= 4.2.5', '< 6', require: false
+gem 'activerecord', '>= 4.2.5', 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,17 +18,13 @@ group :development, :test do
   gem 'guard-rspec', require: false
   gem 'rspec', require: false
 
-  gem 'rubocop', '~> 1.12.0', require: false
+  gem 'rubocop', '~> 1.59.0', require: false
   gem 'rubocop-rake', require: false
-  gem 'rubocop-rspec', '~> 2.2.0', require: false
+  gem 'rubocop-rspec', '~> 2.25.0', require: false
   gem 'simplecov', require: false
   gem 'terminal-notifier-guard', require: false
 
-  gem 'codeclimate-test-reporter'
-  gem 'coveralls'
-
   gem 'overcommit'
-  gem 'ruby_dep', '1.5.0'
 
   platforms :mri, :mingw do
     gem 'pry', require: false
@@ -38,6 +33,5 @@ group :development, :test do
 end
 
 group :test do
-  gem 'files', require: false
   gem 'git', require: false
 end
diff --git a/Gemfile.lock b/Gemfile.lock
new file mode 100644
index 000000000..e4d98dc65
--- /dev/null
+++ b/Gemfile.lock
@@ -0,0 +1,180 @@
+GEM
+  remote: https://rubygems.org/
+  specs:
+    activemodel (7.1.5.1)
+      activesupport (= 7.1.5.1)
+    activerecord (7.1.5.1)
+      activemodel (= 7.1.5.1)
+      activesupport (= 7.1.5.1)
+      timeout (>= 0.4.0)
+    activesupport (7.1.5.1)
+      base64
+      benchmark (>= 0.3)
+      bigdecimal
+      concurrent-ruby (~> 1.0, >= 1.0.2)
+      connection_pool (>= 2.2.5)
+      drb
+      i18n (>= 1.6, < 2)
+      logger (>= 1.4.2)
+      minitest (>= 5.1)
+      mutex_m
+      securerandom (>= 0.3)
+      tzinfo (~> 2.0)
+    addressable (2.8.7)
+      public_suffix (>= 2.0.2, < 7.0)
+    ast (2.4.2)
+    base64 (0.2.0)
+    benchmark (0.4.0)
+    bigdecimal (3.1.9)
+    bump (0.10.0)
+    byebug (11.1.3)
+    childprocess (5.1.0)
+      logger (~> 1.5)
+    coderay (1.1.3)
+    concurrent-ruby (1.3.4)
+    connection_pool (2.4.1)
+    diff-lcs (1.5.1)
+    docile (1.4.1)
+    drb (2.2.1)
+    ffi (1.17.0)
+    formatador (1.1.0)
+    git (1.19.1)
+      addressable (~> 2.8)
+      rchardet (~> 1.8)
+    guard (2.19.0)
+      formatador (>= 0.2.4)
+      listen (>= 2.7, < 4.0)
+      lumberjack (>= 1.0.12, < 2.0)
+      nenv (~> 0.1)
+      notiffany (~> 0.0)
+      pry (>= 0.13.0)
+      shellany (~> 0.0)
+      thor (>= 0.18.1)
+    guard-compat (1.2.1)
+    guard-rspec (4.7.3)
+      guard (~> 2.1)
+      guard-compat (~> 1.1)
+      rspec (>= 2.99.0, < 4.0)
+    i18n (1.14.6)
+      concurrent-ruby (~> 1.0)
+    iniparse (1.5.0)
+    json (2.9.1)
+    language_server-protocol (3.17.0.3)
+    listen (3.9.0)
+      rb-fsevent (~> 0.10, >= 0.10.3)
+      rb-inotify (~> 0.9, >= 0.9.10)
+    logger (1.6.4)
+    lumberjack (1.2.10)
+    method_source (1.1.0)
+    mg (0.0.8)
+      rake
+    minitest (5.25.4)
+    mutex_m (0.3.0)
+    nenv (0.3.0)
+    notiffany (0.1.3)
+      nenv (~> 0.1)
+      shellany (~> 0.0)
+    overcommit (0.64.1)
+      childprocess (>= 0.6.3, < 6)
+      iniparse (~> 1.4)
+      rexml (>= 3.3.9)
+    parallel (1.26.3)
+    parser (3.3.6.0)
+      ast (~> 2.4.1)
+      racc
+    pry (0.15.2)
+      coderay (~> 1.1)
+      method_source (~> 1.0)
+    pry-byebug (3.8.0)
+      byebug (~> 11.0)
+      pry (~> 0.10)
+    public_suffix (5.1.1)
+    racc (1.8.1)
+    rainbow (3.1.1)
+    rake (13.2.1)
+    rb-fsevent (0.11.2)
+    rb-inotify (0.11.1)
+      ffi (~> 1.0)
+    rchardet (1.8.0)
+    regexp_parser (2.10.0)
+    rexml (3.4.0)
+    rspec (3.13.0)
+      rspec-core (~> 3.13.0)
+      rspec-expectations (~> 3.13.0)
+      rspec-mocks (~> 3.13.0)
+    rspec-core (3.13.2)
+      rspec-support (~> 3.13.0)
+    rspec-expectations (3.13.3)
+      diff-lcs (>= 1.2.0, < 2.0)
+      rspec-support (~> 3.13.0)
+    rspec-mocks (3.13.2)
+      diff-lcs (>= 1.2.0, < 2.0)
+      rspec-support (~> 3.13.0)
+    rspec-support (3.13.2)
+    rubocop (1.59.0)
+      json (~> 2.3)
+      language_server-protocol (>= 3.17.0)
+      parallel (~> 1.10)
+      parser (>= 3.2.2.4)
+      rainbow (>= 2.2.2, < 4.0)
+      regexp_parser (>= 1.8, < 3.0)
+      rexml (>= 3.2.5, < 4.0)
+      rubocop-ast (>= 1.30.0, < 2.0)
+      ruby-progressbar (~> 1.7)
+      unicode-display_width (>= 2.4.0, < 3.0)
+    rubocop-ast (1.37.0)
+      parser (>= 3.3.1.0)
+    rubocop-capybara (2.21.0)
+      rubocop (~> 1.41)
+    rubocop-factory_bot (2.26.0)
+      rubocop (~> 1.41)
+    rubocop-rake (0.6.0)
+      rubocop (~> 1.0)
+    rubocop-rspec (2.25.0)
+      rubocop (~> 1.40)
+      rubocop-capybara (~> 2.17)
+      rubocop-factory_bot (~> 2.22)
+    ruby-progressbar (1.13.0)
+    securerandom (0.3.2)
+    shellany (0.0.1)
+    simplecov (0.22.0)
+      docile (~> 1.1)
+      simplecov-html (~> 0.11)
+      simplecov_json_formatter (~> 0.1)
+    simplecov-html (0.13.1)
+    simplecov_json_formatter (0.1.4)
+    terminal-notifier-guard (1.7.0)
+    thor (1.3.2)
+    timeout (0.4.3)
+    tzinfo (2.0.6)
+      concurrent-ruby (~> 1.0)
+    unicode-display_width (2.6.0)
+    yard (0.9.37)
+
+PLATFORMS
+  ruby
+
+DEPENDENCIES
+  activerecord (>= 4.2.5)
+  bump
+  byebug
+  git
+  guard-rspec
+  mg
+  overcommit
+  pry
+  pry-byebug
+  rake
+  rspec
+  rubocop (~> 1.59.0)
+  rubocop-rake
+  rubocop-rspec (~> 2.25.0)
+  simplecov
+  terminal-notifier-guard
+  yard
+
+RUBY VERSION
+   ruby 2.7.7p221
+
+BUNDLED WITH
+   2.1.4
diff --git a/README.md b/README.md
index bac488d5d..b2b4d79cb 100644
--- a/README.md
+++ b/README.md
@@ -49,7 +49,7 @@ when using `SpatialAdapter`, `PostgisAdapter` or `PostGISAdapter`:
 #  path            :geometry        line_string, 4326
 ```
 
-Also, if you pass the `-r` option, it'll annotate `routes.rb` with the output of `rake routes`.
+Also, if you pass the `-r` option, it'll annotate `routes.rb` with the output of `rake/rails routes`.
 
 
 ## Upgrading to 3.X and annotate models not working?
@@ -217,7 +217,7 @@ you can do so with a simple environment variable, instead of editing the
                                          If --w option is used, the same text will be used as opening and closing
             --wo, --wrapper-open STR     Annotation wrapper opening.
             --wc, --wrapper-close STR    Annotation wrapper closing
-        -r, --routes                     Annotate routes.rb with the output of 'rake routes'
+        -r, --routes                     Annotate routes.rb with the output of 'rake/rails routes'
             --models                     Annotate ActiveRecord models
         -a, --active-admin               Annotate active_admin models
         -v, --version                    Show the current version of this gem
diff --git a/annotate.gemspec b/annotate.gemspec
index 43b2ac990..f04a0ff44 100644
--- a/annotate.gemspec
+++ b/annotate.gemspec
@@ -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<rake>, '>= 10.4', '< 14.0')
-  s.add_runtime_dependency(%q<activerecord>, ['>= 3.2', '< 8.0'])
+  s.add_runtime_dependency(%q<activerecord>, ['>= 3.2'])
 
   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/annotate_models.iml b/annotate_models.iml
new file mode 100644
index 000000000..7a2f51e5b
--- /dev/null
+++ b/annotate_models.iml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="RUBY_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="true">
+    <exclude-output />
+    <content url="file://$MODULE_DIR$">
+      <sourceFolder url="file://$MODULE_DIR$/features" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/lib" isTestSource="false" />
+      <sourceFolder url="file://$MODULE_DIR$/spec" isTestSource="true" />
+      <sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
+    </content>
+    <orderEntry type="jdk" jdkName="rbenv: 2.7.6" jdkType="RUBY_SDK" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="library" scope="PROVIDED" name="activemodel (v5.2.8.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="activerecord (v5.2.8.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="activesupport (v5.2.8.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="addressable (v2.8.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="arel (v9.0.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="ast (v2.4.2, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="bump (v0.10.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="bundler (v2.4.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="byebug (v11.1.3, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="childprocess (v4.1.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="codeclimate-test-reporter (v1.0.7, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="coderay (v1.1.3, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="concurrent-ruby (v1.1.10, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="coveralls (v0.8.23, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="diff-lcs (v1.5.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="docile (v1.4.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="faraday (v1.10.2, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="faraday-em_http (v1.0.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="faraday-em_synchrony (v1.0.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="faraday-excon (v1.1.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="faraday-httpclient (v1.0.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="faraday-multipart (v1.0.4, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="faraday-net_http (v1.0.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="faraday-net_http_persistent (v1.2.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="faraday-patron (v1.0.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="faraday-rack (v1.0.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="faraday-retry (v1.0.3, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="faraday_middleware (v1.2.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="ffi (v1.15.5, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="files (v0.4.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="formatador (v1.1.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="gh (v0.18.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="git (v1.13.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="guard (v2.18.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="guard-compat (v1.2.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="guard-rspec (v4.7.3, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="highline (v2.0.3, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="i18n (v1.12.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="iniparse (v1.5.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="json (v2.6.3, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="json_pure (v2.6.3, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="launchy (v2.4.3, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="listen (v3.7.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="lumberjack (v1.2.8, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="method_source (v1.0.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="mg (v0.0.8, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="minitest (v5.16.3, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="multi_json (v1.15.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="multipart-post (v2.2.3, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="nenv (v0.3.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="net-http-persistent (v2.9.4, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="net-http-pipeline (v1.0.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="notiffany (v0.1.3, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="overcommit (v0.59.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="parallel (v1.22.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="parser (v3.1.3.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="pry (v0.14.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="pry-byebug (v3.10.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="public_suffix (v5.0.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="pusher-client (v0.6.2, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="rainbow (v3.1.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="rake (v13.0.6, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="rb-fsevent (v0.11.2, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="rb-inotify (v0.10.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="rchardet (v1.8.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="regexp_parser (v2.6.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="rexml (v3.2.5, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="rspec (v3.12.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="rspec-core (v3.12.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="rspec-expectations (v3.12.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="rspec-mocks (v3.12.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="rspec-support (v3.12.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="rubocop (v1.12.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="rubocop-ast (v1.24.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="rubocop-rake (v0.6.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="rubocop-rspec (v2.2.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="ruby-progressbar (v1.11.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="ruby2_keywords (v0.0.5, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="ruby_dep (v1.5.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="shellany (v0.0.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="simplecov (v0.16.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="simplecov-html (v0.10.2, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="sync (v0.5.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="term-ansicolor (v1.7.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="terminal-notifier-guard (v1.7.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="thor (v1.2.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="thread_safe (v0.3.6, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="tins (v1.32.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="travis (v1.11.1, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="tzinfo (v1.2.10, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="unicode-display_width (v2.3.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="webrick (v1.7.0, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="websocket (v1.2.9, rbenv: 2.7.6) [gem]" level="application" />
+    <orderEntry type="library" scope="PROVIDED" name="yard (v0.9.28, rbenv: 2.7.6) [gem]" level="application" />
+  </component>
+</module>
\ No newline at end of file
diff --git a/bin/annotate b/bin/annotate
index feb0c0421..59643481f 100755
--- a/bin/annotate
+++ b/bin/annotate
@@ -24,7 +24,7 @@ options_result = Annotate::Parser.parse(ARGV)
 exit if options_result[:exit]
 
 options = Annotate.setup_options(
-  is_rake: ENV['is_rake'] && !ENV['is_rake'].empty?
+  is_rake: ENV.fetch('is_rake', nil) && !ENV['is_rake'].empty?
 )
 Annotate.eager_load(options) if Annotate::Helpers.include_models?
 
diff --git a/devbox.json b/devbox.json
new file mode 100644
index 000000000..14d2b499d
--- /dev/null
+++ b/devbox.json
@@ -0,0 +1,12 @@
+{
+  "packages": [
+    "ruby",
+    "bundler"
+  ],
+  "shell": {
+    "init_hook": "bundle install"
+  },
+  "nixpkgs": {
+    "commit": "52e3e80afff4b16ccb7c52e9f0f5220552f03d04"
+  }
+}
\ No newline at end of file
diff --git a/lib/annotate.rb b/lib/annotate.rb
index 7c54e9ea6..279417581 100644
--- a/lib/annotate.rb
+++ b/lib/annotate.rb
@@ -44,13 +44,13 @@ def self.set_defaults(options = {})
   #
   def self.setup_options(options = {})
     Constants::POSITION_OPTIONS.each do |key|
-      options[key] = Annotate::Helpers.fallback(ENV[key.to_s], ENV['position'], 'before')
+      options[key] = Annotate::Helpers.fallback(ENV.fetch(key.to_s, nil), ENV.fetch('position', nil), 'before')
     end
     Constants::FLAG_OPTIONS.each do |key|
-      options[key] = Annotate::Helpers.true?(ENV[key.to_s])
+      options[key] = Annotate::Helpers.true?(ENV.fetch(key.to_s, nil))
     end
     Constants::OTHER_OPTIONS.each do |key|
-      options[key] = !ENV[key.to_s].blank? ? ENV[key.to_s] : nil
+      options[key] = !ENV[key.to_s].blank? ? ENV.fetch(key.to_s, nil) : nil
     end
     Constants::PATH_OPTIONS.each do |key|
       options[key] = !ENV[key.to_s].blank? ? ENV[key.to_s].split(',') : []
diff --git a/lib/annotate/annotate_models.rb b/lib/annotate/annotate_models.rb
index dc2901a32..b346c3c1c 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))/.freeze
 
   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<tt>%s</tt>", "*#{col_name}*::", attrs.unshift(col_type).join(", ")).rstrip + "\n"
+          info << (sprintf("# %-#{max_size}.#{max_size}s<tt>%s</tt>", "*#{col_name}*::", attrs.unshift(col_type).join(", ")).rstrip + "\n")
         elsif options[:format_yard]
-          info << sprintf("# @!attribute #{col_name}") + "\n"
+          info << (sprintf("# @!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 << (sprintf("#   @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
@@ -433,7 +433,7 @@ def annotate_one_file(file_name, info_block, position, options = {})
       old_header = old_content.match(header_pattern).to_s
       new_header = info_block.match(header_pattern).to_s
 
-      column_pattern = /^#[\t ]+[\w\*\.`]+[\t ]+.+$/
+      column_pattern = /^#[\t ]+[^\t ]+[\t ]+.+$/
       old_columns = old_header && old_header.scan(column_pattern).sort
       new_columns = new_header && new_header.scan(column_pattern).sort
 
@@ -521,6 +521,7 @@ def matched_types(options)
     #  :position_in_fixture<Symbol>:: where to place the annotated section in fixture file
     #  :position_in_factory<Symbol>:: where to place the annotated section in factory file
     #  :position_in_serializer<Symbol>:: where to place the annotated section in serializer file
+    #  :position_in_additional_file_patterns<Symbol>:: where to place the annotated section in files that match additional patterns
     #  :exclude_tests<Symbol>:: whether to skip modification of test/spec files
     #  :exclude_fixtures<Symbol>:: whether to skip modification of fixture files
     #  :exclude_factories<Symbol>:: whether to skip modification of factory files
@@ -669,6 +670,15 @@ def get_loaded_model(model_path, file)
         $LOAD_PATH.map(&:to_s)
                   .select { |path| absolute_file.include?(path) }
                   .map { |path| absolute_file.sub(path, '').sub(/\.rb$/, '').sub(/^\//, '') }
+
+      # Handle Rails apps with collapsed model paths
+      model_paths = model_paths
+                    .select do |mpath|
+                      defined?(Rails) &&
+                      Rails.autoloaders.main.collapse_dirs.any? &&
+                      Rails.autoloaders.main.collapse_dirs.select { |path| path.match(mpath) }
+                    end.map { |mpath| mpath.sub(/\/models/, '') }
+
       model_paths
         .map { |path| get_loaded_model_by_path(path) }
         .find { |loaded_model| !loaded_model.nil? }
@@ -694,7 +704,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
 
diff --git a/lib/annotate/annotate_routes.rb b/lib/annotate/annotate_routes.rb
index c9a2218ac..dd4e9fcdb 100644
--- a/lib/annotate/annotate_routes.rb
+++ b/lib/annotate/annotate_routes.rb
@@ -4,7 +4,7 @@
 #
 #
 #
-# Prepends the output of "rake routes" to the top of your routes.rb file.
+# Prepends the output of "rake/rails routes" to the top of your routes.rb file.
 # Yes, it's simple but I'm thick and often need a reminder of what my routes
 # mean.
 #
@@ -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..ce370f720 100644
--- a/lib/annotate/annotate_routes/header_generator.rb
+++ b/lib/annotate/annotate_routes/header_generator.rb
@@ -1,3 +1,4 @@
+require 'active_record'
 require_relative './helpers'
 
 module AnnotateRoutes
@@ -16,7 +17,8 @@ def generate(options = {})
       private
 
       def routes_map(options)
-        result = `rake routes`.chomp("\n").split(/\n/, -1)
+        command = ActiveRecord.version.to_s.first.to_i > 5 ? `rails routes` : `rake routes`
+        result = command.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 +31,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
@@ -53,9 +55,9 @@ def generate
 
       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..9cf0dc303 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*:.+-\*-)/.freeze
 
     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..24b978acd 100644
--- a/lib/annotate/constants.rb
+++ b/lib/annotate/constants.rb
@@ -8,7 +8,7 @@ module Constants
     POSITION_OPTIONS = [
       :position_in_routes, :position_in_class, :position_in_test,
       :position_in_fixture, :position_in_factory, :position,
-      :position_in_serializer
+      :position_in_serializer, :position_in_additional_file_patterns
     ].freeze
 
     FLAG_OPTIONS = [
diff --git a/lib/annotate/helpers.rb b/lib/annotate/helpers.rb
index 705685790..b65b46747 100644
--- a/lib/annotate/helpers.rb
+++ b/lib/annotate/helpers.rb
@@ -3,15 +3,15 @@ module Annotate
   class Helpers
     class << self
       def skip_on_migration?
-        ENV['ANNOTATE_SKIP_ON_DB_MIGRATE'] =~ Constants::TRUE_RE || ENV['skip_on_db_migrate'] =~ Constants::TRUE_RE
+        ENV.fetch('ANNOTATE_SKIP_ON_DB_MIGRATE', nil) =~ Constants::TRUE_RE || ENV.fetch('skip_on_db_migrate', nil) =~ Constants::TRUE_RE
       end
 
       def include_routes?
-        ENV['routes'] =~ Constants::TRUE_RE
+        ENV.fetch('routes', nil) =~ Constants::TRUE_RE
       end
 
       def include_models?
-        ENV['models'] =~ Constants::TRUE_RE
+        ENV.fetch('models', nil) =~ Constants::TRUE_RE
       end
 
       def true?(val)
diff --git a/lib/annotate/parser.rb b/lib/annotate/parser.rb
index ad85caf50..93f9ff1e5 100644
--- a/lib/annotate/parser.rb
+++ b/lib/annotate/parser.rb
@@ -15,7 +15,7 @@ def self.parse(args, env = {})
     }.freeze
 
     ANNOTATION_POSITIONS = %w[before top after bottom].freeze
-    FILE_TYPE_POSITIONS = %w[position_in_class position_in_factory position_in_fixture position_in_test position_in_routes position_in_serializer].freeze
+    FILE_TYPE_POSITIONS = %w[position_in_class position_in_factory position_in_fixture position_in_test position_in_routes position_in_serializer position_in_additional_file_patterns].freeze
     EXCLUSION_LIST = %w[tests fixtures factories serializers].freeze
     FORMAT_TYPES = %w[bare rdoc yard markdown].freeze
 
@@ -124,6 +124,14 @@ def add_options_to_parser(option_parser) # rubocop:disable Metrics/MethodLength,
         has_set_position['position_in_serializer'] = true
       end
 
+      option_parser.on('--pa',
+                       '--position-in-additional-file-patterns [before|top|after|bottom]',
+                       ANNOTATION_POSITIONS,
+                       'Place the annotations at the top (before) or the bottom (after) of files that match additional patterns') do |position_in_additional_file_patterns|
+        env['position_in_additional_file_patterns'] = position_in_additional_file_patterns
+        has_set_position['position_in_additional_file_patterns'] = true
+      end
+
       option_parser.on('--w',
                        '--wrapper STR',
                        'Wrap annotation with the text passed as parameter.',
@@ -145,7 +153,7 @@ def add_options_to_parser(option_parser) # rubocop:disable Metrics/MethodLength,
 
       option_parser.on('-r',
                        '--routes',
-                       "Annotate routes.rb with the output of 'rake routes'") do
+                       "Annotate routes.rb with the output of 'rake/rails routes'") do
         env['routes'] = 'true'
       end
 
diff --git a/lib/generators/annotate/templates/auto_annotate_models.rake b/lib/generators/annotate/templates/auto_annotate_models.rake
index 61cdcd7a1..3dfed2d4e 100644
--- a/lib/generators/annotate/templates/auto_annotate_models.rake
+++ b/lib/generators/annotate/templates/auto_annotate_models.rake
@@ -7,53 +7,52 @@ if Rails.env.development?
     # You can override any of these by setting an environment variable of the
     # same name.
     Annotate.set_defaults(
-      'active_admin'                => 'false',
-      'additional_file_patterns'    => [],
-      'routes'                      => 'false',
-      'models'                      => 'true',
-      'position_in_routes'          => 'before',
-      'position_in_class'           => 'before',
-      'position_in_test'            => 'before',
-      'position_in_fixture'         => 'before',
-      'position_in_factory'         => 'before',
-      'position_in_serializer'      => 'before',
-      'show_check_constraints'      => 'false',
-      'show_foreign_keys'           => 'true',
-      'show_complete_foreign_keys'  => 'false',
-      'show_indexes'                => 'true',
-      'simple_indexes'              => 'false',
-      'model_dir'                   => 'app/models',
-      'root_dir'                    => '',
-      'include_version'             => 'false',
-      'require'                     => '',
-      'exclude_tests'               => 'false',
-      'exclude_fixtures'            => 'false',
-      'exclude_factories'           => 'false',
-      'exclude_serializers'         => 'false',
-      'exclude_scaffolds'           => 'true',
-      'exclude_controllers'         => 'true',
-      'exclude_helpers'             => 'true',
-      'exclude_sti_subclasses'      => 'false',
-      'ignore_model_sub_dir'        => 'false',
-      'ignore_columns'              => nil,
-      'ignore_routes'               => nil,
-      'ignore_unknown_models'       => 'false',
-      'hide_limit_column_types'     => '<%= AnnotateModels::NO_LIMIT_COL_TYPES.join(",") %>',
-      'hide_default_column_types'   => '<%= AnnotateModels::NO_DEFAULT_COL_TYPES.join(",") %>',
-      'skip_on_db_migrate'          => 'false',
-      'format_bare'                 => 'true',
-      'format_rdoc'                 => 'false',
-      'format_yard'                 => 'false',
-      'format_markdown'             => 'false',
-      'sort'                        => 'false',
-      'force'                       => 'false',
-      'frozen'                      => 'false',
-      'classified_sort'             => 'true',
-      'trace'                       => 'false',
-      'wrapper_open'                => nil,
-      'wrapper_close'               => nil,
-      'with_comment'                => 'true',
-      'with_comment_column'         => 'false'
+      'active_admin'                         => 'false',
+      'additional_file_patterns'             => [],
+      'routes'                               => 'false',
+      'models'                               => 'true',
+      'position_in_routes'                   => 'before',
+      'position_in_class'                    => 'before',
+      'position_in_test'                     => 'before',
+      'position_in_fixture'                  => 'before',
+      'position_in_factory'                  => 'before',
+      'position_in_serializer'               => 'before',
+      'position_in_additional_file_patterns' => 'before',
+      'show_foreign_keys'                    => 'true',
+      'show_complete_foreign_keys'           => 'false',
+      'show_indexes'                         => 'true',
+      'simple_indexes'                       => 'false',
+      'model_dir'                            => 'app/models',
+      'root_dir'                             => '',
+      'include_version'                      => 'false',
+      'require'                              => '',
+      'exclude_tests'                        => 'false',
+      'exclude_fixtures'                     => 'false',
+      'exclude_factories'                    => 'false',
+      'exclude_serializers'                  => 'false',
+      'exclude_scaffolds'                    => 'true',
+      'exclude_controllers'                  => 'true',
+      'exclude_helpers'                      => 'true',
+      'exclude_sti_subclasses'               => 'false',
+      'ignore_model_sub_dir'                 => 'false',
+      'ignore_columns'                       => nil,
+      'ignore_routes'                        => nil,
+      'ignore_unknown_models'                => 'false',
+      'hide_limit_column_types'              => '<%= AnnotateModels::NO_LIMIT_COL_TYPES.join(",") %>',
+      'hide_default_column_types'            => '<%= AnnotateModels::NO_DEFAULT_COL_TYPES.join(",") %>',
+      'skip_on_db_migrate'                   => 'false',
+      'format_bare'                          => 'true',
+      'format_rdoc'                          => 'false',
+      'format_yard'                          => 'false',
+      'format_markdown'                      => 'false',
+      'sort'                                 => 'false',
+      'force'                                => 'false',
+      'frozen'                               => 'false',
+      'classified_sort'                      => 'true',
+      'trace'                                => 'false',
+      'wrapper_open'                         => nil,
+      'wrapper_close'                        => nil,
+      'with_comment'                         => 'true'
     )
   end
 
diff --git a/lib/tasks/annotate_models.rake b/lib/tasks/annotate_models.rake
index 776f97ba3..385afddf5 100644
--- a/lib/tasks/annotate_models.rake
+++ b/lib/tasks/annotate_models.rake
@@ -11,48 +11,48 @@ task annotate_models: :environment do
   require "#{annotate_lib}/annotate/active_record_patch"
 
   options = {is_rake: true}
-  ENV['position'] = options[:position] = Annotate::Helpers.fallback(ENV['position'], 'before')
+  ENV['position'] = options[:position] = Annotate::Helpers.fallback(ENV.fetch('position', nil), 'before')
   options[:additional_file_patterns] = ENV['additional_file_patterns'] ? ENV['additional_file_patterns'].split(',') : []
-  options[:position_in_class] = Annotate::Helpers.fallback(ENV['position_in_class'], ENV['position'])
-  options[:position_in_fixture] = Annotate::Helpers.fallback(ENV['position_in_fixture'], ENV['position'])
-  options[:position_in_factory] = Annotate::Helpers.fallback(ENV['position_in_factory'], ENV['position'])
-  options[:position_in_test] = Annotate::Helpers.fallback(ENV['position_in_test'], ENV['position'])
-  options[:position_in_serializer] = Annotate::Helpers.fallback(ENV['position_in_serializer'], ENV['position'])
-  options[:show_check_constraints] = Annotate::Helpers.true?(ENV['show_check_constraints'])
-  options[:show_foreign_keys] = Annotate::Helpers.true?(ENV['show_foreign_keys'])
-  options[:show_complete_foreign_keys] = Annotate::Helpers.true?(ENV['show_complete_foreign_keys'])
-  options[:show_indexes] = Annotate::Helpers.true?(ENV['show_indexes'])
-  options[:simple_indexes] = Annotate::Helpers.true?(ENV['simple_indexes'])
+  options[:position_in_class] = Annotate::Helpers.fallback(ENV.fetch('position_in_class', nil), ENV.fetch('position', nil))
+  options[:position_in_fixture] = Annotate::Helpers.fallback(ENV.fetch('position_in_fixture', nil), ENV.fetch('position', nil))
+  options[:position_in_factory] = Annotate::Helpers.fallback(ENV.fetch('position_in_factory', nil), ENV.fetch('position', nil))
+  options[:position_in_test] = Annotate::Helpers.fallback(ENV.fetch('position_in_test', nil), ENV.fetch('position', nil))
+  options[:position_in_serializer] = Annotate::Helpers.fallback(ENV.fetch('position_in_serializer', nil), ENV.fetch('position', nil))
+  options[:show_check_constraints] = Annotate::Helpers.true?(ENV.fetch('show_check_constraints', nil))
+  options[:show_foreign_keys] = Annotate::Helpers.true?(ENV.fetch('show_foreign_keys', nil))
+  options[:show_complete_foreign_keys] = Annotate::Helpers.true?(ENV.fetch('show_complete_foreign_keys', nil))
+  options[:show_indexes] = Annotate::Helpers.true?(ENV.fetch('show_indexes', nil))
+  options[:simple_indexes] = Annotate::Helpers.true?(ENV.fetch('simple_indexes', nil))
   options[:model_dir] = ENV['model_dir'] ? ENV['model_dir'].split(',') : ['app/models']
-  options[:root_dir] = ENV['root_dir']
-  options[:include_version] = Annotate::Helpers.true?(ENV['include_version'])
+  options[:root_dir] = ENV.fetch('root_dir', nil)
+  options[:include_version] = Annotate::Helpers.true?(ENV.fetch('include_version', nil))
   options[:require] = ENV['require'] ? ENV['require'].split(',') : []
-  options[:exclude_tests] = Annotate::Helpers.true?(ENV['exclude_tests'])
-  options[:exclude_factories] = Annotate::Helpers.true?(ENV['exclude_factories'])
-  options[:exclude_fixtures] = Annotate::Helpers.true?(ENV['exclude_fixtures'])
-  options[:exclude_serializers] = Annotate::Helpers.true?(ENV['exclude_serializers'])
-  options[:exclude_scaffolds] = Annotate::Helpers.true?(ENV['exclude_scaffolds'])
+  options[:exclude_tests] = Annotate::Helpers.true?(ENV.fetch('exclude_tests', nil))
+  options[:exclude_factories] = Annotate::Helpers.true?(ENV.fetch('exclude_factories', nil))
+  options[:exclude_fixtures] = Annotate::Helpers.true?(ENV.fetch('exclude_fixtures', nil))
+  options[:exclude_serializers] = Annotate::Helpers.true?(ENV.fetch('exclude_serializers', nil))
+  options[:exclude_scaffolds] = Annotate::Helpers.true?(ENV.fetch('exclude_scaffolds', nil))
   options[:exclude_controllers] = Annotate::Helpers.true?(ENV.fetch('exclude_controllers', 'true'))
   options[:exclude_helpers] = Annotate::Helpers.true?(ENV.fetch('exclude_helpers', 'true'))
-  options[:exclude_sti_subclasses] = Annotate::Helpers.true?(ENV['exclude_sti_subclasses'])
-  options[:ignore_model_sub_dir] = Annotate::Helpers.true?(ENV['ignore_model_sub_dir'])
-  options[:format_bare] = Annotate::Helpers.true?(ENV['format_bare'])
-  options[:format_rdoc] = Annotate::Helpers.true?(ENV['format_rdoc'])
-  options[:format_yard] = Annotate::Helpers.true?(ENV['format_yard'])
-  options[:format_markdown] = Annotate::Helpers.true?(ENV['format_markdown'])
-  options[:sort] = Annotate::Helpers.true?(ENV['sort'])
-  options[:force] = Annotate::Helpers.true?(ENV['force'])
-  options[:frozen] = Annotate::Helpers.true?(ENV['frozen'])
-  options[:classified_sort] = Annotate::Helpers.true?(ENV['classified_sort'])
-  options[:trace] = Annotate::Helpers.true?(ENV['trace'])
-  options[:wrapper_open] = Annotate::Helpers.fallback(ENV['wrapper_open'], ENV['wrapper'])
-  options[:wrapper_close] = Annotate::Helpers.fallback(ENV['wrapper_close'], ENV['wrapper'])
+  options[:exclude_sti_subclasses] = Annotate::Helpers.true?(ENV.fetch('exclude_sti_subclasses', nil))
+  options[:ignore_model_sub_dir] = Annotate::Helpers.true?(ENV.fetch('ignore_model_sub_dir', nil))
+  options[:format_bare] = Annotate::Helpers.true?(ENV.fetch('format_bare', nil))
+  options[:format_rdoc] = Annotate::Helpers.true?(ENV.fetch('format_rdoc', nil))
+  options[:format_yard] = Annotate::Helpers.true?(ENV.fetch('format_yard', nil))
+  options[:format_markdown] = Annotate::Helpers.true?(ENV.fetch('format_markdown', nil))
+  options[:sort] = Annotate::Helpers.true?(ENV.fetch('sort', nil))
+  options[:force] = Annotate::Helpers.true?(ENV.fetch('force', nil))
+  options[:frozen] = Annotate::Helpers.true?(ENV.fetch('frozen', nil))
+  options[:classified_sort] = Annotate::Helpers.true?(ENV.fetch('classified_sort', nil))
+  options[:trace] = Annotate::Helpers.true?(ENV.fetch('trace', nil))
+  options[:wrapper_open] = Annotate::Helpers.fallback(ENV.fetch('wrapper_open', nil), ENV.fetch('wrapper', nil))
+  options[:wrapper_close] = Annotate::Helpers.fallback(ENV.fetch('wrapper_close', nil), ENV.fetch('wrapper', nil))
   options[:ignore_columns] = ENV.fetch('ignore_columns', nil)
   options[:ignore_routes] = ENV.fetch('ignore_routes', nil)
-  options[:hide_limit_column_types] = Annotate::Helpers.fallback(ENV['hide_limit_column_types'], '')
-  options[:hide_default_column_types] = Annotate::Helpers.fallback(ENV['hide_default_column_types'], '')
-  options[:with_comment] = Annotate::Helpers.true?(ENV['with_comment'])
-  options[:with_comment_column] = Annotate::Helpers.true?(ENV['with_comment_column'])
+  options[:hide_limit_column_types] = Annotate::Helpers.fallback(ENV.fetch('hide_limit_column_types', nil), '')
+  options[:hide_default_column_types] = Annotate::Helpers.fallback(ENV.fetch('hide_default_column_types', nil), '')
+  options[:with_comment] = Annotate::Helpers.true?(ENV.fetch('with_comment', nil))
+  options[:with_comment_column] = Annotate::Helpers.true?(ENV.fetch('with_comment_column', nil))
   options[:ignore_unknown_models] = Annotate::Helpers.true?(ENV.fetch('ignore_unknown_models', 'false'))
 
   AnnotateModels.do_annotations(options)
@@ -64,9 +64,9 @@ task remove_annotation: :environment do
   require "#{annotate_lib}/annotate/active_record_patch"
 
   options = {is_rake: true}
-  options[:model_dir] = ENV['model_dir']
-  options[:root_dir] = ENV['root_dir']
+  options[:model_dir] = ENV.fetch('model_dir', nil)
+  options[:root_dir] = ENV.fetch('root_dir', nil)
   options[:require] = ENV['require'] ? ENV['require'].split(',') : []
-  options[:trace] = Annotate::Helpers.true?(ENV['trace'])
+  options[:trace] = Annotate::Helpers.true?(ENV.fetch('trace', nil))
   AnnotateModels.remove_annotations(options)
 end
diff --git a/lib/tasks/annotate_routes.rake b/lib/tasks/annotate_routes.rake
index b2832d443..d882803f6 100644
--- a/lib/tasks/annotate_routes.rake
+++ b/lib/tasks/annotate_routes.rake
@@ -10,13 +10,13 @@ task :annotate_routes => :environment do
   require "#{annotate_lib}/annotate/annotate_routes"
 
   options={}
-  ENV['position'] = options[:position] = Annotate::Helpers.fallback(ENV['position'], 'before')
-  options[:position_in_routes] = Annotate::Helpers.fallback(ENV['position_in_routes'], ENV['position'])
-  options[:ignore_routes] = Annotate::Helpers.fallback(ENV['ignore_routes'],  nil)
+  ENV['position'] = options[:position] = Annotate::Helpers.fallback(ENV.fetch('position', nil), 'before')
+  options[:position_in_routes] = Annotate::Helpers.fallback(ENV.fetch('position_in_routes', nil), ENV.fetch('position', nil))
+  options[:ignore_routes] = Annotate::Helpers.fallback(ENV.fetch('ignore_routes', nil),  nil)
   options[:require] = ENV['require'] ? ENV['require'].split(',') : []
-  options[:frozen] = Annotate::Helpers.true?(ENV['frozen'])
-  options[:wrapper_open] = Annotate::Helpers.fallback(ENV['wrapper_open'], ENV['wrapper'])
-  options[:wrapper_close] = Annotate::Helpers.fallback(ENV['wrapper_close'], ENV['wrapper'])
+  options[:frozen] = Annotate::Helpers.true?(ENV.fetch('frozen', nil))
+  options[:wrapper_open] = Annotate::Helpers.fallback(ENV.fetch('wrapper_open', nil), ENV.fetch('wrapper', nil))
+  options[:wrapper_close] = Annotate::Helpers.fallback(ENV.fetch('wrapper_close', nil), ENV.fetch('wrapper', nil))
   AnnotateRoutes.do_annotations(options)
 end
 
diff --git a/spec/lib/annotate/annotate_models_spec.rb b/spec/lib/annotate/annotate_models_spec.rb
index 096474610..ede8091f9 100644
--- a/spec/lib/annotate/annotate_models_spec.rb
+++ b/spec/lib/annotate/annotate_models_spec.rb
@@ -3,7 +3,6 @@
 require 'annotate/annotate_models'
 require 'annotate/active_record_patch'
 require 'active_support/core_ext/string'
-require 'files'
 require 'tmpdir'
 
 describe AnnotateModels do
@@ -206,7 +205,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 +219,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
@@ -1944,7 +1943,7 @@ def mock_column(name, type, options = {})
 
   describe '.set_defaults' do
     subject do
-      Annotate::Helpers.true?(ENV['show_complete_foreign_keys'])
+      Annotate::Helpers.true?(ENV.fetch('show_complete_foreign_keys', nil))
     end
 
     after :each do
@@ -2014,18 +2013,15 @@ def mock_column(name, type, options = {})
 
     context 'when `model_dir` is valid' do
       let(:model_dir) do
-        Files do
-          file 'foo.rb'
-          dir 'bar' do
-            file 'baz.rb'
-            dir 'qux' do
-              file 'quux.rb'
-            end
-          end
-          dir 'concerns' do
-            file 'corge.rb'
-          end
-        end
+        dir = Dir.mktmpdir
+        FileUtils.touch(File.join(dir, 'foo.rb'))
+        FileUtils.mkdir_p(File.join(dir, 'bar'))
+        FileUtils.touch(File.join(dir, 'bar', 'baz.rb'))
+        FileUtils.mkdir_p(File.join(dir, 'bar', 'qux'))
+        FileUtils.touch(File.join(dir, 'bar', 'qux', 'quux.rb'))
+        FileUtils.mkdir_p(File.join(dir, 'concerns'))
+        FileUtils.touch(File.join(dir, 'concerns', 'corge.rb'))
+        dir
       end
 
       context 'when the model files are not specified' do
@@ -2198,6 +2194,37 @@ class FooInsideBar < ActiveRecord::Base
           expect(klass.name).to eq('Bar::FooInsideBar')
         end
       end
+
+      context 'when class Bar::Foo is defined in Rails collapsed directory' do
+        before do
+          Rails = double('Rails')
+          # rubocop:disable RSpec/MessageChain
+          allow(Rails).to receive_message_chain(:autoloaders, :main, :collapse_dirs) { Set.new(["bar/models"]) }
+          # rubocop:enable RSpec/MessageChain
+          $LOAD_PATH.unshift(dir)
+        end
+
+        let :dir do
+          AnnotateModels.model_dir[0]
+        end
+
+        let :filename do
+          'bar/models/foo.rb'
+        end
+
+        let :file_content do
+          <<~EOS
+            module Bar
+              class Foo < ActiveRecord::Base
+              end
+            end
+          EOS
+        end
+
+        it 'works' do
+          expect(klass.name).to eql('Bar::Foo')
+        end
+      end
     end
 
     context 'when class is defined inside module and class name is not capitalized normally' do
@@ -2842,7 +2869,7 @@ 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
@@ -2922,6 +2949,34 @@ def annotate_one_file(options = {})
           expect(File.read(@model_file_name)).to eq("#{@schema_info}#{@file_content}")
         end
       end
+
+      context 'of multibyte comments' do
+        before do
+          klass = mock_class(:users,
+                             :id,
+                             [
+                               mock_column(:id, :integer, comment: 'ID'),
+                             ],
+                             [],
+                             [])
+          @schema_info = AnnotateModels.get_schema_info(klass, '== Schema Info', with_comment: true)
+          annotate_one_file
+        end
+
+        it 'should update columns' do
+          klass = mock_class(:users,
+                             :id,
+                             [
+                               mock_column(:id, :integer, comment: 'ID'),
+                               mock_column(:active, :boolean, limit: 1, comment: 'ACTIVE'),
+                             ],
+                             [],
+                             [])
+          @schema_info = AnnotateModels.get_schema_info(klass, '== Schema Info', with_comment: true)
+          annotate_one_file
+          expect(File.read(@model_file_name)).to eq("#{@schema_info}#{@file_content}")
+        end
+      end
     end
 
     describe 'with existing annotation => :before' do
@@ -3093,11 +3148,11 @@ class User < ActiveRecord::Base
     end
 
     describe 'frozen option' do
-      it "should abort without existing annotation when frozen: true " 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`./
       end
 
-      it "should abort with different annotation when frozen: true " do
+      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
@@ -3105,7 +3160,7 @@ class User < ActiveRecord::Base
         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
+      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
@@ -3126,7 +3181,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..c413c3b70 100644
--- a/spec/lib/annotate/helpers_spec.rb
+++ b/spec/lib/annotate/helpers_spec.rb
@@ -5,7 +5,7 @@
     subject { described_class.skip_on_migration? }
 
     before do
-      allow(ENV).to receive(:[]).and_return(nil)
+      allow(ENV).to receive(:fetch).with(anything, nil).and_return(nil)
     end
 
     it { is_expected.to be_falsy }
@@ -15,7 +15,7 @@
       let(:env_value) { '1' }
 
       before do
-        allow(ENV).to receive(:[]).with(key).and_return(env_value)
+        allow(ENV).to receive(:fetch).with(key, nil).and_return(env_value)
       end
 
       it { is_expected.to be_truthy }
@@ -26,7 +26,7 @@
       let(:env_value) { '1' }
 
       before do
-        allow(ENV).to receive(:[]).with(key).and_return(env_value)
+        allow(ENV).to receive(:fetch).with(key, nil).and_return(env_value)
       end
 
       it { is_expected.to be_truthy }
@@ -37,7 +37,7 @@
     subject { described_class.include_routes? }
 
     before do
-      allow(ENV).to receive(:[]).and_return(nil)
+      allow(ENV).to receive(:fetch).with(anything, nil).and_return(nil)
     end
 
     it { is_expected.to be_falsy }
@@ -47,7 +47,7 @@
       let(:env_value) { '1' }
 
       before do
-        allow(ENV).to receive(:[]).with(key).and_return(env_value)
+        allow(ENV).to receive(:fetch).with(key, nil).and_return(env_value)
       end
 
       it { is_expected.to be_truthy }
@@ -68,7 +68,7 @@
       let(:env_value) { '1' }
 
       before do
-        allow(ENV).to receive(:[]).with(key).and_return(env_value)
+        allow(ENV).to receive(:fetch).with(key, nil).and_return(env_value)
       end
 
       it { is_expected.to be_truthy }
@@ -102,20 +102,20 @@
 
   describe '.fallback' do
     subject { described_class.fallback(*args) }
-    let(:args) { [arg_1, arg_2] }
+    let(:args) { [first_arg, second_arg] }
 
-    let(:arg_1) { '' } # is considered blank
-    let(:arg_2) { 'yes' }
+    let(:first_arg) { '' } # is considered blank
+    let(:second_arg) { 'yes' }
 
     it 'returns the first non-blank argument' do
-      is_expected.to eq(arg_2)
+      is_expected.to eq(second_arg)
     end
 
     context 'when the first argument is non-blank' do
-      let(:arg_1) { 'yes' }
-      let(:arg_2) { 'no' }
+      let(:first_arg) { 'yes' }
+      let(:second_arg) { 'no' }
 
-      it { is_expected.to eq(arg_1) }
+      it { is_expected.to eq(first_arg) }
     end
   end
 
diff --git a/spec/lib/annotate/parser_spec.rb b/spec/lib/annotate/parser_spec.rb
index 16084b02b..ced05f013 100644
--- a/spec/lib/annotate/parser_spec.rb
+++ b/spec/lib/annotate/parser_spec.rb
@@ -72,8 +72,8 @@ module Annotate # rubocop:disable Metrics/ModuleLength
           options = other_commands + position_command
 
           Parser.parse(options)
-          expect(ENV['position_in_class']).to eq('top')
-          expect(ENV['position']).to eq('bottom')
+          expect(ENV.fetch('position_in_class', nil)).to eq('top')
+          expect(ENV.fetch('position', nil)).to eq('bottom')
         end
       end
     end
@@ -174,6 +174,22 @@ module Annotate # rubocop:disable Metrics/ModuleLength
       end
     end
 
+    %w[--pa --position-in-additional-file-patterns].each do |option|
+      describe option do
+        let(:env_key) { 'position_in_additional_file_patterns' }
+
+        Parser::ANNOTATION_POSITIONS.each do |position|
+          context "when specifying #{position}" do
+            it "sets the ENV variable to #{position}" do
+              allow(ENV).to receive(:[]=)
+              Parser.parse([option, position])
+              expect(ENV).to have_received(:[]=).with(env_key, position)
+            end
+          end
+        end
+      end
+    end
+
     %w[--w --wrapper].each do |option|
       describe option do
         let(:env_key) { 'wrapper' }