Module: AutoMigrations::ClassMethods

Defined in:
lib/db_auto_migrations.rb

Instance Method Summary collapse

Instance Method Details

#auto_add_index(method, *args, &block) ⇒ Object



152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/db_auto_migrations.rb', line 152

def auto_add_index(method, *args, &block)
  table_name = args.shift.to_s
  fields     = Array(args.shift).map(&:to_s)
  options    = args.shift

  index_name = options[:name] if options
  index_name ||= ActiveRecord::Base.connection.index_name(table_name, :column => fields)

  (self.indexes_in_schema ||= []) << index_name

  unless ActiveRecord::Base.connection.indexes(table_name).detect { |i| i.name == index_name }
    method_missing_without_auto_migration(method, *[table_name, fields, options], &block)
  end
end

#auto_create_table(method, *args) {|table_definition| ... } ⇒ Object

Yields:

  • (table_definition)


66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/db_auto_migrations.rb', line 66

def auto_create_table(method, *args, &block)
  table_name = args.shift.to_s
  options    = args.pop || {}

  (self.tables_in_schema ||= []) << table_name

  # Table doesn't exist, create it
  unless (ActiveRecord::VERSION::MAJOR >= 5 ? ActiveRecord::Base.connection.data_sources : ActiveRecord::Base.connection.tables).include?(table_name)
    return method_missing_without_auto_migration(method, *[table_name, options], &block)
  end

  # Grab database columns
  fields_in_db = ActiveRecord::Base.connection.columns(table_name).inject({}) do |hash, column|
    hash[column.name] = column
    hash
  end

  # Grab schema columns (lifted from active_record/connection_adapters/abstract/schema_statements.rb)
  table_definition = create_table_definition table_name, options[:temporary], options[:options]
  primary_key = options[:primary_key] || "id"
  table_definition.primary_key(primary_key) unless options[:id] == false
  yield table_definition
  fields_in_schema = table_definition.columns.inject({}) do |hash, column|
    hash[column.name.to_s] = column
    hash
  end

  # Add fields to db new to schema
  (fields_in_schema.keys - fields_in_db.keys).each do |field|
    column  = fields_in_schema[field]
    options = {:limit => column.limit, :precision => column.precision, :scale => column.scale}
    options[:default] = column.default if !column.default.nil?
    options[:null]    = column.null    if !column.null.nil?
    add_column table_name, column.name, column.type.to_sym, options
  end

  # Remove fields from db no longer in schema
  (fields_in_db.keys - fields_in_schema.keys & fields_in_db.keys).each do |field|
    column = fields_in_db[field]
    remove_column table_name, column.name
  end

  (fields_in_schema.keys & fields_in_db.keys).each do |field|
    if field != primary_key #ActiveRecord::Base.get_primary_key(table_name)
      changed  = false  # flag
      new_type = fields_in_schema[field].type.to_sym
      new_attr = {}

      # First, check if the field type changed
      if fields_in_schema[field].type.to_sym != fields_in_db[field].type.to_sym
        changed = true
      end

      # Special catch for precision/scale, since *both* must be specified together
      # Always include them in the attr struct, but they'll only get applied if changed = true
      new_attr[:precision] = fields_in_schema[field].precision
      new_attr[:scale]     = fields_in_schema[field].scale

      # Next, iterate through our extended attributes, looking for any differences
      # This catches stuff like :null, :precision, etc
      fields_in_schema[field][:options].each_pair do |att,value|
        next unless [:limit, :precision, :scale, :default, :null, :collation, :comment].include?(att)

        if !value.nil?
          value_in_db = fields_in_db[field].send(att)
          value_in_db = value_in_db.to_i if att == :default && new_type == :integer && value_in_db.class == String
          value_in_db = value_in_db.to_f if att == :default && new_type == :float && value_in_db.class == String
          if att == :default && new_type == :boolean && value_in_db.class == String
            value_in_db_to_i = value_in_db.to_i
            value_in_db = false if value_in_db_to_i == 0
            value_in_db = true  if value_in_db_to_i == 1
          end

          if value != value_in_db
            new_attr[att] = value
            changed = true
          end
        end
      end

      # Change the column if applicable
      change_column table_name, field, new_type, new_attr if changed
    end
  end
end

#drop_unused_indexesObject



173
174
175
176
177
178
179
180
# File 'lib/db_auto_migrations.rb', line 173

def drop_unused_indexes
  tables_in_schema.each do |table_name|
    indexes_in_db = ActiveRecord::Base.connection.indexes(table_name).map(&:name)
    (indexes_in_db - indexes_in_schema & indexes_in_db).each do |index_name|
      remove_index table_name, :name => index_name
    end
  end
end

#drop_unused_tablesObject



167
168
169
170
171
# File 'lib/db_auto_migrations.rb', line 167

def drop_unused_tables
  ((ActiveRecord::VERSION::MAJOR >= 5 ? ActiveRecord::Base.connection.data_sources : ActiveRecord::Base.connection.tables) - tables_in_schema - %w(schema_info schema_migrations)).each do |table|
    drop_table table
  end
end

#method_missing_with_auto_migration(method, *args, &block) ⇒ Object



55
56
57
58
59
60
61
62
63
64
# File 'lib/db_auto_migrations.rb', line 55

def method_missing_with_auto_migration(method, *args, &block)
  case method
  when :create_table
    auto_create_table(method, *args, &block)
  when :add_index
    auto_add_index(method, *args, &block)
  else
    method_missing_without_auto_migration(method, *args, &block)
  end
end

#update_schema_version(version) ⇒ Object



182
183
184
185
186
187
188
189
190
# File 'lib/db_auto_migrations.rb', line 182

def update_schema_version(version)
  if (ActiveRecord::VERSION::MAJOR >= 5 ? ActiveRecord::Base.connection.data_sources : ActiveRecord::Base.connection.tables).include?("schema_migrations")
    ActiveRecord::Base.connection.update("INSERT INTO schema_migrations VALUES ('#{version}')")
  end
  schema_file = File.join(Rails.root, "db", "schema.rb")
  schema = File.read(schema_file)
  schema.sub!(/:version => \d+/, ":version => #{version}")
  File.open(schema_file, "w") { |f| f << schema }
end