activerecord 3.2.22.4 → 4.0.13

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 (173) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +2799 -617
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +23 -32
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +40 -34
  7. data/lib/active_record/association_relation.rb +22 -0
  8. data/lib/active_record/associations/alias_tracker.rb +4 -2
  9. data/lib/active_record/associations/association.rb +60 -46
  10. data/lib/active_record/associations/association_scope.rb +46 -40
  11. data/lib/active_record/associations/belongs_to_association.rb +17 -4
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
  13. data/lib/active_record/associations/builder/association.rb +81 -28
  14. data/lib/active_record/associations/builder/belongs_to.rb +73 -56
  15. data/lib/active_record/associations/builder/collection_association.rb +54 -40
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
  17. data/lib/active_record/associations/builder/has_many.rb +8 -64
  18. data/lib/active_record/associations/builder/has_one.rb +13 -50
  19. data/lib/active_record/associations/builder/singular_association.rb +13 -13
  20. data/lib/active_record/associations/collection_association.rb +130 -96
  21. data/lib/active_record/associations/collection_proxy.rb +916 -63
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -13
  23. data/lib/active_record/associations/has_many_association.rb +35 -8
  24. data/lib/active_record/associations/has_many_through_association.rb +37 -17
  25. data/lib/active_record/associations/has_one_association.rb +42 -19
  26. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  27. data/lib/active_record/associations/join_dependency/join_association.rb +39 -22
  28. data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
  29. data/lib/active_record/associations/join_dependency/join_part.rb +21 -8
  30. data/lib/active_record/associations/join_dependency.rb +30 -9
  31. data/lib/active_record/associations/join_helper.rb +1 -11
  32. data/lib/active_record/associations/preloader/association.rb +29 -33
  33. data/lib/active_record/associations/preloader/collection_association.rb +1 -1
  34. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +2 -2
  35. data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
  36. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  37. data/lib/active_record/associations/preloader/through_association.rb +13 -17
  38. data/lib/active_record/associations/preloader.rb +20 -43
  39. data/lib/active_record/associations/singular_association.rb +11 -11
  40. data/lib/active_record/associations/through_association.rb +3 -3
  41. data/lib/active_record/associations.rb +223 -282
  42. data/lib/active_record/attribute_assignment.rb +134 -154
  43. data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
  44. data/lib/active_record/attribute_methods/dirty.rb +36 -29
  45. data/lib/active_record/attribute_methods/primary_key.rb +45 -31
  46. data/lib/active_record/attribute_methods/query.rb +5 -4
  47. data/lib/active_record/attribute_methods/read.rb +67 -90
  48. data/lib/active_record/attribute_methods/serialization.rb +133 -70
  49. data/lib/active_record/attribute_methods/time_zone_conversion.rb +51 -45
  50. data/lib/active_record/attribute_methods/write.rb +34 -39
  51. data/lib/active_record/attribute_methods.rb +268 -108
  52. data/lib/active_record/autosave_association.rb +80 -73
  53. data/lib/active_record/base.rb +54 -451
  54. data/lib/active_record/callbacks.rb +60 -22
  55. data/lib/active_record/coders/yaml_column.rb +18 -21
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +347 -197
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +146 -138
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -19
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +19 -3
  61. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +151 -142
  62. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +499 -217
  64. data/lib/active_record/connection_adapters/abstract/transaction.rb +208 -0
  65. data/lib/active_record/connection_adapters/abstract_adapter.rb +209 -44
  66. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +169 -61
  67. data/lib/active_record/connection_adapters/column.rb +67 -36
  68. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +28 -29
  70. data/lib/active_record/connection_adapters/mysql_adapter.rb +200 -73
  71. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +98 -0
  72. data/lib/active_record/connection_adapters/postgresql/cast.rb +160 -0
  73. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +240 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid.rb +374 -0
  75. data/lib/active_record/connection_adapters/postgresql/quoting.rb +183 -0
  76. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  77. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +508 -0
  78. data/lib/active_record/connection_adapters/postgresql_adapter.rb +544 -899
  79. data/lib/active_record/connection_adapters/schema_cache.rb +76 -16
  80. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +595 -16
  81. data/lib/active_record/connection_handling.rb +98 -0
  82. data/lib/active_record/core.rb +472 -0
  83. data/lib/active_record/counter_cache.rb +107 -108
  84. data/lib/active_record/dynamic_matchers.rb +115 -63
  85. data/lib/active_record/errors.rb +36 -18
  86. data/lib/active_record/explain.rb +15 -63
  87. data/lib/active_record/explain_registry.rb +30 -0
  88. data/lib/active_record/explain_subscriber.rb +8 -4
  89. data/lib/active_record/fixture_set/file.rb +55 -0
  90. data/lib/active_record/fixtures.rb +159 -155
  91. data/lib/active_record/inheritance.rb +93 -59
  92. data/lib/active_record/integration.rb +8 -8
  93. data/lib/active_record/locale/en.yml +8 -1
  94. data/lib/active_record/locking/optimistic.rb +39 -43
  95. data/lib/active_record/locking/pessimistic.rb +4 -4
  96. data/lib/active_record/log_subscriber.rb +19 -9
  97. data/lib/active_record/migration/command_recorder.rb +102 -33
  98. data/lib/active_record/migration/join_table.rb +15 -0
  99. data/lib/active_record/migration.rb +411 -173
  100. data/lib/active_record/model_schema.rb +81 -94
  101. data/lib/active_record/nested_attributes.rb +173 -131
  102. data/lib/active_record/null_relation.rb +67 -0
  103. data/lib/active_record/persistence.rb +254 -106
  104. data/lib/active_record/query_cache.rb +18 -36
  105. data/lib/active_record/querying.rb +19 -15
  106. data/lib/active_record/railtie.rb +113 -38
  107. data/lib/active_record/railties/console_sandbox.rb +3 -4
  108. data/lib/active_record/railties/controller_runtime.rb +4 -3
  109. data/lib/active_record/railties/databases.rake +115 -368
  110. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  111. data/lib/active_record/readonly_attributes.rb +7 -3
  112. data/lib/active_record/reflection.rb +110 -61
  113. data/lib/active_record/relation/batches.rb +29 -29
  114. data/lib/active_record/relation/calculations.rb +155 -125
  115. data/lib/active_record/relation/delegation.rb +94 -18
  116. data/lib/active_record/relation/finder_methods.rb +151 -203
  117. data/lib/active_record/relation/merger.rb +188 -0
  118. data/lib/active_record/relation/predicate_builder.rb +85 -42
  119. data/lib/active_record/relation/query_methods.rb +793 -146
  120. data/lib/active_record/relation/spawn_methods.rb +43 -150
  121. data/lib/active_record/relation.rb +293 -173
  122. data/lib/active_record/result.rb +48 -7
  123. data/lib/active_record/runtime_registry.rb +17 -0
  124. data/lib/active_record/sanitization.rb +41 -54
  125. data/lib/active_record/schema.rb +19 -12
  126. data/lib/active_record/schema_dumper.rb +41 -41
  127. data/lib/active_record/schema_migration.rb +46 -0
  128. data/lib/active_record/scoping/default.rb +56 -52
  129. data/lib/active_record/scoping/named.rb +78 -103
  130. data/lib/active_record/scoping.rb +54 -124
  131. data/lib/active_record/serialization.rb +6 -2
  132. data/lib/active_record/serializers/xml_serializer.rb +9 -15
  133. data/lib/active_record/statement_cache.rb +26 -0
  134. data/lib/active_record/store.rb +131 -15
  135. data/lib/active_record/tasks/database_tasks.rb +204 -0
  136. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  137. data/lib/active_record/tasks/mysql_database_tasks.rb +144 -0
  138. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  139. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  140. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  141. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  142. data/lib/active_record/test_case.rb +67 -38
  143. data/lib/active_record/timestamp.rb +16 -11
  144. data/lib/active_record/transactions.rb +73 -51
  145. data/lib/active_record/validations/associated.rb +19 -13
  146. data/lib/active_record/validations/presence.rb +65 -0
  147. data/lib/active_record/validations/uniqueness.rb +110 -57
  148. data/lib/active_record/validations.rb +18 -17
  149. data/lib/active_record/version.rb +7 -6
  150. data/lib/active_record.rb +63 -45
  151. data/lib/rails/generators/active_record/migration/migration_generator.rb +45 -8
  152. data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
  153. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  154. data/lib/rails/generators/active_record/model/model_generator.rb +5 -4
  155. data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
  156. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  157. data/lib/rails/generators/active_record.rb +3 -5
  158. metadata +43 -29
  159. data/examples/associations.png +0 -0
  160. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  161. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  162. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  163. data/lib/active_record/dynamic_finder_match.rb +0 -68
  164. data/lib/active_record/dynamic_scope_match.rb +0 -23
  165. data/lib/active_record/fixtures/file.rb +0 -65
  166. data/lib/active_record/identity_map.rb +0 -162
  167. data/lib/active_record/observer.rb +0 -121
  168. data/lib/active_record/session_store.rb +0 -360
  169. data/lib/rails/generators/active_record/migration.rb +0 -15
  170. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  171. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  172. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  173. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2004-2011 David Heinemeier Hansson
1
+ Copyright (c) 2004-2013 David Heinemeier Hansson
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.rdoc CHANGED
@@ -49,10 +49,10 @@ A short rundown of some of the major features:
49
49
  * Aggregations of value objects.
50
50
 
51
51
  class Account < ActiveRecord::Base
52
- composed_of :balance, :class_name => "Money",
53
- :mapping => %w(balance amount)
52
+ composed_of :balance, class_name: 'Money',
53
+ mapping: %w(balance amount)
54
54
  composed_of :address,
55
- :mapping => [%w(address_street street), %w(address_city city)]
55
+ mapping: [%w(address_street street), %w(address_city city)]
56
56
  end
57
57
 
58
58
  {Learn more}[link:classes/ActiveRecord/Aggregations/ClassMethods.html]
@@ -61,10 +61,10 @@ A short rundown of some of the major features:
61
61
  * Validation rules that can differ for new or existing objects.
62
62
 
63
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
64
+ validates :subdomain, :name, :email_address, :password, presence: true
65
+ validates :subdomain, uniqueness: true
66
+ validates :terms_of_service, acceptance: true, on: :create
67
+ validates :password, :email_address, confirmation: true, on: :create
68
68
  end
69
69
 
70
70
  {Learn more}[link:classes/ActiveRecord/Validations.html]
@@ -80,17 +80,6 @@ A short rundown of some of the major features:
80
80
  {Learn more}[link:classes/ActiveRecord/Callbacks.html]
81
81
 
82
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
- CommentMailer.new_comment_email("david@loudthinking.com", comment).deliver
88
- end
89
- end
90
-
91
- {Learn more}[link:classes/ActiveRecord/Observer.html]
92
-
93
-
94
83
  * Inheritance hierarchies.
95
84
 
96
85
  class Company < ActiveRecord::Base; end
@@ -124,15 +113,15 @@ A short rundown of some of the major features:
124
113
  * Database abstraction through simple adapters.
125
114
 
126
115
  # connect to SQLite3
127
- ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => "dbfile.sqlite3")
116
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: 'dbfile.sqlite3')
128
117
 
129
118
  # connect to MySQL with authentication
130
119
  ActiveRecord::Base.establish_connection(
131
- :adapter => "mysql2",
132
- :host => "localhost",
133
- :username => "me",
134
- :password => "secret",
135
- :database => "activerecord"
120
+ adapter: 'mysql2',
121
+ host: 'localhost',
122
+ username: 'me',
123
+ password: 'secret',
124
+ database: 'activerecord'
136
125
  )
137
126
 
138
127
  {Learn more}[link:classes/ActiveRecord/Base.html] and read about the built-in support for
@@ -141,10 +130,10 @@ A short rundown of some of the major features:
141
130
  SQLite3[link:classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html].
142
131
 
143
132
 
144
- * Logging support for Log4r[http://log4r.sourceforge.net] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc].
133
+ * Logging support for Log4r[http://log4r.rubyforge.org] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc].
145
134
 
146
- ActiveRecord::Base.logger = Logger.new(STDOUT)
147
- ActiveRecord::Base.logger = Log4r::Logger.new("Application Log")
135
+ ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
136
+ ActiveRecord::Base.logger = Log4r::Logger.new('Application Log')
148
137
 
149
138
 
150
139
  * Database agnostic schema management with Migrations.
@@ -159,7 +148,7 @@ A short rundown of some of the major features:
159
148
  t.integer :position
160
149
  end
161
150
 
162
- SystemSetting.create :name => "notice", :label => "Use notice?", :value => 1
151
+ SystemSetting.create name: 'notice', label: 'Use notice?', value: 1
163
152
  end
164
153
 
165
154
  def down
@@ -201,19 +190,21 @@ The latest version of Active Record can be installed with RubyGems:
201
190
 
202
191
  % [sudo] gem install activerecord
203
192
 
204
- Source code can be downloaded as part of the Rails project on GitHub
193
+ Source code can be downloaded as part of the Rails project on GitHub:
205
194
 
206
- * https://github.com/rails/rails/tree/3-2-stable/activerecord
195
+ * https://github.com/rails/rails/tree/4-0-stable/activerecord
207
196
 
208
197
 
209
198
  == License
210
199
 
211
- Active Record is released under the MIT license.
200
+ Active Record is released under the MIT license:
201
+
202
+ * http://www.opensource.org/licenses/MIT
212
203
 
213
204
 
214
205
  == Support
215
206
 
216
- API documentation is at
207
+ API documentation is at:
217
208
 
218
209
  * http://api.rubyonrails.org
219
210
 
@@ -143,7 +143,7 @@ Benchmark.ips(TIME) do |x|
143
143
  end
144
144
 
145
145
  x.report 'Resource#update' do
146
- Exhibit.first.update_attributes(:name => 'bob')
146
+ Exhibit.first.update(name: 'bob')
147
147
  end
148
148
 
149
149
  x.report 'Resource#destroy' do
@@ -10,14 +10,14 @@ module ActiveRecord
10
10
  # Active Record implements aggregation through a macro-like class method called +composed_of+
11
11
  # for representing attributes as value objects. It expresses relationships like "Account [is]
12
12
  # composed of Money [among other things]" or "Person [is] composed of [an] address". Each call
13
- # to the macro adds a description of how the value objects are created from the attributes of
14
- # the entity object (when the entity is initialized either as a new object or from finding an
15
- # existing object) and how it can be turned back into attributes (when the entity is saved to
13
+ # to the macro adds a description of how the value objects are created from the attributes of
14
+ # the entity object (when the entity is initialized either as a new object or from finding an
15
+ # existing object) and how it can be turned back into attributes (when the entity is saved to
16
16
  # the database).
17
17
  #
18
18
  # class Customer < ActiveRecord::Base
19
- # composed_of :balance, :class_name => "Money", :mapping => %w(balance amount)
20
- # composed_of :address, :mapping => [ %w(address_street street), %w(address_city city) ]
19
+ # composed_of :balance, class_name: "Money", mapping: %w(balance amount)
20
+ # composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
21
21
  # end
22
22
  #
23
23
  # The customer class now has the following methods to manipulate the value objects:
@@ -71,7 +71,7 @@ module ActiveRecord
71
71
  # Now it's possible to access attributes from the database through the value objects instead. If
72
72
  # you choose to name the composition the same as the attribute's name, it will be the only way to
73
73
  # access that attribute. That's the case with our +balance+ attribute. You interact with the value
74
- # objects just like you would any other attribute, though:
74
+ # objects just like you would with any other attribute:
75
75
  #
76
76
  # customer.balance = Money.new(20) # sets the Money value object and the attribute
77
77
  # customer.balance # => Money value object
@@ -86,6 +86,12 @@ module ActiveRecord
86
86
  # customer.address_street = "Hyancintvej"
87
87
  # customer.address_city = "Copenhagen"
88
88
  # customer.address # => Address.new("Hyancintvej", "Copenhagen")
89
+ #
90
+ # customer.address_street = "Vesterbrogade"
91
+ # customer.address # => Address.new("Hyancintvej", "Copenhagen")
92
+ # customer.clear_aggregation_cache
93
+ # customer.address # => Address.new("Vesterbrogade", "Copenhagen")
94
+ #
89
95
  # customer.address = Address.new("May Street", "Chicago")
90
96
  # customer.address_street # => "May Street"
91
97
  # customer.address_city # => "Chicago"
@@ -101,13 +107,13 @@ module ActiveRecord
101
107
  # ActiveRecord::Base classes are entity objects.
102
108
  #
103
109
  # It's also important to treat the value objects as immutable. Don't allow the Money object to have
104
- # its amount changed after creation. Create a new Money object with the new value instead. This
105
- # is exemplified by the Money#exchange_to method that returns a new value object instead of changing
110
+ # its amount changed after creation. Create a new Money object with the new value instead. The
111
+ # Money#exchange_to method is an example of this. It returns a new value object instead of changing
106
112
  # its own values. Active Record won't persist value objects that have been changed through means
107
113
  # other than the writer method.
108
114
  #
109
115
  # The immutable requirement is enforced by Active Record by freezing any object assigned as a value
110
- # object. Attempting to change it afterwards will result in a ActiveSupport::FrozenObjectError.
116
+ # object. Attempting to change it afterwards will result in a RuntimeError.
111
117
  #
112
118
  # Read more about value objects on http://c2.com/cgi/wiki?ValueObject and on the dangers of not
113
119
  # keeping value objects immutable on http://c2.com/cgi/wiki?ValueObjectsShouldBeImmutable
@@ -119,7 +125,7 @@ module ActiveRecord
119
125
  # option, as arguments. If the value class doesn't support this convention then +composed_of+ allows
120
126
  # a custom constructor to be specified.
121
127
  #
122
- # When a new value is assigned to the value object the default assumption is that the new value
128
+ # When a new value is assigned to the value object, the default assumption is that the new value
123
129
  # is an instance of the value class. Specifying a custom converter allows the new value to be automatically
124
130
  # converted to an instance of value class if necessary.
125
131
  #
@@ -132,15 +138,15 @@ module ActiveRecord
132
138
  #
133
139
  # class NetworkResource < ActiveRecord::Base
134
140
  # composed_of :cidr,
135
- # :class_name => 'NetAddr::CIDR',
136
- # :mapping => [ %w(network_address network), %w(cidr_range bits) ],
137
- # :allow_nil => true,
138
- # :constructor => Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") },
139
- # :converter => Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) }
141
+ # class_name: 'NetAddr::CIDR',
142
+ # mapping: [ %w(network_address network), %w(cidr_range bits) ],
143
+ # allow_nil: true,
144
+ # constructor: Proc.new { |network_address, cidr_range| NetAddr::CIDR.create("#{network_address}/#{cidr_range}") },
145
+ # converter: Proc.new { |value| NetAddr::CIDR.create(value.is_a?(Array) ? value.join('/') : value) }
140
146
  # end
141
147
  #
142
148
  # # This calls the :constructor
143
- # network_resource = NetworkResource.new(:network_address => '192.168.0.1', :cidr_range => 24)
149
+ # network_resource = NetworkResource.new(network_address: '192.168.0.1', cidr_range: 24)
144
150
  #
145
151
  # # These assignments will both use the :converter
146
152
  # network_resource.cidr = [ '192.168.2.1', 8 ]
@@ -159,7 +165,7 @@ module ActiveRecord
159
165
  # by specifying an instance of the value object in the conditions hash. The following example
160
166
  # finds all customers with +balance_amount+ equal to 20 and +balance_currency+ equal to "USD":
161
167
  #
162
- # Customer.where(:balance => Money.new(20, "USD")).all
168
+ # Customer.where(balance: Money.new(20, "USD"))
163
169
  #
164
170
  module ClassMethods
165
171
  # Adds reader and writer methods for manipulating a value object:
@@ -187,20 +193,21 @@ module ActiveRecord
187
193
  # * <tt>:converter</tt> - A symbol specifying the name of a class method of <tt>:class_name</tt>
188
194
  # or a Proc that is called when a new value is assigned to the value object. The converter is
189
195
  # passed the single value that is used in the assignment and is only called if the new value is
190
- # not an instance of <tt>:class_name</tt>.
196
+ # not an instance of <tt>:class_name</tt>. If <tt>:allow_nil</tt> is set to true, the converter
197
+ # can return nil to skip the assignment.
191
198
  #
192
199
  # Option examples:
193
- # composed_of :temperature, :mapping => %w(reading celsius)
194
- # composed_of :balance, :class_name => "Money", :mapping => %w(balance amount),
195
- # :converter => Proc.new { |balance| balance.to_money }
196
- # composed_of :address, :mapping => [ %w(address_street street), %w(address_city city) ]
200
+ # composed_of :temperature, mapping: %w(reading celsius)
201
+ # composed_of :balance, class_name: "Money", mapping: %w(balance amount),
202
+ # converter: Proc.new { |balance| balance.to_money }
203
+ # composed_of :address, mapping: [ %w(address_street street), %w(address_city city) ]
197
204
  # composed_of :gps_location
198
- # composed_of :gps_location, :allow_nil => true
205
+ # composed_of :gps_location, allow_nil: true
199
206
  # composed_of :ip_address,
200
- # :class_name => 'IPAddr',
201
- # :mapping => %w(ip to_i),
202
- # :constructor => Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
203
- # :converter => Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
207
+ # class_name: 'IPAddr',
208
+ # mapping: %w(ip to_i),
209
+ # constructor: Proc.new { |ip| IPAddr.new(ip, Socket::AF_INET) },
210
+ # converter: Proc.new { |ip| ip.is_a?(Integer) ? IPAddr.new(ip, Socket::AF_INET) : IPAddr.new(ip.to_s) }
204
211
  #
205
212
  def composed_of(part_id, options = {})
206
213
  options.assert_valid_keys(:class_name, :mapping, :allow_nil, :constructor, :converter)
@@ -216,7 +223,7 @@ module ActiveRecord
216
223
  reader_method(name, class_name, mapping, allow_nil, constructor)
217
224
  writer_method(name, class_name, mapping, allow_nil, converter)
218
225
 
219
- create_reflection(:composed_of, part_id, options, self)
226
+ create_reflection(:composed_of, part_id, nil, options, self)
220
227
  end
221
228
 
222
229
  private
@@ -235,16 +242,15 @@ module ActiveRecord
235
242
 
236
243
  def writer_method(name, class_name, mapping, allow_nil, converter)
237
244
  define_method("#{name}=") do |part|
245
+ klass = class_name.constantize
246
+ unless part.is_a?(klass) || converter.nil? || part.nil?
247
+ part = converter.respond_to?(:call) ? converter.call(part) : klass.send(converter, part)
248
+ end
249
+
238
250
  if part.nil? && allow_nil
239
251
  mapping.each { |pair| self[pair.first] = nil }
240
252
  @aggregation_cache[name] = nil
241
253
  else
242
- unless part.is_a?(class_name.constantize) || converter.nil?
243
- part = converter.respond_to?(:call) ?
244
- converter.call(part) :
245
- class_name.constantize.send(converter, part)
246
- end
247
-
248
254
  mapping.each { |pair| self[pair.first] = part.send(pair.last) }
249
255
  @aggregation_cache[name] = part.freeze
250
256
  end
@@ -0,0 +1,22 @@
1
+ module ActiveRecord
2
+ class AssociationRelation < Relation
3
+ def initialize(klass, table, association)
4
+ super(klass, table)
5
+ @association = association
6
+ end
7
+
8
+ def proxy_association
9
+ @association
10
+ end
11
+
12
+ def ==(other)
13
+ other == to_a
14
+ end
15
+
16
+ private
17
+
18
+ def exec_queries
19
+ super.each { |r| @association.set_inverse_instance r }
20
+ end
21
+ end
22
+ end
@@ -8,7 +8,7 @@ module ActiveRecord
8
8
  attr_reader :aliases, :table_joins, :connection
9
9
 
10
10
  # table_joins is an array of arel joins which might conflict with the aliases we assign here
11
- def initialize(connection = ActiveRecord::Model.connection, table_joins = [])
11
+ def initialize(connection = Base.connection, table_joins = [])
12
12
  @aliases = Hash.new { |h,k| h[k] = initial_count_for(k) }
13
13
  @table_joins = table_joins
14
14
  @connection = connection
@@ -60,8 +60,10 @@ module ActiveRecord
60
60
  join.left.downcase.scan(
61
61
  /join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/
62
62
  ).size
63
- else
63
+ elsif join.respond_to? :left
64
64
  join.left.table_name == name ? 1 : 0
65
+ else
66
+ 0
65
67
  end
66
68
  end
67
69
 
@@ -1,5 +1,4 @@
1
1
  require 'active_support/core_ext/array/wrap'
2
- require 'active_support/core_ext/object/inclusion'
3
2
 
4
3
  module ActiveRecord
5
4
  module Associations
@@ -19,34 +18,33 @@ module ActiveRecord
19
18
  # HasManyThroughAssociation + ThroughAssociation
20
19
  class Association #:nodoc:
21
20
  attr_reader :owner, :target, :reflection
21
+ attr_accessor :inversed
22
22
 
23
23
  delegate :options, :to => :reflection
24
24
 
25
25
  def initialize(owner, reflection)
26
26
  reflection.check_validity!
27
27
 
28
- @target = nil
29
28
  @owner, @reflection = owner, reflection
30
- @updated = false
31
29
 
32
30
  reset
33
31
  reset_scope
34
32
  end
35
33
 
36
- # Returns the name of the table of the related class:
34
+ # Returns the name of the table of the associated class:
37
35
  #
38
36
  # post.comments.aliased_table_name # => "comments"
39
37
  #
40
38
  def aliased_table_name
41
- reflection.klass.table_name
39
+ klass.table_name
42
40
  end
43
41
 
44
42
  # Resets the \loaded flag to +false+ and sets the \target to +nil+.
45
43
  def reset
46
44
  @loaded = false
47
- IdentityMap.remove(target) if IdentityMap.enabled? && target
48
45
  @target = nil
49
46
  @stale_state = nil
47
+ @inversed = false
50
48
  end
51
49
 
52
50
  # Reloads the \target and returns +self+ on success.
@@ -64,18 +62,19 @@ module ActiveRecord
64
62
 
65
63
  # Asserts the \target has been loaded setting the \loaded flag to +true+.
66
64
  def loaded!
67
- @loaded = true
65
+ @loaded = true
68
66
  @stale_state = stale_state
67
+ @inversed = false
69
68
  end
70
69
 
71
70
  # The target is stale if the target no longer points to the record(s) that the
72
71
  # relevant foreign_key(s) refers to. If stale, the association accessor method
73
72
  # on the owner will reload the target. It's up to subclasses to implement the
74
- # state_state method if relevant.
73
+ # stale_state method if relevant.
75
74
  #
76
75
  # Note that if the target has not been loaded, it is not considered stale.
77
76
  def stale_target?
78
- loaded? && @stale_state != stale_state
77
+ !inversed && loaded? && @stale_state != stale_state
79
78
  end
80
79
 
81
80
  # Sets the target of this association to <tt>\target</tt>, and the \loaded flag to +true+.
@@ -84,14 +83,19 @@ module ActiveRecord
84
83
  loaded!
85
84
  end
86
85
 
87
- def scoped
86
+ def scope
88
87
  target_scope.merge(association_scope)
89
88
  end
90
89
 
90
+ def scoped
91
+ ActiveSupport::Deprecation.warn "#scoped is deprecated. use #scope instead."
92
+ scope
93
+ end
94
+
91
95
  # The scope for this association.
92
96
  #
93
97
  # Note that the association_scope is merged into the target_scope only when the
94
- # scoped method is called. This is because at that point the call may be surrounded
98
+ # scope method is called. This is because at that point the call may be surrounded
95
99
  # by scope.scoping { ... } or with_scope { ... } etc, which affects the scope which
96
100
  # actually gets built.
97
101
  def association_scope
@@ -109,10 +113,11 @@ module ActiveRecord
109
113
  if record && invertible_for?(record)
110
114
  inverse = record.association(inverse_reflection_for(record).name)
111
115
  inverse.target = owner
116
+ inverse.inversed = true
112
117
  end
113
118
  end
114
119
 
115
- # This class of the target. belongs_to polymorphic overrides this to look at the
120
+ # Returns the class of the target. belongs_to polymorphic overrides this to look at the
116
121
  # polymorphic_type field on the owner.
117
122
  def klass
118
123
  reflection.klass
@@ -121,7 +126,11 @@ module ActiveRecord
121
126
  # Can be overridden (i.e. in ThroughAssociation) to merge in other scopes (i.e. the
122
127
  # through association's scope)
123
128
  def target_scope
124
- klass.scoped
129
+ all = klass.all
130
+ scope = AssociationRelation.new(klass, klass.arel_table, self)
131
+ scope.merge! all
132
+ scope.default_scoped = all.default_scoped?
133
+ scope
125
134
  end
126
135
 
127
136
  # Loads the \target if needed and returns it.
@@ -129,28 +138,14 @@ module ActiveRecord
129
138
  # This method is abstract in the sense that it relies on +find_target+,
130
139
  # which is expected to be provided by descendants.
131
140
  #
132
- # If the \target is stale(the target no longer points to the record(s) that the
133
- # relevant foreign_key(s) refers to.), force reload the \target.
134
- #
135
- # Otherwise if the \target is already \loaded it is just returned. Thus, you can
136
- # call +load_target+ unconditionally to get the \target.
141
+ # If the \target is already \loaded it is just returned. Thus, you can call
142
+ # +load_target+ unconditionally to get the \target.
137
143
  #
138
144
  # ActiveRecord::RecordNotFound is rescued within the method, and it is
139
145
  # not reraised. The proxy is \reset and +nil+ is the return value.
140
146
  def load_target
141
- if (@stale_state && stale_target?) || find_target?
142
- begin
143
- if IdentityMap.enabled? && association_class && association_class.respond_to?(:base_class)
144
- @target = IdentityMap.get(association_class, owner[reflection.foreign_key])
145
- elsif @stale_state && stale_target?
146
- @target = find_target
147
- end
148
- rescue NameError
149
- nil
150
- ensure
151
- @target ||= find_target
152
- end
153
- end
147
+ @target = find_target if (@stale_state && stale_target?) || find_target?
148
+
154
149
  loaded! unless loaded?
155
150
  target
156
151
  rescue ActiveRecord::RecordNotFound
@@ -159,12 +154,31 @@ module ActiveRecord
159
154
 
160
155
  def interpolate(sql, record = nil)
161
156
  if sql.respond_to?(:to_proc)
162
- owner.send(:instance_exec, record, &sql)
157
+ owner.instance_exec(record, &sql)
163
158
  else
164
159
  sql
165
160
  end
166
161
  end
167
162
 
163
+ # We can't dump @reflection since it contains the scope proc
164
+ def marshal_dump
165
+ ivars = (instance_variables - [:@reflection]).map { |name| [name, instance_variable_get(name)] }
166
+ [@reflection.name, ivars]
167
+ end
168
+
169
+ def marshal_load(data)
170
+ reflection_name, ivars = data
171
+ ivars.each { |name, val| instance_variable_set(name, val) }
172
+ @reflection = @owner.class.reflect_on_association(reflection_name)
173
+ end
174
+
175
+ def initialize_attributes(record) #:nodoc:
176
+ skip_assign = [reflection.foreign_key, reflection.type].compact
177
+ attributes = create_scope.except(*(record.changed - skip_assign))
178
+ record.assign_attributes(attributes)
179
+ set_inverse_instance(record)
180
+ end
181
+
168
182
  private
169
183
 
170
184
  def find_target?
@@ -174,7 +188,7 @@ module ActiveRecord
174
188
  def creation_attributes
175
189
  attributes = {}
176
190
 
177
- if reflection.macro.in?([:has_one, :has_many]) && !options[:through]
191
+ if (reflection.macro == :has_one || reflection.macro == :has_many) && !options[:through]
178
192
  attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
179
193
 
180
194
  if reflection.options[:as]
@@ -204,7 +218,7 @@ module ActiveRecord
204
218
  # Raises ActiveRecord::AssociationTypeMismatch unless +record+ is of
205
219
  # the kind of the class of the associated objects. Meant to be used as
206
220
  # a sanity check when you are about to assign an associated record.
207
- def raise_on_type_mismatch(record)
221
+ def raise_on_type_mismatch!(record)
208
222
  unless record.is_a?(reflection.klass) || record.is_a?(reflection.class_name.constantize)
209
223
  message = "#{reflection.class_name}(##{reflection.klass.object_id}) expected, got #{record.class}(##{record.class.object_id})"
210
224
  raise ActiveRecord::AssociationTypeMismatch, message
@@ -218,28 +232,28 @@ module ActiveRecord
218
232
  reflection.inverse_of
219
233
  end
220
234
 
221
- # Is this association invertible? Can be redefined by subclasses.
235
+ # Returns true if inverse association on the given record needs to be set.
236
+ # This method is redefined by subclasses.
222
237
  def invertible_for?(record)
223
- inverse_reflection_for(record)
238
+ foreign_key_for?(record) && inverse_reflection_for(record)
239
+ end
240
+
241
+ # Returns true if record contains the foreign_key
242
+ def foreign_key_for?(record)
243
+ record.has_attribute?(reflection.foreign_key)
224
244
  end
225
245
 
226
246
  # This should be implemented to return the values of the relevant key(s) on the owner,
227
- # so that when state_state is different from the value stored on the last find_target,
247
+ # so that when stale_state is different from the value stored on the last find_target,
228
248
  # the target is stale.
229
249
  #
230
250
  # This is only relevant to certain associations, which is why it returns nil by default.
231
251
  def stale_state
232
252
  end
233
253
 
234
- def association_class
235
- @reflection.klass
236
- end
237
-
238
- def build_record(attributes, options)
239
- reflection.build_association(attributes, options) do |record|
240
- skip_assign = [reflection.foreign_key, reflection.type].compact
241
- attributes = create_scope.except(*(record.changed - skip_assign))
242
- record.assign_attributes(attributes, :without_protection => true)
254
+ def build_record(attributes)
255
+ reflection.build_association(attributes) do |record|
256
+ initialize_attributes(record)
243
257
  end
244
258
  end
245
259
  end