after_migrate 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a04f2af70c48d8299c3c1eedce77608a6a57aca044711bbc2c0e8030299d18e3
4
+ data.tar.gz: d511331553aacbe753d40b2004af8d523b2c823ef4ba425f8491eb8a0336d234
5
+ SHA512:
6
+ metadata.gz: c3f07535162f76e33ff8a7a399bbdee3b45d534b48a5609c600504a1f8b0e9c8d4d2e027ec653b7106c12e6663f91194370a9617912bd35860cdb77e6c451e6d
7
+ data.tar.gz: 8c529f7d643720957caf72ae2607055e286dbcafc7c15004a03e3908a6be9eab15c7dcc9c66b83c1b741265c70a2a1476e440846178bea2799a4884857639187
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
data/.rubocop.yml ADDED
@@ -0,0 +1,2 @@
1
+ AllCops:
2
+ NewCops: enable
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.4.7
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 3.4.7
7
+ before_install: gem install bundler -v 2.7.2
data/CHANGELOG.md ADDED
@@ -0,0 +1,29 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [0.1.0] - 2025-04-05
6
+
7
+ ### Added
8
+ - **Smart table detection** – automatically identifies tables touched during migrations using `sql.active_record` events
9
+ - **PostgreSQL support**
10
+ - `ANALYZE` on affected tables (default) or all tables
11
+ - Optional `VACUUM` via config
12
+ - **SQLite support**
13
+ - `PRAGMA optimize` (SQLite 3.35+)
14
+ - Fallback to `VACUUM` + `ANALYZE` on older versions
15
+ - **MySQL support** (MySQL 5.6+ / MariaDB)
16
+ - `ANALYZE TABLE` on affected tables
17
+ - **Zero false positives** – bulletproof SQL parser ignores:
18
+ - Views, materialized views, functions
19
+ - Column names, joins, CTEs
20
+ - System schemas (`pg_catalog`, `information_schema`)
21
+ - **Configurable via environment variables or block DSL**:
22
+ ```ruby
23
+ AfterMigrate.configure do |config|
24
+ config.enabled = false # default: true
25
+ config.verbose = false # default: true
26
+ config.vacuum = false # default: true
27
+ config.analyze = 'none' # only_affected_tables (default) or 'all_tables' or 'none'
28
+ end
29
+ ```
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ # Specify your gem's dependencies in after_migrate.gemspec
8
+ gemspec
9
+
10
+ gem 'bundler', '~> 4'
11
+ gem 'rake', '~> 13.0'
12
+ gem 'rspec', '~> 3.0'
13
+ gem 'rubocop', '~> 1.81'
data/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # AfterMigrate
2
+
3
+ **Automatically run database maintenance after Rails migrations**
4
+
5
+ `after_migrate` detects tables touched during `rails db:migrate` (or related tasks) and runs the appropriate optimizer commands:
6
+
7
+ - **PostgreSQL** → `ANALYZE` (affected tables or all) + optional `VACUUM`
8
+ - **SQLite** → `PRAGMA optimize` (or `VACUUM` + `ANALYZE`)
9
+ - **MySQL** → `ANALYZE TABLE`
10
+
11
+ Stale statistics and fragmentation after schema changes silently hurt query performance.
12
+ `after_migrate` fixes it - automatically and precisely.
13
+
14
+ ![after_migrate](./logo.png)
15
+
16
+ > **Because every migration deserves a cleanup.**
17
+
18
+ [![Gem Version](https://badge.fury.io/rb/after_migrate.svg)](https://badge.fury.io/rb/after_migrate)
19
+
20
+ ---
21
+
22
+ ## ✨ Features
23
+
24
+ - Smart detection of affected tables (CREATE/ALTER/INSERT/UPDATE/DELETE/etc.)
25
+ - Zero false positives - ignores views, columns, system tables, and complex joins
26
+ - Configurable via environment variables or initializer block
27
+ - Supports `db:migrate`, `db:rollback`, `db:migrate:redo`
28
+ - No monkey-patching of ActiveRecord core classes
29
+ - Works in development, test, CI, and production
30
+ - Rails 7.0+ / Ruby 3.2+ only
31
+ - Dependency-free
32
+
33
+ ---
34
+
35
+ ## 📦 Installation
36
+
37
+ Add to your Gemfile:
38
+
39
+ ```ruby
40
+ gem 'after_migrate'
41
+ ```
42
+
43
+ Then run:
44
+
45
+ ```bash
46
+ bundle install
47
+ ```
48
+
49
+ ---
50
+
51
+ ## 🚀 Usage
52
+
53
+ The gem activates automatically. No code required for default behavior.
54
+
55
+ ### Default behavior (recommended)
56
+
57
+ Out of the box, it runs `ANALYZE` on **only the tables touched** during the migration (PostgreSQL default).
58
+
59
+ ### Configuration
60
+
61
+ Create `config/initializers/after_migrate.rb`:
62
+
63
+ ```ruby
64
+ AfterMigrate.configure do |config|
65
+ # Enable/disable the gem
66
+ config.enabled = true
67
+
68
+ # Log what’s happening
69
+ config.verbose = true
70
+
71
+ # Run VACUUM on affected tables (PostgreSQL only)
72
+ config.vacuum = false
73
+
74
+ # Choose ANALYZE strategy
75
+ # "only_affected_tables" - default, precise
76
+ # "all_tables" - full database analyze
77
+ # "none" - skip ANALYZE entirely
78
+ config.analyze = "only_affected_tables"
79
+
80
+ # Enhance rake tasks (runs maintenance after db:migrate etc.)
81
+ # Set to false in test env if needed
82
+ config.rake_tasks_enhanced = true
83
+ end
84
+ ```
85
+ ---
86
+
87
+ ## 🤝 Contributing
88
+
89
+ Bug reports, feature requests, and pull requests are very welcome!
90
+
91
+ https://github.com/moskvin/after_migrate
92
+
93
+ ---
94
+
95
+ ## 📝 License
96
+
97
+ This project is available under the MIT License.
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task default: :spec
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'after_migrate/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'after_migrate'
9
+ spec.version = AfterMigrate::VERSION
10
+ spec.authors = ['Nikolay Moskvin']
11
+ spec.email = ['nikolay.moskvin@gmail.com']
12
+
13
+ spec.summary = 'Automatically ANALYZE and VACUUM tables touched during Rails migrations.'
14
+ spec.description = <<~DESC
15
+ Runs database maintenance (ANALYZE, VACUUM, PRAGMA optimize) on exactly the tables
16
+ created or modified during `rails db:migrate`. Keeps query planner statistics fresh
17
+ and prevents fragmentation after every schema change - automatically.
18
+ DESC
19
+ spec.homepage = 'https://github.com/moskvin/after_migrate'
20
+
21
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
22
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
23
+ if spec.respond_to?(:metadata)
24
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
25
+
26
+ spec.metadata['homepage_uri'] = spec.homepage
27
+ spec.metadata['source_code_uri'] = 'https://github.com/moskvin/after_migrate'
28
+ spec.metadata['changelog_uri'] = 'https://github.com/moskvin/after_migrate/blob/master/CHANGELOG.md'
29
+ spec.metadata['rubygems_mfa_required'] = 'true'
30
+ else
31
+ raise 'RubyGems 2.0 or newer is required to protect against ' \
32
+ 'public gem pushes.'
33
+ end
34
+
35
+ # Specify which files should be added to the gem when it is released.
36
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
37
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
38
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
39
+ end
40
+ spec.bindir = 'exe'
41
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
42
+ spec.require_paths = ['lib']
43
+
44
+ spec.required_ruby_version = '>= 3.2'
45
+
46
+ spec.add_dependency 'rails', '>= 7.0'
47
+ end
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'after_migrate'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AfterMigrate
4
+ module Mysql
5
+ module_function
6
+
7
+ def optimize_tables(connection:, table_names:, **)
8
+ table_names.each do |t|
9
+ quoted = connection.quote_table_name(t)
10
+ AfterMigrate.log("ANALYZE TABLE #{quoted}")
11
+ connection.execute("ANALYZE TABLE #{quoted}")
12
+ end
13
+ end
14
+
15
+ def all_tables(schema: nil)
16
+ connection = ActiveRecord::Base.connection
17
+ database = schema.to_s.presence || connection.current_database
18
+
19
+ connection.select_values(<<~SQL.squish)
20
+ SELECT table_name
21
+ FROM information_schema.tables
22
+ WHERE table_schema = #{connection.quote(database)}
23
+ AND table_type = 'BASE TABLE' -- exclude views
24
+ AND table_name NOT LIKE 'ar_internal_metadata'
25
+ AND table_name NOT LIKE 'schema_migrations'
26
+ ORDER BY table_name
27
+ SQL
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AfterMigrate
4
+ module Postgresql
5
+ module_function
6
+
7
+ def vacuum(table_name, schema: nil, verbose: true)
8
+ table = ActiveRecord::Base.connection.quote_table_name("#{schema}.#{table_name}")
9
+ query = <<~SQL.squish
10
+ VACUUM (#{'VERBOSE, ' if verbose}ANALYZE, INDEX_CLEANUP ON) #{table};
11
+ SQL
12
+
13
+ ActiveRecord::Base.connection.execute(query)
14
+ end
15
+
16
+ def dead_tuples(schema: nil, table: nil, sort: nil)
17
+ allowed_sorts = %w[schemaname relname n_dead_tup n_live_tup dead_tuple_ratio autovacuum_count]
18
+ sort = 'dead_tuple_ratio' unless allowed_sorts.include?(sort)
19
+ query = <<~SQL.squish
20
+ SELECT
21
+ schemaname,
22
+ relname,
23
+ last_vacuum,
24
+ last_autovacuum,
25
+ vacuum_count,
26
+ autovacuum_count,
27
+ n_dead_tup,
28
+ n_live_tup,
29
+ (COALESCE(n_dead_tup, 0)::numeric / GREATEST(COALESCE(n_live_tup, 0) + COALESCE(n_dead_tup, 0), 1)::numeric) AS dead_tuple_ratio
30
+ FROM pg_stat_all_tables
31
+ WHERE COALESCE(n_dead_tup, 0) > 0
32
+ #{"AND schemaname = #{ActiveRecord::Base.connection.quote(schema)}" if schema}
33
+ #{"AND relname = #{ActiveRecord::Base.connection.quote(table)}" if table}
34
+ ORDER BY #{sort} DESC NULLS LAST;
35
+ SQL
36
+
37
+ ActiveRecord::Base.connection.execute(query)
38
+ end
39
+
40
+ def run_vacuum(schema:)
41
+ tables_with_dead_tuples = dead_tuples(schema:).pluck('relname')
42
+ AfterMigrate.log("Vacuuming #{tables_with_dead_tuples.size} tables in schema #{schema}...")
43
+ tables_with_dead_tuples.each { |t| vacuum(t) }
44
+ tables_with_dead_tuples
45
+ end
46
+
47
+ def run_analyze(schema:, tables:)
48
+ connection = ActiveRecord::Base.connection
49
+ tables.each do |t|
50
+ table = if t.include?('.')
51
+ connection.quote_table_name(t)
52
+ else
53
+ connection.quote_table_name("#{schema}.#{t}")
54
+ end
55
+ AfterMigrate.log("ANALYZE VERBOSE #{table}")
56
+ connection.execute("ANALYZE#{AfterMigrate.configuration.verbose ? ' VERBOSE ' : ' '}#{table}")
57
+ end
58
+ end
59
+
60
+ def optimize_tables(table_names:, schema:, **)
61
+ cleaned_tables = []
62
+ cleaned_tables = run_vacuum(schema:) if AfterMigrate.configuration.vacuum
63
+
64
+ tables = table_names - cleaned_tables
65
+ run_analyze(schema:, tables:)
66
+ end
67
+ end
68
+
69
+ def all_tables(schema: nil)
70
+ connection = ActiveRecord::Base.connection
71
+ schema_value = schema ? schema.to_s.strip : 'public'
72
+
73
+ query = <<~SQL.squish
74
+ SELECT c.relname AS table_name
75
+ FROM pg_class c
76
+ JOIN pg_namespace n ON n.oid = c.relnamespace
77
+ WHERE n.nspname = #{connection.quote(schema_value)}
78
+ AND c.relkind IN ('r', 'p') -- ordinary tables + partitioned tables
79
+ AND c.relispartition = FALSE -- exclude partition child tables
80
+ ORDER BY table_name
81
+ SQL
82
+
83
+ connection.select_values(query)
84
+ end
85
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AfterMigrate
4
+ module Sqlite
5
+ module_function
6
+
7
+ def optimize_tables(connection:, **)
8
+ version = connection.respond_to?(:sqlite_version) ? connection.sqlite_version : '0'
9
+ if Gem::Version.new(version.split.first || '0') >= Gem::Version.new('3.35.0')
10
+ AfterMigrate.log('Running PRAGMA optimize')
11
+ connection.execute('PRAGMA optimize;')
12
+ else
13
+ AfterMigrate.log('Running VACUUM; ANALYZE;')
14
+ connection.execute('VACUUM;')
15
+ connection.execute('ANALYZE;')
16
+ end
17
+ end
18
+
19
+ def all_tables(**)
20
+ # SQLite has no concept of schema (everything is in one file)
21
+ # The `schema:` parameter is ignored — there's only one database
22
+ ActiveRecord::Base.connection.select_values(<<~SQL.squish)
23
+ SELECT name
24
+ FROM sqlite_master
25
+ WHERE type = 'table'
26
+ AND name NOT LIKE 'sqlite_%'
27
+ AND name NOT IN ('ar_internal_metadata', 'schema_migrations')
28
+ ORDER BY name
29
+ SQL
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AfterMigrate
4
+ module Collector
5
+ extend self
6
+
7
+ module_function
8
+
9
+ def call(*)
10
+ event = ActiveSupport::Notifications::Event.new(*)
11
+ sql = event.payload[:sql]&.strip
12
+ return unless sql
13
+ return unless sql.match?(/\A\s*(CREATE|ALTER|DROP|INSERT|UPDATE|DELETE|RENAME\s+TABLE|TRUNCATE)/i)
14
+
15
+ table_names = sql.scan(/(?:from|join|update|into|table)\s+((?:"\w+"|\w+)(?:\.(?:"\w+"|\w+))*)/i).flatten
16
+ schema ||= fetch_schema
17
+ # AfterMigrate.log("[#{schema}] Detected change from '#{sql}' to table: #{table_names}") if table_names.present?
18
+ collect_tables(schema:, table_names:)
19
+ end
20
+
21
+ private
22
+
23
+ def collect_tables(schema:, table_names:)
24
+ return if table_names.blank?
25
+
26
+ AfterMigrate::Current.affected_tables ||= Hash.new { |h, k| h[k] = Concurrent::Set.new }
27
+ AfterMigrate::Current.affected_tables[schema].merge(table_names)
28
+ end
29
+
30
+ def fetch_schema
31
+ connection = ActiveRecord::Base.connection
32
+ adapter = connection.adapter_name
33
+ case adapter
34
+ when 'PostgreSQL'
35
+ quoted = connection.schema_search_path.split(',').first
36
+ quoted&.delete('"')
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/current_attributes'
4
+
5
+ module AfterMigrate
6
+ class Current < ActiveSupport::CurrentAttributes
7
+ attribute :affected_tables
8
+
9
+ resets do
10
+ self.affected_tables = Hash.new { |h, k| h[k] = Concurrent::Set.new }
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'after_migrate/adapters/mysql'
4
+ require 'after_migrate/adapters/postgresql'
5
+ require 'after_migrate/adapters/sqlite'
6
+
7
+ module AfterMigrate
8
+ module Executor
9
+ extend self
10
+
11
+ module_function
12
+
13
+ def call(reset: true, schema: nil)
14
+ # AfterMigrate.log("Executing schema: #{schema} -> #{target_tables}...")
15
+ return if target_tables.blank?
16
+ return run_optimize(schema:, tables: target_tables[schema]) if schema.present?
17
+
18
+ target_tables.each do |s, tables|
19
+ run_optimize(schema: s, tables:)
20
+ end
21
+ ensure
22
+ AfterMigrate::Current.reset if reset
23
+ end
24
+
25
+ public :call
26
+
27
+ private
28
+
29
+ def run_optimize(schema:, tables:)
30
+ table_names = tables.to_a.sort
31
+ return if table_names.empty?
32
+
33
+ AfterMigrate.log("Migration touched #{table_names.size} table(s): #{table_names.join(', ')}")
34
+ optimize_tables(schema:, table_names:)
35
+ end
36
+
37
+ def optimize_tables(schema:, table_names:)
38
+ connection = ActiveRecord::Base.connection
39
+ adapter = connection.adapter_name
40
+ case adapter
41
+ when 'PostgreSQL'
42
+ AfterMigrate::Postgresql.optimize_tables(schema:, table_names:, connection:)
43
+ when 'SQLite'
44
+ AfterMigrate::Sqlite.optimize_tables(schema:, table_names:, connection:)
45
+ when 'Mysql2', 'Trilogy'
46
+ AfterMigrate::Mysql.optimize_tables(schema:, table_names:, connection:)
47
+ else
48
+ AfterMigrate.log("No maintenance implemented for #{adapter}")
49
+ end
50
+ end
51
+
52
+ def target_tables
53
+ case AfterMigrate.configuration.analyze
54
+ when 'all_tables'
55
+ AfterMigrate::Current.affected_tables.each_value do |schema|
56
+ all_tables(schema:)
57
+ end
58
+ when 'only_affected_tables'
59
+ AfterMigrate::Current.affected_tables
60
+ else
61
+ []
62
+ end
63
+ end
64
+
65
+ def all_tables(schema:)
66
+ connection = ActiveRecord::Base.connection
67
+ adapter = connection.adapter_name
68
+ case adapter
69
+ when 'PostgreSQL'
70
+ AfterMigrate::Postgresql.all_tables(schema:)
71
+ when 'SQLite'
72
+ AfterMigrate::Sqlite.all_tables(schema:)
73
+ when 'Mysql2', 'Trilogy'
74
+ AfterMigrate::Mysql.all_tables(schema:)
75
+ else
76
+ []
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails'
4
+ require 'active_support/notifications'
5
+
6
+ module AfterMigrate
7
+ class Railtie < ::Rails::Railtie
8
+ initializer 'after_migrate.subscribe' do |app|
9
+ next unless AfterMigrate.configuration.enabled
10
+
11
+ subscription = ActiveSupport::Notifications.subscribe 'sql.active_record' do |*args|
12
+ AfterMigrate::Collector.call(*args)
13
+ end
14
+
15
+ app.executor.to_run { ActiveSupport::Notifications.unsubscribe(subscription) }
16
+ end
17
+
18
+ rake_tasks do
19
+ %w[
20
+ db:migrate
21
+ db:migrate:up
22
+ db:migrate:redo
23
+ ].each do |task_name|
24
+ Rake::Task[task_name].enhance do
25
+ next unless AfterMigrate.configuration.enabled
26
+ next unless AfterMigrate.configuration.rake_tasks_enhanced
27
+
28
+ AfterMigrate::Executor.call(reset: true)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AfterMigrate
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'after_migrate/version'
4
+ require 'after_migrate/current'
5
+ require 'after_migrate/collector'
6
+ require 'after_migrate/executor'
7
+ require 'after_migrate/railtie'
8
+
9
+ module AfterMigrate
10
+ class Configuration
11
+ attr_accessor :enabled, :verbose, :vacuum, :analyze, :rake_tasks_enhanced
12
+
13
+ def initialize
14
+ @enabled = true
15
+ @verbose = true
16
+ @vacuum = true
17
+ @analyze = 'only_affected_tables'
18
+ @rake_tasks_enhanced = true
19
+ end
20
+ end
21
+
22
+ class << self
23
+ def configuration
24
+ @configuration ||= Configuration.new
25
+ end
26
+
27
+ def configure
28
+ yield(configuration)
29
+ end
30
+
31
+ def log(msg)
32
+ warn "[after_migrate] #{msg}" if AfterMigrate.configuration.verbose
33
+ end
34
+ end
35
+ end
data/logo.png ADDED
Binary file
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: after_migrate
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nikolay Moskvin
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rails
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '7.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '7.0'
26
+ description: |
27
+ Runs database maintenance (ANALYZE, VACUUM, PRAGMA optimize) on exactly the tables
28
+ created or modified during `rails db:migrate`. Keeps query planner statistics fresh
29
+ and prevents fragmentation after every schema change - automatically.
30
+ email:
31
+ - nikolay.moskvin@gmail.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - ".gitignore"
37
+ - ".rspec"
38
+ - ".rubocop.yml"
39
+ - ".ruby-version"
40
+ - ".travis.yml"
41
+ - CHANGELOG.md
42
+ - Gemfile
43
+ - README.md
44
+ - Rakefile
45
+ - after_migrate.gemspec
46
+ - bin/console
47
+ - bin/setup
48
+ - lib/after_migrate.rb
49
+ - lib/after_migrate/adapters/mysql.rb
50
+ - lib/after_migrate/adapters/postgresql.rb
51
+ - lib/after_migrate/adapters/sqlite.rb
52
+ - lib/after_migrate/collector.rb
53
+ - lib/after_migrate/current.rb
54
+ - lib/after_migrate/executor.rb
55
+ - lib/after_migrate/railtie.rb
56
+ - lib/after_migrate/version.rb
57
+ - logo.png
58
+ homepage: https://github.com/moskvin/after_migrate
59
+ licenses: []
60
+ metadata:
61
+ allowed_push_host: https://rubygems.org
62
+ homepage_uri: https://github.com/moskvin/after_migrate
63
+ source_code_uri: https://github.com/moskvin/after_migrate
64
+ changelog_uri: https://github.com/moskvin/after_migrate/blob/master/CHANGELOG.md
65
+ rubygems_mfa_required: 'true'
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '3.2'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubygems_version: 3.6.9
81
+ specification_version: 4
82
+ summary: Automatically ANALYZE and VACUUM tables touched during Rails migrations.
83
+ test_files: []