From 12218c5b5996305b64c2901e536abab8378df9d4 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Mon, 24 Jun 2024 17:31:14 +0300 Subject: [PATCH 01/36] Add UI --- .../actual_db_schema/migrations_controller.rb | 48 +++++++++++++++++++ .../migrations/index.html.erb | 31 ++++++++++++ config/routes.rb | 8 ++++ lib/actual_db_schema.rb | 1 + lib/actual_db_schema/engine.rb | 8 ++++ 5 files changed, 96 insertions(+) create mode 100644 app/controllers/actual_db_schema/migrations_controller.rb create mode 100644 app/views/actual_db_schema/migrations/index.html.erb create mode 100644 config/routes.rb create mode 100644 lib/actual_db_schema/engine.rb diff --git a/app/controllers/actual_db_schema/migrations_controller.rb b/app/controllers/actual_db_schema/migrations_controller.rb new file mode 100644 index 0000000..570a595 --- /dev/null +++ b/app/controllers/actual_db_schema/migrations_controller.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +module ActualDbSchema + class MigrationsController < ActionController::Base + def index + @phantom_migrations = [] + + ActualDbSchema.for_each_db_connection do + context = fetch_migration_context + context.extend(ActualDbSchema::Patches::MigrationContext) + + 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 + + @phantom_migrations << { + status: status, + version: version, + branch: branch_for(version), + filename: migration.filename.gsub("#{Rails.root}/", "") + } + end + end + end + + private + + def fetch_migration_context + ar_version = Gem::Version.new(ActiveRecord::VERSION::STRING) + if ar_version >= Gem::Version.new("7.2.0") || + (ar_version >= Gem::Version.new("7.1.0") && ar_version.prerelease?) + ActiveRecord::Base.connection_pool.migration_context + else + ActiveRecord::Base.connection.migration_context + end + end + + def branch_for(version) + metadata.fetch(version.to_s, {})[:branch] || "unknown" + end + + def metadata + @metadata ||= ActualDbSchema::Store.instance.read + end + end +end diff --git a/app/views/actual_db_schema/migrations/index.html.erb b/app/views/actual_db_schema/migrations/index.html.erb new file mode 100644 index 0000000..e8d11e3 --- /dev/null +++ b/app/views/actual_db_schema/migrations/index.html.erb @@ -0,0 +1,31 @@ + + + + Migrations + + +

Phantom Migrations

+ + + + + + + + + + + + <% @phantom_migrations.each do |migration| %> + + + + + + + <% end %> + +
StatusMigration IDBranchMigration File
<%= migration[:status] %><%= migration[:version] %><%= migration[:branch] %><%= migration[:filename] %>
+ + + diff --git a/config/routes.rb b/config/routes.rb new file mode 100644 index 0000000..887f9b0 --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +ActualDbSchema::Engine.routes.draw do + root to: "migrations#index" + namespace :actual_db_schema do + resources :migrations, only: [:index] + end +end diff --git a/lib/actual_db_schema.rb b/lib/actual_db_schema.rb index 314002a..fc5e92d 100644 --- a/lib/actual_db_schema.rb +++ b/lib/actual_db_schema.rb @@ -1,5 +1,6 @@ # frozen_string_literal: true +require "actual_db_schema/engine" require "active_record/migration" require "csv" require_relative "actual_db_schema/git" diff --git a/lib/actual_db_schema/engine.rb b/lib/actual_db_schema/engine.rb new file mode 100644 index 0000000..966e0cc --- /dev/null +++ b/lib/actual_db_schema/engine.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +module ActualDbSchema + # It isolates the namespace to avoid conflicts with the main application. + class Engine < ::Rails::Engine + isolate_namespace ActualDbSchema + end +end From 930ffa11cccd59089b9e4d97a683b0c405e92c4f Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Tue, 25 Jun 2024 20:16:02 +0300 Subject: [PATCH 02/36] fixup! Add UI --- .../actual_db_schema/migrations_controller.rb | 84 ++++++++++++--- .../migrations/index.html.erb | 67 +++++++++++- .../actual_db_schema/migrations/show.html.erb | 102 ++++++++++++++++++ config/routes.rb | 6 +- lib/actual_db_schema/engine.rb | 6 ++ 5 files changed, 246 insertions(+), 19 deletions(-) create mode 100644 app/views/actual_db_schema/migrations/show.html.erb diff --git a/app/controllers/actual_db_schema/migrations_controller.rb b/app/controllers/actual_db_schema/migrations_controller.rb index 570a595..08d06e5 100644 --- a/app/controllers/actual_db_schema/migrations_controller.rb +++ b/app/controllers/actual_db_schema/migrations_controller.rb @@ -1,31 +1,83 @@ # frozen_string_literal: true module ActualDbSchema + # Controller to display the list of phantom migrations for each database connection. class MigrationsController < ActionController::Base - def index - @phantom_migrations = [] + before_action :load_migrations, only: %i[index] + before_action :load_migration, only: %i[show rollback] + + def index; end + + def show; end + + def rollback + version = params[:id] ActualDbSchema.for_each_db_connection do - context = fetch_migration_context - context.extend(ActualDbSchema::Patches::MigrationContext) + context = prepare_context + context.rollback_branches(manual_mode: false) if context.migrations.detect { |m| m.version.to_s == version } + end + + redirect_to actual_db_schema.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 - @phantom_migrations << { - status: status, - version: version, - branch: branch_for(version), - filename: migration.filename.gsub("#{Rails.root}/", "") - } + @migrations << build_migration_hash(status, version, migration) end end end - private + def load_migration + @migration = find_migration_in_all_connections(params[:id]) + render_not_found unless @migration + end + + def find_migration_in_all_connections(version) + migration = nil + + ActualDbSchema.for_each_db_connection do + context = prepare_context + migration = find_migration_in_context(context, version) + break if migration + end + + migration + end + + def find_migration_in_context(context, version) + migration = context.migrations.detect { |m| m.version.to_s == version } + status = context.migrations_status.detect { |_s, v| v.to_s == version }&.first || 'unknown' + build_migration_hash(status, migration.version.to_s, migration) + end + + def prepare_context + context = fetch_migration_context + context.extend(ActualDbSchema::Patches::MigrationContext) + context + end + + def build_migration_hash(status, version, migration) + { + status: status, + version: version, + name: migration.name, + branch: branch_for(version), + database: db_config[:database], + filename: migration.filename + } + end def fetch_migration_context ar_version = Gem::Version.new(ActiveRecord::VERSION::STRING) @@ -37,12 +89,20 @@ def fetch_migration_context end end + def db_config + if ActiveRecord::Base.respond_to?(:connection_db_config) + ActiveRecord::Base.connection_db_config.configuration_hash + else + ActiveRecord::Base.connection_config + end + end + def branch_for(version) metadata.fetch(version.to_s, {})[:branch] || "unknown" end def metadata - @metadata ||= ActualDbSchema::Store.instance.read + ActualDbSchema::Store.instance.read end end end diff --git a/app/views/actual_db_schema/migrations/index.html.erb b/app/views/actual_db_schema/migrations/index.html.erb index e8d11e3..c48ffbd 100644 --- a/app/views/actual_db_schema/migrations/index.html.erb +++ b/app/views/actual_db_schema/migrations/index.html.erb @@ -1,7 +1,58 @@ - Migrations + Phantom Migrations +

Phantom Migrations

@@ -11,21 +62,27 @@ Status Migration ID + Name Branch - Migration File + Database + Actions - <% @phantom_migrations.each do |migration| %> + <% @migrations.each do |migration| %> <%= migration[:status] %> <%= migration[:version] %> + <%= migration[:name] %> <%= migration[:branch] %> - <%= migration[:filename] %> + <%= migration[:database] %> + + <%= link_to 'Show', actual_db_schema.migration_path(migration[:version]), class: 'button' %> + <%= button_to 'Rollback', rollback_migration_path(migration[:version]), method: :post, class: 'button button-rollback' %> + <% end %> - diff --git a/app/views/actual_db_schema/migrations/show.html.erb b/app/views/actual_db_schema/migrations/show.html.erb new file mode 100644 index 0000000..aac3861 --- /dev/null +++ b/app/views/actual_db_schema/migrations/show.html.erb @@ -0,0 +1,102 @@ + + + + Migration Details + + + +

Migration Details

+ +
+

Migration <%= @migration[:version] %>: <%= @migration[:name] %>

+ + + + + + + + + + + + + + + + + +
Status<%= @migration[:status] %>
Branch<%= @migration[:branch] %>
Database<%= @migration[:database] %>
Filename<%= @migration[:filename] %>
+ +

Migration Code

+
<%= File.read(@migration[:filename]) %>
+ +
+ <%= link_to 'Back', actual_db_schema.migrations_path, class: 'button' %> + <%= button_to 'Rollback', rollback_migration_path(@migration[:version]), method: :post, class: 'button button-rollback' %> +
+
+ + diff --git a/config/routes.rb b/config/routes.rb index 887f9b0..0cec897 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,7 +2,9 @@ ActualDbSchema::Engine.routes.draw do root to: "migrations#index" - namespace :actual_db_schema do - resources :migrations, only: [:index] + resources :migrations, only: %i[index show] do + member do + post :rollback + end end end diff --git a/lib/actual_db_schema/engine.rb b/lib/actual_db_schema/engine.rb index 966e0cc..5fbf8b4 100644 --- a/lib/actual_db_schema/engine.rb +++ b/lib/actual_db_schema/engine.rb @@ -4,5 +4,11 @@ module ActualDbSchema # It isolates the namespace to avoid conflicts with the main application. class Engine < ::Rails::Engine isolate_namespace ActualDbSchema + + initializer "actual_db_schema.append_routes", after: "add_routing_paths" do |app| + app.routes.append do + mount ActualDbSchema::Engine => "/actual_db_schema" + end + end end end From 4ba814797e7dfccfd06c6dc1f8705624eda5a6dc Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Wed, 26 Jun 2024 19:08:21 +0300 Subject: [PATCH 03/36] fixup! fixup! Add UI --- .../stylesheets/actual_db_schema/styles.css | 68 +++++++++ .../actual_db_schema/migrations_controller.rb | 43 +++--- .../migrations/index.html.erb | 123 +++++----------- .../actual_db_schema/migrations/show.html.erb | 137 +++++------------- lib/actual_db_schema/engine.rb | 4 + 5 files changed, 175 insertions(+), 200 deletions(-) create mode 100644 app/assets/stylesheets/actual_db_schema/styles.css diff --git a/app/assets/stylesheets/actual_db_schema/styles.css b/app/assets/stylesheets/actual_db_schema/styles.css new file mode 100644 index 0000000..4c4346e --- /dev/null +++ b/app/assets/stylesheets/actual_db_schema/styles.css @@ -0,0 +1,68 @@ +body { + margin: 8px; + background-color: #fff; + color: #333; +} + +body, p, td { + font-family: helvetica, verdana, arial, sans-serif; + font-size: 13px; + line-height: 18px; +} + +h2 { + padding-left: 10px; +} + +table { + margin: 0; + border-collapse: collapse; + + thead tr { + border-bottom: 2px solid #ddd; + } + + tbody { + tr { + border-bottom: 1px solid #ddd; + } + + tr:nth-child(odd) { + background: #f2f2f2; + } + } + + td { + padding: 14px 30px; + } +} + +.button { + font-weight: bold; + color: #000; + border: none; + padding: 5px 10px; + text-align: center; + text-decoration: none; + display: inline-block; + margin: 0 2px; + cursor: pointer; + border-radius: 4px; + transition: background-color 0.3s; + background: none; +} + +.button:hover { + color: #fff; + background-color: #000; +} + +.button-container { + display: flex; +} + +pre { + background-color: #f7f7f7; + padding: 10px; + border: 1px solid #ddd; +} diff --git a/app/controllers/actual_db_schema/migrations_controller.rb b/app/controllers/actual_db_schema/migrations_controller.rb index 08d06e5..9276a44 100644 --- a/app/controllers/actual_db_schema/migrations_controller.rb +++ b/app/controllers/actual_db_schema/migrations_controller.rb @@ -4,19 +4,18 @@ 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] - before_action :load_migration, only: %i[show rollback] def index; end - def show; end + def show + @migration = load_migration(params[:id], params[:database]) + end def rollback version = params[:id] + database = params[:database] - ActualDbSchema.for_each_db_connection do - context = prepare_context - context.rollback_branches(manual_mode: false) if context.migrations.detect { |m| m.version.to_s == version } - end + rollback_migration(version, database) redirect_to actual_db_schema.migrations_path, notice: "Migration #{version} has been rolled back." end @@ -39,29 +38,37 @@ def load_migrations end end - def load_migration - @migration = find_migration_in_all_connections(params[:id]) - render_not_found unless @migration - end - - def find_migration_in_all_connections(version) - migration = nil - + def load_migration(version, database) ActualDbSchema.for_each_db_connection do + next unless db_config[:database] == database + context = prepare_context migration = find_migration_in_context(context, version) - break if migration + return migration if migration end + nil + end + + def rollback_migration(version, database) + ActualDbSchema.for_each_db_connection do + next unless db_config[:database] == database - migration + 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 } - status = context.migrations_status.detect { |_s, v| v.to_s == version }&.first || 'unknown' + return unless migration + + status = context.migrations_status.detect { |_s, v| v.to_s == version }&.first || "unknown" build_migration_hash(status, migration.version.to_s, migration) end - + def prepare_context context = fetch_migration_context context.extend(ActualDbSchema::Patches::MigrationContext) diff --git a/app/views/actual_db_schema/migrations/index.html.erb b/app/views/actual_db_schema/migrations/index.html.erb index c48ffbd..b559122 100644 --- a/app/views/actual_db_schema/migrations/index.html.erb +++ b/app/views/actual_db_schema/migrations/index.html.erb @@ -1,88 +1,41 @@ - - Phantom Migrations - - - -

Phantom Migrations

- - - - - - - - - - - - - - <% @migrations.each do |migration| %> - - - - - - - - - <% end %> - -
StatusMigration IDNameBranchDatabaseActions
<%= migration[:status] %><%= migration[:version] %><%= migration[:name] %><%= migration[:branch] %><%= migration[:database] %> - <%= link_to 'Show', actual_db_schema.migration_path(migration[:version]), class: 'button' %> - <%= button_to 'Rollback', rollback_migration_path(migration[:version]), method: :post, class: 'button button-rollback' %> -
- + + Phantom Migrations + <%= stylesheet_link_tag 'actual_db_schema/styles', media: 'all' %> + + +
+

Phantom Migrations

+ + + + + + + + + + + + + <% @migrations.each do |migration| %> + + + + + + + + + <% end %> + +
StatusMigration IDNameBranchDatabaseActions
<%= migration[:status] %><%= migration[:version] %><%= migration[:name] %><%= migration[:branch] %><%= migration[:database] %> +
+ <%= link_to 'Show', actual_db_schema.migration_path(id: migration[:version], database: migration[:database]), class: 'button' %> + <%= button_to 'Rollback', rollback_migration_path(id: migration[:version], database: migration[:database]), method: :post, class: 'button' %> +
+
+
+ diff --git a/app/views/actual_db_schema/migrations/show.html.erb b/app/views/actual_db_schema/migrations/show.html.erb index aac3861..941858c 100644 --- a/app/views/actual_db_schema/migrations/show.html.erb +++ b/app/views/actual_db_schema/migrations/show.html.erb @@ -1,102 +1,45 @@ - - Migration Details - - - -

Migration Details

+ + Migration Details + <%= stylesheet_link_tag 'actual_db_schema/styles', media: 'all' %> + + +
+

Migration <%= @migration[:name] %> Details

+ + + + + + + + + + + + + + + + + + + + + + + +
Status<%= @migration[:status] %>
Migration ID<%= @migration[:version] %>
Branch<%= @migration[:branch] %>
Database<%= @migration[:database] %>
Path<%= @migration[:filename] %>
-
-

Migration <%= @migration[:version] %>: <%= @migration[:name] %>

- - - - - - - - - - - - - - - - - -
Status<%= @migration[:status] %>
Branch<%= @migration[:branch] %>
Database<%= @migration[:database] %>
Filename<%= @migration[:filename] %>
- -

Migration Code

-
<%= File.read(@migration[:filename]) %>
- -
- <%= link_to 'Back', actual_db_schema.migrations_path, class: 'button' %> - <%= button_to 'Rollback', rollback_migration_path(@migration[:version]), method: :post, class: 'button button-rollback' %> +

Migration Code

+
+
<%= File.read(@migration[:filename]) %>
+
+
+ <%= link_to 'Back', actual_db_schema.migrations_path, class: 'button' %> + <%= button_to 'Rollback', rollback_migration_path(id: @migration[:version], database: @migration[:database]), method: :post, class: 'button' %> +
-
- + diff --git a/lib/actual_db_schema/engine.rb b/lib/actual_db_schema/engine.rb index 5fbf8b4..4988c44 100644 --- a/lib/actual_db_schema/engine.rb +++ b/lib/actual_db_schema/engine.rb @@ -10,5 +10,9 @@ class Engine < ::Rails::Engine mount ActualDbSchema::Engine => "/actual_db_schema" end end + + initializer "actual_db_schema.assets.precompile" do |app| + app.config.assets.precompile += %w[actual_db_schema/styles.css] + end end end From 4a73bd1e5431a22565b8547ecdb88961e9f38b49 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Thu, 27 Jun 2024 22:44:20 +0300 Subject: [PATCH 04/36] Add tests draft --- .../migrations_controller_test.rb | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 test/controllers/actual_db_schema/migrations_controller_test.rb diff --git a/test/controllers/actual_db_schema/migrations_controller_test.rb b/test/controllers/actual_db_schema/migrations_controller_test.rb new file mode 100644 index 0000000..4c79c46 --- /dev/null +++ b/test/controllers/actual_db_schema/migrations_controller_test.rb @@ -0,0 +1,30 @@ +# frozen_string_literal: true + +require "test_helper" +require_relative "../../../app/controllers/actual_db_schema/migrations_controller" + +module ActualDbSchema + class MigrationsControllerTest < ActionController::TestCase + def setup + @utils = TestUtils.new + @migration_version = @utils.migration_timestamps.first + @database = "primary" + + ActiveRecord::Base.configurations = { "test" => TestingState.db_config["primary"] } + ActiveRecord::Tasks::DatabaseTasks.database_configuration = { "test" => TestingState.db_config["primary"] } + ActiveRecord::Base.establish_connection(**TestingState.db_config["primary"]) + @utils.cleanup(TestingState.db_config) + @utils.prepare_phantom_migrations(TestingState.db_config) + + @routes = ActualDbSchema::Engine.routes + + @controller = ActualDbSchema::MigrationsController.new + end + + test "should get index" do + get :index + assert_response :success + assert_select "h2", "Phantom Migrations" + end + end +end From ff630e857b38904fb5f924b85bd814b5dac85019 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Fri, 28 Jun 2024 19:37:58 +0300 Subject: [PATCH 05/36] Add tests --- lib/actual_db_schema/engine.rb | 2 +- .../migrations_controller_test.rb | 31 ++++++++++++------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/lib/actual_db_schema/engine.rb b/lib/actual_db_schema/engine.rb index 4988c44..0bd5c87 100644 --- a/lib/actual_db_schema/engine.rb +++ b/lib/actual_db_schema/engine.rb @@ -7,7 +7,7 @@ class Engine < ::Rails::Engine initializer "actual_db_schema.append_routes", after: "add_routing_paths" do |app| app.routes.append do - mount ActualDbSchema::Engine => "/actual_db_schema" + mount ActualDbSchema::Engine => "/rails" end end diff --git a/test/controllers/actual_db_schema/migrations_controller_test.rb b/test/controllers/actual_db_schema/migrations_controller_test.rb index 4c79c46..c670d04 100644 --- a/test/controllers/actual_db_schema/migrations_controller_test.rb +++ b/test/controllers/actual_db_schema/migrations_controller_test.rb @@ -1,30 +1,37 @@ # frozen_string_literal: true -require "test_helper" +require_relative "../../test_helper" require_relative "../../../app/controllers/actual_db_schema/migrations_controller" module ActualDbSchema - class MigrationsControllerTest < ActionController::TestCase + class MigrationsControllerTest < ActionDispatch::IntegrationTest def setup @utils = TestUtils.new - @migration_version = @utils.migration_timestamps.first - @database = "primary" + @app = Rails.application + @routes = @app.routes + Rails.logger = Logger.new($stdout) + ActualDbSchema::MigrationsController.include(@routes.url_helpers) + Rails.application.routes.draw do + get "/rails/migrations" => "actual_db_schema/migrations#index" + end + ActionController::Base.view_paths = [File.expand_path("../../../app/views/", __dir__)] + active_record_setup + @utils.cleanup + end + def active_record_setup ActiveRecord::Base.configurations = { "test" => TestingState.db_config["primary"] } ActiveRecord::Tasks::DatabaseTasks.database_configuration = { "test" => TestingState.db_config["primary"] } ActiveRecord::Base.establish_connection(**TestingState.db_config["primary"]) - @utils.cleanup(TestingState.db_config) - @utils.prepare_phantom_migrations(TestingState.db_config) - - @routes = ActualDbSchema::Engine.routes + end - @controller = ActualDbSchema::MigrationsController.new + test "GET #index route resolves to correct controller action" do + assert_routing "/rails/migrations", controller: "actual_db_schema/migrations", action: "index" end - test "should get index" do - get :index + test "GET #index returns a successful response" do + get "/rails/migrations" assert_response :success - assert_select "h2", "Phantom Migrations" end end end From 0ceffa9bb60cac82972c6fc7834425e552edb9f5 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Mon, 1 Jul 2024 18:15:21 +0300 Subject: [PATCH 06/36] fixup! Add tests --- README.md | 8 +++++ .../actual_db_schema/migrations_controller.rb | 2 +- .../migrations/index.html.erb | 2 +- .../actual_db_schema/migrations/show.html.erb | 2 +- config/routes.rb | 1 - lib/actual_db_schema/engine.rb | 10 +++--- .../migrations_controller_test.rb | 33 +++++++++++++------ 7 files changed, 40 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 052c9fc..1d13ddf 100644 --- a/README.md +++ b/README.md @@ -62,6 +62,14 @@ The gem offers the following rake tasks that can be manually run according to yo - `rails db:rollback_branches:manual` - run it to manually rolls back phantom migrations one by one. - `rails db:phantom_migrations` - displays a list of phantom migrations. +## Accessing the UI + +The UI for managing migrations is enabled automatically. To access the UI, simply navigate to the following URL in your web browser: +``` +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 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: diff --git a/app/controllers/actual_db_schema/migrations_controller.rb b/app/controllers/actual_db_schema/migrations_controller.rb index 9276a44..f1615c2 100644 --- a/app/controllers/actual_db_schema/migrations_controller.rb +++ b/app/controllers/actual_db_schema/migrations_controller.rb @@ -17,7 +17,7 @@ def rollback rollback_migration(version, database) - redirect_to actual_db_schema.migrations_path, notice: "Migration #{version} has been rolled back." + redirect_to migrations_path, notice: "Migration #{version} has been rolled back." end private diff --git a/app/views/actual_db_schema/migrations/index.html.erb b/app/views/actual_db_schema/migrations/index.html.erb index b559122..ca558f2 100644 --- a/app/views/actual_db_schema/migrations/index.html.erb +++ b/app/views/actual_db_schema/migrations/index.html.erb @@ -28,7 +28,7 @@ <%= migration[:database] %>
- <%= link_to 'Show', actual_db_schema.migration_path(id: migration[:version], database: migration[:database]), class: 'button' %> + <%= link_to 'Show', migration_path(id: migration[:version], database: migration[:database]), class: 'button' %> <%= button_to 'Rollback', rollback_migration_path(id: migration[:version], database: migration[:database]), method: :post, class: 'button' %>
diff --git a/app/views/actual_db_schema/migrations/show.html.erb b/app/views/actual_db_schema/migrations/show.html.erb index 941858c..1e80b39 100644 --- a/app/views/actual_db_schema/migrations/show.html.erb +++ b/app/views/actual_db_schema/migrations/show.html.erb @@ -37,7 +37,7 @@
<%= File.read(@migration[:filename]) %>
- <%= link_to 'Back', actual_db_schema.migrations_path, class: 'button' %> + <%= link_to 'Back', migrations_path, class: 'button' %> <%= button_to 'Rollback', rollback_migration_path(id: @migration[:version], database: @migration[:database]), method: :post, class: 'button' %>
diff --git a/config/routes.rb b/config/routes.rb index 0cec897..3195765 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,6 @@ # frozen_string_literal: true ActualDbSchema::Engine.routes.draw do - root to: "migrations#index" resources :migrations, only: %i[index show] do member do post :rollback diff --git a/lib/actual_db_schema/engine.rb b/lib/actual_db_schema/engine.rb index 0bd5c87..5004fbf 100644 --- a/lib/actual_db_schema/engine.rb +++ b/lib/actual_db_schema/engine.rb @@ -5,14 +5,16 @@ module ActualDbSchema class Engine < ::Rails::Engine isolate_namespace ActualDbSchema - initializer "actual_db_schema.append_routes", after: "add_routing_paths" do |app| - app.routes.append do - mount ActualDbSchema::Engine => "/rails" + initializer "actual_db_schema.append_routes", after: :add_routing_paths do |app| + if Rails.env.development? || Rails.env.test? + 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] + app.config.assets.precompile += %w[actual_db_schema/styles.css] if Rails.env.development? end end end diff --git a/test/controllers/actual_db_schema/migrations_controller_test.rb b/test/controllers/actual_db_schema/migrations_controller_test.rb index c670d04..f5fb656 100644 --- a/test/controllers/actual_db_schema/migrations_controller_test.rb +++ b/test/controllers/actual_db_schema/migrations_controller_test.rb @@ -4,19 +4,26 @@ require_relative "../../../app/controllers/actual_db_schema/migrations_controller" module ActualDbSchema - class MigrationsControllerTest < ActionDispatch::IntegrationTest + class MigrationsControllerTest < ActionController::TestCase def setup @utils = TestUtils.new @app = Rails.application - @routes = @app.routes + routes_setup Rails.logger = Logger.new($stdout) - ActualDbSchema::MigrationsController.include(@routes.url_helpers) - Rails.application.routes.draw do - get "/rails/migrations" => "actual_db_schema/migrations#index" - end ActionController::Base.view_paths = [File.expand_path("../../../app/views/", __dir__)] active_record_setup @utils.cleanup + @utils.prepare_phantom_migrations + end + + def routes_setup + @routes = @app.routes + Rails.application.routes.draw do + get "/rails/migrations" => "actual_db_schema/migrations#index", as: "migrations" + get "/rails/migration/:id" => "actual_db_schema/migrations#show", as: "migration" + post "/rails/migration/:id/rollback" => "actual_db_schema/migrations#rollback", as: "rollback_migration" + end + ActualDbSchema::MigrationsController.include(@routes.url_helpers) end def active_record_setup @@ -25,13 +32,19 @@ def active_record_setup ActiveRecord::Base.establish_connection(**TestingState.db_config["primary"]) end - test "GET #index route resolves to correct controller action" do - assert_routing "/rails/migrations", controller: "actual_db_schema/migrations", action: "index" + test "GET #index returns a successful response" do + get :index + assert_response :success end - test "GET #index returns a successful response" do - get "/rails/migrations" + test "GET #show returns a successful response" do + get :show, params: { id: "20130906111511", database: "tmp/primary.sqlite3" } assert_response :success end + + test "POST #rollback returns a successful response" do + post :rollback, params: { id: "20130906111511", database: "tmp/primary.sqlite3" } + assert_response :redirect + end end end From a970630ef0623142959ad959a35f2e7875d96ebf Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Mon, 1 Jul 2024 19:28:46 +0300 Subject: [PATCH 07/36] fixup! fixup! Add tests --- .../migrations_controller_test.rb | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/test/controllers/actual_db_schema/migrations_controller_test.rb b/test/controllers/actual_db_schema/migrations_controller_test.rb index f5fb656..6953be0 100644 --- a/test/controllers/actual_db_schema/migrations_controller_test.rb +++ b/test/controllers/actual_db_schema/migrations_controller_test.rb @@ -35,16 +35,32 @@ def active_record_setup test "GET #index returns a successful response" do get :index assert_response :success + assert_equal(2, @controller.instance_variable_get(:@migrations).count) + assert_equal(2, @controller.instance_variable_get(:@migrations).count { |migration| migration[:status] == "up" }) + assert_equal(0, @controller.instance_variable_get(:@migrations).count do |migration| + migration[:status] == "down" + end) end test "GET #show returns a successful response" do get :show, params: { id: "20130906111511", database: "tmp/primary.sqlite3" } assert_response :success + assert_equal "20130906111511", @controller.instance_variable_get(:@migration)[:version] + assert_equal "up", @controller.instance_variable_get(:@migration)[:status] + assert_equal "First", @controller.instance_variable_get(:@migration)[:name] + assert_select "h2", text: "Migration First Details" end - test "POST #rollback returns a successful response" do + test "POST #rollback changes migration status to down" do post :rollback, params: { id: "20130906111511", database: "tmp/primary.sqlite3" } assert_response :redirect + get :index + assert_equal(1, @controller.instance_variable_get(:@migrations).count { |migration| migration[:status] == "up" }) + assert_equal(1, @controller.instance_variable_get(:@migrations).count do |migration| + migration[:status] == "down" + end) + get :show, params: { id: "20130906111511", database: "tmp/primary.sqlite3" } + assert_equal "down", @controller.instance_variable_get(:@migration)[:status] end end end From 05576d8816fa26b96088ac8230fb6776bf92415d Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Tue, 2 Jul 2024 12:41:38 +0300 Subject: [PATCH 08/36] Refactoring --- .../actual_db_schema/migrations_controller.rb | 46 ++++++------------- lib/actual_db_schema.rb | 19 ++++++++ lib/actual_db_schema/commands/base.rb | 12 +---- lib/actual_db_schema/commands/list.rb | 11 +---- .../patches/migration_context.rb | 10 +--- 5 files changed, 36 insertions(+), 62 deletions(-) diff --git a/app/controllers/actual_db_schema/migrations_controller.rb b/app/controllers/actual_db_schema/migrations_controller.rb index f1615c2..0d6cfe9 100644 --- a/app/controllers/actual_db_schema/migrations_controller.rb +++ b/app/controllers/actual_db_schema/migrations_controller.rb @@ -33,14 +33,14 @@ def load_migrations migration = indexed_phantom_migrations[version] next unless migration - @migrations << build_migration_hash(status, version, migration) + @migrations << build_migration_struct(status, migration) end end end def load_migration(version, database) ActualDbSchema.for_each_db_connection do - next unless db_config[:database] == database + next unless ActualDbSchema.db_config[:database] == database context = prepare_context migration = find_migration_in_context(context, version) @@ -51,7 +51,7 @@ def load_migration(version, database) def rollback_migration(version, database) ActualDbSchema.for_each_db_connection do - next unless db_config[:database] == database + next unless ActualDbSchema.db_config[:database] == database context = prepare_context if context.migrations.detect { |m| m.version.to_s == version } @@ -66,42 +66,24 @@ def find_migration_in_context(context, version) return unless migration status = context.migrations_status.detect { |_s, v| v.to_s == version }&.first || "unknown" - build_migration_hash(status, migration.version.to_s, migration) + build_migration_struct(status, migration) end def prepare_context - context = fetch_migration_context + context = ActualDbSchema.fetch_migration_context context.extend(ActualDbSchema::Patches::MigrationContext) context end - def build_migration_hash(status, version, migration) - { - status: status, - version: version, - name: migration.name, - branch: branch_for(version), - database: db_config[:database], - filename: migration.filename - } - end - - def fetch_migration_context - ar_version = Gem::Version.new(ActiveRecord::VERSION::STRING) - if ar_version >= Gem::Version.new("7.2.0") || - (ar_version >= Gem::Version.new("7.1.0") && ar_version.prerelease?) - ActiveRecord::Base.connection_pool.migration_context - else - ActiveRecord::Base.connection.migration_context - end - end - - def db_config - if ActiveRecord::Base.respond_to?(:connection_db_config) - ActiveRecord::Base.connection_db_config.configuration_hash - else - ActiveRecord::Base.connection_config - end + def build_migration_struct(status, migration) + ActualDbSchema::MigrationStruct.new( + status, + migration.version.to_s, + migration.name, + branch_for(migration.version), + ActualDbSchema.db_config[:database], + migration.filename + ) end def branch_for(version) diff --git a/lib/actual_db_schema.rb b/lib/actual_db_schema.rb index fc5e92d..449d148 100644 --- a/lib/actual_db_schema.rb +++ b/lib/actual_db_schema.rb @@ -30,6 +30,8 @@ class << self auto_rollback_disabled: ENV["ACTUAL_DB_SCHEMA_AUTO_ROLLBACK_DISABLED"].present? } + MigrationStruct = Struct.new(:status, :version, :name, :branch, :database, :filename) + def self.migrated_folder migrated_folders.first end @@ -59,6 +61,23 @@ def self.migrations_paths end end + def self.fetch_migration_context + ar_version = Gem::Version.new(ActiveRecord::VERSION::STRING) + if ar_version >= Gem::Version.new("7.2.0") || (ar_version >= Gem::Version.new("7.1.0") && ar_version.prerelease?) + ActiveRecord::Base.connection_pool.migration_context + else + ActiveRecord::Base.connection.migration_context + end + end + + def self.db_config + if ActiveRecord::Base.respond_to?(:connection_db_config) + ActiveRecord::Base.connection_db_config.configuration_hash + else + ActiveRecord::Base.connection_config + end + end + def self.migration_filename(fullpath) fullpath.split("/").last end diff --git a/lib/actual_db_schema/commands/base.rb b/lib/actual_db_schema/commands/base.rb index 055f35e..ff40d87 100644 --- a/lib/actual_db_schema/commands/base.rb +++ b/lib/actual_db_schema/commands/base.rb @@ -19,20 +19,10 @@ def call_impl end def context - @context ||= fetch_migration_context.tap do |c| + @context ||= ActualDbSchema.fetch_migration_context.tap do |c| c.extend(ActualDbSchema::Patches::MigrationContext) end end - - def fetch_migration_context - ar_version = Gem::Version.new(ActiveRecord::VERSION::STRING) - if ar_version >= Gem::Version.new("7.2.0") || - (ar_version >= Gem::Version.new("7.1.0") && ar_version.prerelease?) - ActiveRecord::Base.connection_pool.migration_context - else - ActiveRecord::Base.connection.migration_context - end - end end end end diff --git a/lib/actual_db_schema/commands/list.rb b/lib/actual_db_schema/commands/list.rb index a45f644..0907fb6 100644 --- a/lib/actual_db_schema/commands/list.rb +++ b/lib/actual_db_schema/commands/list.rb @@ -19,20 +19,11 @@ def preambule puts "\nPhantom migrations\n\n" puts "Below is a list of irrelevant migrations executed in unmerged branches." puts "To bring your database schema up to date, the migrations marked as \"up\" should be rolled back." - database_path = db_config[:database] - puts "\ndatabase: #{database_path}\n\n" + puts "\ndatabase: #{ActualDbSchema.db_config[:database]}\n\n" puts header.join(" ") puts "-" * separator_width end - def db_config - if ActiveRecord::Base.respond_to?(:connection_db_config) - ActiveRecord::Base.connection_db_config.configuration_hash - else - ActiveRecord::Base.connection_config - end - end - def separator_width header.map(&:length).sum + (header.size - 1) * 2 end diff --git a/lib/actual_db_schema/patches/migration_context.rb b/lib/actual_db_schema/patches/migration_context.rb index 5645aac..aa8b30e 100644 --- a/lib/actual_db_schema/patches/migration_context.rb +++ b/lib/actual_db_schema/patches/migration_context.rb @@ -52,7 +52,7 @@ def show_info_for(migration) puts "\n[ActualDbSchema] A phantom migration was found and is about to be rolled back." puts "Please make a decision from the options below to proceed.\n\n" puts "Branch: #{branch_for(migration.version.to_s)}" - puts "Database: #{db_config[:database]}" + puts "Database: #{ActualDbSchema.db_config[:database]}" puts "Version: #{migration.version}\n\n" puts File.read(migration.filename) end @@ -69,14 +69,6 @@ def migrate(migration) migrator.migrate end - def db_config - @db_config ||= if ActiveRecord::Base.respond_to?(:connection_db_config) - ActiveRecord::Base.connection_db_config.configuration_hash - else - ActiveRecord::Base.connection_config - end - end - def branch_for(version) metadata.fetch(version, {})[:branch] || "unknown" end From bc614fa760f6c45272e1cb75a418792e401f9095 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Tue, 2 Jul 2024 12:57:06 +0300 Subject: [PATCH 09/36] fixup! Refactoring --- .../actual_db_schema/migrations_controller.rb | 14 +++++++------- lib/actual_db_schema.rb | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/app/controllers/actual_db_schema/migrations_controller.rb b/app/controllers/actual_db_schema/migrations_controller.rb index 0d6cfe9..f821a08 100644 --- a/app/controllers/actual_db_schema/migrations_controller.rb +++ b/app/controllers/actual_db_schema/migrations_controller.rb @@ -76,13 +76,13 @@ def prepare_context end def build_migration_struct(status, migration) - ActualDbSchema::MigrationStruct.new( - status, - migration.version.to_s, - migration.name, - branch_for(migration.version), - ActualDbSchema.db_config[:database], - migration.filename + 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 diff --git a/lib/actual_db_schema.rb b/lib/actual_db_schema.rb index 449d148..5dcdc65 100644 --- a/lib/actual_db_schema.rb +++ b/lib/actual_db_schema.rb @@ -30,7 +30,7 @@ class << self auto_rollback_disabled: ENV["ACTUAL_DB_SCHEMA_AUTO_ROLLBACK_DISABLED"].present? } - MigrationStruct = Struct.new(:status, :version, :name, :branch, :database, :filename) + MigrationStruct = Struct.new(:status, :version, :name, :branch, :database, :filename, keyword_init: true) def self.migrated_folder migrated_folders.first From 994aea72834d900eb09296b8cd7a1ebed95f201c Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Tue, 2 Jul 2024 21:29:13 +0300 Subject: [PATCH 10/36] fixup! fixup! Refactoring --- CHANGELOG.md | 3 + README.md | 19 ++++ .../actual_db_schema/migrations_controller.rb | 90 ++----------------- .../actual_db_schema/migrations_helper.rb | 54 +++++++++++ .../actual_db_schema/rollback_service.rb | 18 ++++ .../migrations/index.html.erb | 2 +- .../actual_db_schema/migrations/show.html.erb | 69 +++++++------- lib/actual_db_schema.rb | 17 +++- lib/actual_db_schema/commands/base.rb | 4 +- lib/actual_db_schema/commands/list.rb | 8 +- lib/actual_db_schema/engine.rb | 10 +-- .../patches/migration_context.rb | 8 +- .../migrations_controller_test.rb | 59 +++++++++--- 13 files changed, 209 insertions(+), 152 deletions(-) create mode 100644 app/helpers/actual_db_schema/migrations_helper.rb create mode 100644 app/services/actual_db_schema/rollback_service.rb diff --git a/CHANGELOG.md b/CHANGELOG.md index bd47114..fc29883 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index 1d13ddf..4abd5a0 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/app/controllers/actual_db_schema/migrations_controller.rb b/app/controllers/actual_db_schema/migrations_controller.rb index f821a08..c5d3ae6 100644 --- a/app/controllers/actual_db_schema/migrations_controller.rb +++ b/app/controllers/actual_db_schema/migrations_controller.rb @@ -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 diff --git a/app/helpers/actual_db_schema/migrations_helper.rb b/app/helpers/actual_db_schema/migrations_helper.rb new file mode 100644 index 0000000..9f782f4 --- /dev/null +++ b/app/helpers/actual_db_schema/migrations_helper.rb @@ -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 diff --git a/app/services/actual_db_schema/rollback_service.rb b/app/services/actual_db_schema/rollback_service.rb new file mode 100644 index 0000000..9d3e697 --- /dev/null +++ b/app/services/actual_db_schema/rollback_service.rb @@ -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 diff --git a/app/views/actual_db_schema/migrations/index.html.erb b/app/views/actual_db_schema/migrations/index.html.erb index ca558f2..dcea696 100644 --- a/app/views/actual_db_schema/migrations/index.html.erb +++ b/app/views/actual_db_schema/migrations/index.html.erb @@ -19,7 +19,7 @@ - <% @migrations.each do |migration| %> + <% load_migrations.each do |migration| %> <%= migration[:status] %> <%= migration[:version] %> diff --git a/app/views/actual_db_schema/migrations/show.html.erb b/app/views/actual_db_schema/migrations/show.html.erb index 1e80b39..150a833 100644 --- a/app/views/actual_db_schema/migrations/show.html.erb +++ b/app/views/actual_db_schema/migrations/show.html.erb @@ -6,40 +6,45 @@
-

Migration <%= @migration[:name] %> Details

- - - - - - - - - - - - - - - - - - - - - - - -
Status<%= @migration[:status] %>
Migration ID<%= @migration[:version] %>
Branch<%= @migration[:branch] %>
Database<%= @migration[:database] %>
Path<%= @migration[:filename] %>
+ <% if migration = load_migration(params[:id], params[:database]) %> +

Migration <%= migration[:name] %> Details

+ + + + + + + + + + + + + + + + + + + + + + + +
Status<%= migration[:status] %>
Migration ID<%= migration[:version] %>
Branch<%= migration[:branch] %>
Database<%= migration[:database] %>
Path<%= migration[:filename] %>
-

Migration Code

-
-
<%= File.read(@migration[:filename]) %>
-
-
+

Migration Code

+
+
<%= File.read(migration[:filename]) %>
+
+
+ <%= link_to 'Back', migrations_path, class: 'button' %> + <%= button_to 'Rollback', rollback_migration_path(id: params[:id], database: params[:database]), method: :post, class: 'button' %> +
+ <% else %> +

Migration not found

<%= link_to 'Back', migrations_path, class: 'button' %> - <%= button_to 'Rollback', rollback_migration_path(id: @migration[:version], database: @migration[:database]), method: :post, class: 'button' %> -
+ <% end %>
diff --git a/lib/actual_db_schema.rb b/lib/actual_db_schema.rb index 5dcdc65..1d7d2f4 100644 --- a/lib/actual_db_schema.rb +++ b/lib/actual_db_schema.rb @@ -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) @@ -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) diff --git a/lib/actual_db_schema/commands/base.rb b/lib/actual_db_schema/commands/base.rb index ff40d87..9ca09da 100644 --- a/lib/actual_db_schema/commands/base.rb +++ b/lib/actual_db_schema/commands/base.rb @@ -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 diff --git a/lib/actual_db_schema/commands/list.rb b/lib/actual_db_schema/commands/list.rb index 0907fb6..6218b59 100644 --- a/lib/actual_db_schema/commands/list.rb +++ b/lib/actual_db_schema/commands/list.rb @@ -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 diff --git a/lib/actual_db_schema/engine.rb b/lib/actual_db_schema/engine.rb index 5004fbf..b395781 100644 --- a/lib/actual_db_schema/engine.rb +++ b/lib/actual_db_schema/engine.rb @@ -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 diff --git a/lib/actual_db_schema/patches/migration_context.rb b/lib/actual_db_schema/patches/migration_context.rb index aa8b30e..0e8df28 100644 --- a/lib/actual_db_schema/patches/migration_context.rb +++ b/lib/actual_db_schema/patches/migration_context.rb @@ -51,7 +51,7 @@ def user_wants_rollback? def show_info_for(migration) puts "\n[ActualDbSchema] A phantom migration was found and is about to be rolled back." puts "Please make a decision from the options below to proceed.\n\n" - puts "Branch: #{branch_for(migration.version.to_s)}" + puts "Branch: #{ActualDbSchema.branch_for(metadata, migration.version.to_s)}" puts "Database: #{ActualDbSchema.db_config[:database]}" puts "Version: #{migration.version}\n\n" puts File.read(migration.filename) @@ -69,12 +69,8 @@ def migrate(migration) migrator.migrate end - def branch_for(version) - metadata.fetch(version, {})[:branch] || "unknown" - end - def metadata - @metadata ||= ActualDbSchema::Store.instance.read + @metadata ||= ActualDbSchema.metadata end end end diff --git a/test/controllers/actual_db_schema/migrations_controller_test.rb b/test/controllers/actual_db_schema/migrations_controller_test.rb index 6953be0..7a5548b 100644 --- a/test/controllers/actual_db_schema/migrations_controller_test.rb +++ b/test/controllers/actual_db_schema/migrations_controller_test.rb @@ -35,32 +35,65 @@ def active_record_setup test "GET #index returns a successful response" do get :index assert_response :success - assert_equal(2, @controller.instance_variable_get(:@migrations).count) - assert_equal(2, @controller.instance_variable_get(:@migrations).count { |migration| migration[:status] == "up" }) - assert_equal(0, @controller.instance_variable_get(:@migrations).count do |migration| - migration[:status] == "down" - end) + assert_select "table" do + assert_select "tbody" do + assert_select "tr" do + assert_select "td", text: "up" + assert_select "td", text: "20130906111511" + assert_select "td", text: "First" + end + assert_select "tr" do + assert_select "td", text: "up" + assert_select "td", text: "20130906111512" + assert_select "td", text: "Second" + end + end + end end test "GET #show returns a successful response" do get :show, params: { id: "20130906111511", database: "tmp/primary.sqlite3" } assert_response :success - assert_equal "20130906111511", @controller.instance_variable_get(:@migration)[:version] - assert_equal "up", @controller.instance_variable_get(:@migration)[:status] - assert_equal "First", @controller.instance_variable_get(:@migration)[:name] assert_select "h2", text: "Migration First Details" + assert_select "table" do + assert_select "tr" do + assert_select "th", text: "Status" + assert_select "td", text: "up" + end + assert_select "tr" do + assert_select "th", text: "Migration ID" + assert_select "td", text: "20130906111511" + end + assert_select "tr" do + assert_select "th", text: "Database" + assert_select "td", text: "tmp/primary.sqlite3" + end + end end test "POST #rollback changes migration status to down" do post :rollback, params: { id: "20130906111511", database: "tmp/primary.sqlite3" } assert_response :redirect get :index - assert_equal(1, @controller.instance_variable_get(:@migrations).count { |migration| migration[:status] == "up" }) - assert_equal(1, @controller.instance_variable_get(:@migrations).count do |migration| - migration[:status] == "down" - end) + assert_select "table" do + assert_select "tbody" do + assert_select "tr" do + assert_select "td", text: "down" + assert_select "td", text: "20130906111511" + assert_select "td", text: "First" + end + assert_select "tr" do + assert_select "td", text: "up" + assert_select "td", text: "20130906111512" + assert_select "td", text: "Second" + end + end + end get :show, params: { id: "20130906111511", database: "tmp/primary.sqlite3" } - assert_equal "down", @controller.instance_variable_get(:@migration)[:status] + assert_select "tr" do + assert_select "th", text: "Status" + assert_select "td", text: "down" + end end end end From 633536672ee5936cd72e76a02f9fc20fa5854787 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Wed, 3 Jul 2024 12:06:12 +0300 Subject: [PATCH 11/36] fixup! fixup! fixup! Refactoring --- .../actual_db_schema/migrations_controller.rb | 66 +++++++++++++++++-- .../actual_db_schema/migrations_helper.rb | 54 --------------- .../actual_db_schema/rollback_service.rb | 18 ----- test/dummy_app/db/secondary_schema.rb | 7 +- 4 files changed, 65 insertions(+), 80 deletions(-) delete mode 100644 app/helpers/actual_db_schema/migrations_helper.rb delete mode 100644 app/services/actual_db_schema/rollback_service.rb diff --git a/app/controllers/actual_db_schema/migrations_controller.rb b/app/controllers/actual_db_schema/migrations_controller.rb index c5d3ae6..ac89721 100644 --- a/app/controllers/actual_db_schema/migrations_controller.rb +++ b/app/controllers/actual_db_schema/migrations_controller.rb @@ -1,19 +1,75 @@ # 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 - include MigrationsHelper - def index; end def show; end def rollback - RollbackService.perform(params[:id], params[:database]) + rollback_migration(params[:id], params[:database]) redirect_to migrations_path end + + private + + helper_method 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 + + helper_method 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 + + def rollback_migration(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 + + 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 diff --git a/app/helpers/actual_db_schema/migrations_helper.rb b/app/helpers/actual_db_schema/migrations_helper.rb deleted file mode 100644 index 9f782f4..0000000 --- a/app/helpers/actual_db_schema/migrations_helper.rb +++ /dev/null @@ -1,54 +0,0 @@ -# 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 diff --git a/app/services/actual_db_schema/rollback_service.rb b/app/services/actual_db_schema/rollback_service.rb deleted file mode 100644 index 9d3e697..0000000 --- a/app/services/actual_db_schema/rollback_service.rb +++ /dev/null @@ -1,18 +0,0 @@ -# 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 diff --git a/test/dummy_app/db/secondary_schema.rb b/test/dummy_app/db/secondary_schema.rb index be9176e..98fcf7b 100644 --- a/test/dummy_app/db/secondary_schema.rb +++ b/test/dummy_app/db/secondary_schema.rb @@ -2,13 +2,14 @@ # of editing this file, please use the migrations feature of Active Record to # incrementally modify your database, and then regenerate this schema definition. # -# This file is the source Rails uses to define your schema when running `bin/rails -# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to +# This file is the source Rails uses to define your schema when running `rails +# db:schema:load`. When creating a new database, `rails db:schema:load` tends to # be faster and is potentially less error prone than running all of your # migrations from scratch. Old migrations may fail to apply correctly if those # migrations use external dependencies or application code. # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2013_09_06_111515) do +ActiveRecord::Schema.define(version: 2013_09_06_111513) do + end From 9008e5d073ca478066fd6c0c233918d0db29dd13 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Thu, 4 Jul 2024 11:21:05 +0300 Subject: [PATCH 12/36] fixup! fixup! fixup! fixup! Refactoring --- README.md | 12 ++++++------ lib/actual_db_schema.rb | 2 +- lib/actual_db_schema/engine.rb | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 4abd5a0..f6c1932 100644 --- a/README.md +++ b/README.md @@ -68,25 +68,25 @@ The UI for managing migrations is enabled automatically. To access the UI, simpl ``` 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. +This page displays a list of phantom migrations for each database connection and provides options to view details and rollback them. -## Disabling the UI +## UI options -By default, the UI is enabled. If you prefer to disable the UI, you can do so in two ways: +By default, the UI is enabled in the development environment. If you prefer to enable the UI for another environment, you can do so in two ways: ### 1. Using Environment Variable -Set the environment variable `ACTUAL_DB_SCHEMA_UI_DISABLED` to `true`: +Set the environment variable `ACTUAL_DB_SCHEMA_UI_ENABLED` to `true`: ```sh -export ACTUAL_DB_SCHEMA_UI_DISABLED=true +export ACTUAL_DB_SCHEMA_UI_ENABLED=true ``` ### 2. Using Initializer Add the following line to your initializer file (`config/initializers/actual_db_schema.rb`): ```ruby -ActualDbSchema.config[:ui_disabled] = true +ActualDbSchema.config[:ui_enabled] = true ``` ## Disabling Automatic Rollback diff --git a/lib/actual_db_schema.rb b/lib/actual_db_schema.rb index 1d7d2f4..36f1747 100644 --- a/lib/actual_db_schema.rb +++ b/lib/actual_db_schema.rb @@ -28,7 +28,7 @@ class << self self.config = { enabled: Rails.env.development?, auto_rollback_disabled: ENV["ACTUAL_DB_SCHEMA_AUTO_ROLLBACK_DISABLED"].present?, - ui_disabled: Rails.env.production? || ENV["ACTUAL_DB_SCHEMA_UI_DISABLED"].present? + ui_enabled: Rails.env.development? || ENV["ACTUAL_DB_SCHEMA_UI_ENABLED"].present? } MigrationStruct = Struct.new(:status, :version, :name, :branch, :database, :filename, keyword_init: true) diff --git a/lib/actual_db_schema/engine.rb b/lib/actual_db_schema/engine.rb index b395781..a245b27 100644 --- a/lib/actual_db_schema/engine.rb +++ b/lib/actual_db_schema/engine.rb @@ -6,7 +6,7 @@ class Engine < ::Rails::Engine isolate_namespace ActualDbSchema initializer "actual_db_schema.initialize" do |app| - unless ActualDbSchema.config[:ui_disabled] + if ActualDbSchema.config[:ui_enabled] app.routes.append do mount ActualDbSchema::Engine => "/rails" end From 8aa04186de85bd81684956108ea87afce23b2fa0 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Thu, 4 Jul 2024 11:26:42 +0300 Subject: [PATCH 13/36] fixup! fixup! fixup! fixup! fixup! Refactoring --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index f6c1932..17b21e4 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,9 @@ Add the following line to your initializer file (`config/initializers/actual_db_ ActualDbSchema.config[:ui_enabled] = true ``` +>With this option, the UI can be disabled for all environments or be enabled in specific ones. + + ## 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: From f749e4f7fc342c42a36d6684397630444a4976f0 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Thu, 4 Jul 2024 11:27:09 +0300 Subject: [PATCH 14/36] fixup! fixup! fixup! fixup! fixup! fixup! Refactoring --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 17b21e4..871abc3 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,7 @@ Add the following line to your initializer file (`config/initializers/actual_db_ ActualDbSchema.config[:ui_enabled] = true ``` ->With this option, the UI can be disabled for all environments or be enabled in specific ones. +> With this option, the UI can be disabled for all environments or be enabled in specific ones. ## Disabling Automatic Rollback From 7d8e9287fec4577fde9cf813f285c9c16d5c9b44 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Thu, 4 Jul 2024 15:39:56 +0300 Subject: [PATCH 15/36] Add Migration class --- .../actual_db_schema/migrations_controller.rb | 16 +----- .../migrations/index.html.erb | 2 +- lib/actual_db_schema.rb | 1 + lib/actual_db_schema/migration.rb | 51 +++++++++++++++++++ 4 files changed, 55 insertions(+), 15 deletions(-) create mode 100644 lib/actual_db_schema/migration.rb diff --git a/app/controllers/actual_db_schema/migrations_controller.rb b/app/controllers/actual_db_schema/migrations_controller.rb index ac89721..556a379 100644 --- a/app/controllers/actual_db_schema/migrations_controller.rb +++ b/app/controllers/actual_db_schema/migrations_controller.rb @@ -14,20 +14,8 @@ def rollback private - helper_method 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 + helper_method def migrations + ActualDbSchema::Migration.instance.all end helper_method def load_migration(version, database) diff --git a/app/views/actual_db_schema/migrations/index.html.erb b/app/views/actual_db_schema/migrations/index.html.erb index dcea696..f9d10d6 100644 --- a/app/views/actual_db_schema/migrations/index.html.erb +++ b/app/views/actual_db_schema/migrations/index.html.erb @@ -19,7 +19,7 @@ - <% load_migrations.each do |migration| %> + <% migrations.each do |migration| %> <%= migration[:status] %> <%= migration[:version] %> diff --git a/lib/actual_db_schema.rb b/lib/actual_db_schema.rb index 36f1747..a254db4 100644 --- a/lib/actual_db_schema.rb +++ b/lib/actual_db_schema.rb @@ -6,6 +6,7 @@ require_relative "actual_db_schema/git" require_relative "actual_db_schema/store" require_relative "actual_db_schema/version" +require_relative "actual_db_schema/migration" require_relative "actual_db_schema/patches/migration_proxy" require_relative "actual_db_schema/patches/migrator" require_relative "actual_db_schema/patches/migration_context" diff --git a/lib/actual_db_schema/migration.rb b/lib/actual_db_schema/migration.rb new file mode 100644 index 0000000..dcc530f --- /dev/null +++ b/lib/actual_db_schema/migration.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module ActualDbSchema + # The Migration class is responsible for managing and retrieving migration information + class Migration + include Singleton + + Migration = Struct.new(:status, :version, :name, :branch, :database, :filename, keyword_init: true) + + def self.all + instance.all + end + + def all + 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 + + private + + 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 + end + end +end From 80e81cf29c4c8e8d8c44f453046a3825902315f5 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Thu, 4 Jul 2024 15:51:25 +0300 Subject: [PATCH 16/36] fixup! Add Migration class --- .../actual_db_schema/migrations_controller.rb | 23 ++----- .../actual_db_schema/migrations/show.html.erb | 69 +++++++++---------- lib/actual_db_schema/migration.rb | 23 +++++++ 3 files changed, 60 insertions(+), 55 deletions(-) diff --git a/app/controllers/actual_db_schema/migrations_controller.rb b/app/controllers/actual_db_schema/migrations_controller.rb index 556a379..ea9b88d 100644 --- a/app/controllers/actual_db_schema/migrations_controller.rb +++ b/app/controllers/actual_db_schema/migrations_controller.rb @@ -5,7 +5,9 @@ module ActualDbSchema class MigrationsController < ActionController::Base def index; end - def show; end + def show + render :not_found, status: 404 unless migration + end def rollback rollback_migration(params[:id], params[:database]) @@ -18,15 +20,8 @@ def rollback ActualDbSchema::Migration.instance.all end - helper_method 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 + helper_method def migration + ActualDbSchema::Migration.find(params[:id], params[:database]) end def rollback_migration(version, database) @@ -51,13 +46,5 @@ def build_migration_struct(status, migration) 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 diff --git a/app/views/actual_db_schema/migrations/show.html.erb b/app/views/actual_db_schema/migrations/show.html.erb index 150a833..1a88150 100644 --- a/app/views/actual_db_schema/migrations/show.html.erb +++ b/app/views/actual_db_schema/migrations/show.html.erb @@ -6,45 +6,40 @@
- <% if migration = load_migration(params[:id], params[:database]) %> -

Migration <%= migration[:name] %> Details

- - - - - - - - - - - - - - - - - - - - - - - -
Status<%= migration[:status] %>
Migration ID<%= migration[:version] %>
Branch<%= migration[:branch] %>
Database<%= migration[:database] %>
Path<%= migration[:filename] %>
+

Migration <%= migration[:name] %> Details

+ + + + + + + + + + + + + + + + + + + + + + + +
Status<%= migration[:status] %>
Migration ID<%= migration[:version] %>
Branch<%= migration[:branch] %>
Database<%= migration[:database] %>
Path<%= migration[:filename] %>
-

Migration Code

-
-
<%= File.read(migration[:filename]) %>
-
-
- <%= link_to 'Back', migrations_path, class: 'button' %> - <%= button_to 'Rollback', rollback_migration_path(id: params[:id], database: params[:database]), method: :post, class: 'button' %> -
- <% else %> -

Migration not found

+

Migration Code

+
+
<%= File.read(migration[:filename]) %>
+
+
<%= link_to 'Back', migrations_path, class: 'button' %> - <% end %> + <%= button_to 'Rollback', rollback_migration_path(id: params[:id], database: params[:database]), method: :post, class: 'button' %> +
diff --git a/lib/actual_db_schema/migration.rb b/lib/actual_db_schema/migration.rb index dcc530f..95df341 100644 --- a/lib/actual_db_schema/migration.rb +++ b/lib/actual_db_schema/migration.rb @@ -11,6 +11,10 @@ def self.all instance.all end + def self.find(version, database) + instance.find(version, database) + end + def all migrations = [] @@ -27,6 +31,17 @@ def all migrations end + def find(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) @@ -40,6 +55,14 @@ def build_migration_struct(status, migration) ) 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 branch_for(version) metadata.fetch(version.to_s, {})[:branch] || "unknown" end From a45030d13652ce5d0c2fae64be68bb03104aa364 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Thu, 4 Jul 2024 16:04:48 +0300 Subject: [PATCH 17/36] fixup! fixup! Add Migration class --- .../actual_db_schema/migrations_controller.rb | 27 ++----------------- lib/actual_db_schema/migration.rb | 16 +++++++++++ 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/app/controllers/actual_db_schema/migrations_controller.rb b/app/controllers/actual_db_schema/migrations_controller.rb index ea9b88d..6a38b22 100644 --- a/app/controllers/actual_db_schema/migrations_controller.rb +++ b/app/controllers/actual_db_schema/migrations_controller.rb @@ -10,41 +10,18 @@ def show end def rollback - rollback_migration(params[:id], params[:database]) + ActualDbSchema::Migration.rollback(params[:id], params[:database]) redirect_to migrations_path end private helper_method def migrations - ActualDbSchema::Migration.instance.all + ActualDbSchema::Migration.all end helper_method def migration ActualDbSchema::Migration.find(params[:id], params[:database]) end - - def rollback_migration(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 - - 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 end end diff --git a/lib/actual_db_schema/migration.rb b/lib/actual_db_schema/migration.rb index 95df341..9d0db98 100644 --- a/lib/actual_db_schema/migration.rb +++ b/lib/actual_db_schema/migration.rb @@ -15,6 +15,10 @@ def self.find(version, database) instance.find(version, database) end + def self.rollback(version, database) + instance.rollback(version, database) + end + def all migrations = [] @@ -42,6 +46,18 @@ def find(version, database) nil end + def rollback(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 + private def build_migration_struct(status, migration) From 7c22e05fa1e223c38065536004587ceb2d5156b4 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Thu, 4 Jul 2024 19:14:12 +0300 Subject: [PATCH 18/36] Add DatabaseConnection class --- lib/actual_db_schema.rb | 1 + lib/actual_db_schema/database_connection.rb | 36 +++++++++++++++++++++ lib/actual_db_schema/migration.rb | 9 ++---- test/dummy_app/db/secondary_schema.rb | 2 +- 4 files changed, 41 insertions(+), 7 deletions(-) create mode 100644 lib/actual_db_schema/database_connection.rb diff --git a/lib/actual_db_schema.rb b/lib/actual_db_schema.rb index a254db4..9a248df 100644 --- a/lib/actual_db_schema.rb +++ b/lib/actual_db_schema.rb @@ -7,6 +7,7 @@ require_relative "actual_db_schema/store" require_relative "actual_db_schema/version" require_relative "actual_db_schema/migration" +require_relative "actual_db_schema/database_connection" require_relative "actual_db_schema/patches/migration_proxy" require_relative "actual_db_schema/patches/migrator" require_relative "actual_db_schema/patches/migration_context" diff --git a/lib/actual_db_schema/database_connection.rb b/lib/actual_db_schema/database_connection.rb new file mode 100644 index 0000000..54236b0 --- /dev/null +++ b/lib/actual_db_schema/database_connection.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module ActualDbSchema + class DatabaseConnection + include Singleton + + def for_each_db_connection + configs.each do |db_config| + establish_connection(db_config) + yield context + end + end + + private + + def establish_connection(db_config) + config = db_config.respond_to?(:config) ? db_config.config : db_config + ActiveRecord::Base.establish_connection(config) + end + + def configs + ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env) + end + + def context + ar_version = Gem::Version.new(ActiveRecord::VERSION::STRING) + @context ||= if ar_version >= Gem::Version.new("7.2.0") || + (ar_version >= Gem::Version.new("7.1.0") && ar_version.prerelease?) + ActiveRecord::Base.connection_pool.migration_context + else + ActiveRecord::Base.connection.migration_context + end + @context.extend(ActualDbSchema::Patches::MigrationContext) + end + end +end diff --git a/lib/actual_db_schema/migration.rb b/lib/actual_db_schema/migration.rb index 9d0db98..2c809d0 100644 --- a/lib/actual_db_schema/migration.rb +++ b/lib/actual_db_schema/migration.rb @@ -22,8 +22,7 @@ def self.rollback(version, database) def all migrations = [] - ActualDbSchema.for_each_db_connection do - context = ActualDbSchema.prepare_context + DatabaseConnection.instance.for_each_db_connection do |context| indexed_migrations = context.migrations.index_by { |m| m.version.to_s } context.migrations_status.each do |status, version| @@ -36,10 +35,9 @@ def all end def find(version, database) - ActualDbSchema.for_each_db_connection do + DatabaseConnection.instance.for_each_db_connection do |context| next unless ActualDbSchema.db_config[:database] == database - context = ActualDbSchema.prepare_context migration = find_migration_in_context(context, version) return migration if migration end @@ -47,10 +45,9 @@ def find(version, database) end def rollback(version, database) - ActualDbSchema.for_each_db_connection do + DatabaseConnection.instance.for_each_db_connection do |context| 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 diff --git a/test/dummy_app/db/secondary_schema.rb b/test/dummy_app/db/secondary_schema.rb index 98fcf7b..b200061 100644 --- a/test/dummy_app/db/secondary_schema.rb +++ b/test/dummy_app/db/secondary_schema.rb @@ -10,6 +10,6 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2013_09_06_111513) do +ActiveRecord::Schema.define(version: 2013_09_06_111515) do end From fd656052b00f1aadf8b2057d2613f45656bc2430 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Thu, 4 Jul 2024 19:14:47 +0300 Subject: [PATCH 19/36] Add unecode icons to buttons --- app/views/actual_db_schema/migrations/index.html.erb | 4 ++-- app/views/actual_db_schema/migrations/show.html.erb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/views/actual_db_schema/migrations/index.html.erb b/app/views/actual_db_schema/migrations/index.html.erb index f9d10d6..03bde2e 100644 --- a/app/views/actual_db_schema/migrations/index.html.erb +++ b/app/views/actual_db_schema/migrations/index.html.erb @@ -28,8 +28,8 @@ <%= migration[:database] %>
- <%= link_to 'Show', migration_path(id: migration[:version], database: migration[:database]), class: 'button' %> - <%= button_to 'Rollback', rollback_migration_path(id: migration[:version], database: migration[:database]), method: :post, class: 'button' %> + <%= link_to '👁 Show', migration_path(id: migration[:version], database: migration[:database]), class: 'button' %> + <%= button_to '⎌ Rollback', rollback_migration_path(id: migration[:version], database: migration[:database]), method: :post, class: 'button' %>
diff --git a/app/views/actual_db_schema/migrations/show.html.erb b/app/views/actual_db_schema/migrations/show.html.erb index 1a88150..8e75629 100644 --- a/app/views/actual_db_schema/migrations/show.html.erb +++ b/app/views/actual_db_schema/migrations/show.html.erb @@ -37,8 +37,8 @@
<%= File.read(migration[:filename]) %>
- <%= link_to 'Back', migrations_path, class: 'button' %> - <%= button_to 'Rollback', rollback_migration_path(id: params[:id], database: params[:database]), method: :post, class: 'button' %> + <%= link_to '← Back', migrations_path, class: 'button' %> + <%= button_to '⎌ Rollback', rollback_migration_path(id: params[:id], database: params[:database]), method: :post, class: 'button' %>
From ed534ea73968299b02a551f3b5d151be03cd717b Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Thu, 4 Jul 2024 19:19:44 +0300 Subject: [PATCH 20/36] fixup! Add DatabaseConnection class --- lib/actual_db_schema/database_connection.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/actual_db_schema/database_connection.rb b/lib/actual_db_schema/database_connection.rb index 54236b0..6434967 100644 --- a/lib/actual_db_schema/database_connection.rb +++ b/lib/actual_db_schema/database_connection.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true module ActualDbSchema + # The class manages connections to each database and provides the appropriate migration context for each connection. class DatabaseConnection include Singleton From 0a465fd13666cfa278af00696d8d1c49c07d2dad Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Fri, 5 Jul 2024 09:14:05 +0300 Subject: [PATCH 21/36] fixup! fixup! Add DatabaseConnection class --- lib/actual_db_schema/database_connection.rb | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/actual_db_schema/database_connection.rb b/lib/actual_db_schema/database_connection.rb index 6434967..baf7e60 100644 --- a/lib/actual_db_schema/database_connection.rb +++ b/lib/actual_db_schema/database_connection.rb @@ -25,13 +25,13 @@ def configs def context ar_version = Gem::Version.new(ActiveRecord::VERSION::STRING) - @context ||= if ar_version >= Gem::Version.new("7.2.0") || - (ar_version >= Gem::Version.new("7.1.0") && ar_version.prerelease?) - ActiveRecord::Base.connection_pool.migration_context - else - ActiveRecord::Base.connection.migration_context - end - @context.extend(ActualDbSchema::Patches::MigrationContext) + context = if ar_version >= Gem::Version.new("7.2.0") || + (ar_version >= Gem::Version.new("7.1.0") && ar_version.prerelease?) + ActiveRecord::Base.connection_pool.migration_context + else + ActiveRecord::Base.connection.migration_context + end + context.extend(ActualDbSchema::Patches::MigrationContext) end end end From b715574b7df9eb2cdb6485f938e1e6d3605d2a74 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Fri, 5 Jul 2024 10:20:18 +0300 Subject: [PATCH 22/36] fixup! fixup! fixup! fixup! fixup! fixup! fixup! Refactoring --- lib/actual_db_schema.rb | 8 -------- lib/actual_db_schema/commands/list.rb | 8 ++++++-- lib/actual_db_schema/migration.rb | 3 ++- lib/actual_db_schema/patches/migration_context.rb | 8 ++++++-- 4 files changed, 14 insertions(+), 13 deletions(-) diff --git a/lib/actual_db_schema.rb b/lib/actual_db_schema.rb index 9a248df..ed0707d 100644 --- a/lib/actual_db_schema.rb +++ b/lib/actual_db_schema.rb @@ -94,14 +94,6 @@ def self.for_each_db_connection 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) diff --git a/lib/actual_db_schema/commands/list.rb b/lib/actual_db_schema/commands/list.rb index 6218b59..e36c9ee 100644 --- a/lib/actual_db_schema/commands/list.rb +++ b/lib/actual_db_schema/commands/list.rb @@ -52,13 +52,17 @@ def line_for(status, version) [ status.center(8), version.to_s.ljust(14), - ActualDbSchema.branch_for(metadata, version).ljust(branch_column_width), + branch_for(version).ljust(branch_column_width), migration.filename.gsub("#{Rails.root}/", "") ].join(" ") end def metadata - @metadata ||= ActualDbSchema.metadata + @metadata ||= ActualDbSchema::Store.instance.read + end + + def branch_for(version) + metadata.fetch(version, {})[:branch] || "unknown" end def longest_branch_name diff --git a/lib/actual_db_schema/migration.rb b/lib/actual_db_schema/migration.rb index 2c809d0..a938e92 100644 --- a/lib/actual_db_schema/migration.rb +++ b/lib/actual_db_schema/migration.rb @@ -81,7 +81,8 @@ def branch_for(version) end def metadata - ActualDbSchema::Store.instance.read + @metadata ||= {} + @metadata[ActualDbSchema.db_config[:database]] ||= ActualDbSchema::Store.instance.read end end end diff --git a/lib/actual_db_schema/patches/migration_context.rb b/lib/actual_db_schema/patches/migration_context.rb index 0e8df28..7d93e8a 100644 --- a/lib/actual_db_schema/patches/migration_context.rb +++ b/lib/actual_db_schema/patches/migration_context.rb @@ -51,7 +51,7 @@ def user_wants_rollback? def show_info_for(migration) puts "\n[ActualDbSchema] A phantom migration was found and is about to be rolled back." puts "Please make a decision from the options below to proceed.\n\n" - puts "Branch: #{ActualDbSchema.branch_for(metadata, migration.version.to_s)}" + puts "Branch: #{branch_for(migration.version.to_s)}" puts "Database: #{ActualDbSchema.db_config[:database]}" puts "Version: #{migration.version}\n\n" puts File.read(migration.filename) @@ -70,7 +70,11 @@ def migrate(migration) end def metadata - @metadata ||= ActualDbSchema.metadata + @metadata ||= ActualDbSchema::Store.instance.read + end + + def branch_for(version) + metadata.fetch(version, {})[:branch] || "unknown" end end end From 8404a76f93b1ef6ccc0988cbcc04fd3226dbf944 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Fri, 5 Jul 2024 12:33:45 +0300 Subject: [PATCH 23/36] fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Refactoring --- lib/actual_db_schema.rb | 2 -- lib/actual_db_schema/migration.rb | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/actual_db_schema.rb b/lib/actual_db_schema.rb index ed0707d..5a0f7f2 100644 --- a/lib/actual_db_schema.rb +++ b/lib/actual_db_schema.rb @@ -33,8 +33,6 @@ class << self ui_enabled: Rails.env.development? || ENV["ACTUAL_DB_SCHEMA_UI_ENABLED"].present? } - MigrationStruct = Struct.new(:status, :version, :name, :branch, :database, :filename, keyword_init: true) - def self.migrated_folder migrated_folders.first end diff --git a/lib/actual_db_schema/migration.rb b/lib/actual_db_schema/migration.rb index a938e92..d734fe2 100644 --- a/lib/actual_db_schema/migration.rb +++ b/lib/actual_db_schema/migration.rb @@ -58,7 +58,7 @@ def rollback(version, database) private def build_migration_struct(status, migration) - MigrationStruct.new( + Migration.new( status: status, version: migration.version.to_s, name: migration.name, From b7c52f8c5694fb63b00aee5d088b9126353e8c45 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Fri, 5 Jul 2024 15:01:25 +0300 Subject: [PATCH 24/36] fixup! fixup! fixup! Add tests --- .../migrations_controller_test.rb | 43 ++++++++++++++----- test/support/test_utils.rb | 8 ++++ 2 files changed, 41 insertions(+), 10 deletions(-) diff --git a/test/controllers/actual_db_schema/migrations_controller_test.rb b/test/controllers/actual_db_schema/migrations_controller_test.rb index 7a5548b..f35f140 100644 --- a/test/controllers/actual_db_schema/migrations_controller_test.rb +++ b/test/controllers/actual_db_schema/migrations_controller_test.rb @@ -12,8 +12,8 @@ def setup Rails.logger = Logger.new($stdout) ActionController::Base.view_paths = [File.expand_path("../../../app/views/", __dir__)] active_record_setup - @utils.cleanup - @utils.prepare_phantom_migrations + @utils.cleanup(TestingState.db_config) + @utils.prepare_phantom_migrations(TestingState.db_config) end def routes_setup @@ -27,9 +27,8 @@ def routes_setup end def active_record_setup - ActiveRecord::Base.configurations = { "test" => TestingState.db_config["primary"] } - ActiveRecord::Tasks::DatabaseTasks.database_configuration = { "test" => TestingState.db_config["primary"] } - ActiveRecord::Base.establish_connection(**TestingState.db_config["primary"]) + ActiveRecord::Base.configurations = { "test" => TestingState.db_config } + ActiveRecord::Tasks::DatabaseTasks.database_configuration = { "test" => TestingState.db_config } end test "GET #index returns a successful response" do @@ -40,12 +39,30 @@ def active_record_setup assert_select "tr" do assert_select "td", text: "up" assert_select "td", text: "20130906111511" - assert_select "td", text: "First" + assert_select "td", text: "FirstPrimary" + assert_select "td", text: @utils.branch_for("20130906111511") + assert_select "td", text: "tmp/primary.sqlite3" end assert_select "tr" do assert_select "td", text: "up" assert_select "td", text: "20130906111512" - assert_select "td", text: "Second" + assert_select "td", text: "SecondPrimary" + assert_select "td", text: @utils.branch_for("20130906111512") + assert_select "td", text: "tmp/primary.sqlite3" + end + assert_select "tr" do + assert_select "td", text: "up" + assert_select "td", text: "20130906111514" + assert_select "td", text: "FirstSecondary" + assert_select "td", text: @utils.branch_for("20130906111514") + assert_select "td", text: "tmp/secondary.sqlite3" + end + assert_select "tr" do + assert_select "td", text: "up" + assert_select "td", text: "20130906111515" + assert_select "td", text: "SecondSecondary" + assert_select "td", text: @utils.branch_for("20130906111515") + assert_select "td", text: "tmp/secondary.sqlite3" end end end @@ -54,7 +71,7 @@ def active_record_setup test "GET #show returns a successful response" do get :show, params: { id: "20130906111511", database: "tmp/primary.sqlite3" } assert_response :success - assert_select "h2", text: "Migration First Details" + assert_select "h2", text: "Migration FirstPrimary Details" assert_select "table" do assert_select "tr" do assert_select "th", text: "Status" @@ -68,6 +85,10 @@ def active_record_setup assert_select "th", text: "Database" assert_select "td", text: "tmp/primary.sqlite3" end + assert_select "tr" do + assert_select "th", text: "Branch" + assert_select "td", text: @utils.branch_for("20130906111511") + end end end @@ -80,12 +101,14 @@ def active_record_setup assert_select "tr" do assert_select "td", text: "down" assert_select "td", text: "20130906111511" - assert_select "td", text: "First" + assert_select "td", text: "FirstPrimary" + assert_select "td", text: @utils.branch_for("20130906111511") end assert_select "tr" do assert_select "td", text: "up" assert_select "td", text: "20130906111512" - assert_select "td", text: "Second" + assert_select "td", text: "SecondPrimary" + assert_select "td", text: @utils.branch_for("20130906111512") end end end diff --git a/test/support/test_utils.rb b/test/support/test_utils.rb index c00d4b9..36f270d 100644 --- a/test/support/test_utils.rb +++ b/test/support/test_utils.rb @@ -141,6 +141,10 @@ def migrated_files(db_config = nil) end end + def branch_for(version) + metadata.fetch(version.to_s, {})[:branch] + end + private def cleanup_call(prefix_name = nil) @@ -189,4 +193,8 @@ def applied_migrations_call def run_sql(sql) ActiveRecord::Base.connection.execute(sql) end + + def metadata + ActualDbSchema::Store.instance.read + end end From f0b4432d5c85918e8438da66b7fb550cd9ce587b Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Fri, 5 Jul 2024 15:19:54 +0300 Subject: [PATCH 25/36] Add new env var for "disabled" --- README.md | 8 +++++++- lib/actual_db_schema.rb | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 871abc3..4d48090 100644 --- a/README.md +++ b/README.md @@ -76,10 +76,11 @@ By default, the UI is enabled in the development environment. If you prefer to e ### 1. Using Environment Variable -Set the environment variable `ACTUAL_DB_SCHEMA_UI_ENABLED` to `true`: +Set the environment variables `ACTUAL_DB_SCHEMA_UI_ENABLED` and `ACTUAL_DB_SCHEMA_ENABLED` to `true`: ```sh export ACTUAL_DB_SCHEMA_UI_ENABLED=true +export ACTUAL_DB_SCHEMA_ENABLED=true ``` ### 2. Using Initializer @@ -87,10 +88,15 @@ Add the following line to your initializer file (`config/initializers/actual_db_ ```ruby ActualDbSchema.config[:ui_enabled] = true +ActualDbSchema.config[:enabled] = true ``` > With this option, the UI can be disabled for all environments or be enabled in specific ones. +> [!WARNING] +> ActualDbSchema.config[:enabled] = false +This option will completely disable the functionality of the gem in all environments + ## Disabling Automatic Rollback diff --git a/lib/actual_db_schema.rb b/lib/actual_db_schema.rb index 5a0f7f2..ea082b8 100644 --- a/lib/actual_db_schema.rb +++ b/lib/actual_db_schema.rb @@ -28,7 +28,7 @@ class << self self.failed = [] self.config = { - enabled: Rails.env.development?, + enabled: Rails.env.development? || ENV["ACTUAL_DB_SCHEMA_ENABLED"].present?, auto_rollback_disabled: ENV["ACTUAL_DB_SCHEMA_AUTO_ROLLBACK_DISABLED"].present?, ui_enabled: Rails.env.development? || ENV["ACTUAL_DB_SCHEMA_UI_ENABLED"].present? } From ae996f9ecaaec6161c2696697d32a26a0a3af5a4 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Fri, 5 Jul 2024 16:54:11 +0300 Subject: [PATCH 26/36] Remove railtie --- lib/actual_db_schema.rb | 2 -- lib/railtie.rb | 15 --------------- 2 files changed, 17 deletions(-) delete mode 100644 lib/railtie.rb diff --git a/lib/actual_db_schema.rb b/lib/actual_db_schema.rb index ea082b8..7fd9c5b 100644 --- a/lib/actual_db_schema.rb +++ b/lib/actual_db_schema.rb @@ -20,8 +20,6 @@ module ActualDbSchema raise NotImplementedError, "ActualDbSchema is only supported in Rails" unless defined?(Rails) - require "railtie" - class << self attr_accessor :config, :failed end diff --git a/lib/railtie.rb b/lib/railtie.rb deleted file mode 100644 index d64c247..0000000 --- a/lib/railtie.rb +++ /dev/null @@ -1,15 +0,0 @@ -# frozen_string_literal: true - -require "rails" - -module ActualDbSchema - # Load the task into Rails app - class Railtie < Rails::Railtie - railtie_name :actual_db_schema - - rake_tasks do - path = File.expand_path(__dir__) - Dir.glob("#{path}/tasks/**/*.rake").each { |f| load f } - end - end -end From e796256a6009081c4f7ea5cf1341352971f7c88f Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Fri, 5 Jul 2024 18:21:28 +0300 Subject: [PATCH 27/36] fixup! fixup! fixup! Add DatabaseConnection class --- lib/actual_db_schema.rb | 24 --------------------- lib/actual_db_schema/commands/base.rb | 8 ++++--- lib/actual_db_schema/commands/list.rb | 4 ++++ lib/actual_db_schema/commands/rollback.rb | 4 ++-- lib/actual_db_schema/database_connection.rb | 2 +- lib/actual_db_schema/migration.rb | 6 +++--- lib/tasks/db.rake | 12 +++++------ 7 files changed, 21 insertions(+), 39 deletions(-) diff --git a/lib/actual_db_schema.rb b/lib/actual_db_schema.rb index 7fd9c5b..2f30f8e 100644 --- a/lib/actual_db_schema.rb +++ b/lib/actual_db_schema.rb @@ -60,15 +60,6 @@ def self.migrations_paths end end - def self.fetch_migration_context - ar_version = Gem::Version.new(ActiveRecord::VERSION::STRING) - if ar_version >= Gem::Version.new("7.2.0") || (ar_version >= Gem::Version.new("7.1.0") && ar_version.prerelease?) - ActiveRecord::Base.connection_pool.migration_context - else - ActiveRecord::Base.connection.migration_context - end - end - def self.db_config if ActiveRecord::Base.respond_to?(:connection_db_config) ActiveRecord::Base.connection_db_config.configuration_hash @@ -80,21 +71,6 @@ def self.db_config def self.migration_filename(fullpath) fullpath.split("/").last end - - def self.for_each_db_connection - configs = ActiveRecord::Base.configurations.configs_for(env_name: ActiveRecord::Tasks::DatabaseTasks.env) - configs.each do |db_config| - config = db_config.respond_to?(:config) ? db_config.config : db_config - ActiveRecord::Base.establish_connection(config) - yield - end - 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) diff --git a/lib/actual_db_schema/commands/base.rb b/lib/actual_db_schema/commands/base.rb index 9ca09da..fa7917b 100644 --- a/lib/actual_db_schema/commands/base.rb +++ b/lib/actual_db_schema/commands/base.rb @@ -4,6 +4,10 @@ module ActualDbSchema module Commands # Base class for all commands class Base + def initialize(context: nil) + @context = context + end + def call unless ActualDbSchema.config.fetch(:enabled, true) raise "ActualDbSchema is disabled. Set ActualDbSchema.config[:enabled] = true to enable it." @@ -18,9 +22,7 @@ def call_impl raise NotImplementedError end - def context - @context ||= ActualDbSchema.prepare_context - end + attr_reader :context end end end diff --git a/lib/actual_db_schema/commands/list.rb b/lib/actual_db_schema/commands/list.rb index e36c9ee..1c07c04 100644 --- a/lib/actual_db_schema/commands/list.rb +++ b/lib/actual_db_schema/commands/list.rb @@ -4,6 +4,10 @@ module ActualDbSchema module Commands # Shows the list of phantom migrations class List < Base + def initialize(context: nil) + super(context: context) + end + private def call_impl diff --git a/lib/actual_db_schema/commands/rollback.rb b/lib/actual_db_schema/commands/rollback.rb index e0fb7ef..b87734a 100644 --- a/lib/actual_db_schema/commands/rollback.rb +++ b/lib/actual_db_schema/commands/rollback.rb @@ -4,9 +4,9 @@ module ActualDbSchema module Commands # Rolls back all phantom migrations class Rollback < Base - def initialize(manual_mode: false) + def initialize(manual_mode: false, context: nil) @manual_mode = manual_mode || manual_mode_default? - super() + super(context: context) end private diff --git a/lib/actual_db_schema/database_connection.rb b/lib/actual_db_schema/database_connection.rb index baf7e60..dcc83da 100644 --- a/lib/actual_db_schema/database_connection.rb +++ b/lib/actual_db_schema/database_connection.rb @@ -5,7 +5,7 @@ module ActualDbSchema class DatabaseConnection include Singleton - def for_each_db_connection + def for_each_migration_context configs.each do |db_config| establish_connection(db_config) yield context diff --git a/lib/actual_db_schema/migration.rb b/lib/actual_db_schema/migration.rb index d734fe2..a4e5453 100644 --- a/lib/actual_db_schema/migration.rb +++ b/lib/actual_db_schema/migration.rb @@ -22,7 +22,7 @@ def self.rollback(version, database) def all migrations = [] - DatabaseConnection.instance.for_each_db_connection do |context| + DatabaseConnection.instance.for_each_migration_context do |context| indexed_migrations = context.migrations.index_by { |m| m.version.to_s } context.migrations_status.each do |status, version| @@ -35,7 +35,7 @@ def all end def find(version, database) - DatabaseConnection.instance.for_each_db_connection do |context| + DatabaseConnection.instance.for_each_migration_context do |context| next unless ActualDbSchema.db_config[:database] == database migration = find_migration_in_context(context, version) @@ -45,7 +45,7 @@ def find(version, database) end def rollback(version, database) - DatabaseConnection.instance.for_each_db_connection do |context| + DatabaseConnection.instance.for_each_migration_context do |context| next unless ActualDbSchema.db_config[:database] == database if context.migrations.detect { |m| m.version.to_s == version } diff --git a/lib/tasks/db.rake b/lib/tasks/db.rake index 3379f06..8b7fbf2 100644 --- a/lib/tasks/db.rake +++ b/lib/tasks/db.rake @@ -4,8 +4,8 @@ namespace :db do desc "Rollback migrations that were run inside not a merged branch." task rollback_branches: :load_config do ActualDbSchema.failed = [] - ActualDbSchema.for_each_db_connection do - ActualDbSchema::Commands::Rollback.new.call + ActualDbSchema::DatabaseConnection.instance.for_each_migration_context do |context| + ActualDbSchema::Commands::Rollback.new(context: context).call end end @@ -13,16 +13,16 @@ namespace :db do desc "Manually rollback phantom migrations one by one" task manual: :load_config do ActualDbSchema.failed = [] - ActualDbSchema.for_each_db_connection do - ActualDbSchema::Commands::Rollback.new(manual_mode: true).call + ActualDbSchema::DatabaseConnection.instance.for_each_migration_context do |context| + ActualDbSchema::Commands::Rollback.new(manual_mode: true, context: context).call end end end desc "List all phantom migrations - non-relevant migrations that were run inside not a merged branch." task phantom_migrations: :load_config do - ActualDbSchema.for_each_db_connection do - ActualDbSchema::Commands::List.new.call + ActualDbSchema::DatabaseConnection.instance.for_each_migration_context do |context| + ActualDbSchema::Commands::List.new(context: context).call end end From c26ace63e7099f242a1d98e51962869dabb26340 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Fri, 5 Jul 2024 18:54:01 +0300 Subject: [PATCH 28/36] fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! fixup! Refactoring --- CHANGELOG.md | 4 +++- README.md | 9 +-------- lib/actual_db_schema.rb | 4 ++-- lib/actual_db_schema/commands/base.rb | 6 +++--- lib/actual_db_schema/commands/list.rb | 4 ---- lib/actual_db_schema/commands/rollback.rb | 4 ++-- lib/actual_db_schema/migration.rb | 6 +++--- .../{database_connection.rb => migration_context.rb} | 4 ++-- lib/tasks/db.rake | 12 ++++++------ test/dummy_app/db/secondary_schema.rb | 2 +- 10 files changed, 23 insertions(+), 32 deletions(-) rename lib/actual_db_schema/{database_connection.rb => migration_context.rb} (94%) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc29883..f760bb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ -## [0.7.6] - 2024-07-02 +## [0.7.6] - 2024-07-05 - Added UI +- Added environment variable `ACTUAL_DB_SCHEMA_UI_ENABLED` to enable/disable the UI in specific environments +- Added configuration option `ActualDbSchema.config[:ui_enabled]` to enable/disable the UI in specific environments ## [0.7.5] - 2024-06-20 - Added db:rollback_migrations:manual task to manually rolls back phantom migrations one by one diff --git a/README.md b/README.md index 4d48090..bf0d3e8 100644 --- a/README.md +++ b/README.md @@ -76,11 +76,10 @@ By default, the UI is enabled in the development environment. If you prefer to e ### 1. Using Environment Variable -Set the environment variables `ACTUAL_DB_SCHEMA_UI_ENABLED` and `ACTUAL_DB_SCHEMA_ENABLED` to `true`: +Set the environment variable `ACTUAL_DB_SCHEMA_UI_ENABLED` to `true`: ```sh export ACTUAL_DB_SCHEMA_UI_ENABLED=true -export ACTUAL_DB_SCHEMA_ENABLED=true ``` ### 2. Using Initializer @@ -88,16 +87,10 @@ Add the following line to your initializer file (`config/initializers/actual_db_ ```ruby ActualDbSchema.config[:ui_enabled] = true -ActualDbSchema.config[:enabled] = true ``` > With this option, the UI can be disabled for all environments or be enabled in specific ones. -> [!WARNING] -> ActualDbSchema.config[:enabled] = false -This option will completely disable the functionality of the gem in all environments - - ## 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: diff --git a/lib/actual_db_schema.rb b/lib/actual_db_schema.rb index 2f30f8e..b51cd45 100644 --- a/lib/actual_db_schema.rb +++ b/lib/actual_db_schema.rb @@ -7,7 +7,7 @@ require_relative "actual_db_schema/store" require_relative "actual_db_schema/version" require_relative "actual_db_schema/migration" -require_relative "actual_db_schema/database_connection" +require_relative "actual_db_schema/migration_context" require_relative "actual_db_schema/patches/migration_proxy" require_relative "actual_db_schema/patches/migrator" require_relative "actual_db_schema/patches/migration_context" @@ -26,7 +26,7 @@ class << self self.failed = [] self.config = { - enabled: Rails.env.development? || ENV["ACTUAL_DB_SCHEMA_ENABLED"].present?, + enabled: Rails.env.development?, auto_rollback_disabled: ENV["ACTUAL_DB_SCHEMA_AUTO_ROLLBACK_DISABLED"].present?, ui_enabled: Rails.env.development? || ENV["ACTUAL_DB_SCHEMA_UI_ENABLED"].present? } diff --git a/lib/actual_db_schema/commands/base.rb b/lib/actual_db_schema/commands/base.rb index fa7917b..fc49684 100644 --- a/lib/actual_db_schema/commands/base.rb +++ b/lib/actual_db_schema/commands/base.rb @@ -4,7 +4,9 @@ module ActualDbSchema module Commands # Base class for all commands class Base - def initialize(context: nil) + attr_reader :context + + def initialize(context) @context = context end @@ -21,8 +23,6 @@ def call def call_impl raise NotImplementedError end - - attr_reader :context end end end diff --git a/lib/actual_db_schema/commands/list.rb b/lib/actual_db_schema/commands/list.rb index 1c07c04..e36c9ee 100644 --- a/lib/actual_db_schema/commands/list.rb +++ b/lib/actual_db_schema/commands/list.rb @@ -4,10 +4,6 @@ module ActualDbSchema module Commands # Shows the list of phantom migrations class List < Base - def initialize(context: nil) - super(context: context) - end - private def call_impl diff --git a/lib/actual_db_schema/commands/rollback.rb b/lib/actual_db_schema/commands/rollback.rb index b87734a..3ea0850 100644 --- a/lib/actual_db_schema/commands/rollback.rb +++ b/lib/actual_db_schema/commands/rollback.rb @@ -4,9 +4,9 @@ module ActualDbSchema module Commands # Rolls back all phantom migrations class Rollback < Base - def initialize(manual_mode: false, context: nil) + def initialize(context, manual_mode: false) @manual_mode = manual_mode || manual_mode_default? - super(context: context) + super(context) end private diff --git a/lib/actual_db_schema/migration.rb b/lib/actual_db_schema/migration.rb index a4e5453..300cd30 100644 --- a/lib/actual_db_schema/migration.rb +++ b/lib/actual_db_schema/migration.rb @@ -22,7 +22,7 @@ def self.rollback(version, database) def all migrations = [] - DatabaseConnection.instance.for_each_migration_context do |context| + MigrationContext.instance.each do |context| indexed_migrations = context.migrations.index_by { |m| m.version.to_s } context.migrations_status.each do |status, version| @@ -35,7 +35,7 @@ def all end def find(version, database) - DatabaseConnection.instance.for_each_migration_context do |context| + MigrationContext.instance.each do |context| next unless ActualDbSchema.db_config[:database] == database migration = find_migration_in_context(context, version) @@ -45,7 +45,7 @@ def find(version, database) end def rollback(version, database) - DatabaseConnection.instance.for_each_migration_context do |context| + MigrationContext.instance.each do |context| next unless ActualDbSchema.db_config[:database] == database if context.migrations.detect { |m| m.version.to_s == version } diff --git a/lib/actual_db_schema/database_connection.rb b/lib/actual_db_schema/migration_context.rb similarity index 94% rename from lib/actual_db_schema/database_connection.rb rename to lib/actual_db_schema/migration_context.rb index dcc83da..dd5fd01 100644 --- a/lib/actual_db_schema/database_connection.rb +++ b/lib/actual_db_schema/migration_context.rb @@ -2,10 +2,10 @@ module ActualDbSchema # The class manages connections to each database and provides the appropriate migration context for each connection. - class DatabaseConnection + class MigrationContext include Singleton - def for_each_migration_context + def each configs.each do |db_config| establish_connection(db_config) yield context diff --git a/lib/tasks/db.rake b/lib/tasks/db.rake index 8b7fbf2..2c42b8f 100644 --- a/lib/tasks/db.rake +++ b/lib/tasks/db.rake @@ -4,8 +4,8 @@ namespace :db do desc "Rollback migrations that were run inside not a merged branch." task rollback_branches: :load_config do ActualDbSchema.failed = [] - ActualDbSchema::DatabaseConnection.instance.for_each_migration_context do |context| - ActualDbSchema::Commands::Rollback.new(context: context).call + ActualDbSchema::MigrationContext.instance.each do |context| + ActualDbSchema::Commands::Rollback.new(context).call end end @@ -13,16 +13,16 @@ namespace :db do desc "Manually rollback phantom migrations one by one" task manual: :load_config do ActualDbSchema.failed = [] - ActualDbSchema::DatabaseConnection.instance.for_each_migration_context do |context| - ActualDbSchema::Commands::Rollback.new(manual_mode: true, context: context).call + ActualDbSchema::MigrationContext.instance.each do |context| + ActualDbSchema::Commands::Rollback.new(context, manual_mode: true).call end end end desc "List all phantom migrations - non-relevant migrations that were run inside not a merged branch." task phantom_migrations: :load_config do - ActualDbSchema::DatabaseConnection.instance.for_each_migration_context do |context| - ActualDbSchema::Commands::List.new(context: context).call + ActualDbSchema::MigrationContext.instance.each do |context| + ActualDbSchema::Commands::List.new(context).call end end diff --git a/test/dummy_app/db/secondary_schema.rb b/test/dummy_app/db/secondary_schema.rb index b200061..79b5c13 100644 --- a/test/dummy_app/db/secondary_schema.rb +++ b/test/dummy_app/db/secondary_schema.rb @@ -10,6 +10,6 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2013_09_06_111515) do +ActiveRecord::Schema.define(version: 0) do end From 95a53b79a4050c415f622b47b165dc60bc370020 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Mon, 8 Jul 2024 19:28:05 +0300 Subject: [PATCH 29/36] Memoize helper methods --- app/controllers/actual_db_schema/migrations_controller.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/actual_db_schema/migrations_controller.rb b/app/controllers/actual_db_schema/migrations_controller.rb index 6a38b22..c05e818 100644 --- a/app/controllers/actual_db_schema/migrations_controller.rb +++ b/app/controllers/actual_db_schema/migrations_controller.rb @@ -17,11 +17,11 @@ def rollback private helper_method def migrations - ActualDbSchema::Migration.all + @migrations ||= ActualDbSchema::Migration.all end helper_method def migration - ActualDbSchema::Migration.find(params[:id], params[:database]) + @migration ||= ActualDbSchema::Migration.find(params[:id], params[:database]) end end end From 64559bbaf44e9688607741a5f3fad6cf16f624fc Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Mon, 8 Jul 2024 19:44:32 +0300 Subject: [PATCH 30/36] Rename migrations to phantom migrations --- README.md | 2 +- .../actual_db_schema/migrations_controller.rb | 27 ----------- .../phantom_migrations_controller.rb | 27 +++++++++++ .../actual_db_schema/migrations/show.html.erb | 45 ------------------- .../index.html.erb | 6 +-- .../phantom_migrations/show.html.erb | 45 +++++++++++++++++++ config/routes.rb | 2 +- ... => phantom_migrations_controller_test.rb} | 15 ++++--- test/dummy_app/db/secondary_schema.rb | 2 +- 9 files changed, 86 insertions(+), 85 deletions(-) delete mode 100644 app/controllers/actual_db_schema/migrations_controller.rb create mode 100644 app/controllers/actual_db_schema/phantom_migrations_controller.rb delete mode 100644 app/views/actual_db_schema/migrations/show.html.erb rename app/views/actual_db_schema/{migrations => phantom_migrations}/index.html.erb (71%) create mode 100644 app/views/actual_db_schema/phantom_migrations/show.html.erb rename test/controllers/actual_db_schema/{migrations_controller_test.rb => phantom_migrations_controller_test.rb} (85%) diff --git a/README.md b/README.md index bf0d3e8..f1e46ac 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ The gem offers the following rake tasks that can be manually run according to yo The UI for managing migrations is enabled automatically. To access the UI, simply navigate to the following URL in your web browser: ``` -http://localhost:3000/rails/migrations +http://localhost:3000/rails/phantom_migrations ``` This page displays a list of phantom migrations for each database connection and provides options to view details and rollback them. diff --git a/app/controllers/actual_db_schema/migrations_controller.rb b/app/controllers/actual_db_schema/migrations_controller.rb deleted file mode 100644 index c05e818..0000000 --- a/app/controllers/actual_db_schema/migrations_controller.rb +++ /dev/null @@ -1,27 +0,0 @@ -# frozen_string_literal: true - -module ActualDbSchema - # Controller to display the list of phantom migrations for each database connection. - class MigrationsController < ActionController::Base - def index; end - - def show - render :not_found, status: 404 unless migration - end - - def rollback - ActualDbSchema::Migration.rollback(params[:id], params[:database]) - redirect_to migrations_path - end - - private - - helper_method def migrations - @migrations ||= ActualDbSchema::Migration.all - end - - helper_method def migration - @migration ||= ActualDbSchema::Migration.find(params[:id], params[:database]) - end - end -end diff --git a/app/controllers/actual_db_schema/phantom_migrations_controller.rb b/app/controllers/actual_db_schema/phantom_migrations_controller.rb new file mode 100644 index 0000000..7224acb --- /dev/null +++ b/app/controllers/actual_db_schema/phantom_migrations_controller.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module ActualDbSchema + # Controller to display the list of phantom migrations for each database connection. + class PhantomMigrationsController < ActionController::Base + def index; end + + def show + render :not_found, status: 404 unless phantom_migration + end + + def rollback + ActualDbSchema::Migration.rollback(params[:id], params[:database]) + redirect_to phantom_migrations_path + end + + private + + helper_method def phantom_migrations + @phantom_migrations ||= ActualDbSchema::Migration.all + end + + helper_method def phantom_migration + @phantom_migration ||= ActualDbSchema::Migration.find(params[:id], params[:database]) + end + end +end diff --git a/app/views/actual_db_schema/migrations/show.html.erb b/app/views/actual_db_schema/migrations/show.html.erb deleted file mode 100644 index 8e75629..0000000 --- a/app/views/actual_db_schema/migrations/show.html.erb +++ /dev/null @@ -1,45 +0,0 @@ - - - - Migration Details - <%= stylesheet_link_tag 'actual_db_schema/styles', media: 'all' %> - - -
-

Migration <%= migration[:name] %> Details

- - - - - - - - - - - - - - - - - - - - - - - -
Status<%= migration[:status] %>
Migration ID<%= migration[:version] %>
Branch<%= migration[:branch] %>
Database<%= migration[:database] %>
Path<%= migration[:filename] %>
- -

Migration Code

-
-
<%= File.read(migration[:filename]) %>
-
-
- <%= link_to '← Back', migrations_path, class: 'button' %> - <%= button_to '⎌ Rollback', rollback_migration_path(id: params[:id], database: params[:database]), method: :post, class: 'button' %> -
-
- - diff --git a/app/views/actual_db_schema/migrations/index.html.erb b/app/views/actual_db_schema/phantom_migrations/index.html.erb similarity index 71% rename from app/views/actual_db_schema/migrations/index.html.erb rename to app/views/actual_db_schema/phantom_migrations/index.html.erb index 03bde2e..bb7d376 100644 --- a/app/views/actual_db_schema/migrations/index.html.erb +++ b/app/views/actual_db_schema/phantom_migrations/index.html.erb @@ -19,7 +19,7 @@ - <% migrations.each do |migration| %> + <% phantom_migrations.each do |migration| %> <%= migration[:status] %> <%= migration[:version] %> @@ -28,8 +28,8 @@ <%= migration[:database] %>
- <%= link_to '👁 Show', migration_path(id: migration[:version], database: migration[:database]), class: 'button' %> - <%= button_to '⎌ Rollback', rollback_migration_path(id: migration[:version], database: migration[:database]), method: :post, class: 'button' %> + <%= link_to '👁 Show', phantom_migration_path(id: migration[:version], database: migration[:database]), class: 'button' %> + <%= button_to '⎌ Rollback', rollback_phantom_migration_path(id: migration[:version], database: migration[:database]), method: :post, class: 'button' %>
diff --git a/app/views/actual_db_schema/phantom_migrations/show.html.erb b/app/views/actual_db_schema/phantom_migrations/show.html.erb new file mode 100644 index 0000000..96681cf --- /dev/null +++ b/app/views/actual_db_schema/phantom_migrations/show.html.erb @@ -0,0 +1,45 @@ + + + + Phantom Migration Details + <%= stylesheet_link_tag 'actual_db_schema/styles', media: 'all' %> + + +
+

Phantom Migration <%= phantom_migration[:name] %> Details

+ + + + + + + + + + + + + + + + + + + + + + + +
Status<%= phantom_migration[:status] %>
Migration ID<%= phantom_migration[:version] %>
Branch<%= phantom_migration[:branch] %>
Database<%= phantom_migration[:database] %>
Path<%= phantom_migration[:filename] %>
+ +

Migration Code

+
+
<%= File.read(phantom_migration[:filename]) %>
+
+
+ <%= link_to '← Back', phantom_migrations_path, class: 'button' %> + <%= button_to '⎌ Rollback', rollback_phantom_migration_path(id: params[:id], database: params[:database]), method: :post, class: 'button' %> +
+
+ + diff --git a/config/routes.rb b/config/routes.rb index 3195765..5d5d42e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true ActualDbSchema::Engine.routes.draw do - resources :migrations, only: %i[index show] do + resources :phantom_migrations, only: %i[index show] do member do post :rollback end diff --git a/test/controllers/actual_db_schema/migrations_controller_test.rb b/test/controllers/actual_db_schema/phantom_migrations_controller_test.rb similarity index 85% rename from test/controllers/actual_db_schema/migrations_controller_test.rb rename to test/controllers/actual_db_schema/phantom_migrations_controller_test.rb index f35f140..7c778b9 100644 --- a/test/controllers/actual_db_schema/migrations_controller_test.rb +++ b/test/controllers/actual_db_schema/phantom_migrations_controller_test.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true require_relative "../../test_helper" -require_relative "../../../app/controllers/actual_db_schema/migrations_controller" +require_relative "../../../app/controllers/actual_db_schema/phantom_migrations_controller" module ActualDbSchema - class MigrationsControllerTest < ActionController::TestCase + class PhantomMigrationsControllerTest < ActionController::TestCase def setup @utils = TestUtils.new @app = Rails.application @@ -19,11 +19,12 @@ def setup def routes_setup @routes = @app.routes Rails.application.routes.draw do - get "/rails/migrations" => "actual_db_schema/migrations#index", as: "migrations" - get "/rails/migration/:id" => "actual_db_schema/migrations#show", as: "migration" - post "/rails/migration/:id/rollback" => "actual_db_schema/migrations#rollback", as: "rollback_migration" + get "/rails/phantom_migrations" => "actual_db_schema/phantom_migrations#index", as: "phantom_migrations" + get "/rails/phantom_migration/:id" => "actual_db_schema/phantom_migrations#show", as: "phantom_migration" + post "/rails/phantom_migration/:id/rollback" => "actual_db_schema/phantom_migrations#rollback", + as: "rollback_phantom_migration" end - ActualDbSchema::MigrationsController.include(@routes.url_helpers) + ActualDbSchema::PhantomMigrationsController.include(@routes.url_helpers) end def active_record_setup @@ -71,7 +72,7 @@ def active_record_setup test "GET #show returns a successful response" do get :show, params: { id: "20130906111511", database: "tmp/primary.sqlite3" } assert_response :success - assert_select "h2", text: "Migration FirstPrimary Details" + assert_select "h2", text: "Phantom Migration FirstPrimary Details" assert_select "table" do assert_select "tr" do assert_select "th", text: "Status" diff --git a/test/dummy_app/db/secondary_schema.rb b/test/dummy_app/db/secondary_schema.rb index 79b5c13..b200061 100644 --- a/test/dummy_app/db/secondary_schema.rb +++ b/test/dummy_app/db/secondary_schema.rb @@ -10,6 +10,6 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 0) do +ActiveRecord::Schema.define(version: 2013_09_06_111515) do end From af67231794f3139e2f95d458da7d102317df9111 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Tue, 9 Jul 2024 11:43:42 +0300 Subject: [PATCH 31/36] Fix render not_found and add test --- .../actual_db_schema/phantom_migrations_controller.rb | 2 +- .../actual_db_schema/phantom_migrations_controller_test.rb | 5 +++++ test/dummy_app/public/404.html | 0 3 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 test/dummy_app/public/404.html diff --git a/app/controllers/actual_db_schema/phantom_migrations_controller.rb b/app/controllers/actual_db_schema/phantom_migrations_controller.rb index 7224acb..2734075 100644 --- a/app/controllers/actual_db_schema/phantom_migrations_controller.rb +++ b/app/controllers/actual_db_schema/phantom_migrations_controller.rb @@ -6,7 +6,7 @@ class PhantomMigrationsController < ActionController::Base def index; end def show - render :not_found, status: 404 unless phantom_migration + render file: "#{Rails.root}/public/404.html", layout: false, status: :not_found unless phantom_migration end def rollback diff --git a/test/controllers/actual_db_schema/phantom_migrations_controller_test.rb b/test/controllers/actual_db_schema/phantom_migrations_controller_test.rb index 7c778b9..912ff68 100644 --- a/test/controllers/actual_db_schema/phantom_migrations_controller_test.rb +++ b/test/controllers/actual_db_schema/phantom_migrations_controller_test.rb @@ -93,6 +93,11 @@ def active_record_setup end end + test "GET #show returns a 404 response if migration not found" do + get :show, params: { id: "nil", database: "tmp/primary.sqlite3" } + assert_response :not_found + end + test "POST #rollback changes migration status to down" do post :rollback, params: { id: "20130906111511", database: "tmp/primary.sqlite3" } assert_response :redirect diff --git a/test/dummy_app/public/404.html b/test/dummy_app/public/404.html new file mode 100644 index 0000000..e69de29 From bf05208179b7ad60080fea165ec9e77c3d9c1387 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Tue, 9 Jul 2024 11:46:27 +0300 Subject: [PATCH 32/36] Revert changes for metadata method --- lib/actual_db_schema/patches/migration_context.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/actual_db_schema/patches/migration_context.rb b/lib/actual_db_schema/patches/migration_context.rb index 7d93e8a..aa8b30e 100644 --- a/lib/actual_db_schema/patches/migration_context.rb +++ b/lib/actual_db_schema/patches/migration_context.rb @@ -69,13 +69,13 @@ def migrate(migration) migrator.migrate end - def metadata - @metadata ||= ActualDbSchema::Store.instance.read - end - def branch_for(version) metadata.fetch(version, {})[:branch] || "unknown" end + + def metadata + @metadata ||= ActualDbSchema::Store.instance.read + end end end end From 336a0883919edd1f4eca2dc97b8630b837ed796c Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Tue, 9 Jul 2024 14:43:47 +0300 Subject: [PATCH 33/36] add migration not found text --- .../phantom_migrations/index.html.erb | 58 ++++++++++--------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/app/views/actual_db_schema/phantom_migrations/index.html.erb b/app/views/actual_db_schema/phantom_migrations/index.html.erb index bb7d376..f749a64 100644 --- a/app/views/actual_db_schema/phantom_migrations/index.html.erb +++ b/app/views/actual_db_schema/phantom_migrations/index.html.erb @@ -7,35 +7,39 @@

Phantom Migrations

- - - - - - - - - - - - - <% phantom_migrations.each do |migration| %> + <% if phantom_migrations.present? %> +
StatusMigration IDNameBranchDatabaseActions
+ - - - - - - + + + + + + - <% end %> - -
<%= migration[:status] %><%= migration[:version] %><%= migration[:name] %><%= migration[:branch] %><%= migration[:database] %> -
- <%= link_to '👁 Show', phantom_migration_path(id: migration[:version], database: migration[:database]), class: 'button' %> - <%= button_to '⎌ Rollback', rollback_phantom_migration_path(id: migration[:version], database: migration[:database]), method: :post, class: 'button' %> -
-
StatusMigration IDNameBranchDatabaseActions
+ + + <% phantom_migrations.each do |migration| %> + + <%= migration[:status] %> + <%= migration[:version] %> + <%= migration[:name] %> + <%= migration[:branch] %> + <%= migration[:database] %> + +
+ <%= link_to '👁 Show', phantom_migration_path(id: migration[:version], database: migration[:database]), class: 'button' %> + <%= button_to '⎌ Rollback', rollback_phantom_migration_path(id: migration[:version], database: migration[:database]), method: :post, class: 'button' %> +
+ + + <% end %> + + + <% else %> +

No phantom migrations found.

+ <% end %>
From 11aece9594749e9fd5147668b16b9267219e3e6c Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Tue, 9 Jul 2024 14:51:05 +0300 Subject: [PATCH 34/36] hide "down" migrations --- lib/actual_db_schema/migration.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/actual_db_schema/migration.rb b/lib/actual_db_schema/migration.rb index 300cd30..a3a9ad6 100644 --- a/lib/actual_db_schema/migration.rb +++ b/lib/actual_db_schema/migration.rb @@ -27,7 +27,7 @@ def all context.migrations_status.each do |status, version| migration = indexed_migrations[version] - migrations << build_migration_struct(status, migration) if migration + migrations << build_migration_struct(status, migration) if migration && status == "up" end end From da3bad15c973f0d9044897e596ae4f73464cd5b8 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Tue, 9 Jul 2024 14:58:56 +0300 Subject: [PATCH 35/36] fix tests --- .../phantom_migrations_controller_test.rb | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/test/controllers/actual_db_schema/phantom_migrations_controller_test.rb b/test/controllers/actual_db_schema/phantom_migrations_controller_test.rb index 912ff68..e9ea019 100644 --- a/test/controllers/actual_db_schema/phantom_migrations_controller_test.rb +++ b/test/controllers/actual_db_schema/phantom_migrations_controller_test.rb @@ -37,6 +37,11 @@ def active_record_setup assert_response :success assert_select "table" do assert_select "tbody" do + assert_select "tr" do |rows| + rows.each do |row| + assert_no_match(/down/, row.text) + end + end assert_select "tr" do assert_select "td", text: "up" assert_select "td", text: "20130906111511" @@ -98,17 +103,16 @@ def active_record_setup assert_response :not_found end - test "POST #rollback changes migration status to down" do + test "POST #rollback changes migration status to down and hide migration with down status" do post :rollback, params: { id: "20130906111511", database: "tmp/primary.sqlite3" } assert_response :redirect get :index assert_select "table" do assert_select "tbody" do - assert_select "tr" do - assert_select "td", text: "down" - assert_select "td", text: "20130906111511" - assert_select "td", text: "FirstPrimary" - assert_select "td", text: @utils.branch_for("20130906111511") + assert_select "tr" do |rows| + rows.each do |row| + assert_no_match(/down/, row.text) + end end assert_select "tr" do assert_select "td", text: "up" @@ -118,11 +122,6 @@ def active_record_setup end end end - get :show, params: { id: "20130906111511", database: "tmp/primary.sqlite3" } - assert_select "tr" do - assert_select "th", text: "Status" - assert_select "td", text: "down" - end end end end From d7f3ee2bab1860523aceaa138dceea5ad5c44ee2 Mon Sep 17 00:00:00 2001 From: Vladislav Sokov Date: Tue, 9 Jul 2024 15:07:26 +0300 Subject: [PATCH 36/36] fixup! fix tests --- .../actual_db_schema/phantom_migrations_controller_test.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/controllers/actual_db_schema/phantom_migrations_controller_test.rb b/test/controllers/actual_db_schema/phantom_migrations_controller_test.rb index e9ea019..c33840a 100644 --- a/test/controllers/actual_db_schema/phantom_migrations_controller_test.rb +++ b/test/controllers/actual_db_schema/phantom_migrations_controller_test.rb @@ -74,6 +74,13 @@ def active_record_setup end end + test "GET #index when all migrations is down returns a not found text" do + @utils.run_migrations + get :index + assert_response :success + assert_select "p", text: "No phantom migrations found." + end + test "GET #show returns a successful response" do get :show, params: { id: "20130906111511", database: "tmp/primary.sqlite3" } assert_response :success