activerecord 4.0.13 → 4.1.0.beta1

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 (135) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +745 -2700
  3. data/README.rdoc +2 -2
  4. data/examples/performance.rb +30 -18
  5. data/examples/simple.rb +4 -4
  6. data/lib/active_record.rb +2 -6
  7. data/lib/active_record/aggregations.rb +2 -1
  8. data/lib/active_record/association_relation.rb +0 -4
  9. data/lib/active_record/associations.rb +87 -43
  10. data/lib/active_record/associations/alias_tracker.rb +1 -3
  11. data/lib/active_record/associations/association.rb +8 -16
  12. data/lib/active_record/associations/association_scope.rb +5 -16
  13. data/lib/active_record/associations/belongs_to_association.rb +34 -25
  14. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  15. data/lib/active_record/associations/builder/association.rb +78 -54
  16. data/lib/active_record/associations/builder/belongs_to.rb +91 -58
  17. data/lib/active_record/associations/builder/collection_association.rb +47 -45
  18. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -25
  19. data/lib/active_record/associations/builder/has_many.rb +2 -2
  20. data/lib/active_record/associations/builder/has_one.rb +5 -7
  21. data/lib/active_record/associations/builder/singular_association.rb +6 -7
  22. data/lib/active_record/associations/collection_association.rb +68 -105
  23. data/lib/active_record/associations/collection_proxy.rb +12 -15
  24. data/lib/active_record/associations/has_many_association.rb +11 -9
  25. data/lib/active_record/associations/has_many_through_association.rb +16 -12
  26. data/lib/active_record/associations/has_one_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +204 -165
  28. data/lib/active_record/associations/join_dependency/join_association.rb +43 -101
  29. data/lib/active_record/associations/join_dependency/join_base.rb +6 -8
  30. data/lib/active_record/associations/join_dependency/join_part.rb +18 -37
  31. data/lib/active_record/associations/join_helper.rb +2 -11
  32. data/lib/active_record/associations/preloader.rb +89 -34
  33. data/lib/active_record/associations/preloader/association.rb +43 -25
  34. data/lib/active_record/associations/preloader/collection_association.rb +2 -2
  35. data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +58 -26
  38. data/lib/active_record/associations/singular_association.rb +6 -5
  39. data/lib/active_record/associations/through_association.rb +2 -2
  40. data/lib/active_record/attribute_assignment.rb +5 -2
  41. data/lib/active_record/attribute_methods.rb +45 -40
  42. data/lib/active_record/attribute_methods/before_type_cast.rb +2 -1
  43. data/lib/active_record/attribute_methods/dirty.rb +8 -22
  44. data/lib/active_record/attribute_methods/primary_key.rb +1 -7
  45. data/lib/active_record/attribute_methods/read.rb +55 -28
  46. data/lib/active_record/attribute_methods/serialization.rb +12 -33
  47. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -13
  48. data/lib/active_record/attribute_methods/write.rb +37 -12
  49. data/lib/active_record/autosave_association.rb +207 -207
  50. data/lib/active_record/base.rb +5 -1
  51. data/lib/active_record/callbacks.rb +2 -2
  52. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +2 -7
  53. data/lib/active_record/connection_adapters/abstract/database_statements.rb +11 -22
  54. data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -14
  55. data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -5
  56. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  57. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +84 -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 +52 -83
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +0 -5
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +14 -97
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +58 -60
  63. data/lib/active_record/connection_adapters/column.rb +1 -35
  64. data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +3 -4
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +16 -15
  67. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +24 -18
  68. data/lib/active_record/connection_adapters/postgresql/cast.rb +20 -16
  69. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +23 -43
  70. data/lib/active_record/connection_adapters/postgresql/oid.rb +19 -12
  71. data/lib/active_record/connection_adapters/postgresql/quoting.rb +28 -23
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +8 -30
  73. data/lib/active_record/connection_adapters/postgresql_adapter.rb +92 -75
  74. data/lib/active_record/connection_adapters/schema_cache.rb +8 -29
  75. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +31 -64
  76. data/lib/active_record/connection_handling.rb +2 -2
  77. data/lib/active_record/core.rb +22 -43
  78. data/lib/active_record/counter_cache.rb +7 -7
  79. data/lib/active_record/enum.rb +100 -0
  80. data/lib/active_record/errors.rb +10 -5
  81. data/lib/active_record/fixture_set/file.rb +2 -1
  82. data/lib/active_record/fixtures.rb +171 -74
  83. data/lib/active_record/inheritance.rb +16 -22
  84. data/lib/active_record/integration.rb +52 -1
  85. data/lib/active_record/locking/optimistic.rb +7 -2
  86. data/lib/active_record/locking/pessimistic.rb +1 -1
  87. data/lib/active_record/log_subscriber.rb +5 -12
  88. data/lib/active_record/migration.rb +62 -46
  89. data/lib/active_record/migration/command_recorder.rb +7 -13
  90. data/lib/active_record/model_schema.rb +7 -14
  91. data/lib/active_record/nested_attributes.rb +10 -8
  92. data/lib/active_record/no_touching.rb +52 -0
  93. data/lib/active_record/null_relation.rb +3 -3
  94. data/lib/active_record/persistence.rb +16 -34
  95. data/lib/active_record/querying.rb +14 -12
  96. data/lib/active_record/railtie.rb +0 -50
  97. data/lib/active_record/railties/databases.rake +12 -15
  98. data/lib/active_record/readonly_attributes.rb +0 -6
  99. data/lib/active_record/reflection.rb +189 -75
  100. data/lib/active_record/relation.rb +69 -94
  101. data/lib/active_record/relation/batches.rb +57 -23
  102. data/lib/active_record/relation/calculations.rb +36 -43
  103. data/lib/active_record/relation/delegation.rb +54 -39
  104. data/lib/active_record/relation/finder_methods.rb +107 -62
  105. data/lib/active_record/relation/merger.rb +7 -20
  106. data/lib/active_record/relation/predicate_builder.rb +57 -38
  107. data/lib/active_record/relation/predicate_builder/array_handler.rb +29 -0
  108. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  109. data/lib/active_record/relation/query_methods.rb +110 -98
  110. data/lib/active_record/relation/spawn_methods.rb +1 -2
  111. data/lib/active_record/result.rb +45 -6
  112. data/lib/active_record/runtime_registry.rb +5 -0
  113. data/lib/active_record/sanitization.rb +6 -8
  114. data/lib/active_record/schema_dumper.rb +16 -5
  115. data/lib/active_record/schema_migration.rb +24 -25
  116. data/lib/active_record/scoping/default.rb +5 -18
  117. data/lib/active_record/scoping/named.rb +8 -29
  118. data/lib/active_record/store.rb +56 -28
  119. data/lib/active_record/tasks/database_tasks.rb +8 -4
  120. data/lib/active_record/timestamp.rb +4 -4
  121. data/lib/active_record/transactions.rb +8 -10
  122. data/lib/active_record/validations/presence.rb +1 -1
  123. data/lib/active_record/validations/uniqueness.rb +1 -6
  124. data/lib/active_record/version.rb +1 -1
  125. data/lib/rails/generators/active_record.rb +2 -8
  126. data/lib/rails/generators/active_record/migration.rb +18 -0
  127. data/lib/rails/generators/active_record/migration/migration_generator.rb +4 -0
  128. data/lib/rails/generators/active_record/model/model_generator.rb +4 -0
  129. metadata +32 -45
  130. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -65
  131. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  132. data/lib/active_record/tasks/firebird_database_tasks.rb +0 -56
  133. data/lib/active_record/tasks/oracle_database_tasks.rb +0 -45
  134. data/lib/active_record/tasks/sqlserver_database_tasks.rb +0 -48
  135. data/lib/active_record/test_case.rb +0 -102
@@ -175,7 +175,7 @@ by relying on a number of conventions that make it easy for Active Record to inf
175
175
  complex relations and structures from a minimal amount of explicit direction.
176
176
 
177
177
  Convention over Configuration:
178
- * No XML-files!
178
+ * No XML files!
179
179
  * Lots of reflection and run-time extension
180
180
  * Magic is not inherently a bad word
181
181
 
@@ -192,7 +192,7 @@ The latest version of Active Record can be installed with RubyGems:
192
192
 
193
193
  Source code can be downloaded as part of the Rails project on GitHub:
194
194
 
195
- * https://github.com/rails/rails/tree/4-0-stable/activerecord
195
+ * https://github.com/rails/rails/tree/master/activerecord
196
196
 
197
197
 
198
198
  == License
@@ -5,12 +5,12 @@ require 'benchmark/ips'
5
5
  TIME = (ENV['BENCHMARK_TIME'] || 20).to_i
6
6
  RECORDS = (ENV['BENCHMARK_RECORDS'] || TIME*1000).to_i
7
7
 
8
- conn = { :adapter => 'sqlite3', :database => ':memory:' }
8
+ conn = { adapter: 'sqlite3', database: ':memory:' }
9
9
 
10
10
  ActiveRecord::Base.establish_connection(conn)
11
11
 
12
12
  class User < ActiveRecord::Base
13
- connection.create_table :users, :force => true do |t|
13
+ connection.create_table :users, force: true do |t|
14
14
  t.string :name, :email
15
15
  t.timestamps
16
16
  end
@@ -19,7 +19,7 @@ class User < ActiveRecord::Base
19
19
  end
20
20
 
21
21
  class Exhibit < ActiveRecord::Base
22
- connection.create_table :exhibits, :force => true do |t|
22
+ connection.create_table :exhibits, force: true do |t|
23
23
  t.belongs_to :user
24
24
  t.string :name
25
25
  t.text :notes
@@ -43,6 +43,8 @@ class Exhibit < ActiveRecord::Base
43
43
  def self.feel(exhibits) exhibits.each { |e| e.feel } end
44
44
  end
45
45
 
46
+ def progress_bar(int); print "." if (int%100).zero? ; end
47
+
46
48
  puts 'Generating data...'
47
49
 
48
50
  module ActiveRecord
@@ -75,30 +77,32 @@ notes = ActiveRecord::Faker::LOREM.join ' '
75
77
  today = Date.today
76
78
 
77
79
  puts "Inserting #{RECORDS} users and exhibits..."
78
- RECORDS.times do
80
+ RECORDS.times do |record|
79
81
  user = User.create(
80
- :created_at => today,
81
- :name => ActiveRecord::Faker.name,
82
- :email => ActiveRecord::Faker.email
82
+ created_at: today,
83
+ name: ActiveRecord::Faker.name,
84
+ email: ActiveRecord::Faker.email
83
85
  )
84
86
 
85
87
  Exhibit.create(
86
- :created_at => today,
87
- :name => ActiveRecord::Faker.name,
88
- :user => user,
89
- :notes => notes
88
+ created_at: today,
89
+ name: ActiveRecord::Faker.name,
90
+ user: user,
91
+ notes: notes
90
92
  )
93
+ progress_bar(record)
91
94
  end
95
+ puts "Done!\n"
92
96
 
93
97
  Benchmark.ips(TIME) do |x|
94
98
  ar_obj = Exhibit.find(1)
95
- attrs = { :name => 'sam' }
96
- attrs_first = { :name => 'sam' }
97
- attrs_second = { :name => 'tom' }
99
+ attrs = { name: 'sam' }
100
+ attrs_first = { name: 'sam' }
101
+ attrs_second = { name: 'tom' }
98
102
  exhibit = {
99
- :name => ActiveRecord::Faker.name,
100
- :notes => notes,
101
- :created_at => Date.today
103
+ name: ActiveRecord::Faker.name,
104
+ notes: notes,
105
+ created_at: Date.today
102
106
  }
103
107
 
104
108
  x.report("Model#id") do
@@ -117,10 +121,18 @@ Benchmark.ips(TIME) do |x|
117
121
  Exhibit.first.look
118
122
  end
119
123
 
124
+ x.report 'Model.take' do
125
+ Exhibit.take
126
+ end
127
+
120
128
  x.report("Model.all limit(100)") do
121
129
  Exhibit.look Exhibit.limit(100)
122
130
  end
123
131
 
132
+ x.report("Model.all take(100)") do
133
+ Exhibit.look Exhibit.take(100)
134
+ end
135
+
124
136
  x.report "Model.all limit(100) with relationship" do
125
137
  Exhibit.feel Exhibit.limit(100).includes(:user)
126
138
  end
@@ -167,6 +179,6 @@ Benchmark.ips(TIME) do |x|
167
179
  end
168
180
 
169
181
  x.report "AR.execute(query)" do
170
- ActiveRecord::Base.connection.execute("Select * from exhibits where id = #{(rand * 1000 + 1).to_i}")
182
+ ActiveRecord::Base.connection.execute("SELECT * FROM exhibits WHERE id = #{(rand * 1000 + 1).to_i}")
171
183
  end
172
184
  end
@@ -1,14 +1,14 @@
1
- $LOAD_PATH.unshift "#{File.dirname(__FILE__)}/../lib"
1
+ require File.expand_path('../../../load_paths', __FILE__)
2
2
  require 'active_record'
3
3
 
4
4
  class Person < ActiveRecord::Base
5
- establish_connection :adapter => 'sqlite3', :database => 'foobar.db'
6
- connection.create_table table_name, :force => true do |t|
5
+ establish_connection adapter: 'sqlite3', database: 'foobar.db'
6
+ connection.create_table table_name, force: true do |t|
7
7
  t.string :name
8
8
  end
9
9
  end
10
10
 
11
- bob = Person.create!(:name => 'bob')
11
+ bob = Person.create!(name: 'bob')
12
12
  puts Person.all.inspect
13
13
  bob.destroy
14
14
  puts Person.all.inspect
@@ -25,7 +25,6 @@ require 'active_support'
25
25
  require 'active_support/rails'
26
26
  require 'active_model'
27
27
  require 'arel'
28
- require 'active_record/deprecated_finders'
29
28
 
30
29
  require 'active_record/version'
31
30
 
@@ -38,6 +37,7 @@ module ActiveRecord
38
37
  autoload :ConnectionHandling
39
38
  autoload :CounterCache
40
39
  autoload :DynamicMatchers
40
+ autoload :Enum
41
41
  autoload :Explain
42
42
  autoload :Inheritance
43
43
  autoload :Integration
@@ -45,6 +45,7 @@ module ActiveRecord
45
45
  autoload :Migrator, 'active_record/migration'
46
46
  autoload :ModelSchema
47
47
  autoload :NestedAttributes
48
+ autoload :NoTouching
48
49
  autoload :Persistence
49
50
  autoload :QueryCache
50
51
  autoload :Querying
@@ -146,13 +147,8 @@ module ActiveRecord
146
147
  autoload :MySQLDatabaseTasks, 'active_record/tasks/mysql_database_tasks'
147
148
  autoload :PostgreSQLDatabaseTasks,
148
149
  'active_record/tasks/postgresql_database_tasks'
149
-
150
- autoload :FirebirdDatabaseTasks, 'active_record/tasks/firebird_database_tasks'
151
- autoload :SqlserverDatabaseTasks, 'active_record/tasks/sqlserver_database_tasks'
152
- autoload :OracleDatabaseTasks, 'active_record/tasks/oracle_database_tasks'
153
150
  end
154
151
 
155
- autoload :TestCase
156
152
  autoload :TestFixtures, 'active_record/fixtures'
157
153
 
158
154
  def self.eager_load!
@@ -223,7 +223,8 @@ module ActiveRecord
223
223
  reader_method(name, class_name, mapping, allow_nil, constructor)
224
224
  writer_method(name, class_name, mapping, allow_nil, converter)
225
225
 
226
- create_reflection(:composed_of, part_id, nil, options, self)
226
+ reflection = ActiveRecord::Reflection.create(:composed_of, part_id, nil, options, self)
227
+ Reflection.add_aggregate_reflection self, part_id, reflection
227
228
  end
228
229
 
229
230
  private
@@ -9,10 +9,6 @@ module ActiveRecord
9
9
  @association
10
10
  end
11
11
 
12
- def ==(other)
13
- other == to_a
14
- end
15
-
16
12
  private
17
13
 
18
14
  def exec_queries
@@ -4,12 +4,6 @@ require 'active_support/core_ext/module/remove_method'
4
4
  require 'active_record/errors'
5
5
 
6
6
  module ActiveRecord
7
- class AssociationNotFoundError < ConfigurationError #:nodoc:
8
- def initialize(record, association_name)
9
- super("Association named '#{association_name}' was not found on #{record.class.name}; perhaps you misspelled it?")
10
- end
11
- end
12
-
13
7
  class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
14
8
  def initialize(reflection, associated_class = nil)
15
9
  super("Could not find the inverse association for #{reflection.name} (#{reflection.options[:inverse_of].inspect} in #{associated_class.nil? ? reflection.class_name : associated_class.name})")
@@ -79,12 +73,6 @@ module ActiveRecord
79
73
  end
80
74
  end
81
75
 
82
- class HasAndBelongsToManyAssociationForeignKeyNeeded < ActiveRecordError #:nodoc:
83
- def initialize(reflection)
84
- super("Cannot create self referential has_and_belongs_to_many association on '#{reflection.class_name rescue nil}##{reflection.name rescue nil}'. :association_foreign_key cannot be the same as the :foreign_key.")
85
- end
86
- end
87
-
88
76
  class EagerLoadPolymorphicError < ActiveRecordError #:nodoc:
89
77
  def initialize(reflection)
90
78
  super("Can not eagerly load the polymorphic association #{reflection.name.inspect}")
@@ -120,7 +108,6 @@ module ActiveRecord
120
108
 
121
109
  autoload :BelongsToAssociation, 'active_record/associations/belongs_to_association'
122
110
  autoload :BelongsToPolymorphicAssociation, 'active_record/associations/belongs_to_polymorphic_association'
123
- autoload :HasAndBelongsToManyAssociation, 'active_record/associations/has_and_belongs_to_many_association'
124
111
  autoload :HasManyAssociation, 'active_record/associations/has_many_association'
125
112
  autoload :HasManyThroughAssociation, 'active_record/associations/has_many_through_association'
126
113
  autoload :HasOneAssociation, 'active_record/associations/has_one_association'
@@ -159,7 +146,7 @@ module ActiveRecord
159
146
  association = association_instance_get(name)
160
147
 
161
148
  if association.nil?
162
- raise AssociationNotFoundError.new(self, name) unless reflection = self.class.reflect_on_association(name)
149
+ reflection = self.class.reflect_on_association(name)
163
150
  association = reflection.association_class.new(self, reflection)
164
151
  association_instance_set(name, association)
165
152
  end
@@ -170,7 +157,7 @@ module ActiveRecord
170
157
  private
171
158
  # Returns the specified association instance if it responds to :loaded?, nil otherwise.
172
159
  def association_instance_get(name)
173
- @association_cache[name.to_sym]
160
+ @association_cache[name]
174
161
  end
175
162
 
176
163
  # Set the specified association instance.
@@ -178,7 +165,7 @@ module ActiveRecord
178
165
  @association_cache[name] = association
179
166
  end
180
167
 
181
- # Associations are a set of macro-like class methods for tying objects together through
168
+ # \Associations are a set of macro-like class methods for tying objects together through
182
169
  # foreign keys. They express relationships like "Project has one Project Manager"
183
170
  # or "Project belongs to a Portfolio". Each macro adds a number of methods to the
184
171
  # class which are specialized according to the collection or association symbol and the
@@ -371,11 +358,11 @@ module ActiveRecord
371
358
  # there is some special behavior you should be aware of, mostly involving the saving of
372
359
  # associated objects.
373
360
  #
374
- # You can set the :autosave option on a <tt>has_one</tt>, <tt>belongs_to</tt>,
361
+ # You can set the <tt>:autosave</tt> option on a <tt>has_one</tt>, <tt>belongs_to</tt>,
375
362
  # <tt>has_many</tt>, or <tt>has_and_belongs_to_many</tt> association. Setting it
376
363
  # to +true+ will _always_ save the members, whereas setting it to +false+ will
377
- # _never_ save the members. More details about :autosave option is available at
378
- # autosave_association.rb .
364
+ # _never_ save the members. More details about <tt>:autosave</tt> option is available at
365
+ # AutosaveAssociation.
379
366
  #
380
367
  # === One-to-one associations
381
368
  #
@@ -408,7 +395,7 @@ module ActiveRecord
408
395
  #
409
396
  # == Customizing the query
410
397
  #
411
- # Associations are built from <tt>Relation</tt>s, and you can use the <tt>Relation</tt> syntax
398
+ # \Associations are built from <tt>Relation</tt>s, and you can use the <tt>Relation</tt> syntax
412
399
  # to customize them. For example, to add a condition:
413
400
  #
414
401
  # class Blog < ActiveRecord::Base
@@ -574,6 +561,8 @@ module ActiveRecord
574
561
  # @group.avatars << Avatar.new # this would work if User belonged_to Avatar rather than the other way around
575
562
  # @group.avatars.delete(@group.avatars.last) # so would this
576
563
  #
564
+ # == Setting Inverses
565
+ #
577
566
  # If you are using a +belongs_to+ on the join model, it is a good idea to set the
578
567
  # <tt>:inverse_of</tt> option on the +belongs_to+, which will mean that the following example
579
568
  # works correctly (where <tt>tags</tt> is a +has_many+ <tt>:through</tt> association):
@@ -590,7 +579,27 @@ module ActiveRecord
590
579
  # belongs_to :tag, inverse_of: :taggings
591
580
  # end
592
581
  #
593
- # == Nested Associations
582
+ # If you do not set the <tt>:inverse_of</tt> record, the association will
583
+ # do its best to match itself up with the correct inverse. Automatic
584
+ # inverse detection only works on <tt>has_many</tt>, <tt>has_one</tt>, and
585
+ # <tt>belongs_to</tt> associations.
586
+ #
587
+ # Extra options on the associations, as defined in the
588
+ # <tt>AssociationReflection::INVALID_AUTOMATIC_INVERSE_OPTIONS</tt> constant, will
589
+ # also prevent the association's inverse from being found automatically.
590
+ #
591
+ # The automatic guessing of the inverse association uses a heuristic based
592
+ # on the name of the class, so it may not work for all associations,
593
+ # especially the ones with non-standard names.
594
+ #
595
+ # You can turn off the automatic detection of inverse associations by setting
596
+ # the <tt>:inverse_of</tt> option to <tt>false</tt> like so:
597
+ #
598
+ # class Taggable < ActiveRecord::Base
599
+ # belongs_to :tag, inverse_of: false
600
+ # end
601
+ #
602
+ # == Nested \Associations
594
603
  #
595
604
  # You can actually specify *any* association with the <tt>:through</tt> option, including an
596
605
  # association which has a <tt>:through</tt> option itself. For example:
@@ -633,7 +642,7 @@ module ActiveRecord
633
642
  # add a <tt>Commenter</tt> in the example above, there would be no way to tell how to set up the
634
643
  # intermediate <tt>Post</tt> and <tt>Comment</tt> objects.
635
644
  #
636
- # == Polymorphic Associations
645
+ # == Polymorphic \Associations
637
646
  #
638
647
  # Polymorphic associations on models are not restricted on what types of models they
639
648
  # can be associated with. Rather, they specify an interface that a +has_many+ association
@@ -663,8 +672,8 @@ module ActiveRecord
663
672
  # class Asset < ActiveRecord::Base
664
673
  # belongs_to :attachable, polymorphic: true
665
674
  #
666
- # def attachable_type=(klass)
667
- # super(klass.to_s.classify.constantize.base_class.to_s)
675
+ # def attachable_type=(sType)
676
+ # super(sType.to_s.classify.constantize.base_class.to_s)
668
677
  # end
669
678
  # end
670
679
  #
@@ -795,7 +804,7 @@ module ActiveRecord
795
804
  # For example if all the addressables are either of class Person or Company then a total
796
805
  # of 3 queries will be executed. The list of addressable types to load is determined on
797
806
  # the back of the addresses loaded. This is not supported if Active Record has to fallback
798
- # to the previous implementation of eager loading and will raise ActiveRecord::EagerLoadPolymorphicError.
807
+ # to the previous implementation of eager loading and will raise <tt>ActiveRecord::EagerLoadPolymorphicError</tt>.
799
808
  # The reason is that the parent model's type is a column value so its corresponding table
800
809
  # name cannot be put in the +FROM+/+JOIN+ clauses of that query.
801
810
  #
@@ -1030,7 +1039,7 @@ module ActiveRecord
1030
1039
  # An empty array is returned if none are found.
1031
1040
  # [collection<<(object, ...)]
1032
1041
  # Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
1033
- # Note that this operation instantly fires update sql without waiting for the save or update call on the
1042
+ # Note that this operation instantly fires update SQL without waiting for the save or update call on the
1034
1043
  # parent object, unless the parent object is a new record.
1035
1044
  # [collection.delete(object, ...)]
1036
1045
  # Removes one or more objects from the collection by setting their foreign keys to +NULL+.
@@ -1066,10 +1075,10 @@ module ActiveRecord
1066
1075
  # [collection.size]
1067
1076
  # Returns the number of associated objects.
1068
1077
  # [collection.find(...)]
1069
- # Finds an associated object according to the same rules as ActiveRecord::Base.find.
1078
+ # Finds an associated object according to the same rules as <tt>ActiveRecord::Base.find</tt>.
1070
1079
  # [collection.exists?(...)]
1071
1080
  # Checks whether an associated object with the given conditions exists.
1072
- # Uses the same rules as ActiveRecord::Base.exists?.
1081
+ # Uses the same rules as <tt>ActiveRecord::Base.exists?</tt>.
1073
1082
  # [collection.build(attributes = {}, ...)]
1074
1083
  # Returns one or more new objects of the collection type that have been instantiated
1075
1084
  # with +attributes+ and linked to this object through a foreign key, but have not yet
@@ -1088,7 +1097,7 @@ module ActiveRecord
1088
1097
  #
1089
1098
  # === Example
1090
1099
  #
1091
- # Example: A Firm class declares <tt>has_many :clients</tt>, which will add:
1100
+ # A <tt>Firm</tt> class declares <tt>has_many :clients</tt>, which will add:
1092
1101
  # * <tt>Firm#clients</tt> (similar to <tt>Client.where(firm_id: id)</tt>)
1093
1102
  # * <tt>Firm#clients<<</tt>
1094
1103
  # * <tt>Firm#clients.delete</tt>
@@ -1122,8 +1131,8 @@ module ActiveRecord
1122
1131
  # Controls what happens to the associated objects when
1123
1132
  # their owner is destroyed. Note that these are implemented as
1124
1133
  # callbacks, and Rails executes callbacks in order. Therefore, other
1125
- # similar callbacks may affect the :dependent behavior, and the
1126
- # :dependent behavior may affect other callbacks.
1134
+ # similar callbacks may affect the <tt>:dependent</tt> behavior, and the
1135
+ # <tt>:dependent</tt> behavior may affect other callbacks.
1127
1136
  #
1128
1137
  # * <tt>:destroy</tt> causes all the associated objects to also be destroyed.
1129
1138
  # * <tt>:delete_all</tt> causes all the associated objects to be deleted directly from the database (so callbacks will not be executed).
@@ -1169,8 +1178,8 @@ module ActiveRecord
1169
1178
  # If true, always save the associated objects or destroy them if marked for destruction,
1170
1179
  # when saving the parent object. If false, never save or destroy the associated objects.
1171
1180
  # By default, only save associated objects that are new records. This option is implemented as a
1172
- # before_save callback. Because callbacks are run in the order they are defined, associated objects
1173
- # may need to be explicitly saved in any user-defined before_save callbacks.
1181
+ # +before_save+ callback. Because callbacks are run in the order they are defined, associated objects
1182
+ # may need to be explicitly saved in any user-defined +before_save+ callbacks.
1174
1183
  #
1175
1184
  # Note that <tt>accepts_nested_attributes_for</tt> sets <tt>:autosave</tt> to <tt>true</tt>.
1176
1185
  # [:inverse_of]
@@ -1189,13 +1198,14 @@ module ActiveRecord
1189
1198
  # has_many :reports, -> { readonly }
1190
1199
  # has_many :subscribers, through: :subscriptions, source: :user
1191
1200
  def has_many(name, scope = nil, options = {}, &extension)
1192
- Builder::HasMany.build(self, name, scope, options, &extension)
1201
+ reflection = Builder::HasMany.build(self, name, scope, options, &extension)
1202
+ Reflection.add_reflection self, name, reflection
1193
1203
  end
1194
1204
 
1195
1205
  # Specifies a one-to-one association with another class. This method should only be used
1196
1206
  # if the other class contains the foreign key. If the current class contains the foreign key,
1197
1207
  # then you should use +belongs_to+ instead. See also ActiveRecord::Associations::ClassMethods's overview
1198
- # on when to use has_one and when to use belongs_to.
1208
+ # on when to use +has_one+ and when to use +belongs_to+.
1199
1209
  #
1200
1210
  # The following methods for retrieval and query of a single associated object will be added:
1201
1211
  #
@@ -1292,7 +1302,8 @@ module ActiveRecord
1292
1302
  # has_one :club, through: :membership
1293
1303
  # has_one :primary_address, -> { where primary: true }, through: :addressables, source: :addressable
1294
1304
  def has_one(name, scope = nil, options = {})
1295
- Builder::HasOne.build(self, name, scope, options)
1305
+ reflection = Builder::HasOne.build(self, name, scope, options)
1306
+ Reflection.add_reflection self, name, reflection
1296
1307
  end
1297
1308
 
1298
1309
  # Specifies a one-to-one association with another class. This method should only be used
@@ -1363,7 +1374,7 @@ module ActiveRecord
1363
1374
  # class is created and decremented when it's destroyed. This requires that a column
1364
1375
  # named <tt>#{table_name}_count</tt> (such as +comments_count+ for a belonging Comment class)
1365
1376
  # is used on the associate class (such as a Post class) - that is the migration for
1366
- # <tt>#{table_name}_count</tt> is created on the associate class (such that Post.comments_count will
1377
+ # <tt>#{table_name}_count</tt> is created on the associate class (such that <tt>Post.comments_count</tt> will
1367
1378
  # return the count cached, see note below). You can also specify a custom counter
1368
1379
  # cache column by providing a column name instead of a +true+/+false+ value to this
1369
1380
  # option (e.g., <tt>counter_cache: :my_custom_counter</tt>.)
@@ -1396,7 +1407,7 @@ module ActiveRecord
1396
1407
  # belongs_to :firm, foreign_key: "client_of"
1397
1408
  # belongs_to :person, primary_key: "name", foreign_key: "person_name"
1398
1409
  # belongs_to :author, class_name: "Person", foreign_key: "author_id"
1399
- # belongs_to :valid_coupon, ->(o) { where "discounts > ?", o.payments_count },
1410
+ # belongs_to :valid_coupon, ->(o) { where "discounts > #{o.payments_count}" },
1400
1411
  # class_name: "Coupon", foreign_key: "coupon_id"
1401
1412
  # belongs_to :attachable, polymorphic: true
1402
1413
  # belongs_to :project, readonly: true
@@ -1404,7 +1415,8 @@ module ActiveRecord
1404
1415
  # belongs_to :company, touch: true
1405
1416
  # belongs_to :company, touch: :employees_last_updated_at
1406
1417
  def belongs_to(name, scope = nil, options = {})
1407
- Builder::BelongsTo.build(self, name, scope, options)
1418
+ reflection = Builder::BelongsTo.build(self, name, scope, options)
1419
+ Reflection.add_reflection self, name, reflection
1408
1420
  end
1409
1421
 
1410
1422
  # Specifies a many-to-many relationship with another class. This associates two classes via an
@@ -1445,7 +1457,7 @@ module ActiveRecord
1445
1457
  # [collection<<(object, ...)]
1446
1458
  # Adds one or more objects to the collection by creating associations in the join table
1447
1459
  # (<tt>collection.push</tt> and <tt>collection.concat</tt> are aliases to this method).
1448
- # Note that this operation instantly fires update sql without waiting for the save or update call on the
1460
+ # Note that this operation instantly fires update SQL without waiting for the save or update call on the
1449
1461
  # parent object, unless the parent object is a new record.
1450
1462
  # [collection.delete(object, ...)]
1451
1463
  # Removes one or more objects from the collection by removing their associations from the join table.
@@ -1468,10 +1480,10 @@ module ActiveRecord
1468
1480
  # [collection.find(id)]
1469
1481
  # Finds an associated object responding to the +id+ and that
1470
1482
  # meets the condition that it has to be associated with this object.
1471
- # Uses the same rules as ActiveRecord::Base.find.
1483
+ # Uses the same rules as <tt>ActiveRecord::Base.find</tt>.
1472
1484
  # [collection.exists?(...)]
1473
1485
  # Checks whether an associated object with the given conditions exists.
1474
- # Uses the same rules as ActiveRecord::Base.exists?.
1486
+ # Uses the same rules as <tt>ActiveRecord::Base.exists?</tt>.
1475
1487
  # [collection.build(attributes = {})]
1476
1488
  # Returns a new object of the collection type that has been instantiated
1477
1489
  # with +attributes+ and linked to this object through the join table, but has not yet been saved.
@@ -1541,7 +1553,39 @@ module ActiveRecord
1541
1553
  # has_and_belongs_to_many :categories, join_table: "prods_cats"
1542
1554
  # has_and_belongs_to_many :categories, -> { readonly }
1543
1555
  def has_and_belongs_to_many(name, scope = nil, options = {}, &extension)
1544
- Builder::HasAndBelongsToMany.build(self, name, scope, options, &extension)
1556
+ if scope.is_a?(Hash)
1557
+ options = scope
1558
+ scope = nil
1559
+ end
1560
+
1561
+ builder = Builder::HasAndBelongsToMany.new name, self, options
1562
+
1563
+ join_model = builder.through_model
1564
+
1565
+ middle_reflection = builder.middle_reflection join_model
1566
+
1567
+ Builder::HasMany.define_callbacks self, middle_reflection
1568
+ Reflection.add_reflection self, middle_reflection.name, middle_reflection
1569
+
1570
+ include Module.new {
1571
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
1572
+ def destroy_associations
1573
+ association(:#{middle_reflection.name}).delete_all(:delete_all)
1574
+ association(:#{name}).reset
1575
+ super
1576
+ end
1577
+ RUBY
1578
+ }
1579
+
1580
+ hm_options = {}
1581
+ hm_options[:through] = middle_reflection.name
1582
+ hm_options[:source] = join_model.right_reflection.name
1583
+
1584
+ [:before_add, :after_add, :before_remove, :after_remove].each do |k|
1585
+ hm_options[k] = options[k] if options.key? k
1586
+ end
1587
+
1588
+ has_many name, scope, hm_options, &extension
1545
1589
  end
1546
1590
  end
1547
1591
  end