diff options
Diffstat (limited to 'activerecord/lib/active_record')
28 files changed, 138 insertions, 107 deletions
diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index e5b3af8252..278c95e27b 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -309,15 +309,12 @@ module ActiveRecord def replace_on_target(record, index, skip_callbacks) callback(:before_add, record) unless skip_callbacks - was_loaded = loaded? yield(record) if block_given? - unless !was_loaded && loaded? - if index - @target[index] = record - else - @target << record - end + if index + @target[index] = record + else + append_record(record) end callback(:after_add, record) unless skip_callbacks @@ -514,6 +511,10 @@ module ActiveRecord load_target.select { |r| ids.include?(r.id.to_s) } end end + + def append_record(record) + @target << record unless @target.include?(record) + end end end end diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index d258eac0ed..1f264d325a 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -203,6 +203,10 @@ module ActiveRecord def invertible_for?(record) false end + + def append_record(record) + @target << record + end end end end diff --git a/activerecord/lib/active_record/attribute_methods/query.rb b/activerecord/lib/active_record/attribute_methods/query.rb index 10498f4322..05f0e974b6 100644 --- a/activerecord/lib/active_record/attribute_methods/query.rb +++ b/activerecord/lib/active_record/attribute_methods/query.rb @@ -19,7 +19,7 @@ module ActiveRecord if Numeric === value || value !~ /[^0-9]/ !value.to_i.zero? else - return false if ActiveModel::Type::Boolean::FALSE_VALUES.include?(value) + return false if ActiveRecord::Type::Boolean::FALSE_VALUES.include?(value) !value.blank? end elsif value.respond_to?(:zero?) diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index c70178cd2c..945192fe04 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -26,7 +26,7 @@ module ActiveRecord # ==== Parameters # # * +attr_name+ - The field name that should be serialized. - # * +class_name_or_coder+ - Optional, a coder object, which responds to `.load` / `.dump` + # * +class_name_or_coder+ - Optional, a coder object, which responds to +.load+ and +.dump+ # or a class name that the object type should be equal to. # # ==== Example diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb index 6ca53c72ce..2f8a89e88e 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb @@ -65,7 +65,7 @@ module ActiveRecord if @query_cache_enabled && !locked?(arel) arel, binds = binds_from_relation arel, binds sql = to_sql(arel, binds) - cache_sql(sql, binds) { super(sql, name, binds, preparable: preparable) } + cache_sql(sql, name, binds) { super(sql, name, binds, preparable: preparable) } else super end @@ -73,11 +73,17 @@ module ActiveRecord private - def cache_sql(sql, binds) + def cache_sql(sql, name, binds) result = if @query_cache[sql].key?(binds) - ActiveSupport::Notifications.instrument("sql.active_record", - sql: sql, binds: binds, name: "CACHE", connection_id: object_id) + ActiveSupport::Notifications.instrument( + "sql.active_record", + sql: sql, + binds: binds, + name: name, + connection_id: object_id, + cached: true, + ) @query_cache[sql][binds] else @query_cache[sql][binds] = yield diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb index 8bb7362c2e..06c89ca072 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb @@ -7,10 +7,7 @@ module ActiveRecord # Adapter level by over-writing this code inside the database specific adapters module ColumnDumper def column_spec(column) - spec = Hash[prepare_column_options(column).map { |k, v| [k, "#{k}: #{v}"] }] - spec[:name] = column.name.inspect - spec[:type] = schema_type(column).to_s - spec + [schema_type(column), prepare_column_options(column)] end def column_spec_for_primary_key(column) @@ -53,7 +50,7 @@ module ActiveRecord # Lists the valid migration options def migration_keys - [:name, :limit, :precision, :scale, :default, :null, :collation, :comment] + [:limit, :precision, :scale, :default, :null, :collation, :comment] end private diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 29520ed9c8..1df20a0c56 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -120,7 +120,7 @@ module ActiveRecord checks = [] checks << lambda { |c| c.name == column_name } checks << lambda { |c| c.type == type } if type - (migration_keys - [:name]).each do |attr| + migration_keys.each do |attr| checks << lambda { |c| c.send(attr) == options[attr] } if options.key?(attr) end @@ -284,10 +284,10 @@ module ActiveRecord end if supports_comments? && !supports_comments_in_create? - change_table_comment(table_name, comment) if comment + change_table_comment(table_name, comment) if comment.present? td.columns.each do |column| - change_column_comment(table_name, column.name, column.comment) if column.comment + change_column_comment(table_name, column.name, column.comment) if column.comment.present? end end @@ -1192,7 +1192,7 @@ module ActiveRecord def quoted_columns_for_index(column_names, **options) return [column_names] if column_names.is_a?(String) - quoted_columns = Hash[column_names.map { |name| [name, quote_column_name(name).dup] }] + quoted_columns = Hash[column_names.map { |name| [name.to_sym, quote_column_name(name).dup] }] add_options_for_index_columns(quoted_columns, options).values end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index be8511f119..e7bd0e7c12 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -384,11 +384,11 @@ module ActiveRecord mysql_index_type = row[:Index_type].downcase.to_sym index_type = INDEX_TYPES.include?(mysql_index_type) ? mysql_index_type : nil index_using = INDEX_USINGS.include?(mysql_index_type) ? mysql_index_type : nil - indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], [], nil, nil, index_type, index_using, row[:Index_comment].presence) + indexes << IndexDefinition.new(row[:Table], row[:Key_name], row[:Non_unique].to_i == 0, [], {}, nil, nil, index_type, index_using, row[:Index_comment].presence) end indexes.last.columns << row[:Column_name] - indexes.last.lengths << row[:Sub_part] + indexes.last.lengths.merge!(row[:Column_name] => row[:Sub_part].to_i) if row[:Sub_part] end end @@ -509,7 +509,7 @@ module ActiveRecord end def add_sql_comment!(sql, comment) # :nodoc: - sql << " COMMENT #{quote(comment)}" if comment + sql << " COMMENT #{quote(comment)}" if comment.present? sql end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb index 7414eba6c5..092543259f 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb @@ -89,10 +89,10 @@ module ActiveRecord end end - # Executes an SQL statement, returning a PGresult object on success - # or raising a PGError exception otherwise. - # Note: the PGresult object is manually memory managed; if you don't - # need it specifically, you many want consider the exec_query wrapper. + # Executes an SQL statement, returning a PG::Result object on success + # or raising a PG::Error exception otherwise. + # Note: the PG::Result object is manually memory managed; if you don't + # need it specifically, you may want consider the <tt>exec_query</tt> wrapper. def execute(sql, name = nil) log(sql, name) do @connection.async_exec(sql) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index 29a77580f5..69f797da3a 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -86,7 +86,7 @@ module ActiveRecord SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace - WHERE c.relkind IN ('r', 'v','m') -- (r)elation/table, (v)iew, (m)aterialized view + WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view AND n.nspname = ANY (current_schemas(false)) SQL end @@ -108,13 +108,13 @@ module ActiveRecord name = Utils.extract_schema_qualified_name(name.to_s) return false unless name.identifier - select_value(<<-SQL, "SCHEMA").to_i > 0 - SELECT COUNT(*) + select_values(<<-SQL, "SCHEMA").any? + SELECT c.relname FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind IN ('r','v','m') -- (r)elation/table, (v)iew, (m)aterialized view - AND c.relname = '#{name.identifier}' - AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'} + AND c.relname = #{quote(name.identifier)} + AND n.nspname = #{name.schema ? quote(name.schema) : "ANY (current_schemas(false))"} SQL end @@ -137,8 +137,8 @@ module ActiveRecord FROM pg_class c LEFT JOIN pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind IN ('v','m') -- (v)iew, (m)aterialized view - AND c.relname = '#{name.identifier}' - AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'} + AND c.relname = #{quote(name.identifier)} + AND n.nspname = #{name.schema ? quote(name.schema) : "ANY (current_schemas(false))"} SQL end @@ -239,7 +239,9 @@ module ActiveRecord end def table_options(table_name) # :nodoc: - { comment: table_comment(table_name) } + if comment = table_comment(table_name) + { comment: comment } + end end # Returns a comment stored in database for given table @@ -439,7 +441,7 @@ module ActiveRecord WITH pk_constraint AS ( SELECT conrelid, unnest(conkey) AS connum FROM pg_constraint WHERE contype = 'p' - AND conrelid = '#{quote_table_name(table_name)}'::regclass + AND conrelid = #{quote(quote_table_name(table_name))}::regclass ), cons AS ( SELECT conrelid, connum, row_number() OVER() AS rownum FROM pk_constraint ) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 8001c0dd53..acd1eeace7 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -750,7 +750,7 @@ module ActiveRecord col_description(a.attrelid, a.attnum) AS comment FROM pg_attribute a LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum - WHERE a.attrelid = '#{quote_table_name(table_name)}'::regclass + WHERE a.attrelid = #{quote(quote_table_name(table_name))}::regclass AND a.attnum > 0 AND NOT a.attisdropped ORDER BY a.attnum end_sql @@ -771,10 +771,14 @@ module ActiveRecord sql = <<-end_sql SELECT exists( SELECT * FROM pg_proc + WHERE proname = 'lower' + AND proargtypes = ARRAY[#{quote column.sql_type}::regtype]::oidvector + ) OR exists( + SELECT * FROM pg_proc INNER JOIN pg_cast - ON casttarget::text::oidvector = proargtypes + ON ARRAY[casttarget]::oidvector = proargtypes WHERE proname = 'lower' - AND castsource = '#{column.sql_type}'::regtype::oid + AND castsource = #{quote column.sql_type}::regtype ) end_sql execute_and_clear(sql, "SCHEMA", []) do |result| diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index e2b534b511..ebeac425d0 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -530,10 +530,12 @@ module ActiveRecord def table_structure_with_collation(table_name, basic_structure) collation_hash = {} - sql = "SELECT sql FROM - (SELECT * FROM sqlite_master UNION ALL - SELECT * FROM sqlite_temp_master) - WHERE type='table' and name='#{ table_name }' \;" + sql = <<-SQL + SELECT sql FROM + (SELECT * FROM sqlite_master UNION ALL + SELECT * FROM sqlite_temp_master) + WHERE type = 'table' AND name = #{quote(table_name)} + SQL # Result will have following sample string # CREATE TABLE "users" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 3465b68ac6..622df0cfc1 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -452,7 +452,7 @@ module ActiveRecord # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ] def hash if id - [self.class, id].hash + self.class.hash ^ self.id.hash else super end diff --git a/activerecord/lib/active_record/explain_subscriber.rb b/activerecord/lib/active_record/explain_subscriber.rb index 706b57842f..abd8cfc8f2 100644 --- a/activerecord/lib/active_record/explain_subscriber.rb +++ b/activerecord/lib/active_record/explain_subscriber.rb @@ -18,10 +18,13 @@ module ActiveRecord # # On the other hand, we want to monitor the performance of our real database # queries, not the performance of the access to the query cache. - IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE) + IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN) EXPLAINED_SQLS = /\A\s*(with|select|update|delete|insert)\b/i def ignore_payload?(payload) - payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ EXPLAINED_SQLS + payload[:exception] || + payload[:cached] || + IGNORED_PAYLOADS.include?(payload[:name]) || + payload[:sql] !~ EXPLAINED_SQLS end ActiveSupport::Notifications.subscribe("sql.active_record", new) diff --git a/activerecord/lib/active_record/integration.rb b/activerecord/lib/active_record/integration.rb index e4c7a55541..3c54c6048d 100644 --- a/activerecord/lib/active_record/integration.rb +++ b/activerecord/lib/active_record/integration.rb @@ -53,18 +53,21 @@ module ActiveRecord # # Person.find(5).cache_key(:updated_at, :last_reviewed_at) def cache_key(*timestamp_names) - case - when new_record? + if new_record? "#{model_name.cache_key}/new" - when timestamp_names.any? - timestamp = max_updated_column_timestamp(timestamp_names) - timestamp = timestamp.utc.to_s(cache_timestamp_format) - "#{model_name.cache_key}/#{id}-#{timestamp}" - when timestamp = max_updated_column_timestamp - timestamp = timestamp.utc.to_s(cache_timestamp_format) - "#{model_name.cache_key}/#{id}-#{timestamp}" else - "#{model_name.cache_key}/#{id}" + timestamp = if timestamp_names.any? + max_updated_column_timestamp(timestamp_names) + else + max_updated_column_timestamp + end + + if timestamp + timestamp = timestamp.utc.to_s(cache_timestamp_format) + "#{model_name.cache_key}/#{id}-#{timestamp}" + else + "#{model_name.cache_key}/#{id}" + end end end diff --git a/activerecord/lib/active_record/log_subscriber.rb b/activerecord/lib/active_record/log_subscriber.rb index f31931316c..ad71c6cde8 100644 --- a/activerecord/lib/active_record/log_subscriber.rb +++ b/activerecord/lib/active_record/log_subscriber.rb @@ -26,15 +26,15 @@ module ActiveRecord end def sql(event) - return unless logger.debug? - self.class.runtime += event.duration + return unless logger.debug? payload = event.payload return if IGNORE_PAYLOAD_NAMES.include?(payload[:name]) name = "#{payload[:name]} (#{event.duration.round(1)}ms)" + name = "CACHE #{name}" if payload[:cached] sql = payload[:sql] binds = nil diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 05568039d8..627e93b5b6 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -1,4 +1,5 @@ require "set" +require "zlib" require "active_support/core_ext/module/attribute_accessors" require "active_support/core_ext/regexp" diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 978fb27cab..6933f3f9b8 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -107,7 +107,7 @@ module ActiveRecord # # By default, save always runs validations. If any of them fail the action # is cancelled and #save returns +false+, and the record won't be saved. However, if you supply - # validate: false, validations are bypassed altogether. See + # <tt>validate: false</tt>, validations are bypassed altogether. See # ActiveRecord::Validations for more information. # # By default, #save also sets the +updated_at+/+updated_on+ attributes to @@ -134,7 +134,7 @@ module ActiveRecord # # By default, #save! always runs validations. If any of them fail # ActiveRecord::RecordInvalid gets raised, and the record won't be saved. However, if you supply - # validate: false, validations are bypassed altogether. See + # <tt>validate: false</tt>, validations are bypassed altogether. See # ActiveRecord::Validations for more information. # # By default, #save! also sets the +updated_at+/+updated_on+ attributes to @@ -252,7 +252,8 @@ module ActiveRecord name = name.to_s verify_readonly_attribute(name) public_send("#{name}=", value) - save(validate: false) if changed? + + changed? ? save(validate: false) : true end # Updates the attributes of the model from the passed-in hash and saves the diff --git a/activerecord/lib/active_record/query_cache.rb b/activerecord/lib/active_record/query_cache.rb index 387dd8e9bd..c45c8c1697 100644 --- a/activerecord/lib/active_record/query_cache.rb +++ b/activerecord/lib/active_record/query_cache.rb @@ -34,16 +34,14 @@ module ActiveRecord def self.complete(enabled) ActiveRecord::Base.connection.clear_query_cache ActiveRecord::Base.connection.disable_query_cache! unless enabled + + unless ActiveRecord::Base.connected? && ActiveRecord::Base.connection.transaction_open? + ActiveRecord::Base.clear_active_connections! + end end def self.install_executor_hooks(executor = ActiveSupport::Executor) executor.register_hook(self) - - executor.to_complete do - unless ActiveRecord::Base.connected? && ActiveRecord::Base.connection.transaction_open? - ActiveRecord::Base.clear_active_connections! - end - end end end end diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 9b692f55d2..57020e00c9 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -282,6 +282,10 @@ module ActiveRecord end def autosave=(autosave) + # autosave and inverse_of do not get along together nowadays. They may + # for example cause double saves. Thus, we disable this flag. If in the + # future those two flags are known to work well together, this could be + # removed. @automatic_inverse_of = false @options[:autosave] = autosave parent_reflection = self.parent_reflection diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index a887be8a20..e4676f79a5 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -112,10 +112,6 @@ module ActiveRecord # ... # end def calculate(operation, column_name) - if column_name.is_a?(Symbol) && attribute_alias?(column_name) - column_name = attribute_alias(column_name) - end - if has_include?(column_name) relation = construct_relation_for_association_calculations relation = relation.distinct if operation.to_s.downcase == "count" @@ -215,8 +211,8 @@ module ActiveRecord def aggregate_column(column_name) return column_name if Arel::Expressions === column_name - if @klass.column_names.include?(column_name.to_s) - Arel::Attribute.new(@klass.unscoped.table, column_name) + if @klass.has_attribute?(column_name.to_s) || @klass.attribute_alias?(column_name.to_s) + @klass.arel_attribute(column_name) else Arel.sql(column_name == :all ? "*" : column_name.to_s) end diff --git a/activerecord/lib/active_record/relation/record_fetch_warning.rb b/activerecord/lib/active_record/relation/record_fetch_warning.rb index dbd08811fa..31544c730e 100644 --- a/activerecord/lib/active_record/relation/record_fetch_warning.rb +++ b/activerecord/lib/active_record/relation/record_fetch_warning.rb @@ -2,15 +2,15 @@ module ActiveRecord class Relation module RecordFetchWarning # When this module is prepended to ActiveRecord::Relation and - # `config.active_record.warn_on_records_fetched_greater_than` is + # +config.active_record.warn_on_records_fetched_greater_than+ is # set to an integer, if the number of records a query returns is - # greater than the value of `warn_on_records_fetched_greater_than`, + # greater than the value of +warn_on_records_fetched_greater_than+, # a warning is logged. This allows for the detection of queries that # return a large number of records, which could cause memory bloat. # # In most cases, fetching large number of records can be performed # efficiently using the ActiveRecord::Batches methods. - # See active_record/lib/relation/batches.rb for more information. + # See ActiveRecord::Batches for more information. def exec_queries QueryRegistry.reset diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index ab2d64e903..c1c6519cfa 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -115,9 +115,7 @@ HEADER pkcol = columns.detect { |c| c.name == pk } pkcolspec = @connection.column_spec_for_primary_key(pkcol) if pkcolspec.present? - pkcolspec.each do |key, value| - tbl.print ", #{key}: #{value}" - end + tbl.print ", #{format_colspec(pkcolspec)}" end when Array tbl.print ", primary_key: #{pk.inspect}" @@ -128,26 +126,19 @@ HEADER table_options = @connection.table_options(table) if table_options.present? - table_options.each do |key, value| - tbl.print ", #{key}: #{value.inspect}" if value.present? - end + tbl.print ", #{format_options(table_options)}" end tbl.puts " do |t|" # then dump all non-primary key columns - column_specs = columns.map do |column| + columns.each do |column| raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type) next if column.name == pk - @connection.column_spec(column) - end.compact - - # find all migration keys used in this table - keys = @connection.migration_keys - - column_specs.each do |colspec| - values = keys.map { |key| colspec[key] }.compact - tbl.puts " t.#{colspec[:type]} #{values.join(", ")}" + type, colspec = @connection.column_spec(column) + tbl.print " t.#{type} #{column.name.inspect}" + tbl.print ", #{format_colspec(colspec)}" if colspec.present? + tbl.puts end indexes_in_create(table, tbl) @@ -194,12 +185,8 @@ HEADER "name: #{index.name.inspect}", ] index_parts << "unique: true" if index.unique - - index_lengths = (index.lengths || []).compact - index_parts << "length: #{Hash[index.columns.zip(index.lengths)].inspect}" if index_lengths.any? - - index_orders = index.orders || {} - index_parts << "order: #{index.orders.inspect}" if index_orders.any? + index_parts << "length: { #{format_options(index.lengths)} }" if index.lengths.present? + index_parts << "order: { #{format_options(index.orders)} }" if index.orders.present? index_parts << "where: #{index.where.inspect}" if index.where index_parts << "using: #{index.using.inspect}" if index.using index_parts << "type: #{index.type.inspect}" if index.type @@ -237,6 +224,14 @@ HEADER end end + def format_colspec(colspec) + colspec.map { |key, value| "#{key}: #{value}" }.join(", ") + end + + def format_options(options) + options.map { |key, value| "#{key}: #{value.inspect}" }.join(", ") + end + def remove_prefix_and_suffix(table) table.gsub(/^(#{@options[:table_name_prefix]})(.+)(#{@options[:table_name_suffix]})$/, "\\2") end diff --git a/activerecord/lib/active_record/type.rb b/activerecord/lib/active_record/type.rb index 0b48d2186a..84373dddf2 100644 --- a/activerecord/lib/active_record/type.rb +++ b/activerecord/lib/active_record/type.rb @@ -1,4 +1,6 @@ require "active_model/type" +require "active_record/type/helpers" +require "active_record/type/value" require "active_record/type/internal/abstract_json" require "active_record/type/internal/timezone" @@ -48,7 +50,6 @@ module ActiveRecord end end - Helpers = ActiveModel::Type::Helpers BigInteger = ActiveModel::Type::BigInteger Binary = ActiveModel::Type::Binary Boolean = ActiveModel::Type::Boolean @@ -59,7 +60,6 @@ module ActiveRecord String = ActiveModel::Type::String Text = ActiveModel::Type::Text UnsignedInteger = ActiveModel::Type::UnsignedInteger - Value = ActiveModel::Type::Value register(:big_integer, Type::BigInteger, override: false) register(:binary, Type::Binary, override: false) diff --git a/activerecord/lib/active_record/type/helpers.rb b/activerecord/lib/active_record/type/helpers.rb new file mode 100644 index 0000000000..a32ccd4bc3 --- /dev/null +++ b/activerecord/lib/active_record/type/helpers.rb @@ -0,0 +1,5 @@ +module ActiveRecord + module Type + Helpers = ActiveModel::Type::Helpers + end +end diff --git a/activerecord/lib/active_record/type/internal/abstract_json.rb b/activerecord/lib/active_record/type/internal/abstract_json.rb index 513c938088..67028546e4 100644 --- a/activerecord/lib/active_record/type/internal/abstract_json.rb +++ b/activerecord/lib/active_record/type/internal/abstract_json.rb @@ -1,8 +1,8 @@ module ActiveRecord module Type module Internal # :nodoc: - class AbstractJson < ActiveModel::Type::Value # :nodoc: - include ActiveModel::Type::Helpers::Mutable + class AbstractJson < Type::Value # :nodoc: + include Type::Helpers::Mutable def type :json @@ -17,7 +17,11 @@ module ActiveRecord end def serialize(value) - ::ActiveSupport::JSON.encode(value) + if value.nil? + nil + else + ::ActiveSupport::JSON.encode(value) + end end def accessor diff --git a/activerecord/lib/active_record/type/serialized.rb b/activerecord/lib/active_record/type/serialized.rb index ac9134bfcb..ca12c83b1a 100644 --- a/activerecord/lib/active_record/type/serialized.rb +++ b/activerecord/lib/active_record/type/serialized.rb @@ -1,7 +1,7 @@ module ActiveRecord module Type - class Serialized < DelegateClass(ActiveModel::Type::Value) # :nodoc: - include ActiveModel::Type::Helpers::Mutable + class Serialized < DelegateClass(Type::Value) # :nodoc: + include Type::Helpers::Mutable attr_reader :subtype, :coder diff --git a/activerecord/lib/active_record/type/value.rb b/activerecord/lib/active_record/type/value.rb new file mode 100644 index 0000000000..89ef29106b --- /dev/null +++ b/activerecord/lib/active_record/type/value.rb @@ -0,0 +1,5 @@ +module ActiveRecord + module Type + class Value < ActiveModel::Type::Value; end + end +end |