Skip to content

Commit

Permalink
fixup! fixup! Refactoring
Browse files Browse the repository at this point in the history
  • Loading branch information
VladislavSokov committed Jul 2, 2024
1 parent bc614fa commit 994aea7
Show file tree
Hide file tree
Showing 13 changed files with 209 additions and 152 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## [0.7.6] - 2024-07-02
- Added UI

## [0.7.5] - 2024-06-20
- Added db:rollback_migrations:manual task to manually rolls back phantom migrations one by one

Expand Down
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,25 @@ http://localhost:3000/rails/migrations
```
This page will display a list of phantom migrations for each database connection and provide options to view details and rollback migrations.

## Disabling the UI

By default, the UI is enabled. If you prefer to disable the UI, you can do so in two ways:

### 1. Using Environment Variable

Set the environment variable `ACTUAL_DB_SCHEMA_UI_DISABLED` to `true`:

```sh
export ACTUAL_DB_SCHEMA_UI_DISABLED=true
```

### 2. Using Initializer
Add the following line to your initializer file (`config/initializers/actual_db_schema.rb`):

```ruby
ActualDbSchema.config[:ui_disabled] = true
```

## Disabling Automatic Rollback

By default, the automatic rollback of migrations is enabled. If you prefer to perform manual rollbacks, you can disable the automatic rollback in two ways:
Expand Down
90 changes: 6 additions & 84 deletions app/controllers/actual_db_schema/migrations_controller.rb
Original file line number Diff line number Diff line change
@@ -1,97 +1,19 @@
# frozen_string_literal: true

require_relative "../../helpers/actual_db_schema/migrations_helper"
require_relative "../../services/actual_db_schema/rollback_service"
module ActualDbSchema
# Controller to display the list of phantom migrations for each database connection.
class MigrationsController < ActionController::Base
before_action :load_migrations, only: %i[index]
include MigrationsHelper

def index; end

def show
@migration = load_migration(params[:id], params[:database])
end
def show; end

def rollback
version = params[:id]
database = params[:database]

rollback_migration(version, database)

redirect_to migrations_path, notice: "Migration #{version} has been rolled back."
end

private

def load_migrations
@migrations = []

ActualDbSchema.for_each_db_connection do
context = prepare_context
indexed_phantom_migrations = context.migrations.index_by { |m| m.version.to_s }

context.migrations_status.each do |status, version|
migration = indexed_phantom_migrations[version]
next unless migration

@migrations << build_migration_struct(status, migration)
end
end
end

def load_migration(version, database)
ActualDbSchema.for_each_db_connection do
next unless ActualDbSchema.db_config[:database] == database

context = prepare_context
migration = find_migration_in_context(context, version)
return migration if migration
end
nil
end

def rollback_migration(version, database)
ActualDbSchema.for_each_db_connection do
next unless ActualDbSchema.db_config[:database] == database

context = prepare_context
if context.migrations.detect { |m| m.version.to_s == version }
context.run(:down, version.to_i)
break
end
end
end

def find_migration_in_context(context, version)
migration = context.migrations.detect { |m| m.version.to_s == version }
return unless migration

status = context.migrations_status.detect { |_s, v| v.to_s == version }&.first || "unknown"
build_migration_struct(status, migration)
end

def prepare_context
context = ActualDbSchema.fetch_migration_context
context.extend(ActualDbSchema::Patches::MigrationContext)
context
end

def build_migration_struct(status, migration)
MigrationStruct.new(
status: status,
version: migration.version.to_s,
name: migration.name,
branch: branch_for(migration.version),
database: ActualDbSchema.db_config[:database],
filename: migration.filename
)
end

def branch_for(version)
metadata.fetch(version.to_s, {})[:branch] || "unknown"
end

def metadata
ActualDbSchema::Store.instance.read
RollbackService.perform(params[:id], params[:database])
redirect_to migrations_path
end
end
end
54 changes: 54 additions & 0 deletions app/helpers/actual_db_schema/migrations_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# frozen_string_literal: true

module ActualDbSchema
# Helper methods for loading and displaying migrations.
module MigrationsHelper
def load_migrations
migrations = []

ActualDbSchema.for_each_db_connection do
context = ActualDbSchema.prepare_context
indexed_migrations = context.migrations.index_by { |m| m.version.to_s }

context.migrations_status.each do |status, version|
migration = indexed_migrations[version]
migrations << build_migration_struct(status, migration) if migration
end
end

migrations
end

def load_migration(version, database)
ActualDbSchema.for_each_db_connection do
next unless ActualDbSchema.db_config[:database] == database

context = ActualDbSchema.prepare_context
migration = find_migration_in_context(context, version)
return migration if migration
end
nil
end

private

def build_migration_struct(status, migration)
MigrationStruct.new(
status: status,
version: migration.version.to_s,
name: migration.name,
branch: ActualDbSchema.branch_for(ActualDbSchema.metadata, migration.version),
database: ActualDbSchema.db_config[:database],
filename: migration.filename
)
end

def find_migration_in_context(context, version)
migration = context.migrations.detect { |m| m.version.to_s == version }
return unless migration

status = context.migrations_status.detect { |_s, v| v.to_s == version }&.first || "unknown"
build_migration_struct(status, migration)
end
end
end
18 changes: 18 additions & 0 deletions app/services/actual_db_schema/rollback_service.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# frozen_string_literal: true

module ActualDbSchema
# Service class to handle the rollback of database migrations.
class RollbackService
def self.perform(version, database)
ActualDbSchema.for_each_db_connection do
next unless ActualDbSchema.db_config[:database] == database

context = ActualDbSchema.prepare_context
if context.migrations.detect { |m| m.version.to_s == version }
context.run(:down, version.to_i)
break
end
end
end
end
end
2 changes: 1 addition & 1 deletion app/views/actual_db_schema/migrations/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
</tr>
</thead>
<tbody>
<% @migrations.each do |migration| %>
<% load_migrations.each do |migration| %>
<tr>
<td><%= migration[:status] %></td>
<td><%= migration[:version] %></td>
Expand Down
69 changes: 37 additions & 32 deletions app/views/actual_db_schema/migrations/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -6,40 +6,45 @@
</head>
<body>
<div>
<h2>Migration <%= @migration[:name] %> Details</h2>
<table>
<tbody>
<tr>
<th>Status</th>
<td><%= @migration[:status] %></td>
</tr>
<tr>
<th>Migration ID</th>
<td><%= @migration[:version] %></td>
</tr>
<tr>
<th>Branch</th>
<td><%= @migration[:branch] %></td>
</tr>
<tr>
<th>Database</th>
<td><%= @migration[:database] %></td>
</tr>
<tr>
<th>Path</th>
<td><%= @migration[:filename] %></td>
</tr>
</tbody>
</table>
<% if migration = load_migration(params[:id], params[:database]) %>
<h2>Migration <%= migration[:name] %> Details</h2>
<table>
<tbody>
<tr>
<th>Status</th>
<td><%= migration[:status] %></td>
</tr>
<tr>
<th>Migration ID</th>
<td><%= migration[:version] %></td>
</tr>
<tr>
<th>Branch</th>
<td><%= migration[:branch] %></td>
</tr>
<tr>
<th>Database</th>
<td><%= migration[:database] %></td>
</tr>
<tr>
<th>Path</th>
<td><%= migration[:filename] %></td>
</tr>
</tbody>
</table>

<h3>Migration Code</h3>
<div>
<pre><%= File.read(@migration[:filename]) %></pre>
</div>
<div class='button-container'>
<h3>Migration Code</h3>
<div>
<pre><%= File.read(migration[:filename]) %></pre>
</div>
<div class='button-container'>
<%= link_to 'Back', migrations_path, class: 'button' %>
<%= button_to 'Rollback', rollback_migration_path(id: params[:id], database: params[:database]), method: :post, class: 'button' %>
</div>
<% else %>
<h2>Migration not found</h2>
<%= link_to 'Back', migrations_path, class: 'button' %>
<%= button_to 'Rollback', rollback_migration_path(id: @migration[:version], database: @migration[:database]), method: :post, class: 'button' %>
</div>
<% end %>
</div>
</body>
</html>
17 changes: 16 additions & 1 deletion lib/actual_db_schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ class << self
self.failed = []
self.config = {
enabled: Rails.env.development?,
auto_rollback_disabled: ENV["ACTUAL_DB_SCHEMA_AUTO_ROLLBACK_DISABLED"].present?
auto_rollback_disabled: ENV["ACTUAL_DB_SCHEMA_AUTO_ROLLBACK_DISABLED"].present?,
ui_disabled: Rails.env.production? || ENV["ACTUAL_DB_SCHEMA_UI_DISABLED"].present?
}

MigrationStruct = Struct.new(:status, :version, :name, :branch, :database, :filename, keyword_init: true)
Expand Down Expand Up @@ -90,6 +91,20 @@ def self.for_each_db_connection
yield
end
end

def self.branch_for(metadata, version)
metadata.fetch(version, {})[:branch] || "unknown"
end

def self.metadata
ActualDbSchema::Store.instance.read
end

def self.prepare_context
fetch_migration_context.tap do |c|
c.extend(ActualDbSchema::Patches::MigrationContext)
end
end
end

ActiveRecord::MigrationProxy.prepend(ActualDbSchema::Patches::MigrationProxy)
4 changes: 1 addition & 3 deletions lib/actual_db_schema/commands/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,7 @@ def call_impl
end

def context
@context ||= ActualDbSchema.fetch_migration_context.tap do |c|
c.extend(ActualDbSchema::Patches::MigrationContext)
end
@context ||= ActualDbSchema.prepare_context
end
end
end
Expand Down
8 changes: 2 additions & 6 deletions lib/actual_db_schema/commands/list.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,13 @@ def line_for(status, version)
[
status.center(8),
version.to_s.ljust(14),
branch_for(version).ljust(branch_column_width),
ActualDbSchema.branch_for(metadata, version).ljust(branch_column_width),
migration.filename.gsub("#{Rails.root}/", "")
].join(" ")
end

def branch_for(version)
metadata.fetch(version, {})[:branch] || "unknown"
end

def metadata
@metadata ||= ActualDbSchema::Store.instance.read
@metadata ||= ActualDbSchema.metadata
end

def longest_branch_name
Expand Down
10 changes: 4 additions & 6 deletions lib/actual_db_schema/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,14 @@ module ActualDbSchema
class Engine < ::Rails::Engine
isolate_namespace ActualDbSchema

initializer "actual_db_schema.append_routes", after: :add_routing_paths do |app|
if Rails.env.development? || Rails.env.test?
initializer "actual_db_schema.initialize" do |app|
unless ActualDbSchema.config[:ui_disabled]
app.routes.append do
mount ActualDbSchema::Engine => "/rails"
end
end
end

initializer "actual_db_schema.assets.precompile" do |app|
app.config.assets.precompile += %w[actual_db_schema/styles.css] if Rails.env.development?
app.config.assets.precompile += %w[actual_db_schema/styles.css]
end
end
end
end
Loading

0 comments on commit 994aea7

Please sign in to comment.