activerecord 4.2.1 → 4.2.7.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (79) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +368 -1
  3. data/lib/active_record/aggregations.rb +4 -3
  4. data/lib/active_record/association_relation.rb +13 -0
  5. data/lib/active_record/associations/association.rb +15 -3
  6. data/lib/active_record/associations/association_scope.rb +1 -0
  7. data/lib/active_record/associations/belongs_to_association.rb +5 -1
  8. data/lib/active_record/associations/builder/association.rb +1 -1
  9. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -4
  10. data/lib/active_record/associations/collection_association.rb +1 -7
  11. data/lib/active_record/associations/collection_proxy.rb +8 -7
  12. data/lib/active_record/associations/has_many_association.rb +8 -1
  13. data/lib/active_record/associations/has_many_through_association.rb +9 -0
  14. data/lib/active_record/associations/join_dependency.rb +8 -2
  15. data/lib/active_record/associations/preloader/association.rb +5 -1
  16. data/lib/active_record/associations/preloader.rb +4 -4
  17. data/lib/active_record/associations/singular_association.rb +2 -8
  18. data/lib/active_record/associations/through_association.rb +0 -6
  19. data/lib/active_record/associations.rb +1 -1
  20. data/lib/active_record/attribute_assignment.rb +1 -1
  21. data/lib/active_record/attribute_methods/dirty.rb +7 -1
  22. data/lib/active_record/attribute_methods.rb +3 -7
  23. data/lib/active_record/attribute_set/builder.rb +21 -11
  24. data/lib/active_record/attribute_set.rb +4 -0
  25. data/lib/active_record/autosave_association.rb +1 -1
  26. data/lib/active_record/base.rb +4 -5
  27. data/lib/active_record/callbacks.rb +1 -1
  28. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +4 -0
  29. data/lib/active_record/connection_adapters/abstract/database_statements.rb +11 -2
  30. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +16 -24
  31. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +49 -17
  32. data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -5
  33. data/lib/active_record/connection_adapters/abstract_adapter.rb +12 -1
  34. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +38 -5
  35. data/lib/active_record/connection_adapters/column.rb +1 -1
  36. data/lib/active_record/connection_adapters/mysql2_adapter.rb +3 -3
  37. data/lib/active_record/connection_adapters/mysql_adapter.rb +2 -2
  38. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  39. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
  40. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +17 -5
  41. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +15 -6
  42. data/lib/active_record/connection_adapters/postgresql_adapter.rb +9 -9
  43. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -12
  44. data/lib/active_record/core.rb +15 -8
  45. data/lib/active_record/enum.rb +2 -3
  46. data/lib/active_record/errors.rb +6 -5
  47. data/lib/active_record/explain_subscriber.rb +1 -1
  48. data/lib/active_record/fixtures.rb +8 -6
  49. data/lib/active_record/gem_version.rb +2 -2
  50. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  51. data/lib/active_record/migration.rb +7 -4
  52. data/lib/active_record/model_schema.rb +1 -1
  53. data/lib/active_record/nested_attributes.rb +12 -2
  54. data/lib/active_record/persistence.rb +5 -3
  55. data/lib/active_record/railtie.rb +1 -1
  56. data/lib/active_record/railties/databases.rake +10 -7
  57. data/lib/active_record/reflection.rb +39 -27
  58. data/lib/active_record/relation/calculations.rb +8 -1
  59. data/lib/active_record/relation/delegation.rb +1 -1
  60. data/lib/active_record/relation/finder_methods.rb +3 -15
  61. data/lib/active_record/relation/merger.rb +24 -1
  62. data/lib/active_record/relation/predicate_builder/array_handler.rb +2 -1
  63. data/lib/active_record/relation/predicate_builder.rb +11 -2
  64. data/lib/active_record/relation/query_methods.rb +15 -18
  65. data/lib/active_record/relation/spawn_methods.rb +7 -3
  66. data/lib/active_record/relation.rb +2 -1
  67. data/lib/active_record/scoping/default.rb +1 -0
  68. data/lib/active_record/tasks/database_tasks.rb +3 -2
  69. data/lib/active_record/tasks/mysql_database_tasks.rb +30 -16
  70. data/lib/active_record/tasks/postgresql_database_tasks.rb +19 -8
  71. data/lib/active_record/transactions.rb +16 -4
  72. data/lib/active_record/type/boolean.rb +1 -0
  73. data/lib/active_record/type/date.rb +4 -0
  74. data/lib/active_record/type/decimal.rb +12 -2
  75. data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
  76. data/lib/active_record/type/serialized.rb +7 -1
  77. data/lib/active_record/validations/uniqueness.rb +15 -5
  78. data/lib/active_record.rb +2 -0
  79. metadata +9 -7
@@ -58,6 +58,12 @@ module ActiveRecord
58
58
  SchemaCreation.new self
59
59
  end
60
60
 
61
+ def prepare_column_options(column, types) # :nodoc:
62
+ spec = super
63
+ spec.delete(:limit) if :boolean === column.type
64
+ spec
65
+ end
66
+
61
67
  class Column < ConnectionAdapters::Column # :nodoc:
62
68
  attr_reader :collation, :strict, :extra
63
69
 
@@ -197,7 +203,7 @@ module ActiveRecord
197
203
  #
198
204
  # http://bugs.mysql.com/bug.php?id=39170
199
205
  def supports_transaction_isolation?
200
- version[0] >= 5
206
+ version >= '5.0.0'
201
207
  end
202
208
 
203
209
  def supports_indexes_in_create?
@@ -209,7 +215,11 @@ module ActiveRecord
209
215
  end
210
216
 
211
217
  def supports_views?
212
- version[0] >= 5
218
+ version >= '5.0.0'
219
+ end
220
+
221
+ def supports_datetime_with_precision?
222
+ version >= '5.6.4'
213
223
  end
214
224
 
215
225
  def native_database_types
@@ -401,6 +411,7 @@ module ActiveRecord
401
411
  result.collect { |field| field.first }
402
412
  end
403
413
  end
414
+ alias data_sources tables
404
415
 
405
416
  def truncate(table_name, name = nil)
406
417
  execute "TRUNCATE TABLE #{quote_table_name(table_name)}", name
@@ -420,6 +431,7 @@ module ActiveRecord
420
431
 
421
432
  tables(nil, schema, table).any?
422
433
  end
434
+ alias data_source_exists? table_exists?
423
435
 
424
436
  # Returns an array of indexes for the given table.
425
437
  def indexes(table_name, name = nil) #:nodoc:
@@ -598,8 +610,10 @@ module ActiveRecord
598
610
 
599
611
  # SHOW VARIABLES LIKE 'name'
600
612
  def show_variable(name)
601
- variables = select_all("SHOW VARIABLES LIKE '#{name}'", 'SCHEMA')
613
+ variables = select_all("select @@#{name} as 'Value'", 'SCHEMA')
602
614
  variables.first['Value'] unless variables.empty?
615
+ rescue ActiveRecord::StatementInvalid
616
+ nil
603
617
  end
604
618
 
605
619
  # Returns a table's primary key and belonging sequence.
@@ -642,6 +656,21 @@ module ActiveRecord
642
656
  end
643
657
  end
644
658
 
659
+ # In MySQL 5.7.5 and up, ONLY_FULL_GROUP_BY affects handling of queries that use
660
+ # DISTINCT and ORDER BY. It requires the ORDER BY columns in the select list for
661
+ # distinct queries, and requires that the ORDER BY include the distinct column.
662
+ # See https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
663
+ def columns_for_distinct(columns, orders) # :nodoc:
664
+ order_columns = orders.reject(&:blank?).map { |s|
665
+ # Convert Arel node to string
666
+ s = s.to_sql unless s.is_a?(String)
667
+ # Remove any ASC/DESC modifiers
668
+ s.gsub(/\s+(?:ASC|DESC)\b/i, '')
669
+ }.reject(&:blank?).map.with_index { |column, i| "#{column} AS alias_#{i}" }
670
+
671
+ [super, *order_columns].join(', ')
672
+ end
673
+
645
674
  def strict_mode?
646
675
  self.class.type_cast_config_to_boolean(@config.fetch(:strict, true))
647
676
  end
@@ -707,6 +736,10 @@ module ActiveRecord
707
736
  subsubselect = select.clone
708
737
  subsubselect.projections = [key]
709
738
 
739
+ # Materialize subquery by adding distinct
740
+ # to work with MySQL 5.7.6 which sets optimizer_switch='derived_merge=on'
741
+ subsubselect.distinct unless select.limit || select.offset || select.orders.any?
742
+
710
743
  subselect = Arel::SelectManager.new(select.engine)
711
744
  subselect.project Arel.sql(key.name)
712
745
  subselect.from subsubselect.as('__active_record_temp')
@@ -811,7 +844,7 @@ module ActiveRecord
811
844
  private
812
845
 
813
846
  def version
814
- @version ||= full_version.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
847
+ @version ||= Version.new(full_version.match(/^\d+\.\d+\.\d+/)[0])
815
848
  end
816
849
 
817
850
  def mariadb?
@@ -819,7 +852,7 @@ module ActiveRecord
819
852
  end
820
853
 
821
854
  def supports_rename_index?
822
- mariadb? ? false : (version[0] == 5 && version[1] >= 7) || version[0] >= 6
855
+ mariadb? ? false : version >= '5.7.6'
823
856
  end
824
857
 
825
858
  def configure_connection
@@ -31,7 +31,7 @@ module ActiveRecord
31
31
  # It will be mapped to one of the standard Rails SQL types in the <tt>type</tt> attribute.
32
32
  # +null+ determines if this column allows +NULL+ values.
33
33
  def initialize(name, default, cast_type, sql_type = nil, null = true)
34
- @name = name
34
+ @name = name.freeze
35
35
  @cast_type = cast_type
36
36
  @sql_type = sql_type
37
37
  @null = null
@@ -1,6 +1,6 @@
1
1
  require 'active_record/connection_adapters/abstract_mysql_adapter'
2
2
 
3
- gem 'mysql2', '~> 0.3.13'
3
+ gem 'mysql2', '>= 0.3.13', '< 0.5'
4
4
  require 'mysql2'
5
5
 
6
6
  module ActiveRecord
@@ -75,7 +75,7 @@ module ActiveRecord
75
75
  end
76
76
 
77
77
  def quoted_date(value)
78
- if value.acts_like?(:time) && value.respond_to?(:usec)
78
+ if supports_datetime_with_precision? && value.acts_like?(:time) && value.respond_to?(:usec)
79
79
  "#{super}.#{sprintf("%06d", value.usec)}"
80
80
  else
81
81
  super
@@ -271,7 +271,7 @@ module ActiveRecord
271
271
  end
272
272
 
273
273
  def full_version
274
- @full_version ||= @connection.info[:version]
274
+ @full_version ||= @connection.server_info[:version]
275
275
  end
276
276
 
277
277
  def set_field_encoding field_name
@@ -57,7 +57,7 @@ module ActiveRecord
57
57
  # * <tt>:database</tt> - The name of the database. No default, must be provided.
58
58
  # * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection.
59
59
  # * <tt>:reconnect</tt> - Defaults to false (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/auto-reconnect.html).
60
- # * <tt>:strict</tt> - Defaults to true. Enable STRICT_ALL_TABLES. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html)
60
+ # * <tt>:strict</tt> - Defaults to true. Enable STRICT_ALL_TABLES. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/sql-mode.html)
61
61
  # * <tt>:variables</tt> - (Optional) A hash session variables to send as <tt>SET @@SESSION.key = value</tt> on each database connection. Use the value +:default+ to set a variable to its DEFAULT value. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/set-statement.html).
62
62
  # * <tt>:sslca</tt> - Necessary to use MySQL with an SSL connection.
63
63
  # * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection.
@@ -245,7 +245,7 @@ module ActiveRecord
245
245
  return @client_encoding if @client_encoding
246
246
 
247
247
  result = exec_query(
248
- "SHOW VARIABLES WHERE Variable_name = 'character_set_client'",
248
+ "select @@character_set_client",
249
249
  'SCHEMA')
250
250
  @client_encoding = ENCODINGS[result.rows.last.last]
251
251
  end
@@ -18,7 +18,7 @@ module ActiveRecord
18
18
  end
19
19
 
20
20
  attr_reader :subtype, :delimiter
21
- delegate :type, to: :subtype
21
+ delegate :type, :limit, to: :subtype
22
22
 
23
23
  def initialize(subtype, delimiter = ',')
24
24
  @subtype = subtype
@@ -7,7 +7,9 @@ module ActiveRecord
7
7
  :enum
8
8
  end
9
9
 
10
- def type_cast(value)
10
+ private
11
+
12
+ def cast_value(value)
11
13
  value.to_s
12
14
  end
13
15
  end
@@ -15,11 +15,11 @@ module ActiveRecord
15
15
  def run(records)
16
16
  nodes = records.reject { |row| @store.key? row['oid'].to_i }
17
17
  mapped, nodes = nodes.partition { |row| @store.key? row['typname'] }
18
- ranges, nodes = nodes.partition { |row| row['typtype'] == 'r' }
19
- enums, nodes = nodes.partition { |row| row['typtype'] == 'e' }
20
- domains, nodes = nodes.partition { |row| row['typtype'] == 'd' }
21
- arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' }
22
- composites, nodes = nodes.partition { |row| row['typelem'] != '0' }
18
+ ranges, nodes = nodes.partition { |row| row['typtype'] == 'r'.freeze }
19
+ enums, nodes = nodes.partition { |row| row['typtype'] == 'e'.freeze }
20
+ domains, nodes = nodes.partition { |row| row['typtype'] == 'd'.freeze }
21
+ arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in'.freeze }
22
+ composites, nodes = nodes.partition { |row| row['typelem'].to_i != 0 }
23
23
 
24
24
  mapped.each { |row| register_mapped_type(row) }
25
25
  enums.each { |row| register_enum_type(row) }
@@ -29,6 +29,18 @@ module ActiveRecord
29
29
  composites.each { |row| register_composite_type(row) }
30
30
  end
31
31
 
32
+ def query_conditions_for_initial_load(type_map)
33
+ known_type_names = type_map.keys.map { |n| "'#{n}'" }
34
+ known_type_types = %w('r' 'e' 'd')
35
+ <<-SQL % [known_type_names.join(", "), known_type_types.join(", ")]
36
+ WHERE
37
+ t.typname IN (%s)
38
+ OR t.typtype IN (%s)
39
+ OR t.typinput = 'array_in(cstring,oid,integer)'::regprocedure
40
+ OR t.typelem != 0
41
+ SQL
42
+ end
43
+
32
44
  private
33
45
  def register_mapped_type(row)
34
46
  alias_type row['oid'], row['typname']
@@ -86,7 +86,7 @@ module ActiveRecord
86
86
  execute "DROP DATABASE IF EXISTS #{quote_table_name(name)}"
87
87
  end
88
88
 
89
- # Returns the list of all tables in the schema search path or a specified schema.
89
+ # Returns the list of all tables in the schema search path.
90
90
  def tables(name = nil)
91
91
  query(<<-SQL, 'SCHEMA').map { |row| row[0] }
92
92
  SELECT tablename
@@ -95,6 +95,16 @@ module ActiveRecord
95
95
  SQL
96
96
  end
97
97
 
98
+ def data_sources # :nodoc
99
+ select_values(<<-SQL, 'SCHEMA')
100
+ SELECT c.relname
101
+ FROM pg_class c
102
+ LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
103
+ WHERE c.relkind IN ('r', 'v','m') -- (r)elation/table, (v)iew, (m)aterialized view
104
+ AND n.nspname = ANY (current_schemas(false))
105
+ SQL
106
+ end
107
+
98
108
  # Returns true if table exists.
99
109
  # If the schema is not specified as part of +name+ then it will only find tables within
100
110
  # the current schema search path (regardless of permissions to access tables in other schemas)
@@ -111,6 +121,7 @@ module ActiveRecord
111
121
  AND n.nspname = #{name.schema ? "'#{name.schema}'" : 'ANY (current_schemas(false))'}
112
122
  SQL
113
123
  end
124
+ alias data_source_exists? table_exists?
114
125
 
115
126
  def drop_table(table_name, options = {})
116
127
  execute "DROP TABLE #{quote_table_name(table_name)}#{' CASCADE' if options[:force] == :cascade}"
@@ -418,9 +429,7 @@ module ActiveRecord
418
429
  rename_table_indexes(table_name, new_name)
419
430
  end
420
431
 
421
- # Adds a new column to the named table.
422
- # See TableDefinition#column for details of the options you can use.
423
- def add_column(table_name, column_name, type, options = {})
432
+ def add_column(table_name, column_name, type, options = {}) #:nodoc:
424
433
  clear_cache!
425
434
  super
426
435
  end
@@ -468,7 +477,7 @@ module ActiveRecord
468
477
  end
469
478
 
470
479
  # Renames a column in a table.
471
- def rename_column(table_name, column_name, new_column_name)
480
+ def rename_column(table_name, column_name, new_column_name) #:nodoc:
472
481
  clear_cache!
473
482
  execute "ALTER TABLE #{quote_table_name(table_name)} RENAME COLUMN #{quote_column_name(column_name)} TO #{quote_column_name(new_column_name)}"
474
483
  rename_column_indexes(table_name, column_name, new_column_name)
@@ -554,7 +563,7 @@ module ActiveRecord
554
563
  when 1, 2; 'smallint'
555
564
  when 3, 4; 'integer'
556
565
  when 5..8; 'bigint'
557
- else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
566
+ else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with scale 0 instead.")
558
567
  end
559
568
  when 'datetime'
560
569
  return super unless precision
@@ -308,12 +308,8 @@ module ActiveRecord
308
308
  true
309
309
  end
310
310
 
311
- # Enable standard-conforming strings if available.
312
311
  def set_standard_conforming_strings
313
- old, self.client_min_messages = client_min_messages, 'panic'
314
- execute('SET standard_conforming_strings = on', 'SCHEMA') rescue nil
315
- ensure
316
- self.client_min_messages = old
312
+ execute('SET standard_conforming_strings = on', 'SCHEMA')
317
313
  end
318
314
 
319
315
  def supports_ddl_transactions?
@@ -556,6 +552,8 @@ module ActiveRecord
556
552
  end
557
553
 
558
554
  def load_additional_types(type_map, oids = nil) # :nodoc:
555
+ initializer = OID::TypeMapInitializer.new(type_map)
556
+
559
557
  if supports_ranges?
560
558
  query = <<-SQL
561
559
  SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype, t.typtype, t.typbasetype
@@ -571,11 +569,13 @@ module ActiveRecord
571
569
 
572
570
  if oids
573
571
  query += "WHERE t.oid::integer IN (%s)" % oids.join(", ")
572
+ else
573
+ query += initializer.query_conditions_for_initial_load(type_map)
574
574
  end
575
575
 
576
- initializer = OID::TypeMapInitializer.new(type_map)
577
- records = execute(query, 'SCHEMA')
578
- initializer.run(records)
576
+ execute_and_clear(query, 'SCHEMA', []) do |records|
577
+ initializer.run(records)
578
+ end
579
579
  end
580
580
 
581
581
  FEATURE_NOT_SUPPORTED = "0A000" #:nodoc:
@@ -673,7 +673,7 @@ module ActiveRecord
673
673
  self.client_min_messages = @config[:min_messages] || 'warning'
674
674
  self.schema_search_path = @config[:schema_search_path] || @config[:schema_order]
675
675
 
676
- # Use standard-conforming strings if available so we don't have to do the E'...' dance.
676
+ # Use standard-conforming strings so we don't have to do the E'...' dance.
677
677
  set_standard_conforming_strings
678
678
 
679
679
  # If using Active Record's time zone support configure the connection to return
@@ -74,18 +74,6 @@ module ActiveRecord
74
74
  boolean: { name: "boolean" }
75
75
  }
76
76
 
77
- class Version
78
- include Comparable
79
-
80
- def initialize(version_string)
81
- @version = version_string.split('.').map { |v| v.to_i }
82
- end
83
-
84
- def <=>(version_string)
85
- @version <=> version_string.split('.').map { |v| v.to_i }
86
- end
87
- end
88
-
89
77
  class StatementPool < ConnectionAdapters::StatementPool
90
78
  def initialize(connection, max)
91
79
  super
@@ -375,10 +363,12 @@ module ActiveRecord
375
363
  row['name']
376
364
  end
377
365
  end
366
+ alias data_sources tables
378
367
 
379
368
  def table_exists?(table_name)
380
369
  table_name && tables(nil, table_name).any?
381
370
  end
371
+ alias data_source_exists? table_exists?
382
372
 
383
373
  # Returns an array of +Column+ objects for the table specified by +table_name+.
384
374
  def columns(table_name) #:nodoc:
@@ -284,17 +284,22 @@ module ActiveRecord
284
284
  _run_initialize_callbacks
285
285
  end
286
286
 
287
- # Initialize an empty model object from +coder+. +coder+ must contain
288
- # the attributes necessary for initializing an empty model object. For
289
- # example:
287
+ # Initialize an empty model object from +coder+. +coder+ should be
288
+ # the result of previously encoding an Active Record model, using
289
+ # `encode_with`
290
290
  #
291
291
  # class Post < ActiveRecord::Base
292
292
  # end
293
293
  #
294
+ # old_post = Post.new(title: "hello world")
295
+ # coder = {}
296
+ # old_post.encode_with(coder)
297
+ #
294
298
  # post = Post.allocate
295
- # post.init_with('attributes' => { 'title' => 'hello world' })
299
+ # post.init_with(coder)
296
300
  # post.title # => 'hello world'
297
301
  def init_with(coder)
302
+ coder = LegacyYamlAdapter.convert(self.class, coder)
298
303
  @attributes = coder['attributes']
299
304
 
300
305
  init_internals
@@ -368,6 +373,7 @@ module ActiveRecord
368
373
  coder['raw_attributes'] = attributes_before_type_cast
369
374
  coder['attributes'] = @attributes
370
375
  coder['new_record'] = new_record?
376
+ coder['active_record_yaml_version'] = 0
371
377
  end
372
378
 
373
379
  # Returns true if +comparison_object+ is the same exact object, or +comparison_object+
@@ -479,16 +485,16 @@ module ActiveRecord
479
485
  Hash[methods.map! { |method| [method, public_send(method)] }].with_indifferent_access
480
486
  end
481
487
 
488
+ private
489
+
482
490
  def set_transaction_state(state) # :nodoc:
483
491
  @transaction_state = state
484
492
  end
485
493
 
486
494
  def has_transactional_callbacks? # :nodoc:
487
- !_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_create_callbacks.empty?
495
+ !_rollback_callbacks.empty? || !_commit_callbacks.empty?
488
496
  end
489
497
 
490
- private
491
-
492
498
  # Updates the attributes on this particular ActiveRecord object so that
493
499
  # if it is associated with a transaction, then the state of the AR object
494
500
  # will be updated to reflect the current state of the transaction
@@ -511,6 +517,8 @@ module ActiveRecord
511
517
  end
512
518
 
513
519
  def update_attributes_from_transaction_state(transaction_state, depth)
520
+ @reflects_state = [false] if depth == 0
521
+
514
522
  if transaction_state && transaction_state.finalized? && !has_transactional_callbacks?
515
523
  unless @reflects_state[depth]
516
524
  restore_transaction_record_state if transaction_state.rolledback?
@@ -547,7 +555,6 @@ module ActiveRecord
547
555
  @txn = nil
548
556
  @_start_transaction_state = {}
549
557
  @transaction_state = nil
550
- @reflects_state = [false]
551
558
  end
552
559
 
553
560
  def initialize_internals_callback
@@ -18,10 +18,9 @@ module ActiveRecord
18
18
  # conversation.archived? # => true
19
19
  # conversation.status # => "archived"
20
20
  #
21
- # # conversation.update! status: 1
21
+ # # conversation.status = 1
22
22
  # conversation.status = "archived"
23
23
  #
24
- # # conversation.update! status: nil
25
24
  # conversation.status = nil
26
25
  # conversation.status.nil? # => true
27
26
  # conversation.status # => nil
@@ -70,7 +69,7 @@ module ActiveRecord
70
69
  # Where conditions on an enum attribute must use the ordinal value of an enum.
71
70
  module Enum
72
71
  def self.extended(base) # :nodoc:
73
- base.class_attribute(:defined_enums)
72
+ base.class_attribute(:defined_enums, instance_writer: false)
74
73
  base.defined_enums = {}
75
74
  end
76
75
 
@@ -71,9 +71,9 @@ module ActiveRecord
71
71
  class RecordNotDestroyed < ActiveRecordError
72
72
  attr_reader :record
73
73
 
74
- def initialize(record)
74
+ def initialize(message, record = nil)
75
75
  @record = record
76
- super()
76
+ super(message)
77
77
  end
78
78
  end
79
79
 
@@ -219,11 +219,12 @@ module ActiveRecord
219
219
  class UnknownPrimaryKey < ActiveRecordError
220
220
  attr_reader :model
221
221
 
222
- def initialize(model)
223
- super("Unknown primary key for table #{model.table_name} in model #{model}.")
222
+ def initialize(model, description = nil)
223
+ message = "Unknown primary key for table #{model.table_name} in model #{model}."
224
+ message += "\n#{description}" if description
225
+ super(message)
224
226
  @model = model
225
227
  end
226
-
227
228
  end
228
229
 
229
230
  # Raised when a relation cannot be mutated because it's already loaded.
@@ -19,7 +19,7 @@ module ActiveRecord
19
19
  # On the other hand, we want to monitor the performance of our real database
20
20
  # queries, not the performance of the access to the query cache.
21
21
  IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE)
22
- EXPLAINED_SQLS = /\A\s*(select|update|delete|insert)\b/i
22
+ EXPLAINED_SQLS = /\A\s*(with|select|update|delete|insert)\b/i
23
23
  def ignore_payload?(payload)
24
24
  payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ EXPLAINED_SQLS
25
25
  end
@@ -534,12 +534,10 @@ module ActiveRecord
534
534
  conn.insert_fixture(row, fixture_set_name)
535
535
  end
536
536
  end
537
- end
538
537
 
539
- # Cap primary key sequences to max(pk).
540
- if connection.respond_to?(:reset_pk_sequence!)
541
- fixture_sets.each do |fs|
542
- connection.reset_pk_sequence!(fs.table_name)
538
+ # Cap primary key sequences to max(pk).
539
+ if conn.respond_to?(:reset_pk_sequence!)
540
+ conn.reset_pk_sequence!(fs.table_name)
543
541
  end
544
542
  end
545
543
  end
@@ -661,7 +659,7 @@ module ActiveRecord
661
659
  row[association.foreign_type] = $1
662
660
  end
663
661
 
664
- fk_type = association.active_record.columns_hash[fk_name].type
662
+ fk_type = reflection_class.columns_hash[fk_name].type
665
663
  row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type)
666
664
  end
667
665
  when :has_many
@@ -703,6 +701,10 @@ module ActiveRecord
703
701
  def lhs_key
704
702
  @association.through_reflection.foreign_key
705
703
  end
704
+
705
+ def join_table
706
+ @association.through_reflection.table_name
707
+ end
706
708
  end
707
709
 
708
710
  private
@@ -7,8 +7,8 @@ module ActiveRecord
7
7
  module VERSION
8
8
  MAJOR = 4
9
9
  MINOR = 2
10
- TINY = 1
11
- PRE = nil
10
+ TINY = 7
11
+ PRE = "1"
12
12
 
13
13
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
14
14
  end
@@ -0,0 +1,30 @@
1
+ module ActiveRecord
2
+ module LegacyYamlAdapter
3
+ def self.convert(klass, coder)
4
+ return coder unless coder.is_a?(Psych::Coder)
5
+
6
+ case coder["active_record_yaml_version"]
7
+ when 0 then coder
8
+ else
9
+ if coder["attributes"].is_a?(AttributeSet)
10
+ coder
11
+ else
12
+ Rails41.convert(klass, coder)
13
+ end
14
+ end
15
+ end
16
+
17
+ module Rails41
18
+ def self.convert(klass, coder)
19
+ attributes = klass.attributes_builder
20
+ .build_from_database(coder["attributes"])
21
+ new_record = coder["attributes"][klass.primary_key].blank?
22
+
23
+ {
24
+ "attributes" => attributes,
25
+ "new_record" => new_record,
26
+ }
27
+ end
28
+ end
29
+ end
30
+ end
@@ -307,9 +307,8 @@ module ActiveRecord
307
307
  #
308
308
  # == Reversible Migrations
309
309
  #
310
- # Starting with Rails 3.1, you will be able to define reversible migrations.
311
310
  # Reversible migrations are migrations that know how to go +down+ for you.
312
- # You simply supply the +up+ logic, and the Migration system will figure out
311
+ # You simply supply the +up+ logic, and the Migration system figures out
313
312
  # how to execute the down commands for you.
314
313
  #
315
314
  # To define a reversible migration, define the +change+ method in your
@@ -421,7 +420,10 @@ module ActiveRecord
421
420
  new.migrate direction
422
421
  end
423
422
 
424
- # Disable DDL transactions for this migration.
423
+ # Disable the transaction wrapping this migration.
424
+ # You can still create your own transactions even after calling #disable_ddl_transaction!
425
+ #
426
+ # For more details read the {"Transactional Migrations" section above}[rdoc-ref:Migration].
425
427
  def disable_ddl_transaction!
426
428
  @disable_ddl_transaction = true
427
429
  end
@@ -653,7 +655,8 @@ module ActiveRecord
653
655
  unless @connection.respond_to? :revert
654
656
  unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
655
657
  arguments[0] = proper_table_name(arguments.first, table_name_options)
656
- if [:rename_table, :add_foreign_key].include?(method)
658
+ if [:rename_table, :add_foreign_key].include?(method) ||
659
+ (method == :remove_foreign_key && !arguments.second.is_a?(Hash))
657
660
  arguments[1] = proper_table_name(arguments.second, table_name_options)
658
661
  end
659
662
  end
@@ -295,7 +295,7 @@ module ActiveRecord
295
295
  def reset_column_information
296
296
  connection.clear_cache!
297
297
  undefine_attribute_methods
298
- connection.schema_cache.clear_table_cache!(table_name) if table_exists?
298
+ connection.schema_cache.clear_table_cache!(table_name)
299
299
 
300
300
  @arel_engine = nil
301
301
  @column_names = nil
@@ -523,7 +523,7 @@ module ActiveRecord
523
523
  # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
524
524
  # association and evaluates to +true+.
525
525
  def reject_new_record?(association_name, attributes)
526
- has_destroy_flag?(attributes) || call_reject_if(association_name, attributes)
526
+ will_be_destroyed?(association_name, attributes) || call_reject_if(association_name, attributes)
527
527
  end
528
528
 
529
529
  # Determines if a record with the particular +attributes+ should be
@@ -532,7 +532,8 @@ module ActiveRecord
532
532
  #
533
533
  # Returns false if there is a +destroy_flag+ on the attributes.
534
534
  def call_reject_if(association_name, attributes)
535
- return false if has_destroy_flag?(attributes)
535
+ return false if will_be_destroyed?(association_name, attributes)
536
+
536
537
  case callback = self.nested_attributes_options[association_name][:reject_if]
537
538
  when Symbol
538
539
  method(callback).arity == 0 ? send(callback) : send(callback, attributes)
@@ -541,6 +542,15 @@ module ActiveRecord
541
542
  end
542
543
  end
543
544
 
545
+ # Only take into account the destroy flag if <tt>:allow_destroy</tt> is true
546
+ def will_be_destroyed?(association_name, attributes)
547
+ allow_destroy?(association_name) && has_destroy_flag?(attributes)
548
+ end
549
+
550
+ def allow_destroy?(association_name)
551
+ self.nested_attributes_options[association_name][:allow_destroy]
552
+ end
553
+
544
554
  def raise_nested_attributes_record_not_found!(association_name, record_id)
545
555
  raise RecordNotFound, "Couldn't find #{self.class._reflect_on_association(association_name).klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}"
546
556
  end