activerecord 3.0.0.beta4 → 3.0.0.rc

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 (69) hide show
  1. data/CHANGELOG +267 -254
  2. data/README.rdoc +222 -0
  3. data/examples/performance.rb +9 -9
  4. data/lib/active_record/aggregations.rb +3 -4
  5. data/lib/active_record/association_preload.rb +15 -10
  6. data/lib/active_record/associations.rb +54 -37
  7. data/lib/active_record/associations/association_collection.rb +43 -17
  8. data/lib/active_record/associations/association_proxy.rb +2 -0
  9. data/lib/active_record/associations/belongs_to_association.rb +1 -0
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -0
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +22 -7
  12. data/lib/active_record/associations/has_many_association.rb +6 -1
  13. data/lib/active_record/associations/has_many_through_association.rb +1 -0
  14. data/lib/active_record/associations/has_one_association.rb +1 -0
  15. data/lib/active_record/associations/has_one_through_association.rb +1 -0
  16. data/lib/active_record/associations/through_association_scope.rb +3 -2
  17. data/lib/active_record/attribute_methods.rb +1 -0
  18. data/lib/active_record/autosave_association.rb +4 -6
  19. data/lib/active_record/base.rb +106 -240
  20. data/lib/active_record/callbacks.rb +4 -25
  21. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +22 -29
  22. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +2 -8
  23. data/lib/active_record/connection_adapters/abstract/database_statements.rb +2 -2
  24. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +10 -0
  25. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +56 -7
  26. data/lib/active_record/connection_adapters/abstract_adapter.rb +10 -18
  27. data/lib/active_record/connection_adapters/mysql_adapter.rb +2 -2
  28. data/lib/active_record/connection_adapters/postgresql_adapter.rb +65 -69
  29. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +19 -6
  30. data/lib/active_record/connection_adapters/sqlite_adapter.rb +20 -46
  31. data/lib/active_record/counter_cache.rb +14 -4
  32. data/lib/active_record/dynamic_finder_match.rb +9 -0
  33. data/lib/active_record/dynamic_scope_match.rb +7 -0
  34. data/lib/active_record/errors.rb +3 -0
  35. data/lib/active_record/fixtures.rb +5 -6
  36. data/lib/active_record/locale/en.yml +1 -1
  37. data/lib/active_record/locking/optimistic.rb +1 -0
  38. data/lib/active_record/log_subscriber.rb +48 -0
  39. data/lib/active_record/migration.rb +64 -37
  40. data/lib/active_record/named_scope.rb +33 -19
  41. data/lib/active_record/nested_attributes.rb +17 -13
  42. data/lib/active_record/observer.rb +13 -6
  43. data/lib/active_record/persistence.rb +55 -22
  44. data/lib/active_record/query_cache.rb +1 -0
  45. data/lib/active_record/railtie.rb +14 -8
  46. data/lib/active_record/railties/controller_runtime.rb +2 -2
  47. data/lib/active_record/railties/databases.rake +63 -33
  48. data/lib/active_record/reflection.rb +46 -28
  49. data/lib/active_record/relation.rb +38 -24
  50. data/lib/active_record/relation/finder_methods.rb +5 -5
  51. data/lib/active_record/relation/predicate_builder.rb +2 -4
  52. data/lib/active_record/relation/query_methods.rb +134 -115
  53. data/lib/active_record/relation/spawn_methods.rb +1 -1
  54. data/lib/active_record/schema.rb +2 -0
  55. data/lib/active_record/schema_dumper.rb +15 -12
  56. data/lib/active_record/serialization.rb +2 -0
  57. data/lib/active_record/session_store.rb +93 -79
  58. data/lib/active_record/test_case.rb +3 -0
  59. data/lib/active_record/timestamp.rb +49 -29
  60. data/lib/active_record/transactions.rb +5 -2
  61. data/lib/active_record/validations.rb +5 -2
  62. data/lib/active_record/validations/associated.rb +1 -1
  63. data/lib/active_record/validations/uniqueness.rb +1 -1
  64. data/lib/active_record/version.rb +1 -1
  65. data/lib/rails/generators/active_record/migration/templates/migration.rb +12 -6
  66. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  67. metadata +27 -14
  68. data/README +0 -351
  69. data/lib/active_record/railties/log_subscriber.rb +0 -32
@@ -0,0 +1,222 @@
1
+ = Active Record -- Object-relational mapping put on rails
2
+
3
+ Active Record connects classes to relational database tables to establish an
4
+ almost zero-configuration persistence layer for applications. The library
5
+ provides a base class that, when subclassed, sets up a mapping between the new
6
+ class and an existing table in the database. In context of an application,
7
+ these classes are commonly referred to as *models*. Models can also be
8
+ connected to other models; this is done by defining *associations*.
9
+
10
+ Active Record relies heavily on naming in that it uses class and association
11
+ names to establish mappings between respective database tables and foreign key
12
+ columns. Although these mappings can be defined explicitly, it's recommended
13
+ to follow naming conventions, especially when getting started with the
14
+ library.
15
+
16
+ A short rundown of some of the major features:
17
+
18
+ * Automated mapping between classes and tables, attributes and columns.
19
+
20
+ class Product < ActiveRecord::Base
21
+ end
22
+
23
+ The Product class is automatically mapped to the table named "products",
24
+ which might look like this:
25
+
26
+ CREATE TABLE products (
27
+ id int(11) NOT NULL auto_increment,
28
+ name varchar(255),
29
+ PRIMARY KEY (id)
30
+ );
31
+
32
+ This would also define the following accessors: `Product#name` and
33
+ `Product#name=(new_name)`
34
+
35
+ {Learn more}[link:classes/ActiveRecord/Base.html]
36
+
37
+
38
+ * Associations between objects defined by simple class methods.
39
+
40
+ class Firm < ActiveRecord::Base
41
+ has_many :clients
42
+ has_one :account
43
+ belongs_to :conglomerate
44
+ end
45
+
46
+ {Learn more}[link:classes/ActiveRecord/Associations/ClassMethods.html]
47
+
48
+
49
+ * Aggregations of value objects.
50
+
51
+ class Account < ActiveRecord::Base
52
+ composed_of :balance, :class_name => "Money",
53
+ :mapping => %w(balance amount)
54
+ composed_of :address,
55
+ :mapping => [%w(address_street street), %w(address_city city)]
56
+ end
57
+
58
+ {Learn more}[link:classes/ActiveRecord/Aggregations/ClassMethods.html]
59
+
60
+
61
+ * Validation rules that can differ for new or existing objects.
62
+
63
+ class Account < ActiveRecord::Base
64
+ validates_presence_of :subdomain, :name, :email_address, :password
65
+ validates_uniqueness_of :subdomain
66
+ validates_acceptance_of :terms_of_service, :on => :create
67
+ validates_confirmation_of :password, :email_address, :on => :create
68
+ end
69
+
70
+ {Learn more}[link:classes/ActiveRecord/Validations.html]
71
+
72
+
73
+ * Callbacks available for the entire lifecycle (instantiation, saving, destroying, validating, etc.)
74
+
75
+ class Person < ActiveRecord::Base
76
+ before_destroy :invalidate_payment_plan
77
+ # the `invalidate_payment_plan` method gets called just before Person#destroy
78
+ end
79
+
80
+ {Learn more}[link:classes/ActiveRecord/Callbacks.html]
81
+
82
+
83
+ * Observers that react to changes in a model
84
+
85
+ class CommentObserver < ActiveRecord::Observer
86
+ def after_create(comment) # is called just after Comment#save
87
+ Notifications.deliver_new_comment("david@loudthinking.com", comment)
88
+ end
89
+ end
90
+
91
+ {Learn more}[link:classes/ActiveRecord/Observer.html]
92
+
93
+
94
+ * Inheritance hierarchies
95
+
96
+ class Company < ActiveRecord::Base; end
97
+ class Firm < Company; end
98
+ class Client < Company; end
99
+ class PriorityClient < Client; end
100
+
101
+ {Learn more}[link:classes/ActiveRecord/Base.html]
102
+
103
+
104
+ * Transactions
105
+
106
+ # Database transaction
107
+ Account.transaction do
108
+ david.withdrawal(100)
109
+ mary.deposit(100)
110
+ end
111
+
112
+ {Learn more}[link:classes/ActiveRecord/Transactions/ClassMethods.html]
113
+
114
+
115
+ * Reflections on columns, associations, and aggregations
116
+
117
+ reflection = Firm.reflect_on_association(:clients)
118
+ reflection.klass # => Client (class)
119
+ Firm.columns # Returns an array of column descriptors for the firms table
120
+
121
+ {Learn more}[link:classes/ActiveRecord/Reflection/ClassMethods.html]
122
+
123
+
124
+ * Database abstraction through simple adapters
125
+
126
+ # connect to SQLite3
127
+ ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => "dbfile.sqlite3")
128
+
129
+ # connect to MySQL with authentication
130
+ ActiveRecord::Base.establish_connection(
131
+ :adapter => "mysql",
132
+ :host => "localhost",
133
+ :username => "me",
134
+ :password => "secret",
135
+ :database => "activerecord"
136
+ )
137
+
138
+ {Learn more}[link:classes/ActiveRecord/Base.html] and read about the built-in support for
139
+ MySQL[link:classes/ActiveRecord/ConnectionAdapters/MysqlAdapter.html],
140
+ PostgreSQL[link:classes/ActiveRecord/ConnectionAdapters/PostgreSQLAdapter.html], and
141
+ SQLite3[link:classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html].
142
+
143
+
144
+ * Logging support for Log4r[http://log4r.sourceforge.net] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc]
145
+
146
+ ActiveRecord::Base.logger = Logger.new(STDOUT)
147
+ ActiveRecord::Base.logger = Log4r::Logger.new("Application Log")
148
+
149
+
150
+ * Database agnostic schema management with Migrations
151
+
152
+ class AddSystemSettings < ActiveRecord::Migration
153
+ def self.up
154
+ create_table :system_settings do |t|
155
+ t.string :name
156
+ t.string :label
157
+ t.text :value
158
+ t.string :type
159
+ t.integer :position
160
+ end
161
+
162
+ SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1
163
+ end
164
+
165
+ def self.down
166
+ drop_table :system_settings
167
+ end
168
+ end
169
+
170
+ {Learn more}[link:classes/ActiveRecord/Migration.html]
171
+
172
+
173
+ == Philosophy
174
+
175
+ Active Record is an implementation of the object-relational mapping (ORM)
176
+ pattern[http://www.martinfowler.com/eaaCatalog/activeRecord.html] by the same
177
+ name described by Martin Fowler:
178
+
179
+ "An object that wraps a row in a database table or view,
180
+ encapsulates the database access, and adds domain logic on that data."
181
+
182
+ Active Record attempts to provide a coherent wrapper as a solution for the inconvenience that is
183
+ object-relational mapping. The prime directive for this mapping has been to minimize
184
+ the amount of code needed to build a real-world domain model. This is made possible
185
+ by relying on a number of conventions that make it easy for Active Record to infer
186
+ complex relations and structures from a minimal amount of explicit direction.
187
+
188
+ Convention over Configuration:
189
+ * No XML-files!
190
+ * Lots of reflection and run-time extension
191
+ * Magic is not inherently a bad word
192
+
193
+ Admit the Database:
194
+ * Lets you drop down to SQL for odd cases and performance
195
+ * Doesn't attempt to duplicate or replace data definitions
196
+
197
+
198
+ == Download and installation
199
+
200
+ The latest version of Active Record can be installed with Rubygems:
201
+
202
+ % [sudo] gem install activerecord
203
+
204
+ Source code can be downloaded as part of the Rails project on GitHub
205
+
206
+ * http://github.com/rails/rails/tree/master/activerecord/
207
+
208
+
209
+ == License
210
+
211
+ Active Record is released under the MIT license.
212
+
213
+
214
+ == Support
215
+
216
+ API documentation is at
217
+
218
+ * http://api.rubyonrails.com
219
+
220
+ Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
221
+
222
+ * https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env ruby -KU
2
2
 
3
3
  TIMES = (ENV['N'] || 10000).to_i
4
-
5
4
  require 'rubygems'
5
+
6
6
  gem 'addressable', '~>2.0'
7
7
  gem 'faker', '~>0.3.1'
8
8
  gem 'rbench', '~>0.2.3'
9
+
9
10
  require 'addressable/uri'
10
11
  require 'faker'
11
12
  require 'rbench'
12
13
 
13
- __DIR__ = File.dirname(__FILE__)
14
- $:.unshift "#{__DIR__}/../lib"
15
- require 'active_record'
14
+ require File.expand_path("../../../load_paths", __FILE__)
15
+ require "active_record"
16
16
 
17
17
  conn = { :adapter => 'mysql',
18
18
  :database => 'activerecord_unittest',
@@ -55,10 +55,10 @@ class Exhibit < ActiveRecord::Base
55
55
  def self.feel(exhibits) exhibits.each { |e| e.feel } end
56
56
  end
57
57
 
58
- sqlfile = "#{__DIR__}/performance.sql"
58
+ sqlfile = File.expand_path("../performance.sql", __FILE__)
59
59
 
60
60
  if File.exists?(sqlfile)
61
- mysql_bin = %w[mysql mysql5].select { |bin| `which #{bin}`.length > 0 }
61
+ mysql_bin = %w[mysql mysql5].detect { |bin| `which #{bin}`.length > 0 }
62
62
  `#{mysql_bin} -u #{conn[:username]} #{"-p#{conn[:password]}" unless conn[:password].blank?} #{conn[:database]} < #{sqlfile}`
63
63
  else
64
64
  puts 'Generating data...'
@@ -116,15 +116,15 @@ RBench.run(TIMES) do
116
116
  end
117
117
 
118
118
  report 'Model.all limit(100)', (TIMES / 10).ceil do
119
- ar { Exhibit.look Exhibit.all(:limit => 100) }
119
+ ar { Exhibit.look Exhibit.limit(100) }
120
120
  end
121
121
 
122
122
  report 'Model.all limit(100) with relationship', (TIMES / 10).ceil do
123
- ar { Exhibit.feel Exhibit.all(:limit => 100, :include => :user) }
123
+ ar { Exhibit.feel Exhibit.limit(100).includes(:user) }
124
124
  end
125
125
 
126
126
  report 'Model.all limit(10,000)', (TIMES / 1000).ceil do
127
- ar { Exhibit.look Exhibit.all(:limit => 10000) }
127
+ ar { Exhibit.look Exhibit.limit(10000) }
128
128
  end
129
129
 
130
130
  exhibit = {
@@ -1,4 +1,5 @@
1
1
  module ActiveRecord
2
+ # = Active Record Aggregations
2
3
  module Aggregations # :nodoc:
3
4
  extend ActiveSupport::Concern
4
5
 
@@ -189,7 +190,7 @@ module ActiveRecord
189
190
  # :constructor => Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
190
191
  # :converter => Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
191
192
  #
192
- def composed_of(part_id, options = {}, &block)
193
+ def composed_of(part_id, options = {})
193
194
  options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter)
194
195
 
195
196
  name = part_id.id2name
@@ -198,9 +199,7 @@ module ActiveRecord
198
199
  mapping = [ mapping ] unless mapping.first.is_a?(Array)
199
200
  allow_nil = options[:allow_nil] || false
200
201
  constructor = options[:constructor] || :new
201
- converter = options[:converter] || block
202
-
203
- ActiveSupport::Deprecation.warn('The conversion block has been deprecated, use the :converter option instead.', caller) if block_given?
202
+ converter = options[:converter]
204
203
 
205
204
  reader_method(name, class_name, mapping, allow_nil, constructor)
206
205
  writer_method(name, class_name, mapping, allow_nil, converter)
@@ -6,7 +6,7 @@ module ActiveRecord
6
6
  module AssociationPreload #:nodoc:
7
7
  extend ActiveSupport::Concern
8
8
 
9
- # Implements the details of eager loading of ActiveRecord associations.
9
+ # Implements the details of eager loading of Active Record associations.
10
10
  # Application developers should not use this module directly.
11
11
  #
12
12
  # ActiveRecord::Base is extended with this module. The source code in
@@ -18,7 +18,7 @@ module ActiveRecord
18
18
  # The first one is by using table joins. This was only strategy available
19
19
  # prior to Rails 2.1. Suppose that you have an Author model with columns
20
20
  # 'name' and 'age', and a Book model with columns 'name' and 'sales'. Using
21
- # this strategy, ActiveRecord would try to retrieve all data for an author
21
+ # this strategy, Active Record would try to retrieve all data for an author
22
22
  # and all of its books via a single query:
23
23
  #
24
24
  # SELECT * FROM authors
@@ -31,7 +31,7 @@ module ActiveRecord
31
31
  # 'books' table is useful; the joined 'authors' data is just redundant, and
32
32
  # processing this redundant data takes memory and CPU time. The problem
33
33
  # quickly becomes worse and worse as the level of eager loading increases
34
- # (i.e. if ActiveRecord is to eager load the associations' associations as
34
+ # (i.e. if Active Record is to eager load the associations' associations as
35
35
  # well).
36
36
  #
37
37
  # The second strategy is to use multiple database queries, one for each
@@ -45,7 +45,7 @@ module ActiveRecord
45
45
  module ClassMethods
46
46
  protected
47
47
 
48
- # Eager loads the named associations for the given ActiveRecord record(s).
48
+ # Eager loads the named associations for the given Active Record record(s).
49
49
  #
50
50
  # In this description, 'association name' shall refer to the name passed
51
51
  # to an association creation method. For example, a model that specifies
@@ -80,7 +80,7 @@ module ActiveRecord
80
80
  # { :author => :avatar }
81
81
  # [ :books, { :author => :avatar } ]
82
82
  #
83
- # +preload_options+ contains options that will be passed to ActiveRecord#find
83
+ # +preload_options+ contains options that will be passed to ActiveRecord::Base#find
84
84
  # (which is called under the hood for preloading records). But it is passed
85
85
  # only one level deep in the +associations+ argument, i.e. it's not passed
86
86
  # to the child associations when +associations+ is a Hash.
@@ -112,13 +112,13 @@ module ActiveRecord
112
112
  # Not all records have the same class, so group then preload
113
113
  # group on the reflection itself so that if various subclass share the same association then we do not split them
114
114
  # unnecessarily
115
- records.group_by {|record| class_to_reflection[record.class] ||= record.class.reflections[association]}.each do |reflection, records|
115
+ records.group_by { |record| class_to_reflection[record.class] ||= record.class.reflections[association]}.each do |reflection, _records|
116
116
  raise ConfigurationError, "Association named '#{ association }' was not found; perhaps you misspelled it?" unless reflection
117
117
 
118
118
  # 'reflection.macro' can return 'belongs_to', 'has_many', etc. Thus,
119
119
  # the following could call 'preload_belongs_to_association',
120
120
  # 'preload_has_many_association', etc.
121
- send("preload_#{reflection.macro}_association", records, reflection, preload_options)
121
+ send("preload_#{reflection.macro}_association", _records, reflection, preload_options)
122
122
  end
123
123
  end
124
124
 
@@ -166,7 +166,7 @@ module ActiveRecord
166
166
  end
167
167
  end
168
168
 
169
- # Given a collection of ActiveRecord objects, constructs a Hash which maps
169
+ # Given a collection of Active Record objects, constructs a Hash which maps
170
170
  # the objects' IDs to the relevant objects. Returns a 2-tuple
171
171
  # <tt>(id_to_record_map, ids)</tt> where +id_to_record_map+ is the Hash,
172
172
  # and +ids+ is an Array of record IDs.
@@ -282,7 +282,12 @@ module ActiveRecord
282
282
  end
283
283
  end
284
284
  else
285
- records.first.class.preload_associations(records, through_association)
285
+ options = {}
286
+ options[:include] = reflection.options[:include] || reflection.options[:source] if reflection.options[:conditions]
287
+ options[:order] = reflection.options[:order]
288
+ options[:conditions] = reflection.options[:conditions]
289
+ records.first.class.preload_associations(records, through_association, options)
290
+
286
291
  records.each do |record|
287
292
  through_records.concat Array.wrap(record.send(through_association))
288
293
  end
@@ -373,7 +378,7 @@ module ActiveRecord
373
378
  :order => preload_options[:order] || options[:order]
374
379
  }
375
380
 
376
- reflection.klass.unscoped.apply_finder_options(find_options).to_a
381
+ reflection.klass.scoped.apply_finder_options(find_options).to_a
377
382
  end
378
383
 
379
384
 
@@ -3,6 +3,7 @@ require 'active_support/core_ext/enumerable'
3
3
  require 'active_support/core_ext/module/delegation'
4
4
  require 'active_support/core_ext/object/blank'
5
5
  require 'active_support/core_ext/string/conversions'
6
+ require 'active_support/core_ext/module/remove_method'
6
7
 
7
8
  module ActiveRecord
8
9
  class InverseOfAssociationNotFoundError < ActiveRecordError #:nodoc:
@@ -88,8 +89,8 @@ module ActiveRecord
88
89
  end
89
90
  end
90
91
 
91
- # This error is raised when trying to destroy a parent instance in a N:1, 1:1 assosications
92
- # (has_many, has_one) when there is at least 1 child assosociated instance.
92
+ # This error is raised when trying to destroy a parent instance in N:1 or 1:1 associations
93
+ # (has_many, has_one) when there is at least 1 child associated instance.
93
94
  # ex: if @project.tasks.size > 0, DeleteRestrictionError will be raised when trying to destroy @project
94
95
  class DeleteRestrictionError < ActiveRecordError #:nodoc:
95
96
  def initialize(reflection)
@@ -711,7 +712,7 @@ module ActiveRecord
711
712
  #
712
713
  # The +traps+ association on +Dungeon+ and the the +dungeon+ association on +Trap+ are the inverse of each other and the
713
714
  # inverse of the +dungeon+ association on +EvilWizard+ is the +evil_wizard+ association on +Dungeon+ (and vice-versa). By default,
714
- # +ActiveRecord+ doesn't do know anything about these inverse relationships and so no object loading optimisation is possible. For example:
715
+ # Active Record doesn't know anything about these inverse relationships and so no object loading optimisation is possible. For example:
715
716
  #
716
717
  # d = Dungeon.first
717
718
  # t = d.traps.first
@@ -721,7 +722,7 @@ module ActiveRecord
721
722
  #
722
723
  # The +Dungeon+ instances +d+ and <tt>t.dungeon</tt> in the above example refer to the same object data from the database, but are
723
724
  # actually different in-memory copies of that data. Specifying the <tt>:inverse_of</tt> option on associations lets you tell
724
- # +ActiveRecord+ about inverse relationships and it will optimise object loading. For example, if we changed our model definitions to:
725
+ # Active Record about inverse relationships and it will optimise object loading. For example, if we changed our model definitions to:
725
726
  #
726
727
  # class Dungeon < ActiveRecord::Base
727
728
  # has_many :traps, :inverse_of => :dungeon
@@ -763,20 +764,27 @@ module ActiveRecord
763
764
  # An empty array is returned if none are found.
764
765
  # [collection<<(object, ...)]
765
766
  # Adds one or more objects to the collection by setting their foreign keys to the collection's primary key.
767
+ # Note that this operation instantly fires update sql without waiting for the save or update call on the
768
+ # parent object.
766
769
  # [collection.delete(object, ...)]
767
770
  # Removes one or more objects from the collection by setting their foreign keys to +NULL+.
768
771
  # Objects will be in addition destroyed if they're associated with <tt>:dependent => :destroy</tt>,
769
772
  # and deleted if they're associated with <tt>:dependent => :delete_all</tt>.
770
773
  # [collection=objects]
771
- # Replaces the collections content by deleting and adding objects as appropriate.
774
+ # Replaces the collections content by deleting and adding objects as appropriate. If the <tt>:through</tt>
775
+ # option is true callbacks in the join models are triggered except destroy callbacks, since deletion is
776
+ # direct.
772
777
  # [collection_singular_ids]
773
778
  # Returns an array of the associated objects' ids
774
779
  # [collection_singular_ids=ids]
775
- # Replace the collection with the objects identified by the primary keys in +ids+
780
+ # Replace the collection with the objects identified by the primary keys in +ids+. This
781
+ # method loads the models and calls <tt>collection=</tt>. See above.
776
782
  # [collection.clear]
777
783
  # Removes every object from the collection. This destroys the associated objects if they
778
784
  # are associated with <tt>:dependent => :destroy</tt>, deletes them directly from the
779
785
  # database if <tt>:dependent => :delete_all</tt>, otherwise sets their foreign keys to +NULL+.
786
+ # If the <tt>:through</tt> option is true no destroy callbacks are invoked on the join models.
787
+ # Join models are directly deleted.
780
788
  # [collection.empty?]
781
789
  # Returns +true+ if there are no associated objects.
782
790
  # [collection.size]
@@ -869,9 +877,11 @@ module ActiveRecord
869
877
  # [:as]
870
878
  # Specifies a polymorphic interface (See <tt>belongs_to</tt>).
871
879
  # [:through]
872
- # Specifies a Join Model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
880
+ # Specifies a join model through which to perform the query. Options for <tt>:class_name</tt> and <tt>:foreign_key</tt>
873
881
  # are ignored, as the association uses the source reflection. You can only use a <tt>:through</tt> query through a <tt>belongs_to</tt>
874
- # <tt>has_one</tt> or <tt>has_many</tt> association on the join model.
882
+ # <tt>has_one</tt> or <tt>has_many</tt> association on the join model. The collection of join models can be managed via the collection
883
+ # API. For example, new join models are created for newly associated objects, and if some are gone their rows are deleted (directly,
884
+ # no destroy callbacks are triggered).
875
885
  # [:source]
876
886
  # Specifies the source association name used by <tt>has_many :through</tt> queries. Only use it if the name cannot be
877
887
  # inferred from the association. <tt>has_many :subscribers, :through => :subscriptions</tt> will look for either <tt>:subscribers</tt> or
@@ -886,11 +896,13 @@ module ActiveRecord
886
896
  # [:validate]
887
897
  # If false, don't validate the associated objects when saving the parent object. true by default.
888
898
  # [:autosave]
889
- # If true, always save any loaded members and destroy members marked for destruction, when saving the parent object. Off by default.
899
+ # If true, always save the associated objects or destroy them if marked for destruction, when saving the parent object.
900
+ # If false, never save or destroy the associated objects.
901
+ # By default, only save associated objects that are new records.
890
902
  # [:inverse_of]
891
903
  # Specifies the name of the <tt>belongs_to</tt> association on the associated object that is the inverse of this <tt>has_many</tt>
892
904
  # association. Does not work in combination with <tt>:through</tt> or <tt>:as</tt> options.
893
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional assocations for more detail.
905
+ # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
894
906
  #
895
907
  # Option examples:
896
908
  # has_many :comments, :order => "posted_on"
@@ -1001,11 +1013,13 @@ module ActiveRecord
1001
1013
  # [:validate]
1002
1014
  # If false, don't validate the associated object when saving the parent object. +false+ by default.
1003
1015
  # [:autosave]
1004
- # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default.
1016
+ # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object.
1017
+ # If false, never save or destroy the associated object.
1018
+ # By default, only save the associated object if it's a new record.
1005
1019
  # [:inverse_of]
1006
1020
  # Specifies the name of the <tt>belongs_to</tt> association on the associated object that is the inverse of this <tt>has_one</tt>
1007
1021
  # association. Does not work in combination with <tt>:through</tt> or <tt>:as</tt> options.
1008
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional assocations for more detail.
1022
+ # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1009
1023
  #
1010
1024
  # Option examples:
1011
1025
  # has_one :credit_card, :dependent => :destroy # destroys the associated credit card
@@ -1103,14 +1117,16 @@ module ActiveRecord
1103
1117
  # [:validate]
1104
1118
  # If false, don't validate the associated objects when saving the parent object. +false+ by default.
1105
1119
  # [:autosave]
1106
- # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object. Off by default.
1120
+ # If true, always save the associated object or destroy it if marked for destruction, when saving the parent object.
1121
+ # If false, never save or destroy the associated object.
1122
+ # By default, only save the associated object if it's a new record.
1107
1123
  # [:touch]
1108
1124
  # If true, the associated object will be touched (the updated_at/on attributes set to now) when this record is either saved or
1109
1125
  # destroyed. If you specify a symbol, that attribute will be updated with the current time instead of the updated_at/on attribute.
1110
1126
  # [:inverse_of]
1111
1127
  # Specifies the name of the <tt>has_one</tt> or <tt>has_many</tt> association on the associated object that is the inverse of this <tt>belongs_to</tt>
1112
1128
  # association. Does not work in combination with the <tt>:polymorphic</tt> options.
1113
- # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional assocations for more detail.
1129
+ # See ActiveRecord::Associations::ClassMethods's overview on Bi-directional associations for more detail.
1114
1130
  #
1115
1131
  # Option examples:
1116
1132
  # belongs_to :firm, :foreign_key => "client_of"
@@ -1180,6 +1196,8 @@ module ActiveRecord
1180
1196
  # [collection<<(object, ...)]
1181
1197
  # Adds one or more objects to the collection by creating associations in the join table
1182
1198
  # (<tt>collection.push</tt> and <tt>collection.concat</tt> are aliases to this method).
1199
+ # Note that this operation instantly fires update sql without waiting for the save or update call on the
1200
+ # parent object.
1183
1201
  # [collection.delete(object, ...)]
1184
1202
  # Removes one or more objects from the collection by removing their associations from the join table.
1185
1203
  # This does not destroy the objects.
@@ -1290,7 +1308,9 @@ module ActiveRecord
1290
1308
  # [:validate]
1291
1309
  # If false, don't validate the associated objects when saving the parent object. +true+ by default.
1292
1310
  # [:autosave]
1293
- # If true, always save any loaded members and destroy members marked for destruction, when saving the parent object. Off by default.
1311
+ # If true, always save the associated objects or destroy them if marked for destruction, when saving the parent object.
1312
+ # If false, never save or destroy the associated objects.
1313
+ # By default, only save associated objects that are new records.
1294
1314
  #
1295
1315
  # Option examples:
1296
1316
  # has_and_belongs_to_many :projects
@@ -1335,7 +1355,7 @@ module ActiveRecord
1335
1355
  end
1336
1356
 
1337
1357
  def association_accessor_methods(reflection, association_proxy_class)
1338
- define_method(reflection.name) do |*params|
1358
+ redefine_method(reflection.name) do |*params|
1339
1359
  force_reload = params.first unless params.empty?
1340
1360
  association = association_instance_get(reflection.name)
1341
1361
 
@@ -1352,12 +1372,12 @@ module ActiveRecord
1352
1372
  association.target.nil? ? nil : association
1353
1373
  end
1354
1374
 
1355
- define_method("loaded_#{reflection.name}?") do
1375
+ redefine_method("loaded_#{reflection.name}?") do
1356
1376
  association = association_instance_get(reflection.name)
1357
1377
  association && association.loaded?
1358
1378
  end
1359
-
1360
- define_method("#{reflection.name}=") do |new_value|
1379
+
1380
+ redefine_method("#{reflection.name}=") do |new_value|
1361
1381
  association = association_instance_get(reflection.name)
1362
1382
 
1363
1383
  if association.nil? || association.target != new_value
@@ -1367,8 +1387,8 @@ module ActiveRecord
1367
1387
  association.replace(new_value)
1368
1388
  association_instance_set(reflection.name, new_value.nil? ? nil : association)
1369
1389
  end
1370
-
1371
- define_method("set_#{reflection.name}_target") do |target|
1390
+
1391
+ redefine_method("set_#{reflection.name}_target") do |target|
1372
1392
  return if target.nil? and association_proxy_class == BelongsToAssociation
1373
1393
  association = association_proxy_class.new(self, reflection)
1374
1394
  association.target = target
@@ -1377,7 +1397,7 @@ module ActiveRecord
1377
1397
  end
1378
1398
 
1379
1399
  def collection_reader_method(reflection, association_proxy_class)
1380
- define_method(reflection.name) do |*params|
1400
+ redefine_method(reflection.name) do |*params|
1381
1401
  force_reload = params.first unless params.empty?
1382
1402
  association = association_instance_get(reflection.name)
1383
1403
 
@@ -1390,8 +1410,8 @@ module ActiveRecord
1390
1410
 
1391
1411
  association
1392
1412
  end
1393
-
1394
- define_method("#{reflection.name.to_s.singularize}_ids") do
1413
+
1414
+ redefine_method("#{reflection.name.to_s.singularize}_ids") do
1395
1415
  if send(reflection.name).loaded? || reflection.options[:finder_sql]
1396
1416
  send(reflection.name).map(&:id)
1397
1417
  else
@@ -1411,14 +1431,14 @@ module ActiveRecord
1411
1431
  collection_reader_method(reflection, association_proxy_class)
1412
1432
 
1413
1433
  if writer
1414
- define_method("#{reflection.name}=") do |new_value|
1434
+ redefine_method("#{reflection.name}=") do |new_value|
1415
1435
  # Loads proxy class instance (defined in collection_reader_method) if not already loaded
1416
1436
  association = send(reflection.name)
1417
1437
  association.replace(new_value)
1418
1438
  association
1419
1439
  end
1420
-
1421
- define_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
1440
+
1441
+ redefine_method("#{reflection.name.to_s.singularize}_ids=") do |new_value|
1422
1442
  ids = (new_value || []).reject { |nid| nid.blank? }.map(&:to_i)
1423
1443
  send("#{reflection.name}=", reflection.klass.find(ids).index_by(&:id).values_at(*ids))
1424
1444
  end
@@ -1426,7 +1446,7 @@ module ActiveRecord
1426
1446
  end
1427
1447
 
1428
1448
  def association_constructor_method(constructor, reflection, association_proxy_class)
1429
- define_method("#{constructor}_#{reflection.name}") do |*params|
1449
+ redefine_method("#{constructor}_#{reflection.name}") do |*params|
1430
1450
  attributees = params.first unless params.empty?
1431
1451
  replace_existing = params[1].nil? ? true : params[1]
1432
1452
  association = association_instance_get(reflection.name)
@@ -1467,8 +1487,8 @@ module ActiveRecord
1467
1487
  end
1468
1488
 
1469
1489
  def add_touch_callbacks(reflection, touch_attribute)
1470
- method_name = "belongs_to_touch_after_save_or_destroy_for_#{reflection.name}".to_sym
1471
- define_method(method_name) do
1490
+ method_name = :"belongs_to_touch_after_save_or_destroy_for_#{reflection.name}"
1491
+ redefine_method(method_name) do
1472
1492
  association = send(reflection.name)
1473
1493
 
1474
1494
  if touch_attribute == true
@@ -1742,7 +1762,7 @@ module ActiveRecord
1742
1762
  def graft(*associations)
1743
1763
  associations.each do |association|
1744
1764
  join_associations.detect {|a| association == a} ||
1745
- build(association.reflection.name, association.find_parent_in(self), association.join_class)
1765
+ build(association.reflection.name, association.find_parent_in(self) || join_base, association.join_class)
1746
1766
  end
1747
1767
  self
1748
1768
  end
@@ -1946,7 +1966,7 @@ module ActiveRecord
1946
1966
  end
1947
1967
 
1948
1968
  class JoinAssociation < JoinBase # :nodoc:
1949
- attr_reader :reflection, :parent, :aliased_table_name, :aliased_prefix, :aliased_join_table_name, :parent_table_name
1969
+ attr_reader :reflection, :parent, :aliased_table_name, :aliased_prefix, :aliased_join_table_name, :parent_table_name, :join_class
1950
1970
  delegate :options, :klass, :through_reflection, :source_reflection, :to => :reflection
1951
1971
 
1952
1972
  def initialize(reflection, join_dependency, parent = nil)
@@ -1963,6 +1983,7 @@ module ActiveRecord
1963
1983
  @parent_table_name = parent.active_record.table_name
1964
1984
  @aliased_table_name = aliased_table_name_for(table_name)
1965
1985
  @join = nil
1986
+ @join_class = Arel::InnerJoin
1966
1987
 
1967
1988
  if reflection.macro == :has_and_belongs_to_many
1968
1989
  @aliased_join_table_name = aliased_table_name_for(reflection.options[:join_table], "_join")
@@ -1985,10 +2006,6 @@ module ActiveRecord
1985
2006
  end
1986
2007
  end
1987
2008
 
1988
- def join_class
1989
- @join_class ||= Arel::InnerJoin
1990
- end
1991
-
1992
2009
  def with_join_class(join_class)
1993
2010
  @join_class = join_class
1994
2011
  self
@@ -2066,7 +2083,7 @@ module ActiveRecord
2066
2083
  unless klass.descends_from_active_record?
2067
2084
  sti_column = aliased_table[klass.inheritance_column]
2068
2085
  sti_condition = sti_column.eq(klass.sti_name)
2069
- klass.send(:subclasses).each {|subclass| sti_condition = sti_condition.or(sti_column.eq(subclass.sti_name)) }
2086
+ klass.descendants.each {|subclass| sti_condition = sti_condition.or(sti_column.eq(subclass.sti_name)) }
2070
2087
 
2071
2088
  @join << sti_condition
2072
2089
  end