activerecord 4.1.0 → 4.2.0
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 +776 -1330
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/association_relation.rb +4 -0
- data/lib/active_record/associations/alias_tracker.rb +14 -13
- data/lib/active_record/associations/association.rb +2 -2
- data/lib/active_record/associations/association_scope.rb +83 -43
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/builder/association.rb +15 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -6
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +66 -29
- data/lib/active_record/associations/collection_proxy.rb +22 -26
- data/lib/active_record/associations/has_many_association.rb +65 -18
- data/lib/active_record/associations/has_many_through_association.rb +55 -27
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +19 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +20 -12
- data/lib/active_record/associations/preloader/association.rb +34 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +49 -59
- data/lib/active_record/associations/singular_association.rb +25 -4
- data/lib/active_record/associations/through_association.rb +23 -14
- data/lib/active_record/associations.rb +171 -42
- data/lib/active_record/attribute.rb +149 -0
- data/lib/active_record/attribute_assignment.rb +18 -10
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +98 -44
- data/lib/active_record/attribute_methods/primary_key.rb +14 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +37 -147
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +34 -28
- data/lib/active_record/attribute_methods/write.rb +14 -21
- data/lib/active_record/attribute_methods.rb +67 -94
- data/lib/active_record/attribute_set/builder.rb +86 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attributes.rb +139 -0
- data/lib/active_record/autosave_association.rb +45 -38
- data/lib/active_record/base.rb +10 -20
- data/lib/active_record/callbacks.rb +7 -7
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +78 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +38 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -55
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +126 -54
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +198 -64
- data/lib/active_record/connection_adapters/abstract/transaction.rb +126 -114
- data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -55
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +240 -135
- data/lib/active_record/connection_adapters/column.rb +28 -239
- data/lib/active_record/connection_adapters/connection_specification.rb +16 -25
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -22
- data/lib/active_record/connection_adapters/mysql_adapter.rb +65 -149
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -27
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -374
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +55 -135
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +127 -38
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +220 -466
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -61
- data/lib/active_record/connection_handling.rb +3 -3
- data/lib/active_record/core.rb +143 -32
- data/lib/active_record/counter_cache.rb +60 -7
- data/lib/active_record/enum.rb +10 -11
- data/lib/active_record/errors.rb +49 -27
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +56 -70
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +35 -17
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +52 -49
- data/lib/active_record/model_schema.rb +49 -57
- data/lib/active_record/nested_attributes.rb +7 -7
- data/lib/active_record/null_relation.rb +19 -5
- data/lib/active_record/persistence.rb +50 -31
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +14 -11
- data/lib/active_record/railties/databases.rake +56 -54
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +286 -102
- data/lib/active_record/relation/batches.rb +0 -1
- data/lib/active_record/relation/calculations.rb +39 -31
- data/lib/active_record/relation/delegation.rb +2 -2
- data/lib/active_record/relation/finder_methods.rb +80 -36
- data/lib/active_record/relation/merger.rb +25 -30
- data/lib/active_record/relation/predicate_builder/array_handler.rb +31 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +11 -10
- data/lib/active_record/relation/query_methods.rb +141 -55
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +69 -30
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +58 -26
- data/lib/active_record/schema_migration.rb +11 -0
- data/lib/active_record/scoping/default.rb +8 -7
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +19 -10
- data/lib/active_record/tasks/database_tasks.rb +73 -7
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +11 -9
- data/lib/active_record/transactions.rb +37 -21
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +30 -0
- data/lib/active_record/type/date.rb +46 -0
- data/lib/active_record/type/date_time.rb +43 -0
- data/lib/active_record/type/decimal.rb +40 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
- data/lib/active_record/type/integer.rb +55 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +56 -0
- data/lib/active_record/type/string.rb +36 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +6 -4
- data/lib/active_record/validations/uniqueness.rb +11 -17
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +3 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +4 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +65 -10
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
data/README.rdoc
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
= Active Record -- Object-relational mapping
|
1
|
+
= Active Record -- Object-relational mapping in Rails
|
2
2
|
|
3
3
|
Active Record connects classes to relational database tables to establish an
|
4
4
|
almost zero-configuration persistence layer for applications. The library
|
@@ -20,8 +20,10 @@ A short rundown of some of the major features:
|
|
20
20
|
class Product < ActiveRecord::Base
|
21
21
|
end
|
22
22
|
|
23
|
-
|
24
|
-
|
23
|
+
{Learn more}[link:classes/ActiveRecord/Base.html]
|
24
|
+
|
25
|
+
The Product class is automatically mapped to the table named "products",
|
26
|
+
which might look like this:
|
25
27
|
|
26
28
|
CREATE TABLE products (
|
27
29
|
id int(11) NOT NULL auto_increment,
|
@@ -29,10 +31,8 @@ A short rundown of some of the major features:
|
|
29
31
|
PRIMARY KEY (id)
|
30
32
|
);
|
31
33
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
{Learn more}[link:classes/ActiveRecord/Base.html]
|
34
|
+
This would also define the following accessors: `Product#name` and
|
35
|
+
`Product#name=(new_name)`.
|
36
36
|
|
37
37
|
|
38
38
|
* Associations between objects defined by simple class methods.
|
@@ -130,7 +130,7 @@ A short rundown of some of the major features:
|
|
130
130
|
SQLite3[link:classes/ActiveRecord/ConnectionAdapters/SQLite3Adapter.html].
|
131
131
|
|
132
132
|
|
133
|
-
* Logging support for Log4r[
|
133
|
+
* Logging support for Log4r[https://github.com/colbygk/log4r] and Logger[http://www.ruby-doc.org/stdlib/libdoc/logger/rdoc].
|
134
134
|
|
135
135
|
ActiveRecord::Base.logger = ActiveSupport::Logger.new(STDOUT)
|
136
136
|
ActiveRecord::Base.logger = Log4r::Logger.new('Application Log')
|
@@ -192,7 +192,7 @@ The latest version of Active Record can be installed with RubyGems:
|
|
192
192
|
|
193
193
|
Source code can be downloaded as part of the Rails project on GitHub:
|
194
194
|
|
195
|
-
* https://github.com/rails/rails/tree/4-
|
195
|
+
* https://github.com/rails/rails/tree/4-2-stable/activerecord
|
196
196
|
|
197
197
|
|
198
198
|
== License
|
@@ -208,6 +208,11 @@ API documentation is at:
|
|
208
208
|
|
209
209
|
* http://api.rubyonrails.org
|
210
210
|
|
211
|
-
Bug reports
|
211
|
+
Bug reports can be filed for the Ruby on Rails project here:
|
212
212
|
|
213
213
|
* https://github.com/rails/rails/issues
|
214
|
+
|
215
|
+
Feature requests should be discussed on the rails-core mailing list here:
|
216
|
+
|
217
|
+
* https://groups.google.com/forum/?fromgroups#!forum/rubyonrails-core
|
218
|
+
|
@@ -129,10 +129,10 @@ module ActiveRecord
|
|
129
129
|
# is an instance of the value class. Specifying a custom converter allows the new value to be automatically
|
130
130
|
# converted to an instance of value class if necessary.
|
131
131
|
#
|
132
|
-
# For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that
|
133
|
-
#
|
134
|
-
# for the value class is called +create+ and it expects a CIDR address string as a parameter.
|
135
|
-
# values can be assigned to the value object using either another NetAddr::CIDR object, a string
|
132
|
+
# For example, the NetworkResource model has +network_address+ and +cidr_range+ attributes that should be
|
133
|
+
# aggregated using the NetAddr::CIDR value class (http://www.ruby-doc.org/gems/docs/n/netaddr-1.5.0/NetAddr/CIDR.html).
|
134
|
+
# The constructor for the value class is called +create+ and it expects a CIDR address string as a parameter.
|
135
|
+
# New values can be assigned to the value object using either another NetAddr::CIDR object, a string
|
136
136
|
# or an array. The <tt>:constructor</tt> and <tt>:converter</tt> options can be used to meet
|
137
137
|
# these requirements:
|
138
138
|
#
|
@@ -230,8 +230,8 @@ module ActiveRecord
|
|
230
230
|
private
|
231
231
|
def reader_method(name, class_name, mapping, allow_nil, constructor)
|
232
232
|
define_method(name) do
|
233
|
-
if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? {|
|
234
|
-
attrs = mapping.collect {|
|
233
|
+
if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? {|key, _| !_read_attribute(key).nil? })
|
234
|
+
attrs = mapping.collect {|key, _| _read_attribute(key)}
|
235
235
|
object = constructor.respond_to?(:call) ?
|
236
236
|
constructor.call(*attrs) :
|
237
237
|
class_name.constantize.send(constructor, *attrs)
|
@@ -244,15 +244,19 @@ module ActiveRecord
|
|
244
244
|
def writer_method(name, class_name, mapping, allow_nil, converter)
|
245
245
|
define_method("#{name}=") do |part|
|
246
246
|
klass = class_name.constantize
|
247
|
+
if part.is_a?(Hash)
|
248
|
+
part = klass.new(*part.values)
|
249
|
+
end
|
250
|
+
|
247
251
|
unless part.is_a?(klass) || converter.nil? || part.nil?
|
248
252
|
part = converter.respond_to?(:call) ? converter.call(part) : klass.send(converter, part)
|
249
253
|
end
|
250
254
|
|
251
255
|
if part.nil? && allow_nil
|
252
|
-
mapping.each { |
|
256
|
+
mapping.each { |key, _| self[key] = nil }
|
253
257
|
@aggregation_cache[name] = nil
|
254
258
|
else
|
255
|
-
mapping.each { |
|
259
|
+
mapping.each { |key, value| self[key] = part.send(value) }
|
256
260
|
@aggregation_cache[name] = part.freeze
|
257
261
|
end
|
258
262
|
end
|
@@ -32,8 +32,18 @@ module ActiveRecord
|
|
32
32
|
join.left.downcase.scan(
|
33
33
|
/join(?:\s+\w+)?\s+(\S+\s+)?#{quoted_name}\son/
|
34
34
|
).size
|
35
|
-
|
35
|
+
elsif join.respond_to? :left
|
36
36
|
join.left.table_name == name ? 1 : 0
|
37
|
+
else
|
38
|
+
# this branch is reached by two tests:
|
39
|
+
#
|
40
|
+
# activerecord/test/cases/associations/cascaded_eager_loading_test.rb:37
|
41
|
+
# with :posts
|
42
|
+
#
|
43
|
+
# activerecord/test/cases/associations/eager_test.rb:1133
|
44
|
+
# with :comments
|
45
|
+
#
|
46
|
+
0
|
37
47
|
end
|
38
48
|
end
|
39
49
|
|
@@ -47,20 +57,10 @@ module ActiveRecord
|
|
47
57
|
end
|
48
58
|
|
49
59
|
def aliased_table_for(table_name, aliased_name)
|
50
|
-
table_alias = aliased_name_for(table_name, aliased_name)
|
51
|
-
|
52
|
-
if table_alias == table_name
|
53
|
-
Arel::Table.new(table_name)
|
54
|
-
else
|
55
|
-
Arel::Table.new(table_name).alias(table_alias)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def aliased_name_for(table_name, aliased_name)
|
60
60
|
if aliases[table_name].zero?
|
61
61
|
# If it's zero, we can have our table_name
|
62
62
|
aliases[table_name] = 1
|
63
|
-
table_name
|
63
|
+
Arel::Table.new(table_name)
|
64
64
|
else
|
65
65
|
# Otherwise, we need to use an alias
|
66
66
|
aliased_name = connection.table_alias_for(aliased_name)
|
@@ -68,11 +68,12 @@ module ActiveRecord
|
|
68
68
|
# Update the count
|
69
69
|
aliases[aliased_name] += 1
|
70
70
|
|
71
|
-
if aliases[aliased_name] > 1
|
71
|
+
table_alias = if aliases[aliased_name] > 1
|
72
72
|
"#{truncate(aliased_name)}_#{aliases[aliased_name]}"
|
73
73
|
else
|
74
74
|
aliased_name
|
75
75
|
end
|
76
|
+
Arel::Table.new(table_name).alias(table_alias)
|
76
77
|
end
|
77
78
|
end
|
78
79
|
|
@@ -160,7 +160,7 @@ module ActiveRecord
|
|
160
160
|
def marshal_load(data)
|
161
161
|
reflection_name, ivars = data
|
162
162
|
ivars.each { |name, val| instance_variable_set(name, val) }
|
163
|
-
@reflection = @owner.class.
|
163
|
+
@reflection = @owner.class._reflect_on_association(reflection_name)
|
164
164
|
end
|
165
165
|
|
166
166
|
def initialize_attributes(record) #:nodoc:
|
@@ -179,7 +179,7 @@ module ActiveRecord
|
|
179
179
|
def creation_attributes
|
180
180
|
attributes = {}
|
181
181
|
|
182
|
-
if (reflection.
|
182
|
+
if (reflection.has_one? || reflection.collection?) && !options[:through]
|
183
183
|
attributes[reflection.foreign_key] = owner[reflection.active_record_primary_key]
|
184
184
|
|
185
185
|
if reflection.options[:as]
|
@@ -1,12 +1,33 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Associations
|
3
3
|
class AssociationScope #:nodoc:
|
4
|
-
INSTANCE = new
|
5
|
-
|
6
4
|
def self.scope(association, connection)
|
7
5
|
INSTANCE.scope association, connection
|
8
6
|
end
|
9
7
|
|
8
|
+
class BindSubstitution
|
9
|
+
def initialize(block)
|
10
|
+
@block = block
|
11
|
+
end
|
12
|
+
|
13
|
+
def bind_value(scope, column, value, alias_tracker)
|
14
|
+
substitute = alias_tracker.connection.substitute_at(column)
|
15
|
+
scope.bind_values += [[column, @block.call(value)]]
|
16
|
+
substitute
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.create(&block)
|
21
|
+
block = block ? block : lambda { |val| val }
|
22
|
+
new BindSubstitution.new(block)
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(bind_substitution)
|
26
|
+
@bind_substitution = bind_substitution
|
27
|
+
end
|
28
|
+
|
29
|
+
INSTANCE = create
|
30
|
+
|
10
31
|
def scope(association, connection)
|
11
32
|
klass = association.klass
|
12
33
|
reflection = association.reflection
|
@@ -22,6 +43,23 @@ module ActiveRecord
|
|
22
43
|
Arel::Nodes::InnerJoin
|
23
44
|
end
|
24
45
|
|
46
|
+
def self.get_bind_values(owner, chain)
|
47
|
+
binds = []
|
48
|
+
last_reflection = chain.last
|
49
|
+
|
50
|
+
binds << last_reflection.join_id_for(owner)
|
51
|
+
if last_reflection.type
|
52
|
+
binds << owner.class.base_class.name
|
53
|
+
end
|
54
|
+
|
55
|
+
chain.each_cons(2).each do |reflection, next_reflection|
|
56
|
+
if reflection.type
|
57
|
+
binds << next_reflection.klass.base_class.name
|
58
|
+
end
|
59
|
+
end
|
60
|
+
binds
|
61
|
+
end
|
62
|
+
|
25
63
|
private
|
26
64
|
|
27
65
|
def construct_tables(chain, klass, refl, alias_tracker)
|
@@ -49,10 +87,7 @@ module ActiveRecord
|
|
49
87
|
end
|
50
88
|
|
51
89
|
def bind_value(scope, column, value, alias_tracker)
|
52
|
-
|
53
|
-
column, scope.bind_values.length)
|
54
|
-
scope.bind_values += [[column, value]]
|
55
|
-
substitute
|
90
|
+
@bind_substitution.bind_value scope, column, value, alias_tracker
|
56
91
|
end
|
57
92
|
|
58
93
|
def bind(scope, table_name, column_name, value, tracker)
|
@@ -60,47 +95,55 @@ module ActiveRecord
|
|
60
95
|
bind_value scope, column, value, tracker
|
61
96
|
end
|
62
97
|
|
98
|
+
def last_chain_scope(scope, table, reflection, owner, tracker, assoc_klass)
|
99
|
+
join_keys = reflection.join_keys(assoc_klass)
|
100
|
+
key = join_keys.key
|
101
|
+
foreign_key = join_keys.foreign_key
|
102
|
+
|
103
|
+
bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key], tracker
|
104
|
+
scope = scope.where(table[key].eq(bind_val))
|
105
|
+
|
106
|
+
if reflection.type
|
107
|
+
value = owner.class.base_class.name
|
108
|
+
bind_val = bind scope, table.table_name, reflection.type, value, tracker
|
109
|
+
scope = scope.where(table[reflection.type].eq(bind_val))
|
110
|
+
else
|
111
|
+
scope
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def next_chain_scope(scope, table, reflection, tracker, assoc_klass, foreign_table, next_reflection)
|
116
|
+
join_keys = reflection.join_keys(assoc_klass)
|
117
|
+
key = join_keys.key
|
118
|
+
foreign_key = join_keys.foreign_key
|
119
|
+
|
120
|
+
constraint = table[key].eq(foreign_table[foreign_key])
|
121
|
+
|
122
|
+
if reflection.type
|
123
|
+
value = next_reflection.klass.base_class.name
|
124
|
+
bind_val = bind scope, table.table_name, reflection.type, value, tracker
|
125
|
+
scope = scope.where(table[reflection.type].eq(bind_val))
|
126
|
+
end
|
127
|
+
|
128
|
+
scope = scope.joins(join(foreign_table, constraint))
|
129
|
+
end
|
130
|
+
|
63
131
|
def add_constraints(scope, owner, assoc_klass, refl, tracker)
|
64
132
|
chain = refl.chain
|
65
133
|
scope_chain = refl.scope_chain
|
66
134
|
|
67
135
|
tables = construct_tables(chain, assoc_klass, refl, tracker)
|
68
136
|
|
137
|
+
owner_reflection = chain.last
|
138
|
+
table = tables.last
|
139
|
+
scope = last_chain_scope(scope, table, owner_reflection, owner, tracker, assoc_klass)
|
140
|
+
|
69
141
|
chain.each_with_index do |reflection, i|
|
70
142
|
table, foreign_table = tables.shift, tables.first
|
71
143
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
else
|
76
|
-
key = reflection.association_primary_key
|
77
|
-
end
|
78
|
-
|
79
|
-
foreign_key = reflection.foreign_key
|
80
|
-
else
|
81
|
-
key = reflection.foreign_key
|
82
|
-
foreign_key = reflection.active_record_primary_key
|
83
|
-
end
|
84
|
-
|
85
|
-
if reflection == chain.last
|
86
|
-
bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key], tracker
|
87
|
-
scope = scope.where(table[key].eq(bind_val))
|
88
|
-
|
89
|
-
if reflection.type
|
90
|
-
value = owner.class.base_class.name
|
91
|
-
bind_val = bind scope, table.table_name, reflection.type.to_s, value, tracker
|
92
|
-
scope = scope.where(table[reflection.type].eq(bind_val))
|
93
|
-
end
|
94
|
-
else
|
95
|
-
constraint = table[key].eq(foreign_table[foreign_key])
|
96
|
-
|
97
|
-
if reflection.type
|
98
|
-
value = chain[i + 1].klass.base_class.name
|
99
|
-
bind_val = bind scope, table.table_name, reflection.type.to_s, value, tracker
|
100
|
-
scope = scope.where(table[reflection.type].eq(bind_val))
|
101
|
-
end
|
102
|
-
|
103
|
-
scope = scope.joins(join(foreign_table, constraint))
|
144
|
+
unless reflection == chain.last
|
145
|
+
next_reflection = chain[i + 1]
|
146
|
+
scope = next_chain_scope(scope, table, reflection, tracker, assoc_klass, foreign_table, next_reflection)
|
104
147
|
end
|
105
148
|
|
106
149
|
is_first_chain = i == 0
|
@@ -120,6 +163,7 @@ module ActiveRecord
|
|
120
163
|
end
|
121
164
|
|
122
165
|
scope.where_values += item.where_values
|
166
|
+
scope.bind_values += item.bind_values
|
123
167
|
scope.order_values |= item.order_values
|
124
168
|
end
|
125
169
|
end
|
@@ -143,11 +187,7 @@ module ActiveRecord
|
|
143
187
|
end
|
144
188
|
|
145
189
|
def eval_scope(klass, scope, owner)
|
146
|
-
|
147
|
-
scope
|
148
|
-
else
|
149
|
-
klass.unscoped.instance_exec(owner, &scope)
|
150
|
-
end
|
190
|
+
klass.unscoped.instance_exec(owner, &scope)
|
151
191
|
end
|
152
192
|
end
|
153
193
|
end
|
@@ -31,6 +31,14 @@ module ActiveRecord
|
|
31
31
|
@updated
|
32
32
|
end
|
33
33
|
|
34
|
+
def decrement_counters # :nodoc:
|
35
|
+
with_cache_name { |name| decrement_counter name }
|
36
|
+
end
|
37
|
+
|
38
|
+
def increment_counters # :nodoc:
|
39
|
+
with_cache_name { |name| increment_counter name }
|
40
|
+
end
|
41
|
+
|
34
42
|
private
|
35
43
|
|
36
44
|
def find_target?
|
@@ -51,13 +59,15 @@ module ActiveRecord
|
|
51
59
|
end
|
52
60
|
end
|
53
61
|
|
54
|
-
def
|
55
|
-
|
62
|
+
def decrement_counter(counter_cache_name)
|
63
|
+
if foreign_key_present?
|
64
|
+
klass.decrement_counter(counter_cache_name, target_id)
|
65
|
+
end
|
56
66
|
end
|
57
67
|
|
58
|
-
def
|
68
|
+
def increment_counter(counter_cache_name)
|
59
69
|
if foreign_key_present?
|
60
|
-
klass.
|
70
|
+
klass.increment_counter(counter_cache_name, target_id)
|
61
71
|
end
|
62
72
|
end
|
63
73
|
|
@@ -82,7 +92,7 @@ module ActiveRecord
|
|
82
92
|
# has_one associations.
|
83
93
|
def invertible_for?(record)
|
84
94
|
inverse = inverse_reflection_for(record)
|
85
|
-
inverse && inverse.
|
95
|
+
inverse && inverse.has_one?
|
86
96
|
end
|
87
97
|
|
88
98
|
def target_id
|
@@ -36,6 +36,7 @@ module ActiveRecord::Associations::Builder
|
|
36
36
|
reflection = builder.build(model)
|
37
37
|
define_accessors model, reflection
|
38
38
|
define_callbacks model, reflection
|
39
|
+
define_validations model, reflection
|
39
40
|
builder.define_extensions model
|
40
41
|
reflection
|
41
42
|
end
|
@@ -85,7 +86,11 @@ module ActiveRecord::Associations::Builder
|
|
85
86
|
end
|
86
87
|
|
87
88
|
def self.define_callbacks(model, reflection)
|
88
|
-
|
89
|
+
if dependent = reflection.options[:dependent]
|
90
|
+
check_dependent_options(dependent)
|
91
|
+
add_destroy_callbacks(model, reflection)
|
92
|
+
end
|
93
|
+
|
89
94
|
Association.extensions.each do |extension|
|
90
95
|
extension.build model, reflection
|
91
96
|
end
|
@@ -120,17 +125,23 @@ module ActiveRecord::Associations::Builder
|
|
120
125
|
CODE
|
121
126
|
end
|
122
127
|
|
128
|
+
def self.define_validations(model, reflection)
|
129
|
+
# noop
|
130
|
+
end
|
131
|
+
|
123
132
|
def self.valid_dependent_options
|
124
133
|
raise NotImplementedError
|
125
134
|
end
|
126
135
|
|
127
136
|
private
|
128
137
|
|
129
|
-
def self.
|
130
|
-
unless valid_dependent_options.include?
|
131
|
-
raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{
|
138
|
+
def self.check_dependent_options(dependent)
|
139
|
+
unless valid_dependent_options.include? dependent
|
140
|
+
raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{dependent}"
|
132
141
|
end
|
142
|
+
end
|
133
143
|
|
144
|
+
def self.add_destroy_callbacks(model, reflection)
|
134
145
|
name = reflection.name
|
135
146
|
model.before_destroy lambda { |o| o.association(name).handle_dependency }
|
136
147
|
end
|
@@ -26,28 +26,9 @@ module ActiveRecord::Associations::Builder
|
|
26
26
|
private
|
27
27
|
|
28
28
|
def self.add_counter_cache_methods(mixin)
|
29
|
-
return if mixin.method_defined? :
|
29
|
+
return if mixin.method_defined? :belongs_to_counter_cache_after_update
|
30
30
|
|
31
31
|
mixin.class_eval do
|
32
|
-
def belongs_to_counter_cache_after_create(reflection)
|
33
|
-
if record = send(reflection.name)
|
34
|
-
cache_column = reflection.counter_cache_column
|
35
|
-
record.class.increment_counter(cache_column, record.id)
|
36
|
-
@_after_create_counter_called = true
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def belongs_to_counter_cache_before_destroy(reflection)
|
41
|
-
foreign_key = reflection.foreign_key.to_sym
|
42
|
-
unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
|
43
|
-
record = send reflection.name
|
44
|
-
if record && !self.destroyed?
|
45
|
-
cache_column = reflection.counter_cache_column
|
46
|
-
record.class.decrement_counter(cache_column, record.id)
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
32
|
def belongs_to_counter_cache_after_update(reflection)
|
52
33
|
foreign_key = reflection.foreign_key
|
53
34
|
cache_column = reflection.counter_cache_column
|
@@ -73,14 +54,6 @@ module ActiveRecord::Associations::Builder
|
|
73
54
|
def self.add_counter_cache_callbacks(model, reflection)
|
74
55
|
cache_column = reflection.counter_cache_column
|
75
56
|
|
76
|
-
model.after_create lambda { |record|
|
77
|
-
record.belongs_to_counter_cache_after_create(reflection)
|
78
|
-
}
|
79
|
-
|
80
|
-
model.before_destroy lambda { |record|
|
81
|
-
record.belongs_to_counter_cache_before_destroy(reflection)
|
82
|
-
}
|
83
|
-
|
84
57
|
model.after_update lambda { |record|
|
85
58
|
record.belongs_to_counter_cache_after_update(reflection)
|
86
59
|
}
|
@@ -130,9 +103,14 @@ module ActiveRecord::Associations::Builder
|
|
130
103
|
BelongsTo.touch_record(record, foreign_key, n, touch)
|
131
104
|
}
|
132
105
|
|
133
|
-
model.after_save callback
|
106
|
+
model.after_save callback, if: :changed?
|
134
107
|
model.after_touch callback
|
135
108
|
model.after_destroy callback
|
136
109
|
end
|
110
|
+
|
111
|
+
def self.add_destroy_callbacks(model, reflection)
|
112
|
+
name = reflection.name
|
113
|
+
model.after_destroy lambda { |o| o.association(name).handle_dependency }
|
114
|
+
end
|
137
115
|
end
|
138
116
|
end
|
@@ -11,11 +11,14 @@ module ActiveRecord::Associations::Builder
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def join_table
|
14
|
-
@join_table ||= [@lhs_class.table_name, klass.table_name].sort.join("\0").gsub(/^(.*_)(.+)\0\1(.+)/, '\1\2_\3').
|
14
|
+
@join_table ||= [@lhs_class.table_name, klass.table_name].sort.join("\0").gsub(/^(.*[._])(.+)\0\1(.+)/, '\1\2_\3').tr("\0", "_")
|
15
15
|
end
|
16
16
|
|
17
17
|
private
|
18
|
-
|
18
|
+
|
19
|
+
def klass
|
20
|
+
@lhs_class.send(:compute_type, @rhs_class_name)
|
21
|
+
end
|
19
22
|
end
|
20
23
|
|
21
24
|
def self.build(lhs_class, name, options)
|
@@ -60,13 +63,13 @@ module ActiveRecord::Associations::Builder
|
|
60
63
|
|
61
64
|
def self.add_left_association(name, options)
|
62
65
|
belongs_to name, options
|
63
|
-
self.left_reflection =
|
66
|
+
self.left_reflection = _reflect_on_association(name)
|
64
67
|
end
|
65
68
|
|
66
69
|
def self.add_right_association(name, options)
|
67
70
|
rhs_name = name.to_s.singularize.to_sym
|
68
71
|
belongs_to rhs_name, options
|
69
|
-
self.right_reflection =
|
72
|
+
self.right_reflection = _reflect_on_association(rhs_name)
|
70
73
|
end
|
71
74
|
|
72
75
|
}
|
@@ -95,7 +98,7 @@ module ActiveRecord::Associations::Builder
|
|
95
98
|
|
96
99
|
def middle_options(join_model)
|
97
100
|
middle_options = {}
|
98
|
-
middle_options[:
|
101
|
+
middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}"
|
99
102
|
middle_options[:source] = join_model.left_reflection.name
|
100
103
|
if options.key? :foreign_key
|
101
104
|
middle_options[:foreign_key] = options[:foreign_key]
|
@@ -107,7 +110,7 @@ module ActiveRecord::Associations::Builder
|
|
107
110
|
rhs_options = {}
|
108
111
|
|
109
112
|
if options.key? :class_name
|
110
|
-
rhs_options[:foreign_key] = options[:class_name].foreign_key
|
113
|
+
rhs_options[:foreign_key] = options[:class_name].to_s.foreign_key
|
111
114
|
rhs_options[:class_name] = options[:class_name]
|
112
115
|
end
|
113
116
|
|
@@ -5,7 +5,7 @@ module ActiveRecord::Associations::Builder
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def valid_options
|
8
|
-
super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache]
|
8
|
+
super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache, :join_table, :foreign_type]
|
9
9
|
end
|
10
10
|
|
11
11
|
def self.valid_dependent_options
|
@@ -5,7 +5,7 @@ module ActiveRecord::Associations::Builder
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def valid_options
|
8
|
-
valid = super + [:
|
8
|
+
valid = super + [:as, :foreign_type]
|
9
9
|
valid += [:through, :source, :source_type] if options[:through]
|
10
10
|
valid
|
11
11
|
end
|
@@ -16,7 +16,7 @@ module ActiveRecord::Associations::Builder
|
|
16
16
|
|
17
17
|
private
|
18
18
|
|
19
|
-
def self.
|
19
|
+
def self.add_destroy_callbacks(model, reflection)
|
20
20
|
super unless reflection.options[:through]
|
21
21
|
end
|
22
22
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module ActiveRecord::Associations::Builder
|
4
4
|
class SingularAssociation < Association #:nodoc:
|
5
5
|
def valid_options
|
6
|
-
super + [:
|
6
|
+
super + [:dependent, :primary_key, :inverse_of, :required]
|
7
7
|
end
|
8
8
|
|
9
9
|
def self.define_accessors(model, reflection)
|
@@ -27,5 +27,12 @@ module ActiveRecord::Associations::Builder
|
|
27
27
|
end
|
28
28
|
CODE
|
29
29
|
end
|
30
|
+
|
31
|
+
def self.define_validations(model, reflection)
|
32
|
+
super
|
33
|
+
if reflection.options[:required]
|
34
|
+
model.validates_presence_of reflection.name
|
35
|
+
end
|
36
|
+
end
|
30
37
|
end
|
31
38
|
end
|