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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +2799 -617
- data/MIT-LICENSE +1 -1
- data/README.rdoc +23 -32
- data/examples/performance.rb +1 -1
- data/lib/active_record/aggregations.rb +40 -34
- data/lib/active_record/association_relation.rb +22 -0
- data/lib/active_record/associations/alias_tracker.rb +4 -2
- data/lib/active_record/associations/association.rb +60 -46
- data/lib/active_record/associations/association_scope.rb +46 -40
- data/lib/active_record/associations/belongs_to_association.rb +17 -4
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +81 -28
- data/lib/active_record/associations/builder/belongs_to.rb +73 -56
- data/lib/active_record/associations/builder/collection_association.rb +54 -40
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +23 -41
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +13 -50
- data/lib/active_record/associations/builder/singular_association.rb +13 -13
- data/lib/active_record/associations/collection_association.rb +130 -96
- data/lib/active_record/associations/collection_proxy.rb +916 -63
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +15 -13
- data/lib/active_record/associations/has_many_association.rb +35 -8
- data/lib/active_record/associations/has_many_through_association.rb +37 -17
- data/lib/active_record/associations/has_one_association.rb +42 -19
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +39 -22
- data/lib/active_record/associations/join_dependency/join_base.rb +2 -2
- data/lib/active_record/associations/join_dependency/join_part.rb +21 -8
- data/lib/active_record/associations/join_dependency.rb +30 -9
- data/lib/active_record/associations/join_helper.rb +1 -11
- data/lib/active_record/associations/preloader/association.rb +29 -33
- data/lib/active_record/associations/preloader/collection_association.rb +1 -1
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +2 -2
- data/lib/active_record/associations/preloader/has_many_through.rb +6 -2
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/through_association.rb +13 -17
- data/lib/active_record/associations/preloader.rb +20 -43
- data/lib/active_record/associations/singular_association.rb +11 -11
- data/lib/active_record/associations/through_association.rb +3 -3
- data/lib/active_record/associations.rb +223 -282
- data/lib/active_record/attribute_assignment.rb +134 -154
- data/lib/active_record/attribute_methods/before_type_cast.rb +44 -5
- data/lib/active_record/attribute_methods/dirty.rb +36 -29
- data/lib/active_record/attribute_methods/primary_key.rb +45 -31
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +67 -90
- data/lib/active_record/attribute_methods/serialization.rb +133 -70
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +51 -45
- data/lib/active_record/attribute_methods/write.rb +34 -39
- data/lib/active_record/attribute_methods.rb +268 -108
- data/lib/active_record/autosave_association.rb +80 -73
- data/lib/active_record/base.rb +54 -451
- data/lib/active_record/callbacks.rb +60 -22
- data/lib/active_record/coders/yaml_column.rb +18 -21
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +347 -197
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +146 -138
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +25 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +19 -3
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +151 -142
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +499 -217
- data/lib/active_record/connection_adapters/abstract/transaction.rb +208 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +209 -44
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +169 -61
- data/lib/active_record/connection_adapters/column.rb +67 -36
- data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +28 -29
- data/lib/active_record/connection_adapters/mysql_adapter.rb +200 -73
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +98 -0
- data/lib/active_record/connection_adapters/postgresql/cast.rb +160 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +240 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +374 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +183 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +508 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +544 -899
- data/lib/active_record/connection_adapters/schema_cache.rb +76 -16
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +595 -16
- data/lib/active_record/connection_handling.rb +98 -0
- data/lib/active_record/core.rb +472 -0
- data/lib/active_record/counter_cache.rb +107 -108
- data/lib/active_record/dynamic_matchers.rb +115 -63
- data/lib/active_record/errors.rb +36 -18
- data/lib/active_record/explain.rb +15 -63
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +8 -4
- data/lib/active_record/fixture_set/file.rb +55 -0
- data/lib/active_record/fixtures.rb +159 -155
- data/lib/active_record/inheritance.rb +93 -59
- data/lib/active_record/integration.rb +8 -8
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +39 -43
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +19 -9
- data/lib/active_record/migration/command_recorder.rb +102 -33
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +411 -173
- data/lib/active_record/model_schema.rb +81 -94
- data/lib/active_record/nested_attributes.rb +173 -131
- data/lib/active_record/null_relation.rb +67 -0
- data/lib/active_record/persistence.rb +254 -106
- data/lib/active_record/query_cache.rb +18 -36
- data/lib/active_record/querying.rb +19 -15
- data/lib/active_record/railtie.rb +113 -38
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +4 -3
- data/lib/active_record/railties/databases.rake +115 -368
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +7 -3
- data/lib/active_record/reflection.rb +110 -61
- data/lib/active_record/relation/batches.rb +29 -29
- data/lib/active_record/relation/calculations.rb +155 -125
- data/lib/active_record/relation/delegation.rb +94 -18
- data/lib/active_record/relation/finder_methods.rb +151 -203
- data/lib/active_record/relation/merger.rb +188 -0
- data/lib/active_record/relation/predicate_builder.rb +85 -42
- data/lib/active_record/relation/query_methods.rb +793 -146
- data/lib/active_record/relation/spawn_methods.rb +43 -150
- data/lib/active_record/relation.rb +293 -173
- data/lib/active_record/result.rb +48 -7
- data/lib/active_record/runtime_registry.rb +17 -0
- data/lib/active_record/sanitization.rb +41 -54
- data/lib/active_record/schema.rb +19 -12
- data/lib/active_record/schema_dumper.rb +41 -41
- data/lib/active_record/schema_migration.rb +46 -0
- data/lib/active_record/scoping/default.rb +56 -52
- data/lib/active_record/scoping/named.rb +78 -103
- data/lib/active_record/scoping.rb +54 -124
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +9 -15
- data/lib/active_record/statement_cache.rb +26 -0
- data/lib/active_record/store.rb +131 -15
- data/lib/active_record/tasks/database_tasks.rb +204 -0
- data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +144 -0
- data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
- data/lib/active_record/test_case.rb +67 -38
- data/lib/active_record/timestamp.rb +16 -11
- data/lib/active_record/transactions.rb +73 -51
- data/lib/active_record/validations/associated.rb +19 -13
- data/lib/active_record/validations/presence.rb +65 -0
- data/lib/active_record/validations/uniqueness.rb +110 -57
- data/lib/active_record/validations.rb +18 -17
- data/lib/active_record/version.rb +7 -6
- data/lib/active_record.rb +63 -45
- data/lib/rails/generators/active_record/migration/migration_generator.rb +45 -8
- data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +4 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/model/model_generator.rb +5 -4
- data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -5
- metadata +43 -29
- data/examples/associations.png +0 -0
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -360
- data/lib/rails/generators/active_record/migration.rb +0 -15
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
data/MIT-LICENSE
CHANGED
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, :
|
53
|
-
:
|
52
|
+
composed_of :balance, class_name: 'Money',
|
53
|
+
mapping: %w(balance amount)
|
54
54
|
composed_of :address,
|
55
|
-
:
|
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
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
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(:
|
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
|
-
:
|
132
|
-
:
|
133
|
-
:
|
134
|
-
:
|
135
|
-
:
|
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.
|
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(
|
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 :
|
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/
|
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
|
|
data/examples/performance.rb
CHANGED
@@ -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
|
14
|
-
# the entity object (when the entity is initialized either
|
15
|
-
# existing object) and how it can be turned back into attributes
|
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, :
|
20
|
-
# composed_of :address, :
|
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
|
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.
|
105
|
-
# is
|
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
|
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
|
-
# :
|
136
|
-
# :
|
137
|
-
# :
|
138
|
-
# :
|
139
|
-
# :
|
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(:
|
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(:
|
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, :
|
194
|
-
# composed_of :balance, :
|
195
|
-
# :
|
196
|
-
# composed_of :address, :
|
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, :
|
205
|
+
# composed_of :gps_location, allow_nil: true
|
199
206
|
# composed_of :ip_address,
|
200
|
-
# :
|
201
|
-
# :
|
202
|
-
# :
|
203
|
-
# :
|
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 =
|
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
|
-
|
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
|
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
|
-
|
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
|
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
|
-
#
|
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
|
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
|
-
#
|
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
|
-
#
|
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.
|
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
|
133
|
-
#
|
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
|
-
|
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.
|
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
|
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
|
-
#
|
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
|
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
|
235
|
-
|
236
|
-
|
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
|