activerecord 4.0.4 → 4.1.16

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 (143) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1632 -1797
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/examples/performance.rb +30 -18
  6. data/examples/simple.rb +4 -4
  7. data/lib/active_record/aggregations.rb +2 -1
  8. data/lib/active_record/association_relation.rb +4 -0
  9. data/lib/active_record/associations/alias_tracker.rb +49 -29
  10. data/lib/active_record/associations/association.rb +9 -17
  11. data/lib/active_record/associations/association_scope.rb +59 -49
  12. data/lib/active_record/associations/belongs_to_association.rb +34 -25
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +6 -1
  14. data/lib/active_record/associations/builder/association.rb +84 -54
  15. data/lib/active_record/associations/builder/belongs_to.rb +90 -58
  16. data/lib/active_record/associations/builder/collection_association.rb +47 -45
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +119 -25
  18. data/lib/active_record/associations/builder/has_many.rb +3 -3
  19. data/lib/active_record/associations/builder/has_one.rb +5 -7
  20. data/lib/active_record/associations/builder/singular_association.rb +6 -7
  21. data/lib/active_record/associations/collection_association.rb +121 -111
  22. data/lib/active_record/associations/collection_proxy.rb +73 -18
  23. data/lib/active_record/associations/has_many_association.rb +14 -11
  24. data/lib/active_record/associations/has_many_through_association.rb +33 -6
  25. data/lib/active_record/associations/has_one_association.rb +1 -1
  26. data/lib/active_record/associations/join_dependency/join_association.rb +46 -104
  27. data/lib/active_record/associations/join_dependency/join_base.rb +6 -8
  28. data/lib/active_record/associations/join_dependency/join_part.rb +18 -37
  29. data/lib/active_record/associations/join_dependency.rb +208 -168
  30. data/lib/active_record/associations/preloader/association.rb +69 -27
  31. data/lib/active_record/associations/preloader/collection_association.rb +2 -2
  32. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  33. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  34. data/lib/active_record/associations/preloader/through_association.rb +58 -26
  35. data/lib/active_record/associations/preloader.rb +63 -49
  36. data/lib/active_record/associations/singular_association.rb +6 -5
  37. data/lib/active_record/associations/through_association.rb +30 -9
  38. data/lib/active_record/associations.rb +116 -42
  39. data/lib/active_record/attribute_assignment.rb +6 -3
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -1
  41. data/lib/active_record/attribute_methods/dirty.rb +35 -26
  42. data/lib/active_record/attribute_methods/primary_key.rb +8 -1
  43. data/lib/active_record/attribute_methods/read.rb +56 -29
  44. data/lib/active_record/attribute_methods/serialization.rb +44 -12
  45. data/lib/active_record/attribute_methods/time_zone_conversion.rb +13 -1
  46. data/lib/active_record/attribute_methods/write.rb +59 -26
  47. data/lib/active_record/attribute_methods.rb +82 -43
  48. data/lib/active_record/autosave_association.rb +209 -194
  49. data/lib/active_record/base.rb +6 -2
  50. data/lib/active_record/callbacks.rb +2 -2
  51. data/lib/active_record/coders/json.rb +13 -0
  52. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +5 -10
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +14 -24
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +13 -13
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +6 -3
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +90 -0
  58. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -8
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +45 -70
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -0
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +28 -96
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +74 -66
  63. data/lib/active_record/connection_adapters/column.rb +1 -35
  64. data/lib/active_record/connection_adapters/connection_specification.rb +231 -43
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +10 -5
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +24 -17
  67. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +22 -15
  68. data/lib/active_record/connection_adapters/postgresql/cast.rb +12 -4
  69. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -44
  70. data/lib/active_record/connection_adapters/postgresql/oid.rb +38 -14
  71. data/lib/active_record/connection_adapters/postgresql/quoting.rb +37 -12
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +20 -11
  73. data/lib/active_record/connection_adapters/postgresql_adapter.rb +98 -52
  74. data/lib/active_record/connection_adapters/schema_cache.rb +8 -29
  75. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +55 -60
  76. data/lib/active_record/connection_handling.rb +39 -5
  77. data/lib/active_record/core.rb +38 -54
  78. data/lib/active_record/counter_cache.rb +9 -10
  79. data/lib/active_record/dynamic_matchers.rb +6 -2
  80. data/lib/active_record/enum.rb +199 -0
  81. data/lib/active_record/errors.rb +22 -5
  82. data/lib/active_record/fixture_set/file.rb +2 -1
  83. data/lib/active_record/fixtures.rb +173 -76
  84. data/lib/active_record/gem_version.rb +15 -0
  85. data/lib/active_record/inheritance.rb +23 -9
  86. data/lib/active_record/integration.rb +54 -1
  87. data/lib/active_record/locking/optimistic.rb +7 -2
  88. data/lib/active_record/locking/pessimistic.rb +1 -1
  89. data/lib/active_record/log_subscriber.rb +6 -13
  90. data/lib/active_record/migration/command_recorder.rb +8 -2
  91. data/lib/active_record/migration.rb +91 -56
  92. data/lib/active_record/model_schema.rb +7 -14
  93. data/lib/active_record/nested_attributes.rb +25 -13
  94. data/lib/active_record/no_touching.rb +52 -0
  95. data/lib/active_record/null_relation.rb +26 -6
  96. data/lib/active_record/persistence.rb +23 -29
  97. data/lib/active_record/querying.rb +15 -12
  98. data/lib/active_record/railtie.rb +12 -61
  99. data/lib/active_record/railties/databases.rake +37 -56
  100. data/lib/active_record/readonly_attributes.rb +0 -6
  101. data/lib/active_record/reflection.rb +230 -79
  102. data/lib/active_record/relation/batches.rb +74 -24
  103. data/lib/active_record/relation/calculations.rb +52 -48
  104. data/lib/active_record/relation/delegation.rb +54 -39
  105. data/lib/active_record/relation/finder_methods.rb +210 -67
  106. data/lib/active_record/relation/merger.rb +15 -12
  107. data/lib/active_record/relation/predicate_builder/array_handler.rb +29 -0
  108. data/lib/active_record/relation/predicate_builder/relation_handler.rb +17 -0
  109. data/lib/active_record/relation/predicate_builder.rb +81 -40
  110. data/lib/active_record/relation/query_methods.rb +185 -108
  111. data/lib/active_record/relation/spawn_methods.rb +8 -5
  112. data/lib/active_record/relation.rb +79 -84
  113. data/lib/active_record/result.rb +45 -6
  114. data/lib/active_record/runtime_registry.rb +5 -0
  115. data/lib/active_record/sanitization.rb +4 -4
  116. data/lib/active_record/schema_dumper.rb +18 -6
  117. data/lib/active_record/schema_migration.rb +31 -18
  118. data/lib/active_record/scoping/default.rb +5 -18
  119. data/lib/active_record/scoping/named.rb +14 -29
  120. data/lib/active_record/scoping.rb +5 -0
  121. data/lib/active_record/store.rb +67 -18
  122. data/lib/active_record/tasks/database_tasks.rb +66 -26
  123. data/lib/active_record/tasks/mysql_database_tasks.rb +16 -10
  124. data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
  125. data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
  126. data/lib/active_record/timestamp.rb +6 -6
  127. data/lib/active_record/transactions.rb +10 -12
  128. data/lib/active_record/validations/presence.rb +1 -1
  129. data/lib/active_record/validations/uniqueness.rb +19 -9
  130. data/lib/active_record/version.rb +4 -7
  131. data/lib/active_record.rb +5 -7
  132. data/lib/rails/generators/active_record/migration/migration_generator.rb +4 -0
  133. data/lib/rails/generators/active_record/migration.rb +18 -0
  134. data/lib/rails/generators/active_record/model/model_generator.rb +4 -0
  135. data/lib/rails/generators/active_record.rb +2 -8
  136. metadata +18 -30
  137. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -65
  138. data/lib/active_record/associations/join_helper.rb +0 -45
  139. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  140. data/lib/active_record/tasks/firebird_database_tasks.rb +0 -56
  141. data/lib/active_record/tasks/oracle_database_tasks.rb +0 -45
  142. data/lib/active_record/tasks/sqlserver_database_tasks.rb +0 -48
  143. data/lib/active_record/test_case.rb +0 -96
@@ -64,7 +64,7 @@ module ActiveRecord
64
64
  end
65
65
 
66
66
  # Wraps the passed block in a transaction, locking the object
67
- # before yielding. You pass can the SQL locking clause
67
+ # before yielding. You can pass the SQL locking clause
68
68
  # as argument (see <tt>lock!</tt>).
69
69
  def with_lock(lock = true)
70
70
  transaction do
@@ -17,13 +17,15 @@ module ActiveRecord
17
17
 
18
18
  def initialize
19
19
  super
20
- @odd_or_even = false
20
+ @odd = false
21
21
  end
22
22
 
23
23
  def render_bind(column, value)
24
24
  if column
25
25
  if column.binary?
26
- value = "<#{value.bytesize} bytes of binary data>"
26
+ # This specifically deals with the PG adapter that casts bytea columns into a Hash.
27
+ value = value[:value] if value.is_a?(Hash)
28
+ value = value ? "<#{value.bytesize} bytes of binary data>" : "<NULL binary data>"
27
29
  end
28
30
 
29
31
  [column.name, value]
@@ -41,7 +43,7 @@ module ActiveRecord
41
43
  return if IGNORE_PAYLOAD_NAMES.include?(payload[:name])
42
44
 
43
45
  name = "#{payload[:name]} (#{event.duration.round(1)}ms)"
44
- sql = payload[:sql].squeeze(' ')
46
+ sql = payload[:sql]
45
47
  binds = nil
46
48
 
47
49
  unless (payload[:binds] || []).empty?
@@ -60,17 +62,8 @@ module ActiveRecord
60
62
  debug " #{name} #{sql}#{binds}"
61
63
  end
62
64
 
63
- def identity(event)
64
- return unless logger.debug?
65
-
66
- name = color(event.payload[:name], odd? ? CYAN : MAGENTA, true)
67
- line = odd? ? color(event.payload[:line], nil, true) : event.payload[:line]
68
-
69
- debug " #{name} #{line}"
70
- end
71
-
72
65
  def odd?
73
- @odd_or_even = !@odd_or_even
66
+ @odd = !@odd
74
67
  end
75
68
 
76
69
  def logger
@@ -73,8 +73,8 @@ module ActiveRecord
73
73
  [:create_table, :create_join_table, :rename_table, :add_column, :remove_column,
74
74
  :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps,
75
75
  :change_column_default, :add_reference, :remove_reference, :transaction,
76
- :drop_join_table, :drop_table, :execute_block,
77
- :change_column, :execute, :remove_columns, # irreversible methods need to be here too
76
+ :drop_join_table, :drop_table, :execute_block, :enable_extension,
77
+ :change_column, :execute, :remove_columns, :change_column_null # irreversible methods need to be here too
78
78
  ].each do |method|
79
79
  class_eval <<-EOV, __FILE__, __LINE__ + 1
80
80
  def #{method}(*args, &block) # def create_table(*args, &block)
@@ -100,6 +100,7 @@ module ActiveRecord
100
100
  add_column: :remove_column,
101
101
  add_timestamps: :remove_timestamps,
102
102
  add_reference: :remove_reference,
103
+ enable_extension: :disable_extension
103
104
  }.each do |cmd, inv|
104
105
  [[inv, cmd], [cmd, inv]].uniq.each do |method, inverse|
105
106
  class_eval <<-EOV, __FILE__, __LINE__ + 1
@@ -161,6 +162,11 @@ module ActiveRecord
161
162
  alias :invert_add_belongs_to :invert_add_reference
162
163
  alias :invert_remove_belongs_to :invert_remove_reference
163
164
 
165
+ def invert_change_column_null(args)
166
+ args[2] = !args[2]
167
+ [:change_column_null, args]
168
+ end
169
+
164
170
  # Forwards any missing method call to the \target.
165
171
  def method_missing(method, *args, &block)
166
172
  if @delegate.respond_to?(method)
@@ -1,41 +1,48 @@
1
- require "active_support/core_ext/class/attribute_accessors"
1
+ require "active_support/core_ext/module/attribute_accessors"
2
2
  require 'set'
3
3
 
4
4
  module ActiveRecord
5
+ class MigrationError < ActiveRecordError#:nodoc:
6
+ def initialize(message = nil)
7
+ message = "\n\n#{message}\n\n" if message
8
+ super
9
+ end
10
+ end
11
+
5
12
  # Exception that can be raised to stop migrations from going backwards.
6
- class IrreversibleMigration < ActiveRecordError
13
+ class IrreversibleMigration < MigrationError
7
14
  end
8
15
 
9
- class DuplicateMigrationVersionError < ActiveRecordError#:nodoc:
16
+ class DuplicateMigrationVersionError < MigrationError#:nodoc:
10
17
  def initialize(version)
11
18
  super("Multiple migrations have the version number #{version}")
12
19
  end
13
20
  end
14
21
 
15
- class DuplicateMigrationNameError < ActiveRecordError#:nodoc:
22
+ class DuplicateMigrationNameError < MigrationError#:nodoc:
16
23
  def initialize(name)
17
24
  super("Multiple migrations have the name #{name}")
18
25
  end
19
26
  end
20
27
 
21
- class UnknownMigrationVersionError < ActiveRecordError #:nodoc:
28
+ class UnknownMigrationVersionError < MigrationError #:nodoc:
22
29
  def initialize(version)
23
30
  super("No migration with version number #{version}")
24
31
  end
25
32
  end
26
33
 
27
- class IllegalMigrationNameError < ActiveRecordError#:nodoc:
34
+ class IllegalMigrationNameError < MigrationError#:nodoc:
28
35
  def initialize(name)
29
36
  super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)")
30
37
  end
31
38
  end
32
39
 
33
- class PendingMigrationError < ActiveRecordError#:nodoc:
40
+ class PendingMigrationError < MigrationError#:nodoc:
34
41
  def initialize
35
42
  if defined?(Rails)
36
- super("Migrations are pending; run 'bin/rake db:migrate RAILS_ENV=#{::Rails.env}' to resolve this issue.")
43
+ super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rake db:migrate RAILS_ENV=#{::Rails.env}")
37
44
  else
38
- super("Migrations are pending; run 'bin/rake db:migrate' to resolve this issue.")
45
+ super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rake db:migrate")
39
46
  end
40
47
  end
41
48
  end
@@ -377,23 +384,43 @@ module ActiveRecord
377
384
  class << self
378
385
  attr_accessor :delegate # :nodoc:
379
386
  attr_accessor :disable_ddl_transaction # :nodoc:
380
- end
381
387
 
382
- def self.check_pending!
383
- raise ActiveRecord::PendingMigrationError if ActiveRecord::Migrator.needs_migration?
384
- end
388
+ def check_pending!(connection = Base.connection)
389
+ raise ActiveRecord::PendingMigrationError if ActiveRecord::Migrator.needs_migration?(connection)
390
+ end
385
391
 
386
- def self.method_missing(name, *args, &block) # :nodoc:
387
- (delegate || superclass.delegate).send(name, *args, &block)
388
- end
392
+ def load_schema_if_pending!
393
+ if ActiveRecord::Migrator.needs_migration?
394
+ # Roundrip to Rake to allow plugins to hook into database initialization.
395
+ FileUtils.cd Rails.root do
396
+ current_config = Base.connection_config
397
+ Base.clear_all_connections!
398
+ system("bin/rake db:test:prepare")
399
+ # Establish a new connection, the old database may be gone (db:test:prepare uses purge)
400
+ Base.establish_connection(current_config)
401
+ end
402
+ check_pending!
403
+ end
404
+ end
389
405
 
390
- def self.migrate(direction)
391
- new.migrate direction
392
- end
406
+ def maintain_test_schema! # :nodoc:
407
+ if ActiveRecord::Base.maintain_test_schema
408
+ suppress_messages { load_schema_if_pending! }
409
+ end
410
+ end
393
411
 
394
- # Disable DDL transactions for this migration.
395
- def self.disable_ddl_transaction!
396
- @disable_ddl_transaction = true
412
+ def method_missing(name, *args, &block) # :nodoc:
413
+ (delegate || superclass.delegate).send(name, *args, &block)
414
+ end
415
+
416
+ def migrate(direction)
417
+ new.migrate direction
418
+ end
419
+
420
+ # Disable DDL transactions for this migration.
421
+ def disable_ddl_transaction!
422
+ @disable_ddl_transaction = true
423
+ end
397
424
  end
398
425
 
399
426
  def disable_ddl_transaction # :nodoc:
@@ -620,9 +647,9 @@ module ActiveRecord
620
647
 
621
648
  say_with_time "#{method}(#{arg_list})" do
622
649
  unless @connection.respond_to? :revert
623
- unless arguments.empty? || method == :execute
624
- arguments[0] = Migrator.proper_table_name(arguments.first)
625
- arguments[1] = Migrator.proper_table_name(arguments.second) if method == :rename_table
650
+ unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
651
+ arguments[0] = proper_table_name(arguments.first, table_name_options)
652
+ arguments[1] = proper_table_name(arguments.second, table_name_options) if method == :rename_table
626
653
  end
627
654
  end
628
655
  return super unless connection.respond_to?(method)
@@ -675,14 +702,33 @@ module ActiveRecord
675
702
  copied
676
703
  end
677
704
 
705
+ # Finds the correct table name given an Active Record object.
706
+ # Uses the Active Record object's own table_name, or pre/suffix from the
707
+ # options passed in.
708
+ def proper_table_name(name, options = {})
709
+ if name.respond_to? :table_name
710
+ name.table_name
711
+ else
712
+ "#{options[:table_name_prefix]}#{name}#{options[:table_name_suffix]}"
713
+ end
714
+ end
715
+
716
+ # Determines the version number of the next migration.
678
717
  def next_migration_number(number)
679
718
  if ActiveRecord::Base.timestamped_migrations
680
719
  [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % number].max
681
720
  else
682
- "%.3d" % number
721
+ SchemaMigration.normalize_migration_number(number)
683
722
  end
684
723
  end
685
724
 
725
+ def table_name_options(config = ActiveRecord::Base)
726
+ {
727
+ table_name_prefix: config.table_name_prefix,
728
+ table_name_suffix: config.table_name_suffix
729
+ }
730
+ end
731
+
686
732
  private
687
733
  def execute_block
688
734
  if connection.respond_to? :execute_block
@@ -787,21 +833,20 @@ module ActiveRecord
787
833
  SchemaMigration.table_name
788
834
  end
789
835
 
790
- def get_all_versions
791
- SchemaMigration.all.map { |x| x.version.to_i }.sort
792
- end
793
-
794
- def current_version
795
- sm_table = schema_migrations_table_name
796
- if Base.connection.table_exists?(sm_table)
797
- get_all_versions.max || 0
836
+ def get_all_versions(connection = Base.connection)
837
+ if connection.table_exists?(schema_migrations_table_name)
838
+ SchemaMigration.all.map { |x| x.version.to_i }.sort
798
839
  else
799
- 0
840
+ []
800
841
  end
801
842
  end
802
843
 
803
- def needs_migration?
804
- current_version < last_version
844
+ def current_version(connection = Base.connection)
845
+ get_all_versions(connection).max || 0
846
+ end
847
+
848
+ def needs_migration?(connection = Base.connection)
849
+ (migrations(migrations_paths).collect(&:version) - get_all_versions(connection)).size > 0
805
850
  end
806
851
 
807
852
  def last_version
@@ -812,12 +857,16 @@ module ActiveRecord
812
857
  migrations(migrations_paths).last || NullMigration.new
813
858
  end
814
859
 
815
- def proper_table_name(name)
816
- # Use the Active Record objects own table_name, or pre/suffix from ActiveRecord::Base if name is a symbol/string
860
+ def proper_table_name(name, options = {})
861
+ ActiveSupport::Deprecation.warn "ActiveRecord::Migrator.proper_table_name is deprecated and will be removed in Rails 4.2. Use the proper_table_name instance method on ActiveRecord::Migration instead"
862
+ options = {
863
+ table_name_prefix: ActiveRecord::Base.table_name_prefix,
864
+ table_name_suffix: ActiveRecord::Base.table_name_suffix
865
+ }.merge(options)
817
866
  if name.respond_to? :table_name
818
867
  name.table_name
819
868
  else
820
- "#{ActiveRecord::Base.table_name_prefix}#{name}#{ActiveRecord::Base.table_name_suffix}"
869
+ "#{options[:table_name_prefix]}#{name}#{options[:table_name_suffix]}"
821
870
  end
822
871
  end
823
872
 
@@ -869,13 +918,7 @@ module ActiveRecord
869
918
  @direction = direction
870
919
  @target_version = target_version
871
920
  @migrated_versions = nil
872
-
873
- if Array(migrations).grep(String).empty?
874
- @migrations = migrations
875
- else
876
- ActiveSupport::Deprecation.warn "instantiate this class with a list of migrations"
877
- @migrations = self.class.migrations(migrations)
878
- end
921
+ @migrations = migrations
879
922
 
880
923
  validate(@migrations)
881
924
 
@@ -909,15 +952,7 @@ module ActiveRecord
909
952
  raise UnknownMigrationVersionError.new(@target_version)
910
953
  end
911
954
 
912
- running = runnable
913
-
914
- if block_given?
915
- message = "block argument to migrate is deprecated, please filter migrations before constructing the migrator"
916
- ActiveSupport::Deprecation.warn message
917
- running.select! { |m| yield m }
918
- end
919
-
920
- running.each do |migration|
955
+ runnable.each do |migration|
921
956
  Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger
922
957
 
923
958
  begin
@@ -32,6 +32,12 @@ module ActiveRecord
32
32
  class_attribute :table_name_suffix, instance_writer: false
33
33
  self.table_name_suffix = ""
34
34
 
35
+ ##
36
+ # :singleton-method:
37
+ # Accessor for the name of the schema migrations table. By default, the value is "schema_migrations"
38
+ class_attribute :schema_migrations_table_name, instance_accessor: false
39
+ self.schema_migrations_table_name = "schema_migrations"
40
+
35
41
  ##
36
42
  # :singleton-method:
37
43
  # Indicates whether table names should be the pluralized versions of the corresponding class names.
@@ -124,7 +130,7 @@ module ActiveRecord
124
130
  @quoted_table_name = nil
125
131
  @arel_table = nil
126
132
  @sequence_name = nil unless defined?(@explicit_sequence_name) && @explicit_sequence_name
127
- @relation = Relation.new(self, arel_table)
133
+ @relation = Relation.create(self, arel_table)
128
134
  end
129
135
 
130
136
  # Returns a quoted version of the table name, used to construct SQL statements.
@@ -260,19 +266,6 @@ module ActiveRecord
260
266
  @content_columns ||= columns.reject { |c| c.primary || c.name =~ /(_id|_count)$/ || c.name == inheritance_column }
261
267
  end
262
268
 
263
- # Returns a hash of all the methods added to query each of the columns in the table with the name of the method as the key
264
- # and true as the value. This makes it possible to do O(1) lookups in respond_to? to check if a given method for attribute
265
- # is available.
266
- def column_methods_hash #:nodoc:
267
- @dynamic_methods_hash ||= column_names.each_with_object(Hash.new(false)) do |attr, methods|
268
- attr_name = attr.to_s
269
- methods[attr.to_sym] = attr_name
270
- methods["#{attr}=".to_sym] = attr_name
271
- methods["#{attr}?".to_sym] = attr_name
272
- methods["#{attr}_before_type_cast".to_sym] = attr_name
273
- end
274
- end
275
-
276
269
  # Resets all the cached information about columns, which will cause them
277
270
  # to be reloaded on the next request.
278
271
  #
@@ -230,6 +230,10 @@ module ActiveRecord
230
230
  # validates_presence_of :member
231
231
  # end
232
232
  #
233
+ # Note that if you do not specify the <tt>inverse_of</tt> option, then
234
+ # Active Record will try to automatically guess the inverse association
235
+ # based on heuristics.
236
+ #
233
237
  # For one-to-one nested associations, if you build the new (in-memory)
234
238
  # child object yourself before assignment, then this module will not
235
239
  # overwrite it, e.g.:
@@ -301,9 +305,9 @@ module ActiveRecord
301
305
  options[:reject_if] = REJECT_ALL_BLANK_PROC if options[:reject_if] == :all_blank
302
306
 
303
307
  attr_names.each do |association_name|
304
- if reflection = reflect_on_association(association_name)
305
- reflection.options[:autosave] = true
306
- add_autosave_association_callbacks(reflection)
308
+ if reflection = _reflect_on_association(association_name)
309
+ reflection.autosave = true
310
+ define_autosave_validation_callbacks(reflection)
307
311
 
308
312
  nested_attributes_options = self.nested_attributes_options.dup
309
313
  nested_attributes_options[association_name.to_sym] = options
@@ -331,7 +335,7 @@ module ActiveRecord
331
335
  # the helper methods defined below. Makes it seem like the nested
332
336
  # associations are just regular associations.
333
337
  def generate_association_writer(association_name, type)
334
- generated_feature_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1
338
+ generated_association_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1
335
339
  if method_defined?(:#{association_name}_attributes=)
336
340
  remove_method(:#{association_name}_attributes=)
337
341
  end
@@ -461,19 +465,17 @@ module ActiveRecord
461
465
  association.build(attributes.except(*UNASSIGNABLE_KEYS))
462
466
  end
463
467
  elsif existing_record = existing_records.detect { |record| record.id.to_s == attributes['id'].to_s }
464
- unless association.loaded? || call_reject_if(association_name, attributes)
468
+ unless call_reject_if(association_name, attributes)
465
469
  # Make sure we are operating on the actual object which is in the association's
466
470
  # proxy_target array (either by finding it, or adding it if not found)
467
- target_record = association.target.detect { |record| record == existing_record }
468
-
471
+ # Take into account that the proxy_target may have changed due to callbacks
472
+ target_record = association.target.detect { |record| record.id.to_s == attributes['id'].to_s }
469
473
  if target_record
470
474
  existing_record = target_record
471
475
  else
472
- association.add_to_target(existing_record)
476
+ association.add_to_target(existing_record, :skip_callbacks)
473
477
  end
474
- end
475
478
 
476
- if !call_reject_if(association_name, attributes)
477
479
  assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy])
478
480
  end
479
481
  else
@@ -521,7 +523,7 @@ module ActiveRecord
521
523
  # has_destroy_flag? or if a <tt>:reject_if</tt> proc exists for this
522
524
  # association and evaluates to +true+.
523
525
  def reject_new_record?(association_name, attributes)
524
- has_destroy_flag?(attributes) || call_reject_if(association_name, attributes)
526
+ will_be_destroyed?(association_name, attributes) || call_reject_if(association_name, attributes)
525
527
  end
526
528
 
527
529
  # Determines if a record with the particular +attributes+ should be
@@ -530,7 +532,8 @@ module ActiveRecord
530
532
  #
531
533
  # Returns false if there is a +destroy_flag+ on the attributes.
532
534
  def call_reject_if(association_name, attributes)
533
- return false if has_destroy_flag?(attributes)
535
+ return false if will_be_destroyed?(association_name, attributes)
536
+
534
537
  case callback = self.nested_attributes_options[association_name][:reject_if]
535
538
  when Symbol
536
539
  method(callback).arity == 0 ? send(callback) : send(callback, attributes)
@@ -539,8 +542,17 @@ module ActiveRecord
539
542
  end
540
543
  end
541
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
+
542
554
  def raise_nested_attributes_record_not_found!(association_name, record_id)
543
- 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}"
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}"
544
556
  end
545
557
  end
546
558
  end
@@ -0,0 +1,52 @@
1
+ module ActiveRecord
2
+ # = Active Record No Touching
3
+ module NoTouching
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ # Lets you selectively disable calls to `touch` for the
8
+ # duration of a block.
9
+ #
10
+ # ==== Examples
11
+ # ActiveRecord::Base.no_touching do
12
+ # Project.first.touch # does nothing
13
+ # Message.first.touch # does nothing
14
+ # end
15
+ #
16
+ # Project.no_touching do
17
+ # Project.first.touch # does nothing
18
+ # Message.first.touch # works, but does not touch the associated project
19
+ # end
20
+ #
21
+ def no_touching(&block)
22
+ NoTouching.apply_to(self, &block)
23
+ end
24
+ end
25
+
26
+ class << self
27
+ def apply_to(klass) #:nodoc:
28
+ klasses.push(klass)
29
+ yield
30
+ ensure
31
+ klasses.pop
32
+ end
33
+
34
+ def applied_to?(klass) #:nodoc:
35
+ klasses.any? { |k| k >= klass }
36
+ end
37
+
38
+ private
39
+ def klasses
40
+ Thread.current[:no_touching_classes] ||= []
41
+ end
42
+ end
43
+
44
+ def no_touching?
45
+ NoTouching.applied_to?(self.class)
46
+ end
47
+
48
+ def touch(*)
49
+ super unless no_touching?
50
+ end
51
+ end
52
+ end
@@ -23,7 +23,7 @@ module ActiveRecord
23
23
  end
24
24
 
25
25
  def size
26
- 0
26
+ calculate :size, nil
27
27
  end
28
28
 
29
29
  def empty?
@@ -39,19 +39,39 @@ module ActiveRecord
39
39
  end
40
40
 
41
41
  def to_sql
42
- @to_sql ||= ""
42
+ ""
43
43
  end
44
44
 
45
45
  def count(*)
46
- 0
46
+ calculate :count, nil
47
47
  end
48
48
 
49
49
  def sum(*)
50
- 0
50
+ calculate :sum, nil
51
+ end
52
+
53
+ def average(*)
54
+ calculate :average, nil
55
+ end
56
+
57
+ def minimum(*)
58
+ calculate :minimum, nil
59
+ end
60
+
61
+ def maximum(*)
62
+ calculate :maximum, nil
51
63
  end
52
64
 
53
- def calculate(_operation, _column_name, _options = {})
54
- nil
65
+ def calculate(operation, _column_name, _options = {})
66
+ # TODO: Remove _options argument as soon we remove support to
67
+ # activerecord-deprecated_finders.
68
+ if [:count, :sum, :size].include? operation
69
+ group_values.any? ? Hash.new : 0
70
+ elsif [:average, :minimum, :maximum].include?(operation) && group_values.any?
71
+ Hash.new
72
+ else
73
+ nil
74
+ end
55
75
  end
56
76
 
57
77
  def exists?(_id = false)