Class: SlashMigrate::MigrationBuilder

Inherits:
Object
  • Object
show all
Defined in:
app/services/slash_migrate/migration_builder.rb

Overview

Builds the migration (and model) for a brand-new table from a column plan. Unlike the old generator-backed path, this emits the Ruby itself, so it can express options the ‘rails g` grammar can’t (null:, default:). A create_table migration is always reversible, so the output is a plain ‘def change`.

Fidelity with Rails’ own output is guarded by a spec that diffs this against ‘rails g model` for the subset both can express.

Constant Summary collapse

SELF_TABLE =

The “references table” picker uses this sentinel for a self-reference, since the table being created doesn’t exist to list yet. We resolve it to the new table’s name here, where it’s known.

"__self__".freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name:, columns: []) ⇒ MigrationBuilder

Returns a new instance of MigrationBuilder.



30
31
32
33
# File 'app/services/slash_migrate/migration_builder.rb', line 30

def initialize(name:, columns: [])
  @name = name.to_s.strip
  @columns = columns.reject(&:blank?)
end

Instance Attribute Details

#nameObject (readonly)

Returns the value of attribute name.



28
29
30
# File 'app/services/slash_migrate/migration_builder.rb', line 28

def name
  @name
end

Class Method Details

.from_params(name:, rows:) ⇒ Object



15
16
17
18
19
# File 'app/services/slash_migrate/migration_builder.rb', line 15

def self.from_params(name:, rows:)
  table = name.to_s.strip.underscore.pluralize
  columns = Array(rows).map { |row| Column.from_params(resolve_self_reference(row, table)) }
  new(name: name, columns: columns)
end

.resolve_self_reference(row, table) ⇒ Object



21
22
23
24
25
26
# File 'app/services/slash_migrate/migration_builder.rb', line 21

def self.resolve_self_reference(row, table)
  return row unless row[:to_table].to_s == SELF_TABLE

  hash = row.respond_to?(:to_unsafe_h) ? row.to_unsafe_h : row.to_h
  hash.symbolize_keys.merge(to_table: table)
end

Instance Method Details

#migration_basenameObject



60
61
62
# File 'app/services/slash_migrate/migration_builder.rb', line 60

def migration_basename
  "create_#{table_name}"
end

#migration_class_nameObject



56
57
58
# File 'app/services/slash_migrate/migration_builder.rb', line 56

def migration_class_name
  "Create#{table_name.camelize}"
end

#migration_sourceObject



68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'app/services/slash_migrate/migration_builder.rb', line 68

def migration_source
  lines = []
  lines << "class #{migration_class_name} < ActiveRecord::Migration[#{ActiveRecord::Migration.current_version}]"
  lines << "  def change"
  lines << "    create_table :#{table_name} do |t|"
  @columns.each { |column| lines << "      #{column.to_ruby}" }
  lines << ""
  lines << "      t.timestamps"
  lines << "    end"
  index_columns.each { |column| lines << "    #{column.index_statement(table_name)}" }
  lines << "  end"
  lines << "end"
  lines.join("\n") + "\n"
end

#model_class_nameObject



47
48
49
# File 'app/services/slash_migrate/migration_builder.rb', line 47

def model_class_name
  name.classify
end

#model_filenameObject



64
65
66
# File 'app/services/slash_migrate/migration_builder.rb', line 64

def model_filename
  "#{name.classify.underscore}.rb"
end

#model_sourceObject



83
84
85
86
87
88
# File 'app/services/slash_migrate/migration_builder.rb', line 83

def model_source
  lines = ["class #{model_class_name} < ApplicationRecord"]
  reference_columns.each { |column| lines << "  #{column.belongs_to_line}" }
  lines << "end"
  lines.join("\n") + "\n"
end

#name_present?Boolean

Returns:

  • (Boolean)


35
36
37
# File 'app/services/slash_migrate/migration_builder.rb', line 35

def name_present?
  !name.empty?
end

#pluralized_input?Boolean

True when the input was plural and we singularized it, so the UI can warn.

Returns:

  • (Boolean)


52
53
54
# File 'app/services/slash_migrate/migration_builder.rb', line 52

def pluralized_input?
  name.present? && name != name.singularize
end

#table_nameObject

A model is singular and its table plural, whatever the student typed. classify and tableize are the same inflections Active Record uses to map between a model and its table, so “Articles”, “articles”, and “Article” all yield the Article model on the articles table.



43
44
45
# File 'app/services/slash_migrate/migration_builder.rb', line 43

def table_name
  name.tableize
end

#write!Object

Writes the migration and model into the host app. Skips an existing model file rather than clobbering it. Returns the paths written, relative to root.



92
93
94
95
96
97
98
99
100
101
102
103
# File 'app/services/slash_migrate/migration_builder.rb', line 92

def write!
  written = [MigrationFileWriter.write(basename: migration_basename, source: migration_source)]

  model_path = Rails.root.join("app/models", model_filename)
  unless model_path.exist?
    model_path.dirname.mkpath # a namespaced model (app/models/admin/…) may need its dir
    File.write(model_path, model_source)
    written << model_path.relative_path_from(Rails.root).to_s
  end

  written
end