Class: Woods::Extractors::MigrationExtractor

Inherits:
Object
  • Object
show all
Includes:
SharedDependencyScanner, SharedUtilityMethods
Defined in:
lib/woods/extractors/migration_extractor.rb

Overview

MigrationExtractor handles ActiveRecord migration file extraction.

Scans ‘db/migrate/*.rb` for migration files and produces one ExtractedUnit per migration. Extracts DDL metadata (tables, columns, indexes, references), reversibility, risk indicators (data migrations, raw SQL), and links to affected models via table name classification.

Examples:

extractor = MigrationExtractor.new
units = extractor.extract_all
create_users = units.find { |u| u.identifier == "CreateUsers" }
create_users.[:tables_affected] # => ["users"]

Constant Summary collapse

INTERNAL_TABLES =

Rails internal tables that should not generate model dependencies

%w[
  schema_migrations
  ar_internal_metadata
  active_storage_blobs
  active_storage_attachments
  active_storage_variant_records
  action_text_rich_texts
  action_mailbox_inbound_emails
].freeze
TABLE_OPERATIONS =

DDL operations that take a table name as the first symbol argument

%w[
  create_table
  drop_table
  rename_table
  add_column
  remove_column
  change_column
  rename_column
  add_index
  remove_index
  add_reference
  remove_reference
  add_belongs_to
  remove_belongs_to
  add_foreign_key
  remove_foreign_key
  add_timestamps
  remove_timestamps
  change_column_default
  change_column_null
].freeze
COLUMN_TYPE_METHODS =

Column type methods used inside create_table blocks

%w[
  string integer float decimal boolean binary text
  date datetime time timestamp
  bigint numeric json jsonb uuid inet cidr
  hstore ltree point polygon
].freeze
DATA_MIGRATION_PATTERNS =

Patterns indicating data migration (not just DDL)

[
  /\.update_all\b/,
  /\.find_each\b/,
  /\.find_in_batches\b/,
  /\.update!\b/,
  /\.update\b/,
  /\.save!\b/,
  /\.save\b/,
  /\.delete_all\b/,
  /\.destroy_all\b/
].freeze

Constants included from SharedDependencyScanner

SharedDependencyScanner::FORM_ACTION_HELPER, SharedDependencyScanner::ROUTE_HELPER_PATTERN

Instance Method Summary collapse

Methods included from SharedDependencyScanner

#extract_constantize_targets, #scan_common_dependencies, #scan_form_dependencies, #scan_job_dependencies, #scan_mailer_dependencies, #scan_model_dependencies, #scan_navigation_dependencies, #scan_service_dependencies

Methods included from SharedUtilityMethods

#app_source?, #condition_label, #count_loc, #detect_entry_points, #extract_action_filter_actions, #extract_callback_conditions, #extract_class_methods, #extract_custom_errors, #extract_initialize_params, #extract_namespace, #extract_parent_class, #extract_public_methods, #resolve_source_location, #skip_file?

Constructor Details

#initializeMigrationExtractor

Returns a new instance of MigrationExtractor.



80
81
82
83
# File 'lib/woods/extractors/migration_extractor.rb', line 80

def initialize
  @migrate_dir = Rails.root.join('db/migrate')
  @has_directory = @migrate_dir.directory?
end

Instance Method Details

#extract_allArray<ExtractedUnit>

Extract all migration files from db/migrate/

Returns:

  • (Array<ExtractedUnit>)

    List of migration units, sorted by timestamp



88
89
90
91
92
93
# File 'lib/woods/extractors/migration_extractor.rb', line 88

def extract_all
  return [] unless @has_directory

  files = Dir[@migrate_dir.join('*.rb')]
  files.filter_map { |file| extract_migration_file(file) }
end

#extract_migration_file(file_path) ⇒ ExtractedUnit?

Extract a single migration file

Parameters:

  • file_path (String)

    Path to the migration file

Returns:

  • (ExtractedUnit, nil)

    The extracted unit or nil if not a migration



99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/woods/extractors/migration_extractor.rb', line 99

def extract_migration_file(file_path)
  source = File.read(file_path)
  class_name = extract_class_name(source)

  return nil unless class_name
  return nil unless migration_class?(source)

  unit = ExtractedUnit.new(
    type: :migration,
    identifier: class_name,
    file_path: file_path
  )

  unit.namespace = extract_namespace(class_name)
  unit. = (source, file_path)
  unit.source_code = annotate_source(source, class_name, unit.)
  unit.dependencies = extract_dependencies(source, unit.)

  unit
rescue StandardError => e
  Rails.logger.error("Failed to extract migration #{file_path}: #{e.message}")
  nil
end