Class: Mbeditor::Rack::PendingMigrationBypass

Inherits:
Object
  • Object
show all
Defined in:
lib/mbeditor/rack/pending_migration_bypass.rb

Overview

Wraps ActiveRecord::Migration::CheckPending so a pending migration cannot lock the developer out of the one tool they need to fix it.

Rails inserts CheckPending after ActionDispatch::Callbacks whenever config.active_record.migration_error is :page_load — the default a real rails new app carries in development. From then until the migration is run, EVERY request raises PendingMigrationError, and that included all of mbeditor's: the file tree, opening a file, and saving one. The editor was unusable in precisely the situation it exists to get you out of, and the migration could not be edited to fix it.

An earlier attempt rescued the error above CheckPending and answered mbeditor's own paths with a fallback shell and a 503 for every XHR. That left the shell without its stylesheets or JavaScript — those live under /assets, which is not an mbeditor path, so they still 500'd — and even once loaded every API call was a 503. Rescuing after the fact was the wrong shape; the check simply must not apply to this traffic.

So the check still runs (it is file-watcher backed and cheap after the first call, and non-editor traffic must keep Rails' behaviour exactly), but for editor traffic the error is recorded and swallowed rather than raised, and the request proceeds. CheckPending raises before it calls anything downstream, so dispatching afterwards runs the request once, not twice.

The recorded message is what surfaces the warning banner, so the developer is still told to migrate — they are just no longer prevented from working.

Constant Summary collapse

DEFAULT_ASSET_PREFIX =
"/assets"
PENDING_HEADER =

Announced on every bypassed response so the editor can warn about a migration created mid-session, not only one that existed at page load.

"X-Mbeditor-Pending-Migration"

Instance Method Summary collapse

Constructor Details

#initialize(app, file_watcher: nil) ⇒ PendingMigrationBypass

Returns a new instance of PendingMigrationBypass.



37
38
39
40
41
42
43
44
45
# File 'lib/mbeditor/rack/pending_migration_bypass.rb', line 37

def initialize(app, file_watcher: nil)
  @app = app
  @checked =
    if file_watcher
      ActiveRecord::Migration::CheckPending.new(app, file_watcher: file_watcher)
    else
      ActiveRecord::Migration::CheckPending.new(app)
    end
end

Instance Method Details

#call(env) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/mbeditor/rack/pending_migration_bypass.rb', line 51

def call(env)
  return @checked.call(env) unless editor_traffic?(env)

  begin
    result = @checked.call(env)
    # The check passed, so anything recorded earlier is stale.
    Mbeditor::PendingMigrations.clear
    result
  rescue StandardError => e
    raise unless pending_migration_error?(e)

    Mbeditor::PendingMigrations.note(e.message)
    status, headers, body = @app.call(env)
    headers[PENDING_HEADER] = header_safe(e.message)
    [status, headers, body]
  end
end