lib/strong_migrations/migration.rb in strong_migrations-0.3.0 vs lib/strong_migrations/migration.rb in strong_migrations-0.3.1

- old
+ new

@@ -12,15 +12,12 @@ @direction = direction super end def method_missing(method, *args, &block) - table = args[0].to_s - unless @safe || ENV["SAFETY_ASSURED"] || is_a?(ActiveRecord::Schema) || @direction == :down || version_safe? ar5 = ActiveRecord::VERSION::MAJOR >= 5 - model = table.classify case method when :remove_column, :remove_columns, :remove_timestamps, :remove_reference, :remove_belongs_to columns = case method @@ -39,102 +36,90 @@ cols end code = ar5 ? "self.ignored_columns = #{columns.inspect}" : "def self.columns\n super.reject { |c| #{columns.inspect}.include?(c.name) }\n end" - command = String.new("#{method} #{sym_str(table)}") - case method - when :remove_column, :remove_reference, :remove_belongs_to - command << ", #{sym_str(args[1])}#{options_str(args[2] || {})}" - when :remove_columns - columns.each do |c| - command << ", #{sym_str(c)}" - end - end - raise_error :remove_column, - model: model, + model: args[0].to_s.classify, code: code, - command: command, + command: command_str(method, args), column_suffix: columns.size > 1 ? "s" : "" when :change_table raise_error :change_table, header: "Possibly dangerous operation" when :rename_table raise_error :rename_table when :rename_column raise_error :rename_column when :add_index - columns = args[1] - options = args[2] || {} + table, columns, options = args + options ||= {} + if columns.is_a?(Array) && columns.size > 3 && !options[:unique] raise_error :add_index_columns, header: "Best practice" end - if postgresql? && options[:algorithm] != :concurrently && !@new_tables.to_a.include?(table) - raise_error :add_index, - table: sym_str(table), - column: column_str(columns), - options: options_str(options.except(:algorithm)) + if postgresql? && options[:algorithm] != :concurrently && !@new_tables.to_a.include?(table.to_s) + raise_error :add_index, command: command_str("add_index", [table, columns, options.merge(algorithm: :concurrently)]) end when :add_column - column = args[1] - type = args[2] - options = args[3] || {} + table, column, type, options = args + options ||= {} default = options[:default] if !default.nil? && !(postgresql? && postgresql_version >= 110000) raise_error :add_column_default, - table: sym_str(table), - column: sym_str(column), - type: sym_str(type), - options: options_str(options.except(:default)), - default: default.inspect, - code: backfill_code(model, column, default) + add_command: command_str("add_column", [table, column, type, options.except(:default)]), + change_command: command_str("change_column_default", [table, column, default]), + remove_command: command_str("remove_column", [table, column]), + code: backfill_code(table, column, default) end if type.to_s == "json" && postgresql? if postgresql_version >= 90400 raise_error :add_column_json else raise_error :add_column_json_legacy, - model: model, - table: connection.quote_table_name(table) + model: table.to_s.classify, + table: connection.quote_table_name(table.to_s) end end when :change_column + table, column, type = args + safe = false # assume Postgres 9.1+ since previous versions are EOL - if postgresql? && args[2].to_s == "text" - column = connection.columns(table).find { |c| c.name.to_s == args[1].to_s } - safe = column && column.type == :string + if postgresql? && type.to_s == "text" + found_column = connection.columns(table).find { |c| c.name.to_s == column.to_s } + safe = found_column && found_column.type == :string end raise_error :change_column unless safe when :create_table - options = args[1] || {} + table, options = args + options ||= {} + raise_error :create_table if options[:force] - (@new_tables ||= []) << table + + # keep track of new tables of add_index check + (@new_tables ||= []) << table.to_s when :add_reference, :add_belongs_to - options = args[2] || {} + table, reference, options = args + options ||= {} + index_value = options.fetch(:index, ar5) if postgresql? && index_value - reference = args[1] - columns = [] - columns << "#{reference}_type" if options[:polymorphic] - columns << "#{reference}_id" + columns = options[:polymorphic] ? [:"#{reference}_type", :"#{reference}_id"] : :"#{reference}_id" + raise_error :add_reference, - command: method, - table: sym_str(table), - reference: sym_str(reference), - column: column_str(columns), - options: options_str(options.except(:index)) + reference_command: command_str(method, [table, reference, options.merge(index: false)]), + index_command: command_str("add_index", [table, columns, {algorithm: :concurrently}]) end when :execute raise_error :execute, header: "Possibly dangerous operation" when :change_column_null - _, column, null, default = args + table, column, null, default = args if !null && !default.nil? raise_error :change_column_null, - code: backfill_code(model, column, default) + code: backfill_code(table, column, default) end end StrongMigrations.checks.each do |check| instance_exec(method, args, &check) @@ -142,11 +127,11 @@ end result = super if StrongMigrations.auto_analyze && @direction == :up && postgresql? && method == :add_index - connection.execute "ANALYZE VERBOSE #{connection.quote_table_name(table)}" + connection.execute "ANALYZE VERBOSE #{connection.quote_table_name(args[0].to_s)}" end result end @@ -174,28 +159,27 @@ # escape % not followed by { stop!(message.gsub(/%(?!{)/, "%%") % vars, header: header || "Dangerous operation detected") end - def sym_str(v) - v.to_sym.inspect - end + def command_str(command, args) + str_args = args[0..-2].map { |a| a.inspect } - def column_str(columns) - columns = Array(columns).map(&:to_sym) - columns = columns.first if columns.size == 1 - columns.inspect - end - - def options_str(options) - str = String.new("") - options.each do |k, v| - str << ", #{k}: #{v.inspect}" + # prettier last arg + last_arg = args[-1] + if last_arg.is_a?(Hash) + if last_arg.any? + str_args << last_arg.map { |k, v| "#{k}: #{v.inspect}" }.join(", ") + end + else + str_args << last_arg.inspect end - str + + "#{command} #{str_args.join(", ")}" end - def backfill_code(model, column, default) + def backfill_code(table, column, default) + model = table.to_s.classify if ActiveRecord::VERSION::MAJOR >= 5 "#{model}.in_batches.update_all #{column}: #{default.inspect}" else "#{model}.find_in_batches do |records|\n #{model}.where(id: records.map(&:id)).update_all #{column}: #{default.inspect}\n end" end