activerecord 5.0.0 → 5.0.7.2

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 (101) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +431 -2
  3. data/README.rdoc +1 -1
  4. data/lib/active_record/aggregations.rb +4 -2
  5. data/lib/active_record/association_relation.rb +4 -1
  6. data/lib/active_record/associations/association.rb +11 -1
  7. data/lib/active_record/associations/association_scope.rb +1 -1
  8. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +4 -2
  9. data/lib/active_record/associations/builder/singular_association.rb +10 -1
  10. data/lib/active_record/associations/collection_association.rb +56 -48
  11. data/lib/active_record/associations/collection_proxy.rb +38 -20
  12. data/lib/active_record/associations/has_many_association.rb +1 -7
  13. data/lib/active_record/associations/has_many_through_association.rb +2 -4
  14. data/lib/active_record/associations/join_dependency/join_association.rb +6 -11
  15. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  16. data/lib/active_record/associations/join_dependency.rb +10 -4
  17. data/lib/active_record/associations/preloader/association.rb +24 -37
  18. data/lib/active_record/associations/preloader/collection_association.rb +0 -1
  19. data/lib/active_record/associations/preloader/singular_association.rb +0 -1
  20. data/lib/active_record/associations/preloader/through_association.rb +10 -4
  21. data/lib/active_record/associations/singular_association.rb +8 -2
  22. data/lib/active_record/associations/through_association.rb +1 -1
  23. data/lib/active_record/associations.rb +38 -17
  24. data/lib/active_record/attribute.rb +3 -3
  25. data/lib/active_record/attribute_methods/primary_key.rb +14 -1
  26. data/lib/active_record/attribute_methods/read.rb +1 -1
  27. data/lib/active_record/attribute_methods/time_zone_conversion.rb +2 -2
  28. data/lib/active_record/attribute_methods.rb +3 -7
  29. data/lib/active_record/attribute_set/builder.rb +37 -13
  30. data/lib/active_record/attribute_set.rb +2 -0
  31. data/lib/active_record/attributes.rb +3 -3
  32. data/lib/active_record/autosave_association.rb +15 -11
  33. data/lib/active_record/base.rb +1 -1
  34. data/lib/active_record/collection_cache_key.rb +16 -6
  35. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +41 -33
  36. data/lib/active_record/connection_adapters/abstract/database_statements.rb +1 -1
  37. data/lib/active_record/connection_adapters/abstract/query_cache.rb +37 -2
  38. data/lib/active_record/connection_adapters/abstract/quoting.rb +14 -5
  39. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +11 -14
  40. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +68 -56
  41. data/lib/active_record/connection_adapters/abstract_adapter.rb +36 -12
  42. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +63 -60
  43. data/lib/active_record/connection_adapters/column.rb +1 -1
  44. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  45. data/lib/active_record/connection_adapters/mysql/database_statements.rb +8 -25
  46. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  47. data/lib/active_record/connection_adapters/mysql2_adapter.rb +2 -6
  48. data/lib/active_record/connection_adapters/postgresql/column.rb +28 -1
  49. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +8 -0
  50. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +12 -2
  51. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
  52. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
  53. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +10 -0
  54. data/lib/active_record/connection_adapters/postgresql/quoting.rb +32 -3
  55. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +18 -8
  56. data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -2
  57. data/lib/active_record/connection_adapters/postgresql_adapter.rb +25 -19
  58. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  59. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +4 -4
  60. data/lib/active_record/core.rb +6 -4
  61. data/lib/active_record/enum.rb +8 -5
  62. data/lib/active_record/explain.rb +20 -9
  63. data/lib/active_record/fixtures.rb +5 -5
  64. data/lib/active_record/gem_version.rb +2 -2
  65. data/lib/active_record/integration.rb +13 -10
  66. data/lib/active_record/log_subscriber.rb +19 -17
  67. data/lib/active_record/migration.rb +35 -14
  68. data/lib/active_record/model_schema.rb +161 -52
  69. data/lib/active_record/no_touching.rb +4 -0
  70. data/lib/active_record/persistence.rb +41 -20
  71. data/lib/active_record/query_cache.rb +15 -17
  72. data/lib/active_record/querying.rb +3 -3
  73. data/lib/active_record/railties/controller_runtime.rb +1 -1
  74. data/lib/active_record/railties/databases.rake +9 -21
  75. data/lib/active_record/reflection.rb +20 -0
  76. data/lib/active_record/relation/batches.rb +10 -10
  77. data/lib/active_record/relation/calculations.rb +16 -12
  78. data/lib/active_record/relation/delegation.rb +2 -1
  79. data/lib/active_record/relation/finder_methods.rb +13 -11
  80. data/lib/active_record/relation/query_methods.rb +3 -3
  81. data/lib/active_record/relation.rb +12 -5
  82. data/lib/active_record/result.rb +7 -1
  83. data/lib/active_record/sanitization.rb +11 -1
  84. data/lib/active_record/schema_dumper.rb +10 -17
  85. data/lib/active_record/scoping/default.rb +5 -1
  86. data/lib/active_record/scoping/named.rb +18 -6
  87. data/lib/active_record/scoping.rb +4 -3
  88. data/lib/active_record/serialization.rb +1 -1
  89. data/lib/active_record/statement_cache.rb +2 -2
  90. data/lib/active_record/table_metadata.rb +4 -3
  91. data/lib/active_record/tasks/database_tasks.rb +14 -11
  92. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  93. data/lib/active_record/touch_later.rb +6 -1
  94. data/lib/active_record/transactions.rb +1 -1
  95. data/lib/active_record/type/internal/abstract_json.rb +5 -1
  96. data/lib/active_record/validations/uniqueness.rb +3 -4
  97. data/lib/active_record.rb +3 -2
  98. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
  99. data/lib/rails/generators/active_record/migration.rb +8 -0
  100. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  101. metadata +7 -8
@@ -1,78 +1,145 @@
1
+ require "monitor"
2
+
1
3
  module ActiveRecord
2
4
  module ModelSchema
3
5
  extend ActiveSupport::Concern
4
6
 
7
+ ##
8
+ # :singleton-method: primary_key_prefix_type
9
+ # :call-seq: primary_key_prefix_type
10
+ #
11
+ # The prefix type that will be prepended to every primary key column name.
12
+ # The options are +:table_name+ and +:table_name_with_underscore+. If the first is specified,
13
+ # the Product class will look for "productid" instead of "id" as the primary column. If the
14
+ # latter is specified, the Product class will look for "product_id" instead of "id". Remember
15
+ # that this is a global setting for all Active Records.
16
+
17
+ ##
18
+ # :singleton-method: primary_key_prefix_type=
19
+ # :call-seq: primary_key_prefix_type=(prefix_type)
20
+ #
21
+ # Sets the prefix type that will be prepended to every primary key column name.
22
+ # The options are +:table_name+ and +:table_name_with_underscore+. If the first is specified,
23
+ # the Product class will look for "productid" instead of "id" as the primary column. If the
24
+ # latter is specified, the Product class will look for "product_id" instead of "id". Remember
25
+ # that this is a global setting for all Active Records.
26
+
27
+ ##
28
+ # :singleton-method: table_name_prefix
29
+ # :call-seq: table_name_prefix
30
+ #
31
+ # The prefix string to prepend to every table name.
32
+
33
+ ##
34
+ # :singleton-method: table_name_prefix=
35
+ # :call-seq: table_name_prefix=(prefix)
36
+ #
37
+ # Sets the prefix string to prepend to every table name. So if set to "basecamp_", all table
38
+ # names will be named like "basecamp_projects", "basecamp_people", etc. This is a convenient
39
+ # way of creating a namespace for tables in a shared database. By default, the prefix is the
40
+ # empty string.
41
+ #
42
+ # If you are organising your models within modules you can add a prefix to the models within
43
+ # a namespace by defining a singleton method in the parent module called table_name_prefix which
44
+ # returns your chosen prefix.
45
+
46
+ ##
47
+ # :singleton-method: table_name_suffix
48
+ # :call-seq: table_name_suffix
49
+ #
50
+ # The suffix string to append to every table name.
51
+
52
+ ##
53
+ # :singleton-method: table_name_suffix=
54
+ # :call-seq: table_name_suffix=(suffix)
55
+ #
56
+ # Works like +table_name_prefix=+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
57
+ # "people_basecamp"). By default, the suffix is the empty string.
58
+ #
59
+ # If you are organising your models within modules, you can add a suffix to the models within
60
+ # a namespace by defining a singleton method in the parent module called table_name_suffix which
61
+ # returns your chosen suffix.
62
+
63
+ ##
64
+ # :singleton-method: schema_migrations_table_name
65
+ # :call-seq: schema_migrations_table_name
66
+ #
67
+ # The name of the schema migrations table. By default, the value is <tt>"schema_migrations"</tt>.
68
+
69
+ ##
70
+ # :singleton-method: schema_migrations_table_name=
71
+ # :call-seq: schema_migrations_table_name=(table_name)
72
+ #
73
+ # Sets the name of the schema migrations table.
74
+
75
+ ##
76
+ # :singleton-method: internal_metadata_table_name
77
+ # :call-seq: internal_metadata_table_name
78
+ #
79
+ # The name of the internal metadata table. By default, the value is <tt>"ar_internal_metadata"</tt>.
80
+
81
+ ##
82
+ # :singleton-method: internal_metadata_table_name=
83
+ # :call-seq: internal_metadata_table_name=(table_name)
84
+ #
85
+ # Sets the name of the internal metadata table.
86
+
87
+ ##
88
+ # :singleton-method: protected_environments
89
+ # :call-seq: protected_environments
90
+ #
91
+ # The array of names of environments where destructive actions should be prohibited. By default,
92
+ # the value is <tt>["production"]</tt>.
93
+
94
+ ##
95
+ # :singleton-method: protected_environments=
96
+ # :call-seq: protected_environments=(environments)
97
+ #
98
+ # Sets an array of names of environments where destructive actions should be prohibited.
99
+
100
+ ##
101
+ # :singleton-method: pluralize_table_names
102
+ # :call-seq: pluralize_table_names
103
+ #
104
+ # Indicates whether table names should be the pluralized versions of the corresponding class names.
105
+ # If true, the default table name for a Product class will be "products". If false, it would just be "product".
106
+ # See table_name for the full rules on table/class naming. This is true, by default.
107
+
108
+ ##
109
+ # :singleton-method: pluralize_table_names=
110
+ # :call-seq: pluralize_table_names=(value)
111
+ #
112
+ # Set whether table names should be the pluralized versions of the corresponding class names.
113
+ # If true, the default table name for a Product class will be "products". If false, it would just be "product".
114
+ # See table_name for the full rules on table/class naming. This is true, by default.
115
+
5
116
  included do
6
- ##
7
- # :singleton-method:
8
- # Accessor for the prefix type that will be prepended to every primary key column name.
9
- # The options are :table_name and :table_name_with_underscore. If the first is specified,
10
- # the Product class will look for "productid" instead of "id" as the primary column. If the
11
- # latter is specified, the Product class will look for "product_id" instead of "id". Remember
12
- # that this is a global setting for all Active Records.
13
117
  mattr_accessor :primary_key_prefix_type, instance_writer: false
14
118
 
15
- ##
16
- # :singleton-method:
17
- # Accessor for the name of the prefix string to prepend to every table name. So if set
18
- # to "basecamp_", all table names will be named like "basecamp_projects", "basecamp_people",
19
- # etc. This is a convenient way of creating a namespace for tables in a shared database.
20
- # By default, the prefix is the empty string.
21
- #
22
- # If you are organising your models within modules you can add a prefix to the models within
23
- # a namespace by defining a singleton method in the parent module called table_name_prefix which
24
- # returns your chosen prefix.
25
119
  class_attribute :table_name_prefix, instance_writer: false
26
120
  self.table_name_prefix = ""
27
121
 
28
- ##
29
- # :singleton-method:
30
- # Works like +table_name_prefix+, but appends instead of prepends (set to "_basecamp" gives "projects_basecamp",
31
- # "people_basecamp"). By default, the suffix is the empty string.
32
- #
33
- # If you are organising your models within modules, you can add a suffix to the models within
34
- # a namespace by defining a singleton method in the parent module called table_name_suffix which
35
- # returns your chosen suffix.
36
122
  class_attribute :table_name_suffix, instance_writer: false
37
123
  self.table_name_suffix = ""
38
124
 
39
- ##
40
- # :singleton-method:
41
- # Accessor for the name of the schema migrations table. By default, the value is "schema_migrations"
42
125
  class_attribute :schema_migrations_table_name, instance_accessor: false
43
126
  self.schema_migrations_table_name = "schema_migrations"
44
127
 
45
- ##
46
- # :singleton-method:
47
- # Accessor for the name of the internal metadata table. By default, the value is "ar_internal_metadata"
48
128
  class_attribute :internal_metadata_table_name, instance_accessor: false
49
129
  self.internal_metadata_table_name = "ar_internal_metadata"
50
130
 
51
- ##
52
- # :singleton-method:
53
- # Accessor for an array of names of environments where destructive actions should be prohibited. By default,
54
- # the value is ["production"]
55
131
  class_attribute :protected_environments, instance_accessor: false
56
132
  self.protected_environments = ["production"]
57
133
 
58
- ##
59
- # :singleton-method:
60
- # Indicates whether table names should be the pluralized versions of the corresponding class names.
61
- # If true, the default table name for a Product class will be +products+. If false, it would just be +product+.
62
- # See table_name for the full rules on table/class naming. This is true, by default.
63
134
  class_attribute :pluralize_table_names, instance_writer: false
64
135
  self.pluralize_table_names = true
65
136
 
66
- ##
67
- # :singleton-method:
68
- # Accessor for the list of columns names the model should ignore. Ignored columns won't have attribute
69
- # accessors defined, and won't be referenced in SQL queries.
70
- class_attribute :ignored_columns, instance_accessor: false
71
- self.ignored_columns = [].freeze
72
-
73
137
  self.inheritance_column = 'type'
138
+ self.ignored_columns = [].freeze
74
139
 
75
140
  delegate :type_for_attribute, to: :class
141
+
142
+ initialize_load_schema_monitor
76
143
  end
77
144
 
78
145
  # Derives the join table name for +first_table+ and +second_table+. The
@@ -199,6 +266,22 @@ module ActiveRecord
199
266
  @explicit_inheritance_column = true
200
267
  end
201
268
 
269
+ # The list of columns names the model should ignore. Ignored columns won't have attribute
270
+ # accessors defined, and won't be referenced in SQL queries.
271
+ def ignored_columns
272
+ if defined?(@ignored_columns)
273
+ @ignored_columns
274
+ else
275
+ superclass.ignored_columns
276
+ end
277
+ end
278
+
279
+ # Sets the columns names the model should ignore. Ignored columns won't have attribute
280
+ # accessors defined, and won't be referenced in SQL queries.
281
+ def ignored_columns=(columns)
282
+ @ignored_columns = columns.map(&:to_s)
283
+ end
284
+
202
285
  def sequence_name
203
286
  if base_class == self
204
287
  @sequence_name ||= reset_sequence_name
@@ -249,7 +332,11 @@ module ActiveRecord
249
332
  end
250
333
 
251
334
  def attributes_builder # :nodoc:
252
- @attributes_builder ||= AttributeSet::Builder.new(attribute_types, primary_key)
335
+ unless defined?(@attributes_builder) && @attributes_builder
336
+ defaults = _default_attributes.except(*(column_names - [primary_key]))
337
+ @attributes_builder = AttributeSet::Builder.new(attribute_types, defaults)
338
+ end
339
+ @attributes_builder
253
340
  end
254
341
 
255
342
  def columns_hash # :nodoc:
@@ -278,8 +365,12 @@ module ActiveRecord
278
365
  #
279
366
  # +attr_name+ The name of the attribute to retrieve the type for. Must be
280
367
  # a string
281
- def type_for_attribute(attr_name)
282
- attribute_types[attr_name]
368
+ def type_for_attribute(attr_name, &block)
369
+ if block
370
+ attribute_types.fetch(attr_name, &block)
371
+ else
372
+ attribute_types[attr_name]
373
+ end
283
374
  end
284
375
 
285
376
  # Returns a hash where the keys are column names and the values are
@@ -336,17 +427,34 @@ module ActiveRecord
336
427
  connection.schema_cache.clear_data_source_cache!(table_name)
337
428
 
338
429
  reload_schema_from_cache
430
+ initialize_find_by_cache
431
+ end
432
+
433
+ protected
434
+
435
+ def initialize_load_schema_monitor
436
+ @load_schema_monitor = Monitor.new
339
437
  end
340
438
 
341
439
  private
342
440
 
441
+ def inherited(child_class)
442
+ super
443
+ child_class.initialize_load_schema_monitor
444
+ end
445
+
343
446
  def schema_loaded?
344
- defined?(@columns_hash) && @columns_hash
447
+ defined?(@schema_loaded) && @schema_loaded
345
448
  end
346
449
 
347
450
  def load_schema
348
- unless schema_loaded?
451
+ return if schema_loaded?
452
+ @load_schema_monitor.synchronize do
453
+ return if defined?(@columns_hash) && @columns_hash
454
+
349
455
  load_schema!
456
+
457
+ @schema_loaded = true
350
458
  end
351
459
  end
352
460
 
@@ -374,6 +482,7 @@ module ActiveRecord
374
482
  @attributes_builder = nil
375
483
  @columns = nil
376
484
  @columns_hash = nil
485
+ @schema_loaded = false
377
486
  @attribute_names = nil
378
487
  direct_descendants.each do |descendant|
379
488
  descendant.send(:reload_schema_from_cache)
@@ -45,6 +45,10 @@ module ActiveRecord
45
45
  NoTouching.applied_to?(self.class)
46
46
  end
47
47
 
48
+ def touch_later(*) # :nodoc:
49
+ super unless no_touching?
50
+ end
51
+
48
52
  def touch(*) # :nodoc:
49
53
  super unless no_touching?
50
54
  end
@@ -63,10 +63,10 @@ module ActiveRecord
63
63
  #
64
64
  # See <tt>ActiveRecord::Inheritance#discriminate_class_for_record</tt> to see
65
65
  # how this "single-table" inheritance mapping is implemented.
66
- def instantiate(attributes, column_types = {})
66
+ def instantiate(attributes, column_types = {}, &block)
67
67
  klass = discriminate_class_for_record(attributes)
68
68
  attributes = klass.attributes_builder.build_from_database(attributes, column_types)
69
- klass.allocate.init_with('attributes' => attributes, 'new_record' => false)
69
+ klass.allocate.init_with("attributes" => attributes, "new_record" => false, &block)
70
70
  end
71
71
 
72
72
  private
@@ -100,6 +100,10 @@ module ActiveRecord
100
100
  !(@new_record || @destroyed)
101
101
  end
102
102
 
103
+ ##
104
+ # :call-seq:
105
+ # save(*args)
106
+ #
103
107
  # Saves the model.
104
108
  #
105
109
  # If the model is new, a record gets created in the database, otherwise
@@ -121,12 +125,16 @@ module ActiveRecord
121
125
  #
122
126
  # Attributes marked as readonly are silently ignored if the record is
123
127
  # being updated.
124
- def save(*args)
125
- create_or_update(*args)
128
+ def save(*args, &block)
129
+ create_or_update(*args, &block)
126
130
  rescue ActiveRecord::RecordInvalid
127
131
  false
128
132
  end
129
133
 
134
+ ##
135
+ # :call-seq:
136
+ # save!(*args)
137
+ #
130
138
  # Saves the model.
131
139
  #
132
140
  # If the model is new, a record gets created in the database, otherwise
@@ -148,8 +156,8 @@ module ActiveRecord
148
156
  #
149
157
  # Attributes marked as readonly are silently ignored if the record is
150
158
  # being updated.
151
- def save!(*args)
152
- create_or_update(*args) || raise(RecordNotSaved.new("Failed to save the record", self))
159
+ def save!(*args, &block)
160
+ create_or_update(*args, &block) || raise(RecordNotSaved.new("Failed to save the record", self))
153
161
  end
154
162
 
155
163
  # Deletes the record in the database and freezes this instance to
@@ -252,7 +260,8 @@ module ActiveRecord
252
260
  name = name.to_s
253
261
  verify_readonly_attribute(name)
254
262
  public_send("#{name}=", value)
255
- save(validate: false) if changed?
263
+
264
+ changed? ? save(validate: false) : true
256
265
  end
257
266
 
258
267
  # Updates the attributes of the model from the passed-in hash and saves the
@@ -329,10 +338,10 @@ module ActiveRecord
329
338
  self
330
339
  end
331
340
 
332
- # Wrapper around #increment that saves the record. This method differs from
333
- # its non-bang version in that it passes through the attribute setter.
334
- # Saving is not subjected to validation checks. Returns +true+ if the
335
- # record could be saved.
341
+ # Wrapper around #increment that writes the update to the database.
342
+ # Only +attribute+ is updated; the record itself is not saved.
343
+ # This means that any other modified attributes will still be dirty.
344
+ # Validations and callbacks are skipped. Returns +self+.
336
345
  def increment!(attribute, by = 1)
337
346
  increment(attribute, by)
338
347
  change = public_send(attribute) - (attribute_was(attribute.to_s) || 0)
@@ -348,10 +357,10 @@ module ActiveRecord
348
357
  increment(attribute, -by)
349
358
  end
350
359
 
351
- # Wrapper around #decrement that saves the record. This method differs from
352
- # its non-bang version in the sense that it passes through the attribute setter.
353
- # Saving is not subjected to validation checks. Returns +true+ if the
354
- # record could be saved.
360
+ # Wrapper around #decrement that writes the update to the database.
361
+ # Only +attribute+ is updated; the record itself is not saved.
362
+ # This means that any other modified attributes will still be dirty.
363
+ # Validations and callbacks are skipped. Returns +self+.
355
364
  def decrement!(attribute, by = 1)
356
365
  increment!(attribute, -by)
357
366
  end
@@ -479,7 +488,12 @@ module ActiveRecord
479
488
  # ball.touch(:updated_at) # => raises ActiveRecordError
480
489
  #
481
490
  def touch(*names, time: nil)
482
- raise ActiveRecordError, "cannot touch on a new record object" unless persisted?
491
+ unless persisted?
492
+ raise ActiveRecordError, <<-MSG.squish
493
+ cannot touch on a new or destroyed record object. Consider using
494
+ persisted?, new_record?, or destroyed? before touching
495
+ MSG
496
+ end
483
497
 
484
498
  time ||= current_time_from_proper_timezone
485
499
  attributes = timestamp_attributes_for_update_in_model
@@ -529,9 +543,9 @@ module ActiveRecord
529
543
  self.class.unscoped.where(self.class.primary_key => id)
530
544
  end
531
545
 
532
- def create_or_update(*args)
546
+ def create_or_update(*args, &block)
533
547
  raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly?
534
- result = new_record? ? _create_record : _update_record(*args)
548
+ result = new_record? ? _create_record(&block) : _update_record(*args, &block)
535
549
  result != false
536
550
  end
537
551
 
@@ -540,10 +554,14 @@ module ActiveRecord
540
554
  def _update_record(attribute_names = self.attribute_names)
541
555
  attributes_values = arel_attributes_with_values_for_update(attribute_names)
542
556
  if attributes_values.empty?
543
- 0
557
+ rows_affected = 0
544
558
  else
545
- self.class.unscoped._update_record attributes_values, id, id_was
559
+ rows_affected = self.class.unscoped._update_record attributes_values, id, id_was
546
560
  end
561
+
562
+ yield(self) if block_given?
563
+
564
+ rows_affected
547
565
  end
548
566
 
549
567
  # Creates a record with values matching those of the instance attributes
@@ -555,6 +573,9 @@ module ActiveRecord
555
573
  self.id ||= new_id if self.class.primary_key
556
574
 
557
575
  @new_record = false
576
+
577
+ yield(self) if block_given?
578
+
558
579
  id
559
580
  end
560
581
 
@@ -5,7 +5,7 @@ module ActiveRecord
5
5
  # Enable the query cache within the block if Active Record is configured.
6
6
  # If it's not, it will execute the given block.
7
7
  def cache(&block)
8
- if connected?
8
+ if connected? || !configurations.empty?
9
9
  connection.cache(&block)
10
10
  else
11
11
  yield
@@ -15,7 +15,7 @@ module ActiveRecord
15
15
  # Disable the query cache within the block if Active Record is configured.
16
16
  # If it's not, it will execute the given block.
17
17
  def uncached(&block)
18
- if connected?
18
+ if connected? || !configurations.empty?
19
19
  connection.uncached(&block)
20
20
  else
21
21
  yield
@@ -24,30 +24,28 @@ module ActiveRecord
24
24
  end
25
25
 
26
26
  def self.run
27
- connection = ActiveRecord::Base.connection
28
- enabled = connection.query_cache_enabled
29
27
  connection_id = ActiveRecord::Base.connection_id
30
- connection.enable_query_cache!
31
28
 
32
- [enabled, connection_id]
33
- end
29
+ caching_pool = ActiveRecord::Base.connection_pool
30
+ caching_was_enabled = caching_pool.query_cache_enabled
31
+
32
+ caching_pool.enable_query_cache!
34
33
 
35
- def self.complete(state)
36
- enabled, connection_id = state
34
+ [caching_pool, caching_was_enabled, connection_id]
35
+ end
37
36
 
37
+ def self.complete((caching_pool, caching_was_enabled, connection_id))
38
38
  ActiveRecord::Base.connection_id = connection_id
39
- ActiveRecord::Base.connection.clear_query_cache
40
- ActiveRecord::Base.connection.disable_query_cache! unless enabled
39
+
40
+ caching_pool.disable_query_cache! unless caching_was_enabled
41
+
42
+ ActiveRecord::Base.connection_handler.connection_pool_list.each do |pool|
43
+ pool.release_connection if pool.active_connection? && !pool.connection.transaction_open?
44
+ end
41
45
  end
42
46
 
43
47
  def self.install_executor_hooks(executor = ActiveSupport::Executor)
44
48
  executor.register_hook(self)
45
-
46
- executor.to_complete do
47
- unless ActiveRecord::Base.connection.transaction_open?
48
- ActiveRecord::Base.clear_active_connections!
49
- end
50
- end
51
49
  end
52
50
  end
53
51
  end
@@ -35,7 +35,7 @@ module ActiveRecord
35
35
  #
36
36
  # Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
37
37
  # Post.find_by_sql ["SELECT body FROM comments WHERE author = :user_id OR approved_by = :user_id", { :user_id => user_id }]
38
- def find_by_sql(sql, binds = [], preparable: nil)
38
+ def find_by_sql(sql, binds = [], preparable: nil, &block)
39
39
  result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds, preparable: preparable)
40
40
  column_types = result_set.column_types.dup
41
41
  columns_hash.each_key { |k| column_types.delete k }
@@ -46,8 +46,8 @@ module ActiveRecord
46
46
  class_name: name
47
47
  }
48
48
 
49
- message_bus.instrument('instantiation.active_record', payload) do
50
- result_set.map { |record| instantiate(record, column_types) }
49
+ message_bus.instrument("instantiation.active_record", payload) do
50
+ result_set.map { |record| instantiate(record, column_types, &block) }
51
51
  end
52
52
  end
53
53
 
@@ -19,7 +19,7 @@ module ActiveRecord
19
19
  end
20
20
 
21
21
  def cleanup_view_runtime
22
- if logger.info? && ActiveRecord::Base.connected?
22
+ if logger && logger.info? && ActiveRecord::Base.connected?
23
23
  db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime
24
24
  self.db_runtime = (db_runtime || 0) + db_rt_before_render
25
25
  runtime = super
@@ -77,6 +77,8 @@ db_namespace = namespace :db do
77
77
  namespace :migrate do
78
78
  # desc 'Rollbacks the database one migration and re migrate up (options: STEP=x, VERSION=x).'
79
79
  task :redo => [:environment, :load_config] do
80
+ raise "Empty VERSION provided" if ENV["VERSION"] && ENV["VERSION"].empty?
81
+
80
82
  if ENV['VERSION']
81
83
  db_namespace['migrate:down'].invoke
82
84
  db_namespace['migrate:up'].invoke
@@ -91,16 +93,17 @@ db_namespace = namespace :db do
91
93
 
92
94
  # desc 'Runs the "up" for a given migration VERSION.'
93
95
  task :up => [:environment, :load_config] do
96
+ raise "VERSION is required" if !ENV["VERSION"] || ENV["VERSION"].empty?
97
+
94
98
  version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
95
- raise 'VERSION is required' unless version
96
99
  ActiveRecord::Migrator.run(:up, ActiveRecord::Tasks::DatabaseTasks.migrations_paths, version)
97
100
  db_namespace['_dump'].invoke
98
101
  end
99
102
 
100
103
  # desc 'Runs the "down" for a given migration VERSION.'
101
104
  task :down => [:environment, :load_config] do
105
+ raise "VERSION is required - To go down one migration, use db:rollback" if !ENV["VERSION"] || ENV["VERSION"].empty?
102
106
  version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil
103
- raise 'VERSION is required - To go down one migration, run db:rollback' unless version
104
107
  ActiveRecord::Migrator.run(:down, ActiveRecord::Tasks::DatabaseTasks.migrations_paths, version)
105
108
  db_namespace['_dump'].invoke
106
109
  end
@@ -110,28 +113,13 @@ db_namespace = namespace :db do
110
113
  unless ActiveRecord::SchemaMigration.table_exists?
111
114
  abort 'Schema migrations table does not exist yet.'
112
115
  end
113
- db_list = ActiveRecord::SchemaMigration.normalized_versions
114
-
115
- file_list =
116
- ActiveRecord::Tasks::DatabaseTasks.migrations_paths.flat_map do |path|
117
- Dir.foreach(path).map do |file|
118
- next unless ActiveRecord::Migrator.match_to_migration_filename?(file)
119
-
120
- version, name, scope = ActiveRecord::Migrator.parse_migration_filename(file)
121
- version = ActiveRecord::SchemaMigration.normalize_migration_number(version)
122
- status = db_list.delete(version) ? 'up' : 'down'
123
- [status, version, (name + scope).humanize]
124
- end.compact
125
- end
126
116
 
127
- db_list.map! do |version|
128
- ['up', version, '********** NO FILE **********']
129
- end
130
117
  # output
131
118
  puts "\ndatabase: #{ActiveRecord::Base.connection_config[:database]}\n\n"
132
119
  puts "#{'Status'.center(8)} #{'Migration ID'.ljust(14)} Migration Name"
133
120
  puts "-" * 50
134
- (db_list + file_list).sort_by { |_, version, _| version }.each do |status, version, name|
121
+ paths = ActiveRecord::Tasks::DatabaseTasks.migrations_paths
122
+ ActiveRecord::Migrator.migrations_status(paths).each do |status, version, name|
135
123
  puts "#{status.center(8)} #{version.ljust(14)} #{name}"
136
124
  end
137
125
  puts
@@ -335,7 +323,7 @@ db_namespace = namespace :db do
335
323
  begin
336
324
  should_reconnect = ActiveRecord::Base.connection_pool.active_connection?
337
325
  ActiveRecord::Schema.verbose = false
338
- ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations['test'], :ruby, ENV['SCHEMA']
326
+ ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations["test"], :ruby, ENV["SCHEMA"], "test"
339
327
  ensure
340
328
  if should_reconnect
341
329
  ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations[ActiveRecord::Tasks::DatabaseTasks.env])
@@ -345,7 +333,7 @@ db_namespace = namespace :db do
345
333
 
346
334
  # desc "Recreate the test database from an existent structure.sql file"
347
335
  task :load_structure => %w(db:test:purge) do
348
- ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations['test'], :sql, ENV['SCHEMA']
336
+ ActiveRecord::Tasks::DatabaseTasks.load_schema ActiveRecord::Base.configurations["test"], :sql, ENV["SCHEMA"], "test"
349
337
  end
350
338
 
351
339
  # desc "Recreate the test database from a fresh schema"
@@ -311,6 +311,10 @@ module ActiveRecord
311
311
  active_record == other_aggregation.active_record
312
312
  end
313
313
 
314
+ def scope_for(klass)
315
+ scope ? klass.unscoped.instance_exec(nil, &scope) : klass.unscoped
316
+ end
317
+
314
318
  private
315
319
  def derive_class_name
316
320
  name.to_s.camelize
@@ -394,6 +398,10 @@ module ActiveRecord
394
398
  options[:primary_key] || primary_key(klass || self.klass)
395
399
  end
396
400
 
401
+ def association_primary_key_type
402
+ klass.type_for_attribute(association_primary_key.to_s)
403
+ end
404
+
397
405
  def active_record_primary_key
398
406
  @active_record_primary_key ||= options[:primary_key] || primary_key(active_record)
399
407
  end
@@ -519,6 +527,10 @@ module ActiveRecord
519
527
  seed + [self]
520
528
  end
521
529
 
530
+ def extensions
531
+ Array(options[:extend])
532
+ end
533
+
522
534
  protected
523
535
 
524
536
  def actual_source_reflection # FIXME: this is a horrible name
@@ -632,6 +644,10 @@ module ActiveRecord
632
644
  Associations::HasManyAssociation
633
645
  end
634
646
  end
647
+
648
+ def association_primary_key(klass = nil)
649
+ primary_key(klass || self.klass)
650
+ end
635
651
  end
636
652
 
637
653
  class HasOneReflection < AssociationReflection # :nodoc:
@@ -847,6 +863,10 @@ module ActiveRecord
847
863
  actual_source_reflection.options[:primary_key] || primary_key(klass || self.klass)
848
864
  end
849
865
 
866
+ def association_primary_key_type
867
+ klass.type_for_attribute(association_primary_key.to_s)
868
+ end
869
+
850
870
  # Gets an array of possible <tt>:through</tt> source reflection names in both singular and plural form.
851
871
  #
852
872
  # class Post < ActiveRecord::Base