Class: SlashMigrate::Column

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

Overview

A single column in a migration plan. Renders itself as the ‘t.<type> :name` line for a create_table block, including the options Rails’ generator grammar can’t express (null:, default:). Shared by the new-model flow and, later, the modify-table flow.

Constant Summary collapse

TYPES =
%w[
  string text integer bigint float decimal boolean
  date datetime time references binary json
].freeze
REFERENCE_TYPES =
%w[references belongs_to].freeze
INDEX_CHOICES =
%w[index uniq].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name:, type: "string", null: true, default: nil, limit: nil, precision: nil, scale: nil, index: "", to_table: nil) ⇒ Column

Returns a new instance of Column.



49
50
51
52
53
54
55
56
57
58
59
60
# File 'app/services/slash_migrate/column.rb', line 49

def initialize(name:, type: "string", null: true, default: nil,
  limit: nil, precision: nil, scale: nil, index: "", to_table: nil)
  @name = name.to_s.strip
  @type = type.to_s
  @null = null
  @default = presence(default)
  @limit = presence(limit)
  @precision = presence(precision)
  @scale = presence(scale)
  @index = index.to_s
  @to_table = presence(to_table)
end

Instance Attribute Details

#defaultObject (readonly)

Returns the value of attribute default.



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

def default
  @default
end

#indexObject (readonly)

Returns the value of attribute index.



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

def index
  @index
end

#limitObject (readonly)

Returns the value of attribute limit.



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

def limit
  @limit
end

#nameObject (readonly)

Returns the value of attribute name.



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

def name
  @name
end

#precisionObject (readonly)

Returns the value of attribute precision.



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

def precision
  @precision
end

#scaleObject (readonly)

Returns the value of attribute scale.



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

def scale
  @scale
end

#typeObject (readonly)

Returns the value of attribute type.



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

def type
  @type
end

Class Method Details

.from_params(row) ⇒ Object



14
15
16
17
18
19
20
21
22
23
# File 'app/services/slash_migrate/column.rb', line 14

def self.from_params(row)
  new(
    name: row[:name],
    type: row[:type],
    null: row[:null].to_s != "not_null",
    default: row[:default],
    index: row[:index],
    to_table: row[:to_table]
  )
end

.from_schema(ar_column) ⇒ Object

Builds a Column from an Active Record column (the live schema), so the engine can reconstruct a column’s current definition — needed to make a drop reversible and to pre-fill the edit form.



28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'app/services/slash_migrate/column.rb', line 28

def self.from_schema(ar_column)
  meta = ar_column.
  new(
    name: ar_column.name,
    type: ar_column.type.to_s,
    null: ar_column.null,
    # Normalize to a String, matching the form-input path. Active Record
    # casts defaults differently across versions and adapters (Rails 8.0
    # hands back "0", 8.1 casts it to 0; a boolean default arrives as
    # false), and the rest of this class — presence, downcase, literal
    # interpolation — assumes a string. Without &.to_s, presence(false)
    # would also silently drop a boolean's `default: false`.
    default: ar_column.default&.to_s,
    limit: meta&.limit,
    precision: meta&.precision,
    scale: meta&.scale
  )
end

Instance Method Details

#add_statement(table_name) ⇒ Object

The standalone statement that adds this column to an existing table, reusing the same option rendering as the create_table line.



104
105
106
107
108
109
110
111
112
113
114
# File 'app/services/slash_migrate/column.rb', line 104

def add_statement(table_name)
  if reference?
    options = reference_options
    statement = "add_reference :#{table_name}, :#{name}"
  else
    options = column_options
    statement = "add_column :#{table_name}, :#{name}, :#{type}"
  end
  statement += ", #{options.join(", ")}" unless options.empty?
  statement
end

#allow_null?Boolean

Returns:

  • (Boolean)


70
71
72
# File 'app/services/slash_migrate/column.rb', line 70

def allow_null?
  @null
end

#belongs_to_lineObject



130
131
132
133
134
135
# File 'app/services/slash_migrate/column.rb', line 130

def belongs_to_line
  options = []
  options << "class_name: #{@to_table.classify.inspect}" if foreign_key? && !conventional_reference?
  options << requiredness_option if requiredness_option
  options.empty? ? "belongs_to :#{name}" : "belongs_to :#{name}, #{options.join(", ")}"
end

#blank?Boolean

Returns:

  • (Boolean)


62
63
64
# File 'app/services/slash_migrate/column.rb', line 62

def blank?
  name.empty?
end

#default_sqlObject

The default rendered as a Ruby literal (or “nil”), for change_column_default’s from:/to: arguments.



126
127
128
# File 'app/services/slash_migrate/column.rb', line 126

def default_sql
  @default.nil? ? "nil" : rendered_default
end

#foreign_key?Boolean

A references column gets a foreign key only when it’s pointed at a real target table; with no target it’s just the _id column (plus its index), which always migrates cleanly even if no such table exists yet.

Returns:

  • (Boolean)


77
78
79
# File 'app/services/slash_migrate/column.rb', line 77

def foreign_key?
  !@to_table.nil?
end

#index_statement(table_name) ⇒ Object



96
97
98
99
100
# File 'app/services/slash_migrate/column.rb', line 96

def index_statement(table_name)
  statement = "add_index :#{table_name}, :#{name}"
  statement += ", unique: true" if unique_index?
  statement
end

#indexed?Boolean

References are indexed inline by Rails, so they never get a separate add_index line.

Returns:

  • (Boolean)


83
84
85
# File 'app/services/slash_migrate/column.rb', line 83

def indexed?
  !reference? && INDEX_CHOICES.include?(index)
end

#reference?Boolean

Returns:

  • (Boolean)


66
67
68
# File 'app/services/slash_migrate/column.rb', line 66

def reference?
  REFERENCE_TYPES.include?(type)
end

#remove_statement(table_name) ⇒ Object

remove_column is only reversible when given the column’s type; we also pass the other options so a rollback recreates the column faithfully.



118
119
120
121
122
# File 'app/services/slash_migrate/column.rb', line 118

def remove_statement(table_name)
  statement = "remove_column :#{table_name}, :#{name}, :#{type}"
  statement += ", #{column_options.join(", ")}" unless column_options.empty?
  statement
end

#to_rubyObject

The line that goes inside ‘create_table do |t|`.



92
93
94
# File 'app/services/slash_migrate/column.rb', line 92

def to_ruby
  "t.#{type} :#{name}#{format_options}"
end

#unique_index?Boolean

Returns:

  • (Boolean)


87
88
89
# File 'app/services/slash_migrate/column.rb', line 87

def unique_index?
  index == "uniq"
end