activerecord 4.1.15 → 4.2.0.beta1
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 +634 -2176
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/association_scope.rb +53 -21
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -11
- 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 +32 -44
- data/lib/active_record/associations/collection_proxy.rb +1 -10
- data/lib/active_record/associations/has_many_association.rb +60 -14
- data/lib/active_record/associations/has_many_through_association.rb +34 -23
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
- data/lib/active_record/associations/join_dependency.rb +7 -9
- data/lib/active_record/associations/preloader/association.rb +9 -5
- data/lib/active_record/associations/preloader/through_association.rb +3 -3
- data/lib/active_record/associations/preloader.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +16 -1
- data/lib/active_record/associations/through_association.rb +6 -22
- data/lib/active_record/associations.rb +58 -33
- data/lib/active_record/attribute.rb +131 -0
- data/lib/active_record/attribute_assignment.rb +19 -11
- 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 +85 -42
- data/lib/active_record/attribute_methods/primary_key.rb +6 -8
- data/lib/active_record/attribute_methods/read.rb +14 -57
- data/lib/active_record/attribute_methods/serialization.rb +12 -146
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
- data/lib/active_record/attribute_methods/write.rb +8 -23
- data/lib/active_record/attribute_methods.rb +53 -90
- data/lib/active_record/attribute_set/builder.rb +32 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attributes.rb +122 -0
- data/lib/active_record/autosave_association.rb +11 -21
- data/lib/active_record/base.rb +9 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
- data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
- data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
- data/lib/active_record/connection_adapters/column.rb +13 -244
- data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
- data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
- 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 -20
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -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 +76 -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 +85 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -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 -388
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +42 -122
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
- data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +119 -22
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -10
- data/lib/active_record/errors.rb +27 -26
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +52 -45
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +33 -8
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +34 -16
- 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 +22 -32
- data/lib/active_record/model_schema.rb +39 -48
- data/lib/active_record/nested_attributes.rb +8 -18
- data/lib/active_record/persistence.rb +39 -22
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +1 -8
- data/lib/active_record/railtie.rb +17 -10
- data/lib/active_record/railties/databases.rake +47 -42
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +225 -92
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +28 -32
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +42 -20
- data/lib/active_record/relation/merger.rb +0 -1
- data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
- data/lib/active_record/relation/predicate_builder.rb +1 -22
- data/lib/active_record/relation/query_methods.rb +98 -62
- data/lib/active_record/relation/spawn_methods.rb +6 -7
- data/lib/active_record/relation.rb +35 -11
- data/lib/active_record/result.rb +16 -9
- data/lib/active_record/sanitization.rb +8 -1
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +51 -9
- data/lib/active_record/schema_migration.rb +4 -0
- data/lib/active_record/scoping/default.rb +5 -4
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +79 -5
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +37 -5
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +35 -21
- data/lib/active_record/type/binary.rb +40 -0
- data/lib/active_record/type/boolean.rb +19 -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/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
- data/lib/active_record/type/integer.rb +23 -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 +51 -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 +48 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/type.rb +20 -0
- data/lib/active_record/validations/uniqueness.rb +9 -23
- data/lib/active_record/validations.rb +21 -16
- data/lib/active_record.rb +2 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +71 -14
- 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/
|
195
|
+
* https://github.com/rails/rails/tree/master/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
|
@@ -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,34 @@
|
|
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(
|
15
|
+
column, scope.bind_values.length)
|
16
|
+
scope.bind_values += [[column, @block.call(value)]]
|
17
|
+
substitute
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.create(&block)
|
22
|
+
block = block ? block : lambda { |val| val }
|
23
|
+
new BindSubstitution.new(block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(bind_substitution)
|
27
|
+
@bind_substitution = bind_substitution
|
28
|
+
end
|
29
|
+
|
30
|
+
INSTANCE = create
|
31
|
+
|
10
32
|
def scope(association, connection)
|
11
33
|
klass = association.klass
|
12
34
|
reflection = association.reflection
|
@@ -22,6 +44,23 @@ module ActiveRecord
|
|
22
44
|
Arel::Nodes::InnerJoin
|
23
45
|
end
|
24
46
|
|
47
|
+
def self.get_bind_values(owner, chain)
|
48
|
+
bvs = []
|
49
|
+
chain.each_with_index do |reflection, i|
|
50
|
+
if reflection == chain.last
|
51
|
+
bvs << reflection.join_id_for(owner)
|
52
|
+
if reflection.type
|
53
|
+
bvs << owner.class.base_class.name
|
54
|
+
end
|
55
|
+
else
|
56
|
+
if reflection.type
|
57
|
+
bvs << chain[i + 1].klass.base_class.name
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
bvs
|
62
|
+
end
|
63
|
+
|
25
64
|
private
|
26
65
|
|
27
66
|
def construct_tables(chain, klass, refl, alias_tracker)
|
@@ -49,10 +88,7 @@ module ActiveRecord
|
|
49
88
|
end
|
50
89
|
|
51
90
|
def bind_value(scope, column, value, alias_tracker)
|
52
|
-
|
53
|
-
column, scope.bind_values.length)
|
54
|
-
scope.bind_values += [[column, value]]
|
55
|
-
substitute
|
91
|
+
@bind_substitution.bind_value scope, column, value, alias_tracker
|
56
92
|
end
|
57
93
|
|
58
94
|
def bind(scope, table_name, column_name, value, tracker)
|
@@ -69,18 +105,9 @@ module ActiveRecord
|
|
69
105
|
chain.each_with_index do |reflection, i|
|
70
106
|
table, foreign_table = tables.shift, tables.first
|
71
107
|
|
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
|
108
|
+
join_keys = reflection.join_keys(assoc_klass)
|
109
|
+
key = join_keys.key
|
110
|
+
foreign_key = join_keys.foreign_key
|
84
111
|
|
85
112
|
if reflection == chain.last
|
86
113
|
bind_val = bind scope, table.table_name, key.to_s, owner[foreign_key], tracker
|
@@ -88,7 +115,7 @@ module ActiveRecord
|
|
88
115
|
|
89
116
|
if reflection.type
|
90
117
|
value = owner.class.base_class.name
|
91
|
-
bind_val = bind scope, table.table_name, reflection.type
|
118
|
+
bind_val = bind scope, table.table_name, reflection.type, value, tracker
|
92
119
|
scope = scope.where(table[reflection.type].eq(bind_val))
|
93
120
|
end
|
94
121
|
else
|
@@ -96,7 +123,7 @@ module ActiveRecord
|
|
96
123
|
|
97
124
|
if reflection.type
|
98
125
|
value = chain[i + 1].klass.base_class.name
|
99
|
-
bind_val = bind scope, table.table_name, reflection.type
|
126
|
+
bind_val = bind scope, table.table_name, reflection.type, value, tracker
|
100
127
|
scope = scope.where(table[reflection.type].eq(bind_val))
|
101
128
|
end
|
102
129
|
|
@@ -120,6 +147,7 @@ module ActiveRecord
|
|
120
147
|
end
|
121
148
|
|
122
149
|
scope.where_values += item.where_values
|
150
|
+
scope.bind_values += item.bind_values
|
123
151
|
scope.order_values |= item.order_values
|
124
152
|
end
|
125
153
|
end
|
@@ -143,7 +171,11 @@ module ActiveRecord
|
|
143
171
|
end
|
144
172
|
|
145
173
|
def eval_scope(klass, scope, owner)
|
146
|
-
|
174
|
+
if scope.is_a?(Relation)
|
175
|
+
scope
|
176
|
+
else
|
177
|
+
klass.unscoped.instance_exec(owner, &scope)
|
178
|
+
end
|
147
179
|
end
|
148
180
|
end
|
149
181
|
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
|
@@ -21,7 +21,7 @@ module ActiveRecord::Associations::Builder
|
|
21
21
|
end
|
22
22
|
self.extensions = []
|
23
23
|
|
24
|
-
self.valid_options = [:class_name, :
|
24
|
+
self.valid_options = [:class_name, :class, :foreign_key, :validate]
|
25
25
|
|
26
26
|
attr_reader :name, :scope, :options
|
27
27
|
|
@@ -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
|
@@ -72,22 +72,13 @@ module ActiveRecord::Associations::Builder
|
|
72
72
|
self.right_reflection = _reflect_on_association(rhs_name)
|
73
73
|
end
|
74
74
|
|
75
|
-
def hash
|
76
|
-
object_id.hash
|
77
|
-
end
|
78
|
-
|
79
|
-
def ==(other)
|
80
|
-
equal?(other)
|
81
|
-
end
|
82
|
-
alias :eql? :==
|
83
|
-
|
84
75
|
}
|
85
76
|
|
86
77
|
join_model.name = "HABTM_#{association_name.to_s.camelize}"
|
87
78
|
join_model.table_name_resolver = habtm
|
88
79
|
join_model.class_resolver = lhs_model
|
89
80
|
|
90
|
-
join_model.add_left_association :left_side,
|
81
|
+
join_model.add_left_association :left_side, class: lhs_model
|
91
82
|
join_model.add_right_association association_name, belongs_to_options(options)
|
92
83
|
join_model
|
93
84
|
end
|
@@ -107,7 +98,7 @@ module ActiveRecord::Associations::Builder
|
|
107
98
|
|
108
99
|
def middle_options(join_model)
|
109
100
|
middle_options = {}
|
110
|
-
middle_options[:
|
101
|
+
middle_options[:class] = join_model
|
111
102
|
middle_options[:source] = join_model.left_reflection.name
|
112
103
|
if options.key? :foreign_key
|
113
104
|
middle_options[:foreign_key] = options[:foreign_key]
|
@@ -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]
|
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
|
@@ -33,13 +33,7 @@ module ActiveRecord
|
|
33
33
|
reload
|
34
34
|
end
|
35
35
|
|
36
|
-
|
37
|
-
# Cache the proxy separately before the owner has an id
|
38
|
-
# or else a post-save proxy will still lack the id
|
39
|
-
@new_record_proxy ||= CollectionProxy.create(klass, self)
|
40
|
-
else
|
41
|
-
@proxy ||= CollectionProxy.create(klass, self)
|
42
|
-
end
|
36
|
+
@proxy ||= CollectionProxy.create(klass, self)
|
43
37
|
end
|
44
38
|
|
45
39
|
# Implements the writer method, e.g. foo.items= for Foo.has_many :items
|
@@ -61,9 +55,9 @@ module ActiveRecord
|
|
61
55
|
|
62
56
|
# Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items
|
63
57
|
def ids_writer(ids)
|
64
|
-
|
58
|
+
pk_type = reflection.primary_key_type
|
65
59
|
ids = Array(ids).reject { |id| id.blank? }
|
66
|
-
ids.map! { |i|
|
60
|
+
ids.map! { |i| pk_type.type_cast_from_user(i) }
|
67
61
|
replace(klass.find(ids).index_by { |r| r.id }.values_at(*ids))
|
68
62
|
end
|
69
63
|
|
@@ -129,16 +123,6 @@ module ActiveRecord
|
|
129
123
|
first_nth_or_last(:last, *args)
|
130
124
|
end
|
131
125
|
|
132
|
-
def take(n = nil)
|
133
|
-
if loaded?
|
134
|
-
n ? target.take(n) : target.first
|
135
|
-
else
|
136
|
-
scope.take(n).tap do |record|
|
137
|
-
set_inverse_instance record if record.is_a? ActiveRecord::Base
|
138
|
-
end
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
126
|
def build(attributes = {}, &block)
|
143
127
|
if attributes.is_a?(Array)
|
144
128
|
attributes.collect { |attr| build(attr, &block) }
|
@@ -161,9 +145,8 @@ module ActiveRecord
|
|
161
145
|
# be chained. Since << flattens its argument list and inserts each record,
|
162
146
|
# +push+ and +concat+ behave identically.
|
163
147
|
def concat(*records)
|
164
|
-
load_target if owner.new_record?
|
165
|
-
|
166
148
|
if owner.new_record?
|
149
|
+
load_target
|
167
150
|
concat_records(records)
|
168
151
|
else
|
169
152
|
transaction { concat_records(records) }
|
@@ -199,11 +182,11 @@ module ActiveRecord
|
|
199
182
|
#
|
200
183
|
# See delete for more info.
|
201
184
|
def delete_all(dependent = nil)
|
202
|
-
if dependent
|
185
|
+
if dependent && ![:nullify, :delete_all].include?(dependent)
|
203
186
|
raise ArgumentError, "Valid values are :nullify or :delete_all"
|
204
187
|
end
|
205
188
|
|
206
|
-
dependent = if dependent
|
189
|
+
dependent = if dependent
|
207
190
|
dependent
|
208
191
|
elsif options[:dependent] == :destroy
|
209
192
|
:delete_all
|
@@ -211,7 +194,7 @@ module ActiveRecord
|
|
211
194
|
options[:dependent]
|
212
195
|
end
|
213
196
|
|
214
|
-
|
197
|
+
delete_or_nullify_all_records(dependent).tap do
|
215
198
|
reset
|
216
199
|
loaded!
|
217
200
|
end
|
@@ -261,19 +244,12 @@ module ActiveRecord
|
|
261
244
|
# are actually removed from the database, that depends precisely on
|
262
245
|
# +delete_records+. They are in any case removed from the collection.
|
263
246
|
def delete(*records)
|
247
|
+
return if records.empty?
|
264
248
|
_options = records.extract_options!
|
265
249
|
dependent = _options[:dependent] || options[:dependent]
|
266
250
|
|
267
|
-
if records.
|
268
|
-
|
269
|
-
delete_or_destroy(load_target, dependent)
|
270
|
-
else
|
271
|
-
delete_records(:all, dependent)
|
272
|
-
end
|
273
|
-
else
|
274
|
-
records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
|
275
|
-
delete_or_destroy(records, dependent)
|
276
|
-
end
|
251
|
+
records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
|
252
|
+
delete_or_destroy(records, dependent)
|
277
253
|
end
|
278
254
|
|
279
255
|
# Deletes the +records+ and removes them from this association calling
|
@@ -282,6 +258,7 @@ module ActiveRecord
|
|
282
258
|
# Note that this method removes records from the database ignoring the
|
283
259
|
# +:dependent+ option.
|
284
260
|
def destroy(*records)
|
261
|
+
return if records.empty?
|
285
262
|
records = find(records) if records.any? { |record| record.kind_of?(Fixnum) || record.kind_of?(String) }
|
286
263
|
delete_or_destroy(records, :destroy)
|
287
264
|
end
|
@@ -375,7 +352,9 @@ module ActiveRecord
|
|
375
352
|
if owner.new_record?
|
376
353
|
replace_records(other_array, original_target)
|
377
354
|
else
|
378
|
-
|
355
|
+
if other_array != original_target
|
356
|
+
transaction { replace_records(other_array, original_target) }
|
357
|
+
end
|
379
358
|
end
|
380
359
|
end
|
381
360
|
|
@@ -384,7 +363,7 @@ module ActiveRecord
|
|
384
363
|
if record.new_record?
|
385
364
|
include_in_memory?(record)
|
386
365
|
else
|
387
|
-
loaded? ? target.include?(record) : scope.exists?(record)
|
366
|
+
loaded? ? target.include?(record) : scope.exists?(record.id)
|
388
367
|
end
|
389
368
|
else
|
390
369
|
false
|
@@ -427,9 +406,23 @@ module ActiveRecord
|
|
427
406
|
end
|
428
407
|
|
429
408
|
private
|
409
|
+
def get_records
|
410
|
+
return scope.to_a if reflection.scope_chain.any?(&:any?)
|
411
|
+
|
412
|
+
conn = klass.connection
|
413
|
+
sc = reflection.association_scope_cache(conn, owner) do
|
414
|
+
StatementCache.create(conn) { |params|
|
415
|
+
as = AssociationScope.create { params.bind }
|
416
|
+
target_scope.merge as.scope(self, conn)
|
417
|
+
}
|
418
|
+
end
|
419
|
+
|
420
|
+
binds = AssociationScope.get_bind_values(owner, reflection.chain)
|
421
|
+
sc.execute binds, klass, klass.connection
|
422
|
+
end
|
430
423
|
|
431
424
|
def find_target
|
432
|
-
records =
|
425
|
+
records = get_records
|
433
426
|
records.each { |record| set_inverse_instance(record) }
|
434
427
|
records
|
435
428
|
end
|
@@ -576,13 +569,8 @@ module ActiveRecord
|
|
576
569
|
if reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)
|
577
570
|
assoc = owner.association(reflection.through_reflection.name)
|
578
571
|
assoc.reader.any? { |source|
|
579
|
-
|
580
|
-
|
581
|
-
if target_association.respond_to?(:include?)
|
582
|
-
target_association.include?(record)
|
583
|
-
else
|
584
|
-
target_association == record
|
585
|
-
end
|
572
|
+
target = source.send(reflection.source_reflection.name)
|
573
|
+
target.respond_to?(:include?) ? target.include?(record) : target == record
|
586
574
|
} || target.include?(record)
|
587
575
|
else
|
588
576
|
target.include?(record)
|