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.



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

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 = default.to_s.strip.presence
  @limit = limit.to_s.strip.presence
  @precision = precision.to_s.strip.presence
  @scale = scale.to_s.strip.presence
  @index = index.to_s
  @to_table = to_table.to_s.strip.presence
end

Instance Attribute Details

#defaultObject (readonly)

Returns the value of attribute default.



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

def default
  @default
end

#indexObject (readonly)

Returns the value of attribute index.



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

def index
  @index
end

#limitObject (readonly)

Returns the value of attribute limit.



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

def limit
  @limit
end

#nameObject (readonly)

Returns the value of attribute name.



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

def name
  @name
end

#precisionObject (readonly)

Returns the value of attribute precision.



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

def precision
  @precision
end

#scaleObject (readonly)

Returns the value of attribute scale.



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

def scale
  @scale
end

#typeObject (readonly)

Returns the value of attribute type.



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

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
# 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 — to_s.strip.presence, downcase,
    # literal interpolation — assumes a string.
    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.



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

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)


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

def allow_null?
  @null
end

#belongs_to_lineObject



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

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)


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

def blank?
  name.empty?
end

#default_sqlObject

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



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

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)


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

def foreign_key?
  !@to_table.nil?
end

#index_statement(table_name) ⇒ Object



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

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)


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

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

#reference?Boolean

Returns:

  • (Boolean)


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

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.



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

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|`.



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

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

#unique_index?Boolean

Returns:

  • (Boolean)


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

def unique_index?
  index == "uniq"
end