Fix lazy-loading of ActiveRecord

An effort begun by Eric Proulx in
https://github.com/paper-trail-gem/paper_trail/pull/1237

- Replaces Engine with simpler Railtie
- Critically, defers certain `requires`
  - With Rails, defers via a Lazy Load Hook in the Railtie
  - Without Rails, just by require-ing AR first
This commit is contained in:
Eric Proulx 2020-03-12 09:57:41 +01:00 committed by Jared Beck
parent a69e678887
commit fc6c5f6941
8 changed files with 56 additions and 62 deletions

View File

@ -7,9 +7,11 @@ recommendations of [keepachangelog.com](http://keepachangelog.com/).
### Breaking Changes
- In the PT rails engine, the `paper_trail` configuration has been
removed. This configuration object was deprecated in 10.2.0. Please review
docs section [2.d. Turning PaperTrail
- Rails: Instead of an `Engine`, PT now provides a `Railtie`, which is simpler.
- Rails: The deprecated `config.paper_trail` configuration technique
has been removed. This configuration object was deprecated in 10.2.0. It only
had one key, `config.paper_trail.enabled`. Please review docs section [2.d.
Turning PaperTrail
Off](https://github.com/paper-trail-gem/paper_trail/#2d-turning-papertrail-off)
for alternatives.

View File

@ -8,34 +8,17 @@
# can revisit this decision.
require "active_support/all"
# AR is required for, eg. has_paper_trail.rb, so we could put this `require` in
# all of those files, but it seems easier to troubleshoot if we just make sure
# AR is loaded here before loading *any* of PT. See discussion of
# performance/simplicity tradeoff for activesupport above.
require "active_record"
require "request_store"
require "paper_trail/cleaner"
require "paper_trail/compatibility"
require "paper_trail/config"
require "paper_trail/has_paper_trail"
require "paper_trail/record_history"
require "paper_trail/reifier"
require "paper_trail/request"
require "paper_trail/version_concern"
require "paper_trail/version_number"
require "paper_trail/serializers/json"
require "paper_trail/serializers/yaml"
# An ActiveRecord extension that tracks changes to your models, for auditing or
# versioning.
module PaperTrail
E_RAILS_NOT_LOADED = <<-EOS.squish.freeze
PaperTrail has been loaded too early, before rails is loaded. This can
happen when another gem defines the ::Rails namespace, then PT is loaded,
all before rails is loaded. You may want to reorder your Gemfile, or defer
the loading of PT by using `require: false` and a manual require elsewhere.
EOS
E_TIMESTAMP_FIELD_CONFIG = <<-EOS.squish.freeze
PaperTrail.timestamp_field= has been removed, without replacement. It is no
longer configurable. The timestamp column in the versions table must now be
@ -126,27 +109,18 @@ module PaperTrail
end
end
# We use the `on_load` "hook" instead of `ActiveRecord::Base.include` because we
# don't want to cause all of AR to be autloaded yet. See
# https://guides.rubyonrails.org/engines.html#what-are-on-load-hooks-questionmark
# to learn more about `on_load`.
ActiveSupport.on_load(:active_record) do
include PaperTrail::Model
end
# Require frameworks
if defined?(::Rails)
# Rails module is sometimes defined by gems like rails-html-sanitizer
# so we check for presence of Rails.application.
if defined?(::Rails.application)
require "paper_trail/frameworks/rails"
else
::Kernel.warn(::PaperTrail::E_RAILS_NOT_LOADED)
end
# PT is built on ActiveRecord, but does not require Rails. If Rails is defined,
# our Railtie makes sure not to load the AR-dependent parts of PT until AR is
# ready. A typical Rails `application.rb` has:
#
# ```
# require 'rails/all' # Defines `Rails`
# Bundler.require(*Rails.groups) # require 'paper_trail' (this file)
# ```
#
# Non-rails applications should take similar care to load AR before PT.
if defined?(Rails)
require "paper_trail/frameworks/rails"
else
require "paper_trail/frameworks/active_record"
end
if defined?(::ActiveRecord)
::PaperTrail::Compatibility.check_activerecord(::ActiveRecord.gem_version)
end

View File

@ -1,5 +1,12 @@
# frozen_string_literal: true
# This file only needs to be loaded if the gem is being used outside of Rails,
# since otherwise the model(s) will get loaded in via the `Rails::Engine`.
# Either ActiveRecord has already been loaded by the Lazy Load Hook in our
# Railtie, or else we load it now.
require "active_record"
::PaperTrail::Compatibility.check_activerecord(::ActiveRecord.gem_version)
# Now we can load the parts of PT that depend on AR.
require "paper_trail/has_paper_trail"
require "paper_trail/reifier"
require "paper_trail/frameworks/active_record/models/paper_trail/version"
ActiveRecord::Base.include PaperTrail::Model

View File

@ -1,4 +1,3 @@
# frozen_string_literal: true
require "paper_trail/frameworks/rails/controller"
require "paper_trail/frameworks/rails/engine"
require "paper_trail/frameworks/rails/railtie"

View File

@ -101,9 +101,3 @@ module PaperTrail
end
end
end
if defined?(::ActionController)
::ActiveSupport.on_load(:action_controller) do
include ::PaperTrail::Rails::Controller
end
end

View File

@ -1,10 +0,0 @@
# frozen_string_literal: true
module PaperTrail
module Rails
# See http://guides.rubyonrails.org/engines.html
class Engine < ::Rails::Engine
paths["app/models"] << "lib/paper_trail/frameworks/active_record/models"
end
end
end

View File

@ -0,0 +1,28 @@
# frozen_string_literal: true
module PaperTrail
# Represents code to load within Rails framework. See documentation in
# `rails/railtie.rb`.
# @api private
class Railtie < ::Rails::Railtie
# PaperTrail only has one initializer. The `initializer` method can take a
# `before:` or `after:` argument, but that's only relevant for railties with
# more than one initializer.
initializer "paper_trail" do
# `on_load` is a "lazy load hook". It "declares a block that will be
# executed when a Rails component is fully loaded". (See
# `active_support/lazy_load_hooks.rb`)
ActiveSupport.on_load(:action_controller) do
require "paper_trail/frameworks/rails/controller"
# Mix our extensions into `ActionController::Base`, which is `self`
# because of the `class_eval` in `lazy_load_hooks.rb`.
include PaperTrail::Rails::Controller
end
ActiveSupport.on_load(:active_record) do
require "paper_trail/frameworks/active_record"
end
end
end
end

View File

@ -7,7 +7,7 @@ require "byebug"
require_relative "support/pt_arel_helpers"
unless File.exist?(File.expand_path("dummy_app/config/database.yml", __dir__))
warn "No database.yml detected for the dummy app, please run `rake prepare` first"
warn "No database.yml detected for the dummy app, please run `rake install_database_yml` first"
end
RSpec.configure do |config|