diff --git a/CHANGELOG.md b/CHANGELOG.md
index bd47114..f760bb1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+## [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 052c9fc..f1e46ac 100644
--- a/README.md
+++ b/README.md
@@ -62,6 +62,35 @@ 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/phantom_migrations
+```
+This page displays a list of phantom migrations for each database connection and provides options to view details and rollback them.
+
+## UI options
+
+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_ENABLED` to `true`:
+
+```sh
+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_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:
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/phantom_migrations_controller.rb b/app/controllers/actual_db_schema/phantom_migrations_controller.rb
new file mode 100644
index 0000000..2734075
--- /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 file: "#{Rails.root}/public/404.html", layout: false, status: :not_found 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/phantom_migrations/index.html.erb b/app/views/actual_db_schema/phantom_migrations/index.html.erb
new file mode 100644
index 0000000..f749a64
--- /dev/null
+++ b/app/views/actual_db_schema/phantom_migrations/index.html.erb
@@ -0,0 +1,45 @@
+
+
+
+ Phantom Migrations
+ <%= stylesheet_link_tag 'actual_db_schema/styles', media: 'all' %>
+
+
+
+
Phantom Migrations
+ <% if phantom_migrations.present? %>
+
+
+
+ Status |
+ Migration ID |
+ Name |
+ Branch |
+ Database |
+ Actions |
+
+
+
+ <% 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 %>
+
+
+
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
new file mode 100644
index 0000000..5d5d42e
--- /dev/null
+++ b/config/routes.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+ActualDbSchema::Engine.routes.draw do
+ resources :phantom_migrations, only: %i[index show] do
+ member do
+ post :rollback
+ end
+ end
+end
diff --git a/lib/actual_db_schema.rb b/lib/actual_db_schema.rb
index 314002a..b51cd45 100644
--- a/lib/actual_db_schema.rb
+++ b/lib/actual_db_schema.rb
@@ -1,10 +1,13 @@
# frozen_string_literal: true
+require "actual_db_schema/engine"
require "active_record/migration"
require "csv"
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/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"
@@ -17,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
@@ -26,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_enabled: Rails.env.development? || ENV["ACTUAL_DB_SCHEMA_UI_ENABLED"].present?
}
def self.migrated_folder
@@ -58,17 +60,16 @@ def self.migrations_paths
end
end
- def self.migration_filename(fullpath)
- fullpath.split("/").last
+ 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.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
+ def self.migration_filename(fullpath)
+ fullpath.split("/").last
end
end
diff --git a/lib/actual_db_schema/commands/base.rb b/lib/actual_db_schema/commands/base.rb
index 055f35e..fc49684 100644
--- a/lib/actual_db_schema/commands/base.rb
+++ b/lib/actual_db_schema/commands/base.rb
@@ -4,6 +4,12 @@ module ActualDbSchema
module Commands
# Base class for all commands
class Base
+ attr_reader :context
+
+ def initialize(context)
+ @context = context
+ end
+
def call
unless ActualDbSchema.config.fetch(:enabled, true)
raise "ActualDbSchema is disabled. Set ActualDbSchema.config[:enabled] = true to enable it."
@@ -17,22 +23,6 @@ def call
def call_impl
raise NotImplementedError
end
-
- def context
- @context ||= 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..e36c9ee 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
@@ -66,14 +57,14 @@ def line_for(status, version)
].join(" ")
end
- def branch_for(version)
- metadata.fetch(version, {})[:branch] || "unknown"
- end
-
def metadata
@metadata ||= ActualDbSchema::Store.instance.read
end
+ def branch_for(version)
+ metadata.fetch(version, {})[:branch] || "unknown"
+ end
+
def longest_branch_name
@longest_branch_name ||=
metadata.values.map { |v| v[:branch] }.compact.max_by(&:length) || "unknown"
diff --git a/lib/actual_db_schema/commands/rollback.rb b/lib/actual_db_schema/commands/rollback.rb
index e0fb7ef..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)
+ def initialize(context, manual_mode: false)
@manual_mode = manual_mode || manual_mode_default?
- super()
+ super(context)
end
private
diff --git a/lib/actual_db_schema/engine.rb b/lib/actual_db_schema/engine.rb
new file mode 100644
index 0000000..a245b27
--- /dev/null
+++ b/lib/actual_db_schema/engine.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+module ActualDbSchema
+ # It isolates the namespace to avoid conflicts with the main application.
+ class Engine < ::Rails::Engine
+ isolate_namespace ActualDbSchema
+
+ initializer "actual_db_schema.initialize" do |app|
+ if ActualDbSchema.config[:ui_enabled]
+ app.routes.append do
+ mount ActualDbSchema::Engine => "/rails"
+ end
+
+ app.config.assets.precompile += %w[actual_db_schema/styles.css]
+ end
+ end
+ end
+end
diff --git a/lib/actual_db_schema/migration.rb b/lib/actual_db_schema/migration.rb
new file mode 100644
index 0000000..a3a9ad6
--- /dev/null
+++ b/lib/actual_db_schema/migration.rb
@@ -0,0 +1,88 @@
+# 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 self.find(version, database)
+ instance.find(version, database)
+ end
+
+ def self.rollback(version, database)
+ instance.rollback(version, database)
+ end
+
+ def all
+ migrations = []
+
+ MigrationContext.instance.each do |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 && status == "up"
+ end
+ end
+
+ migrations
+ end
+
+ def find(version, database)
+ MigrationContext.instance.each do |context|
+ next unless ActualDbSchema.db_config[:database] == database
+
+ migration = find_migration_in_context(context, version)
+ return migration if migration
+ end
+ nil
+ end
+
+ def rollback(version, database)
+ MigrationContext.instance.each do |context|
+ next unless ActualDbSchema.db_config[:database] == database
+
+ 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)
+ Migration.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 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
+
+ def metadata
+ @metadata ||= {}
+ @metadata[ActualDbSchema.db_config[:database]] ||= ActualDbSchema::Store.instance.read
+ end
+ end
+end
diff --git a/lib/actual_db_schema/migration_context.rb b/lib/actual_db_schema/migration_context.rb
new file mode 100644
index 0000000..dd5fd01
--- /dev/null
+++ b/lib/actual_db_schema/migration_context.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module ActualDbSchema
+ # The class manages connections to each database and provides the appropriate migration context for each connection.
+ class MigrationContext
+ include Singleton
+
+ def each
+ 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/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
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
diff --git a/lib/tasks/db.rake b/lib/tasks/db.rake
index 3379f06..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.for_each_db_connection do
- ActualDbSchema::Commands::Rollback.new.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.for_each_db_connection do
- ActualDbSchema::Commands::Rollback.new(manual_mode: true).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.for_each_db_connection do
- ActualDbSchema::Commands::List.new.call
+ ActualDbSchema::MigrationContext.instance.each do |context|
+ ActualDbSchema::Commands::List.new(context).call
end
end
diff --git a/test/controllers/actual_db_schema/phantom_migrations_controller_test.rb b/test/controllers/actual_db_schema/phantom_migrations_controller_test.rb
new file mode 100644
index 0000000..c33840a
--- /dev/null
+++ b/test/controllers/actual_db_schema/phantom_migrations_controller_test.rb
@@ -0,0 +1,134 @@
+# frozen_string_literal: true
+
+require_relative "../../test_helper"
+require_relative "../../../app/controllers/actual_db_schema/phantom_migrations_controller"
+
+module ActualDbSchema
+ class PhantomMigrationsControllerTest < ActionController::TestCase
+ def setup
+ @utils = TestUtils.new
+ @app = Rails.application
+ routes_setup
+ Rails.logger = Logger.new($stdout)
+ ActionController::Base.view_paths = [File.expand_path("../../../app/views/", __dir__)]
+ active_record_setup
+ @utils.cleanup(TestingState.db_config)
+ @utils.prepare_phantom_migrations(TestingState.db_config)
+ end
+
+ def routes_setup
+ @routes = @app.routes
+ Rails.application.routes.draw do
+ 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::PhantomMigrationsController.include(@routes.url_helpers)
+ end
+
+ def active_record_setup
+ 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
+ get :index
+ 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"
+ 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: "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
+ 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
+ assert_select "h2", text: "Phantom Migration FirstPrimary 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
+ assert_select "tr" do
+ assert_select "th", text: "Branch"
+ assert_select "td", text: @utils.branch_for("20130906111511")
+ end
+ 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 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 |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: "20130906111512"
+ assert_select "td", text: "SecondPrimary"
+ assert_select "td", text: @utils.branch_for("20130906111512")
+ end
+ 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..b200061 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_111515) do
+
end
diff --git a/test/dummy_app/public/404.html b/test/dummy_app/public/404.html
new file mode 100644
index 0000000..e69de29
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