activerecord 3.1.12 → 3.2.22.1
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 +804 -338
- data/README.rdoc +3 -3
- data/examples/performance.rb +20 -1
- data/lib/active_record/aggregations.rb +1 -1
- data/lib/active_record/associations/alias_tracker.rb +3 -6
- data/lib/active_record/associations/association.rb +13 -45
- data/lib/active_record/associations/association_scope.rb +3 -15
- data/lib/active_record/associations/belongs_to_association.rb +1 -1
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +2 -1
- data/lib/active_record/associations/builder/association.rb +6 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -4
- data/lib/active_record/associations/builder/collection_association.rb +2 -2
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +5 -6
- data/lib/active_record/associations/builder/singular_association.rb +3 -16
- data/lib/active_record/associations/collection_association.rb +65 -32
- data/lib/active_record/associations/collection_proxy.rb +8 -41
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +1 -0
- data/lib/active_record/associations/has_many_association.rb +11 -7
- data/lib/active_record/associations/has_many_through_association.rb +19 -9
- data/lib/active_record/associations/has_one_association.rb +23 -13
- data/lib/active_record/associations/join_dependency/join_association.rb +6 -1
- data/lib/active_record/associations/join_dependency.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +3 -3
- data/lib/active_record/associations/preloader.rb +14 -10
- data/lib/active_record/associations/through_association.rb +8 -4
- data/lib/active_record/associations.rb +92 -76
- data/lib/active_record/attribute_assignment.rb +221 -0
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
- data/lib/active_record/attribute_methods/dirty.rb +21 -11
- data/lib/active_record/attribute_methods/primary_key.rb +62 -25
- data/lib/active_record/attribute_methods/read.rb +73 -83
- data/lib/active_record/attribute_methods/serialization.rb +120 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -14
- data/lib/active_record/attribute_methods/write.rb +32 -6
- data/lib/active_record/attribute_methods.rb +231 -30
- data/lib/active_record/autosave_association.rb +44 -26
- data/lib/active_record/base.rb +227 -1708
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +150 -148
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +85 -29
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +7 -34
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +10 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +7 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +39 -28
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +48 -19
- data/lib/active_record/connection_adapters/abstract_adapter.rb +77 -42
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +676 -0
- data/lib/active_record/connection_adapters/column.rb +37 -11
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +133 -581
- data/lib/active_record/connection_adapters/mysql_adapter.rb +136 -693
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +209 -97
- data/lib/active_record/connection_adapters/schema_cache.rb +69 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -6
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +62 -35
- data/lib/active_record/counter_cache.rb +9 -4
- data/lib/active_record/dynamic_finder_match.rb +12 -0
- data/lib/active_record/dynamic_matchers.rb +84 -0
- data/lib/active_record/errors.rb +11 -1
- data/lib/active_record/explain.rb +86 -0
- data/lib/active_record/explain_subscriber.rb +25 -0
- data/lib/active_record/fixtures/file.rb +65 -0
- data/lib/active_record/fixtures.rb +57 -86
- data/lib/active_record/identity_map.rb +3 -4
- data/lib/active_record/inheritance.rb +174 -0
- data/lib/active_record/integration.rb +60 -0
- data/lib/active_record/locking/optimistic.rb +33 -26
- data/lib/active_record/locking/pessimistic.rb +23 -1
- data/lib/active_record/log_subscriber.rb +8 -4
- data/lib/active_record/migration/command_recorder.rb +8 -8
- data/lib/active_record/migration.rb +68 -35
- data/lib/active_record/model_schema.rb +368 -0
- data/lib/active_record/nested_attributes.rb +60 -24
- data/lib/active_record/persistence.rb +57 -11
- data/lib/active_record/query_cache.rb +6 -6
- data/lib/active_record/querying.rb +58 -0
- data/lib/active_record/railtie.rb +37 -29
- data/lib/active_record/railties/controller_runtime.rb +3 -1
- data/lib/active_record/railties/databases.rake +213 -117
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +26 -0
- data/lib/active_record/reflection.rb +7 -15
- data/lib/active_record/relation/batches.rb +7 -4
- data/lib/active_record/relation/calculations.rb +55 -16
- data/lib/active_record/relation/delegation.rb +49 -0
- data/lib/active_record/relation/finder_methods.rb +16 -11
- data/lib/active_record/relation/predicate_builder.rb +8 -6
- data/lib/active_record/relation/query_methods.rb +75 -9
- data/lib/active_record/relation/spawn_methods.rb +48 -7
- data/lib/active_record/relation.rb +78 -32
- data/lib/active_record/result.rb +10 -4
- data/lib/active_record/sanitization.rb +194 -0
- data/lib/active_record/schema_dumper.rb +12 -5
- data/lib/active_record/scoping/default.rb +142 -0
- data/lib/active_record/scoping/named.rb +200 -0
- data/lib/active_record/scoping.rb +152 -0
- data/lib/active_record/serialization.rb +1 -43
- data/lib/active_record/serializers/xml_serializer.rb +4 -45
- data/lib/active_record/session_store.rb +18 -16
- data/lib/active_record/store.rb +52 -0
- data/lib/active_record/test_case.rb +11 -7
- data/lib/active_record/timestamp.rb +17 -3
- data/lib/active_record/transactions.rb +27 -6
- data/lib/active_record/translation.rb +22 -0
- data/lib/active_record/validations/associated.rb +5 -4
- data/lib/active_record/validations/uniqueness.rb +8 -8
- data/lib/active_record/validations.rb +1 -1
- data/lib/active_record/version.rb +3 -3
- data/lib/active_record.rb +38 -3
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +12 -3
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -1
- data/lib/rails/generators/active_record/model/templates/migration.rb +3 -5
- data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +1 -5
- metadata +49 -28
- data/lib/active_record/named_scope.rb +0 -200
data/README.rdoc
CHANGED
@@ -197,13 +197,13 @@ Admit the Database:
|
|
197
197
|
|
198
198
|
== Download and installation
|
199
199
|
|
200
|
-
The latest version of Active Record can be installed with
|
200
|
+
The latest version of Active Record can be installed with RubyGems:
|
201
201
|
|
202
202
|
% [sudo] gem install activerecord
|
203
203
|
|
204
204
|
Source code can be downloaded as part of the Rails project on GitHub
|
205
205
|
|
206
|
-
* https://github.com/rails/rails/tree/
|
206
|
+
* https://github.com/rails/rails/tree/3-2-stable/activerecord
|
207
207
|
|
208
208
|
|
209
209
|
== License
|
@@ -215,7 +215,7 @@ Active Record is released under the MIT license.
|
|
215
215
|
|
216
216
|
API documentation is at
|
217
217
|
|
218
|
-
* http://api.rubyonrails.
|
218
|
+
* http://api.rubyonrails.org
|
219
219
|
|
220
220
|
Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
|
221
221
|
|
data/examples/performance.rb
CHANGED
@@ -31,6 +31,14 @@ class Exhibit < ActiveRecord::Base
|
|
31
31
|
def look; attributes end
|
32
32
|
def feel; look; user.name end
|
33
33
|
|
34
|
+
def self.with_name
|
35
|
+
where("name IS NOT NULL")
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.with_notes
|
39
|
+
where("notes IS NOT NULL")
|
40
|
+
end
|
41
|
+
|
34
42
|
def self.look(exhibits) exhibits.each { |e| e.look } end
|
35
43
|
def self.feel(exhibits) exhibits.each { |e| e.feel } end
|
36
44
|
end
|
@@ -39,7 +47,14 @@ puts 'Generating data...'
|
|
39
47
|
|
40
48
|
module ActiveRecord
|
41
49
|
class Faker
|
42
|
-
LOREM =
|
50
|
+
LOREM = %Q{Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse non aliquet diam. Curabitur vel urna metus, quis malesuada elit.
|
51
|
+
Integer consequat tincidunt felis. Etiam non erat dolor. Vivamus imperdiet nibh sit amet diam eleifend id posuere diam malesuada. Mauris at accumsan sem.
|
52
|
+
Donec id lorem neque. Fusce erat lorem, ornare eu congue vitae, malesuada quis neque. Maecenas vel urna a velit pretium fermentum. Donec tortor enim,
|
53
|
+
tempor venenatis egestas a, tempor sed ipsum. Ut arcu justo, faucibus non imperdiet ac, interdum at diam. Pellentesque ipsum enim, venenatis ut iaculis vitae,
|
54
|
+
varius vitae sem. Sed rutrum quam ac elit euismod bibendum. Donec ultricies ultricies magna, at lacinia libero mollis aliquam. Sed ac arcu in tortor elementum
|
55
|
+
tincidunt vel interdum sem. Curabitur eget erat arcu. Praesent eget eros leo. Nam magna enim, sollicitudin vehicula scelerisque in, vulputate ut libero.
|
56
|
+
Praesent varius tincidunt commodo}.split
|
57
|
+
|
43
58
|
def self.name
|
44
59
|
LOREM.grep(/^\w*$/).sort_by { rand }.first(2).join ' '
|
45
60
|
end
|
@@ -114,6 +129,10 @@ Benchmark.ips(TIME) do |x|
|
|
114
129
|
Exhibit.look Exhibit.limit(10000)
|
115
130
|
end
|
116
131
|
|
132
|
+
x.report 'Model.named_scope' do
|
133
|
+
Exhibit.limit(10).with_name.with_notes
|
134
|
+
end
|
135
|
+
|
117
136
|
x.report 'Model.create' do
|
118
137
|
Exhibit.create(exhibit)
|
119
138
|
end
|
@@ -176,7 +176,7 @@ module ActiveRecord
|
|
176
176
|
# order in which mappings are defined determines the order in which attributes are sent to the
|
177
177
|
# value class constructor.
|
178
178
|
# * <tt>:allow_nil</tt> - Specifies that the value object will not be instantiated when all mapped
|
179
|
-
# attributes are +nil+.
|
179
|
+
# attributes are +nil+. Setting the value object to +nil+ has the effect of writing +nil+ to all
|
180
180
|
# mapped attributes.
|
181
181
|
# This defaults to +false+.
|
182
182
|
# * <tt>:constructor</tt> - A symbol specifying the name of the constructor method or a Proc that
|
@@ -5,12 +5,13 @@ module ActiveRecord
|
|
5
5
|
# Keeps track of table aliases for ActiveRecord::Associations::ClassMethods::JoinDependency and
|
6
6
|
# ActiveRecord::Associations::ThroughAssociationScope
|
7
7
|
class AliasTracker # :nodoc:
|
8
|
-
attr_reader :aliases, :table_joins
|
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(table_joins = [])
|
11
|
+
def initialize(connection = ActiveRecord::Model.connection, table_joins = [])
|
12
12
|
@aliases = Hash.new { |h,k| h[k] = initial_count_for(k) }
|
13
13
|
@table_joins = table_joins
|
14
|
+
@connection = connection
|
14
15
|
end
|
15
16
|
|
16
17
|
def aliased_table_for(table_name, aliased_name = nil)
|
@@ -70,10 +71,6 @@ module ActiveRecord
|
|
70
71
|
def truncate(name)
|
71
72
|
name.slice(0, connection.table_alias_length - 2)
|
72
73
|
end
|
73
|
-
|
74
|
-
def connection
|
75
|
-
ActiveRecord::Base.connection
|
76
|
-
end
|
77
74
|
end
|
78
75
|
end
|
79
76
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'active_support/core_ext/array/wrap'
|
2
2
|
require 'active_support/core_ext/object/inclusion'
|
3
|
-
require 'active_support/deprecation'
|
4
3
|
|
5
4
|
module ActiveRecord
|
6
5
|
module Associations
|
@@ -47,6 +46,7 @@ module ActiveRecord
|
|
47
46
|
@loaded = false
|
48
47
|
IdentityMap.remove(target) if IdentityMap.enabled? && target
|
49
48
|
@target = nil
|
49
|
+
@stale_state = nil
|
50
50
|
end
|
51
51
|
|
52
52
|
# Reloads the \target and returns +self+ on success.
|
@@ -129,16 +129,21 @@ module ActiveRecord
|
|
129
129
|
# This method is abstract in the sense that it relies on +find_target+,
|
130
130
|
# which is expected to be provided by descendants.
|
131
131
|
#
|
132
|
-
# If the \target is
|
133
|
-
#
|
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.
|
134
137
|
#
|
135
138
|
# ActiveRecord::RecordNotFound is rescued within the method, and it is
|
136
139
|
# not reraised. The proxy is \reset and +nil+ is the return value.
|
137
140
|
def load_target
|
138
|
-
if find_target?
|
141
|
+
if (@stale_state && stale_target?) || find_target?
|
139
142
|
begin
|
140
143
|
if IdentityMap.enabled? && association_class && association_class.respond_to?(:base_class)
|
141
144
|
@target = IdentityMap.get(association_class, owner[reflection.foreign_key])
|
145
|
+
elsif @stale_state && stale_target?
|
146
|
+
@target = find_target
|
142
147
|
end
|
143
148
|
rescue NameError
|
144
149
|
nil
|
@@ -231,48 +236,11 @@ module ActiveRecord
|
|
231
236
|
end
|
232
237
|
|
233
238
|
def build_record(attributes, options)
|
234
|
-
reflection.
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
create_scope.except(*r.changed),
|
239
|
-
:without_protection => true
|
240
|
-
)
|
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)
|
241
243
|
end
|
242
|
-
|
243
|
-
if !reflection.original_build_association_called &&
|
244
|
-
(record.changed & create_scope.keys) != create_scope.keys
|
245
|
-
# We have detected that there is an overridden AssociationReflection#build_association
|
246
|
-
# method, but it looks like it has not passed through the block above. So try again and
|
247
|
-
# show a noisy deprecation warning.
|
248
|
-
|
249
|
-
record.assign_attributes(
|
250
|
-
create_scope.except(*record.changed),
|
251
|
-
:without_protection => true
|
252
|
-
)
|
253
|
-
|
254
|
-
method = reflection.method(:build_association)
|
255
|
-
if RUBY_VERSION >= '1.9.2'
|
256
|
-
source = method.source_location
|
257
|
-
debug_info = "It looks like the method is defined in #{source[0]} at line #{source[1]}."
|
258
|
-
else
|
259
|
-
debug_info = "This might help you find the method: #{method}. If you run this on Ruby 1.9.2 we can tell you exactly where the method is."
|
260
|
-
end
|
261
|
-
|
262
|
-
ActiveSupport::Deprecation.warn <<-WARN
|
263
|
-
It looks like ActiveRecord::Reflection::AssociationReflection#build_association has been redefined, either by you or by a plugin or library that you are using. The signature of this method has changed.
|
264
|
-
|
265
|
-
Before: def build_association(*options)
|
266
|
-
After: def build_association(*options, &block)
|
267
|
-
|
268
|
-
The block argument now needs to be passed through to ActiveRecord::Base#new when this method is overridden, or else your associations will not function correctly in Rails 3.2.
|
269
|
-
|
270
|
-
#{debug_info}
|
271
|
-
|
272
|
-
WARN
|
273
|
-
end
|
274
|
-
|
275
|
-
record
|
276
244
|
end
|
277
245
|
end
|
278
246
|
end
|
@@ -10,7 +10,7 @@ module ActiveRecord
|
|
10
10
|
|
11
11
|
def initialize(association)
|
12
12
|
@association = association
|
13
|
-
@alias_tracker = AliasTracker.new
|
13
|
+
@alias_tracker = AliasTracker.new klass.connection
|
14
14
|
end
|
15
15
|
|
16
16
|
def scope
|
@@ -20,31 +20,19 @@ module ActiveRecord
|
|
20
20
|
# It's okay to just apply all these like this. The options will only be present if the
|
21
21
|
# association supports that option; this is enforced by the association builder.
|
22
22
|
scope = scope.apply_finder_options(options.slice(
|
23
|
-
:readonly, :include, :order, :limit, :joins, :group, :having, :offset))
|
23
|
+
:readonly, :include, :order, :limit, :joins, :group, :having, :offset, :select))
|
24
24
|
|
25
25
|
if options[:through] && !options[:include]
|
26
26
|
scope = scope.includes(source_options[:include])
|
27
27
|
end
|
28
28
|
|
29
|
-
|
30
|
-
scope = scope.select(select)
|
31
|
-
end
|
29
|
+
scope = scope.uniq if options[:uniq]
|
32
30
|
|
33
31
|
add_constraints(scope)
|
34
32
|
end
|
35
33
|
|
36
34
|
private
|
37
35
|
|
38
|
-
def select_value
|
39
|
-
select_value = options[:select]
|
40
|
-
|
41
|
-
if reflection.collection?
|
42
|
-
select_value ||= options[:uniq] && "DISTINCT #{reflection.quoted_table_name}.*"
|
43
|
-
end
|
44
|
-
|
45
|
-
select_value
|
46
|
-
end
|
47
|
-
|
48
36
|
def add_constraints(scope)
|
49
37
|
tables = construct_tables
|
50
38
|
|
@@ -16,6 +16,10 @@ module ActiveRecord::Associations::Builder
|
|
16
16
|
@model, @name, @options = model, name, options
|
17
17
|
end
|
18
18
|
|
19
|
+
def mixin
|
20
|
+
@model.generated_feature_methods
|
21
|
+
end
|
22
|
+
|
19
23
|
def build
|
20
24
|
validate_options
|
21
25
|
reflection = model.create_reflection(self.class.macro, name, options, model)
|
@@ -36,16 +40,14 @@ module ActiveRecord::Associations::Builder
|
|
36
40
|
|
37
41
|
def define_readers
|
38
42
|
name = self.name
|
39
|
-
|
40
|
-
model.redefine_method(name) do |*params|
|
43
|
+
mixin.redefine_method(name) do |*params|
|
41
44
|
association(name).reader(*params)
|
42
45
|
end
|
43
46
|
end
|
44
47
|
|
45
48
|
def define_writers
|
46
49
|
name = self.name
|
47
|
-
|
48
|
-
model.redefine_method("#{name}=") do |value|
|
50
|
+
mixin.redefine_method("#{name}=") do |value|
|
49
51
|
association(name).writer(value)
|
50
52
|
end
|
51
53
|
end
|
@@ -25,16 +25,19 @@ module ActiveRecord::Associations::Builder
|
|
25
25
|
name = self.name
|
26
26
|
|
27
27
|
method_name = "belongs_to_counter_cache_after_create_for_#{name}"
|
28
|
-
|
28
|
+
mixin.redefine_method(method_name) do
|
29
29
|
record = send(name)
|
30
30
|
record.class.increment_counter(cache_column, record.id) unless record.nil?
|
31
31
|
end
|
32
32
|
model.after_create(method_name)
|
33
33
|
|
34
34
|
method_name = "belongs_to_counter_cache_before_destroy_for_#{name}"
|
35
|
-
|
35
|
+
mixin.redefine_method(method_name) do
|
36
36
|
record = send(name)
|
37
|
-
|
37
|
+
|
38
|
+
if record && !self.destroyed?
|
39
|
+
record.class.decrement_counter(cache_column, record.id)
|
40
|
+
end
|
38
41
|
end
|
39
42
|
model.before_destroy(method_name)
|
40
43
|
|
@@ -48,7 +51,7 @@ module ActiveRecord::Associations::Builder
|
|
48
51
|
method_name = "belongs_to_touch_after_save_or_destroy_for_#{name}"
|
49
52
|
touch = options[:touch]
|
50
53
|
|
51
|
-
|
54
|
+
mixin.redefine_method(method_name) do
|
52
55
|
record = send(name)
|
53
56
|
|
54
57
|
unless record.nil?
|
@@ -58,7 +58,7 @@ module ActiveRecord::Associations::Builder
|
|
58
58
|
super
|
59
59
|
|
60
60
|
name = self.name
|
61
|
-
|
61
|
+
mixin.redefine_method("#{name.to_s.singularize}_ids") do
|
62
62
|
association(name).ids_reader
|
63
63
|
end
|
64
64
|
end
|
@@ -67,7 +67,7 @@ module ActiveRecord::Associations::Builder
|
|
67
67
|
super
|
68
68
|
|
69
69
|
name = self.name
|
70
|
-
|
70
|
+
mixin.redefine_method("#{name.to_s.singularize}_ids=") do |ids|
|
71
71
|
association(name).ids_writer(ids)
|
72
72
|
end
|
73
73
|
end
|
@@ -28,7 +28,7 @@ module ActiveRecord::Associations::Builder
|
|
28
28
|
|
29
29
|
def define_destroy_dependency_method
|
30
30
|
name = self.name
|
31
|
-
|
31
|
+
mixin.redefine_method(dependency_method_name) do
|
32
32
|
send(name).each do |o|
|
33
33
|
# No point in executing the counter update since we're going to destroy the parent anyway
|
34
34
|
counter_method = ('belongs_to_counter_cache_before_destroy_for_' + self.class.name.downcase).to_sym
|
@@ -45,21 +45,21 @@ module ActiveRecord::Associations::Builder
|
|
45
45
|
|
46
46
|
def define_delete_all_dependency_method
|
47
47
|
name = self.name
|
48
|
-
|
48
|
+
mixin.redefine_method(dependency_method_name) do
|
49
49
|
association(name).delete_all_on_destroy
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
53
|
def define_nullify_dependency_method
|
54
54
|
name = self.name
|
55
|
-
|
55
|
+
mixin.redefine_method(dependency_method_name) do
|
56
56
|
send(name).delete_all
|
57
57
|
end
|
58
58
|
end
|
59
59
|
|
60
60
|
def define_restrict_dependency_method
|
61
61
|
name = self.name
|
62
|
-
|
62
|
+
mixin.redefine_method(dependency_method_name) do
|
63
63
|
raise ActiveRecord::DeleteRestrictionError.new(name) unless send(name).empty?
|
64
64
|
end
|
65
65
|
end
|
@@ -44,18 +44,17 @@ module ActiveRecord::Associations::Builder
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def define_destroy_dependency_method
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
eoruby
|
47
|
+
name = self.name
|
48
|
+
mixin.redefine_method(dependency_method_name) do
|
49
|
+
association(name).delete
|
50
|
+
end
|
52
51
|
end
|
53
52
|
alias :define_delete_dependency_method :define_destroy_dependency_method
|
54
53
|
alias :define_nullify_dependency_method :define_destroy_dependency_method
|
55
54
|
|
56
55
|
def define_restrict_dependency_method
|
57
56
|
name = self.name
|
58
|
-
|
57
|
+
mixin.redefine_method(dependency_method_name) do
|
59
58
|
raise ActiveRecord::DeleteRestrictionError.new(name) unless send(name).nil?
|
60
59
|
end
|
61
60
|
end
|
@@ -13,31 +13,18 @@ module ActiveRecord::Associations::Builder
|
|
13
13
|
|
14
14
|
private
|
15
15
|
|
16
|
-
def define_readers
|
17
|
-
super
|
18
|
-
name = self.name
|
19
|
-
|
20
|
-
model.redefine_method("#{name}_loaded?") do
|
21
|
-
ActiveSupport::Deprecation.warn(
|
22
|
-
"Calling obj.#{name}_loaded? is deprecated. Please use " \
|
23
|
-
"obj.association(:#{name}).loaded? instead."
|
24
|
-
)
|
25
|
-
association(name).loaded?
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
16
|
def define_constructors
|
30
17
|
name = self.name
|
31
18
|
|
32
|
-
|
19
|
+
mixin.redefine_method("build_#{name}") do |*params, &block|
|
33
20
|
association(name).build(*params, &block)
|
34
21
|
end
|
35
22
|
|
36
|
-
|
23
|
+
mixin.redefine_method("create_#{name}") do |*params, &block|
|
37
24
|
association(name).create(*params, &block)
|
38
25
|
end
|
39
26
|
|
40
|
-
|
27
|
+
mixin.redefine_method("create_#{name}!") do |*params, &block|
|
41
28
|
association(name).create!(*params, &block)
|
42
29
|
end
|
43
30
|
end
|
@@ -43,16 +43,24 @@ module ActiveRecord
|
|
43
43
|
|
44
44
|
# Implements the ids reader method, e.g. foo.item_ids for Foo.has_many :items
|
45
45
|
def ids_reader
|
46
|
-
if loaded? || options[:finder_sql]
|
46
|
+
if owner.new_record? || loaded? || options[:finder_sql]
|
47
47
|
load_target.map do |record|
|
48
48
|
record.send(reflection.association_primary_key)
|
49
49
|
end
|
50
50
|
else
|
51
51
|
column = "#{reflection.quoted_table_name}.#{reflection.association_primary_key}"
|
52
|
+
relation = scoped
|
52
53
|
|
53
|
-
|
54
|
-
|
54
|
+
including = (relation.eager_load_values + relation.includes_values).uniq
|
55
|
+
|
56
|
+
if including.any?
|
57
|
+
join_dependency = ActiveRecord::Associations::JoinDependency.new(reflection.klass, including, [])
|
58
|
+
relation = join_dependency.join_associations.inject(relation) do |r, association|
|
59
|
+
association.join_relation(r)
|
60
|
+
end
|
55
61
|
end
|
62
|
+
|
63
|
+
relation.pluck(column)
|
56
64
|
end
|
57
65
|
end
|
58
66
|
|
@@ -115,22 +123,16 @@ module ActiveRecord
|
|
115
123
|
create_record(attributes, options, true, &block)
|
116
124
|
end
|
117
125
|
|
118
|
-
# Add +records+ to this association.
|
126
|
+
# Add +records+ to this association. Returns +self+ so method calls may be chained.
|
119
127
|
# Since << flattens its argument list and inserts each record, +push+ and +concat+ behave identically.
|
120
128
|
def concat(*records)
|
121
|
-
result = true
|
122
129
|
load_target if owner.new_record?
|
123
130
|
|
124
|
-
|
125
|
-
records
|
126
|
-
|
127
|
-
|
128
|
-
result &&= insert_record(record) unless owner.new_record?
|
129
|
-
end
|
130
|
-
end
|
131
|
+
if owner.new_record?
|
132
|
+
concat_records(records)
|
133
|
+
else
|
134
|
+
transaction { concat_records(records) }
|
131
135
|
end
|
132
|
-
|
133
|
-
result && records
|
134
136
|
end
|
135
137
|
|
136
138
|
# Starts a transaction in the association class's database connection.
|
@@ -188,6 +190,8 @@ module ActiveRecord
|
|
188
190
|
# association, it will be used for the query. Otherwise, construct options and pass them with
|
189
191
|
# scope to the target class's +count+.
|
190
192
|
def count(column_name = nil, count_options = {})
|
193
|
+
return 0 if owner.new_record?
|
194
|
+
|
191
195
|
column_name, count_options = nil, column_name if column_name.is_a?(Hash)
|
192
196
|
|
193
197
|
if options[:counter_sql] || options[:finder_sql]
|
@@ -248,7 +252,7 @@ module ActiveRecord
|
|
248
252
|
# This method is abstract in the sense that it relies on
|
249
253
|
# +count_records+, which is a method descendants have to provide.
|
250
254
|
def size
|
251
|
-
if
|
255
|
+
if !find_target? || (loaded? && !options[:uniq])
|
252
256
|
target.size
|
253
257
|
elsif !loaded? && options[:group]
|
254
258
|
load_target.size
|
@@ -306,14 +310,10 @@ module ActiveRecord
|
|
306
310
|
other_array.each { |val| raise_on_type_mismatch(val) }
|
307
311
|
original_target = load_target.dup
|
308
312
|
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
@target = original_target
|
314
|
-
raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
|
315
|
-
"new records could not be saved."
|
316
|
-
end
|
313
|
+
if owner.new_record?
|
314
|
+
replace_records(other_array, original_target)
|
315
|
+
else
|
316
|
+
transaction { replace_records(other_array, original_target) }
|
317
317
|
end
|
318
318
|
end
|
319
319
|
|
@@ -364,7 +364,7 @@ module ActiveRecord
|
|
364
364
|
# replace the SELECT clause with COUNT(SELECTS), preserving any hints within /* ... */
|
365
365
|
interpolate(options[:finder_sql]).sub(/SELECT\b(\/\*.*?\*\/ )?(.*)\bFROM\b/im) do
|
366
366
|
count_with = $2.to_s
|
367
|
-
count_with = '*' if count_with.blank? || count_with =~ /,/
|
367
|
+
count_with = '*' if count_with.blank? || count_with =~ /,/ || count_with =~ /\.\*/
|
368
368
|
"SELECT #{$1}COUNT(#{count_with}) FROM"
|
369
369
|
end
|
370
370
|
end
|
@@ -409,7 +409,7 @@ module ActiveRecord
|
|
409
409
|
if mem_index
|
410
410
|
mem_record = memory.delete_at(mem_index)
|
411
411
|
|
412
|
-
(record.attribute_names - mem_record.changes.keys).each do |name|
|
412
|
+
((record.attribute_names & mem_record.attribute_names) - mem_record.changes.keys).each do |name|
|
413
413
|
mem_record[name] = record[name]
|
414
414
|
end
|
415
415
|
|
@@ -453,14 +453,20 @@ module ActiveRecord
|
|
453
453
|
records.each { |record| raise_on_type_mismatch(record) }
|
454
454
|
existing_records = records.reject { |r| r.new_record? }
|
455
455
|
|
456
|
-
|
457
|
-
|
456
|
+
if existing_records.empty?
|
457
|
+
remove_records(existing_records, records, method)
|
458
|
+
else
|
459
|
+
transaction { remove_records(existing_records, records, method) }
|
460
|
+
end
|
461
|
+
end
|
458
462
|
|
459
|
-
|
460
|
-
|
463
|
+
def remove_records(existing_records, records, method)
|
464
|
+
records.each { |record| callback(:before_remove, record) }
|
461
465
|
|
462
|
-
|
463
|
-
|
466
|
+
delete_records(existing_records, method) if existing_records.any?
|
467
|
+
records.each { |record| target.delete(record) }
|
468
|
+
|
469
|
+
records.each { |record| callback(:after_remove, record) }
|
464
470
|
end
|
465
471
|
|
466
472
|
# Delete the given records from the association, using one of the methods :destroy,
|
@@ -469,6 +475,31 @@ module ActiveRecord
|
|
469
475
|
raise NotImplementedError
|
470
476
|
end
|
471
477
|
|
478
|
+
def replace_records(new_target, original_target)
|
479
|
+
delete(target - new_target)
|
480
|
+
|
481
|
+
unless concat(new_target - target)
|
482
|
+
@target = original_target
|
483
|
+
raise RecordNotSaved, "Failed to replace #{reflection.name} because one or more of the " \
|
484
|
+
"new records could not be saved."
|
485
|
+
end
|
486
|
+
|
487
|
+
target
|
488
|
+
end
|
489
|
+
|
490
|
+
def concat_records(records)
|
491
|
+
result = true
|
492
|
+
|
493
|
+
records.flatten.each do |record|
|
494
|
+
raise_on_type_mismatch(record)
|
495
|
+
add_to_target(record) do |r|
|
496
|
+
result &&= insert_record(record) unless owner.new_record?
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
result && records
|
501
|
+
end
|
502
|
+
|
472
503
|
def callback(method, record)
|
473
504
|
callbacks_for(method).each do |callback|
|
474
505
|
case callback
|
@@ -540,7 +571,9 @@ module ActiveRecord
|
|
540
571
|
args.shift if args.first.is_a?(Hash) && args.first.empty?
|
541
572
|
|
542
573
|
collection = fetch_first_or_last_using_find?(args) ? scoped : load_target
|
543
|
-
collection.send(type, *args)
|
574
|
+
collection.send(type, *args).tap do |record|
|
575
|
+
set_inverse_instance record if record.is_a? ActiveRecord::Base
|
576
|
+
end
|
544
577
|
end
|
545
578
|
end
|
546
579
|
end
|