activerecord 4.0.3 → 4.0.4.rc1
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 +412 -0
- data/lib/active_record/associations.rb +2 -2
- data/lib/active_record/associations/association.rb +7 -2
- data/lib/active_record/associations/association_scope.rb +7 -2
- data/lib/active_record/associations/builder/belongs_to.rb +9 -3
- data/lib/active_record/associations/builder/singular_association.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +1 -1
- data/lib/active_record/associations/collection_proxy.rb +1 -1
- data/lib/active_record/associations/has_many_through_association.rb +1 -1
- data/lib/active_record/associations/has_one_association.rb +4 -2
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/attribute_methods.rb +32 -15
- data/lib/active_record/attribute_methods/serialization.rb +10 -0
- data/lib/active_record/autosave_association.rb +8 -2
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +14 -3
- data/lib/active_record/connection_adapters/abstract/quoting.rb +3 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +9 -9
- data/lib/active_record/connection_adapters/abstract/transaction.rb +4 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +7 -0
- data/lib/active_record/connection_adapters/column.rb +11 -3
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +1 -1
- data/lib/active_record/connection_adapters/mysql_adapter.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +3 -2
- data/lib/active_record/connection_adapters/postgresql/cast.rb +12 -8
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +20 -15
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +7 -3
- data/lib/active_record/core.rb +17 -9
- data/lib/active_record/counter_cache.rb +1 -1
- data/lib/active_record/dynamic_matchers.rb +7 -2
- data/lib/active_record/inheritance.rb +15 -7
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/migration.rb +10 -6
- data/lib/active_record/migration/command_recorder.rb +12 -5
- data/lib/active_record/persistence.rb +7 -6
- data/lib/active_record/railties/databases.rake +4 -5
- data/lib/active_record/relation/batches.rb +2 -5
- data/lib/active_record/relation/calculations.rb +3 -1
- data/lib/active_record/relation/finder_methods.rb +6 -5
- data/lib/active_record/relation/merger.rb +18 -2
- data/lib/active_record/relation/query_methods.rb +5 -6
- data/lib/active_record/sanitization.rb +6 -3
- data/lib/active_record/store.rb +1 -1
- data/lib/active_record/tasks/mysql_database_tasks.rb +2 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/timestamp.rb +2 -2
- data/lib/active_record/transactions.rb +7 -3
- data/lib/active_record/validations/presence.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +13 -5
- data/lib/active_record/version.rb +1 -1
- metadata +9 -9
@@ -89,17 +89,22 @@ module ActiveRecord
|
|
89
89
|
scope = scope.joins(join(foreign_table, constraint))
|
90
90
|
end
|
91
91
|
|
92
|
+
is_first_chain = i == 0
|
93
|
+
klass = is_first_chain ? self.klass : reflection.klass
|
94
|
+
|
92
95
|
# Exclude the scope of the association itself, because that
|
93
96
|
# was already merged in the #scope method.
|
94
97
|
scope_chain[i].each do |scope_chain_item|
|
95
|
-
klass = i == 0 ? self.klass : reflection.klass
|
96
98
|
item = eval_scope(klass, scope_chain_item)
|
97
99
|
|
98
100
|
if scope_chain_item == self.reflection.scope
|
99
101
|
scope.merge! item.except(:where, :includes)
|
100
102
|
end
|
101
103
|
|
102
|
-
|
104
|
+
if is_first_chain
|
105
|
+
scope.includes! item.includes_values
|
106
|
+
end
|
107
|
+
|
103
108
|
scope.where_values += item.where_values
|
104
109
|
scope.order_values |= item.order_values
|
105
110
|
end
|
@@ -5,7 +5,7 @@ module ActiveRecord::Associations::Builder
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def valid_options
|
8
|
-
super + [:foreign_type, :polymorphic, :touch]
|
8
|
+
super + [:foreign_type, :polymorphic, :touch, :counter_cache]
|
9
9
|
end
|
10
10
|
|
11
11
|
def constructable?
|
@@ -73,7 +73,13 @@ module ActiveRecord::Associations::Builder
|
|
73
73
|
old_foreign_id = changed_attributes[foreign_key_field]
|
74
74
|
|
75
75
|
if old_foreign_id
|
76
|
-
|
76
|
+
association = association(:#{name})
|
77
|
+
reflection = association.reflection
|
78
|
+
if reflection.polymorphic?
|
79
|
+
klass = send("#{reflection.foreign_type}_was").constantize
|
80
|
+
else
|
81
|
+
klass = association.klass
|
82
|
+
end
|
77
83
|
old_record = klass.find_by(klass.primary_key => old_foreign_id)
|
78
84
|
|
79
85
|
if old_record
|
@@ -82,7 +88,7 @@ module ActiveRecord::Associations::Builder
|
|
82
88
|
end
|
83
89
|
|
84
90
|
record = #{name}
|
85
|
-
|
91
|
+
if record && record.persisted?
|
86
92
|
record.touch #{options[:touch].inspect if options[:touch] != true}
|
87
93
|
end
|
88
94
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module ActiveRecord::Associations::Builder
|
2
2
|
class SingularAssociation < Association #:nodoc:
|
3
3
|
def valid_options
|
4
|
-
super + [:remote, :dependent, :
|
4
|
+
super + [:remote, :dependent, :primary_key, :inverse_of]
|
5
5
|
end
|
6
6
|
|
7
7
|
def constructable?
|
@@ -149,7 +149,7 @@ module ActiveRecord
|
|
149
149
|
|
150
150
|
delete_through_records(records)
|
151
151
|
|
152
|
-
if source_reflection.options[:counter_cache]
|
152
|
+
if source_reflection.options[:counter_cache] && method != :destroy
|
153
153
|
counter = source_reflection.counter_cache_column
|
154
154
|
klass.decrement_counter counter, records.map(&:id)
|
155
155
|
end
|
@@ -26,11 +26,13 @@ module ActiveRecord
|
|
26
26
|
load_target
|
27
27
|
|
28
28
|
return self.target if !(target || record)
|
29
|
-
|
29
|
+
|
30
|
+
assigning_another_record = target != record
|
31
|
+
if assigning_another_record || record.changed?
|
30
32
|
save &&= owner.persisted?
|
31
33
|
|
32
34
|
transaction_if(save) do
|
33
|
-
remove_target!(options[:dependent]) if target && !target.destroyed?
|
35
|
+
remove_target!(options[:dependent]) if target && !target.destroyed? && assigning_another_record
|
34
36
|
|
35
37
|
if record
|
36
38
|
set_owner_attributes(record)
|
@@ -48,20 +48,19 @@ module ActiveRecord
|
|
48
48
|
# Use a mutex; we don't want two thread simultaneously trying to define
|
49
49
|
# attribute methods.
|
50
50
|
generated_attribute_methods.synchronize do
|
51
|
-
return if attribute_methods_generated
|
51
|
+
return false if @attribute_methods_generated
|
52
52
|
superclass.define_attribute_methods unless self == base_class
|
53
53
|
super(column_names)
|
54
54
|
@attribute_methods_generated = true
|
55
55
|
end
|
56
|
-
|
57
|
-
|
58
|
-
def attribute_methods_generated? # :nodoc:
|
59
|
-
@attribute_methods_generated
|
56
|
+
true
|
60
57
|
end
|
61
58
|
|
62
59
|
def undefine_attribute_methods # :nodoc:
|
63
|
-
|
64
|
-
|
60
|
+
generated_attribute_methods.synchronize do
|
61
|
+
super if @attribute_methods_generated
|
62
|
+
@attribute_methods_generated = false
|
63
|
+
end
|
65
64
|
end
|
66
65
|
|
67
66
|
# Raises a <tt>ActiveRecord::DangerousAttributeError</tt> exception when an
|
@@ -88,7 +87,8 @@ module ActiveRecord
|
|
88
87
|
else
|
89
88
|
# If B < A and A defines its own attribute method, then we don't want to overwrite that.
|
90
89
|
defined = method_defined_within?(method_name, superclass, superclass.generated_attribute_methods)
|
91
|
-
|
90
|
+
base_defined = Base.method_defined?(method_name) || Base.private_method_defined?(method_name)
|
91
|
+
defined && !base_defined || super
|
92
92
|
end
|
93
93
|
end
|
94
94
|
|
@@ -110,6 +110,16 @@ module ActiveRecord
|
|
110
110
|
end
|
111
111
|
end
|
112
112
|
|
113
|
+
def find_generated_attribute_method(method_name) # :nodoc:
|
114
|
+
klass = self
|
115
|
+
until klass == Base
|
116
|
+
gen_methods = klass.generated_attribute_methods
|
117
|
+
return gen_methods.instance_method(method_name) if method_defined_within?(method_name, gen_methods, Object)
|
118
|
+
klass = klass.superclass
|
119
|
+
end
|
120
|
+
nil
|
121
|
+
end
|
122
|
+
|
113
123
|
# Returns +true+ if +attribute+ is an attribute method and table exists,
|
114
124
|
# +false+ otherwise.
|
115
125
|
#
|
@@ -143,13 +153,15 @@ module ActiveRecord
|
|
143
153
|
# If we haven't generated any methods yet, generate them, then
|
144
154
|
# see if we've created the method we're looking for.
|
145
155
|
def method_missing(method, *args, &block) # :nodoc:
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
156
|
+
self.class.define_attribute_methods
|
157
|
+
if respond_to_without_attributes?(method)
|
158
|
+
# make sure to invoke the correct attribute method, as we might have gotten here via a `super`
|
159
|
+
# call in a overwritten attribute method
|
160
|
+
if attribute_method = self.class.find_generated_attribute_method(method)
|
161
|
+
# this is probably horribly slow, but should only happen at most once for a given AR class
|
162
|
+
attribute_method.bind(self).call(*args, &block)
|
151
163
|
else
|
152
|
-
|
164
|
+
send(method, *args, &block)
|
153
165
|
end
|
154
166
|
else
|
155
167
|
super
|
@@ -188,7 +200,7 @@ module ActiveRecord
|
|
188
200
|
# person.respond_to(:nothing) # => false
|
189
201
|
def respond_to?(name, include_private = false)
|
190
202
|
name = name.to_s
|
191
|
-
self.class.define_attribute_methods
|
203
|
+
self.class.define_attribute_methods
|
192
204
|
result = super
|
193
205
|
|
194
206
|
# If the result is false the answer is false.
|
@@ -244,6 +256,11 @@ module ActiveRecord
|
|
244
256
|
}
|
245
257
|
end
|
246
258
|
|
259
|
+
# Placeholder so it can be overriden when needed by serialization
|
260
|
+
def attributes_for_coder # :nodoc:
|
261
|
+
attributes
|
262
|
+
end
|
263
|
+
|
247
264
|
# Returns an <tt>#inspect</tt>-like string for the value of the
|
248
265
|
# attribute +attr_name+. String attributes are truncated upto 50
|
249
266
|
# characters, and Date and Time attributes are returned in the
|
@@ -156,6 +156,16 @@ module ActiveRecord
|
|
156
156
|
super
|
157
157
|
end
|
158
158
|
end
|
159
|
+
|
160
|
+
def attributes_for_coder
|
161
|
+
attribute_names.each_with_object({}) do |name, attrs|
|
162
|
+
attrs[name] = if self.class.serialized_attributes.include?(name)
|
163
|
+
@attributes[name].serialized_value
|
164
|
+
else
|
165
|
+
read_attribute(name)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
159
169
|
end
|
160
170
|
end
|
161
171
|
end
|
@@ -382,9 +382,10 @@ module ActiveRecord
|
|
382
382
|
|
383
383
|
if autosave && record.marked_for_destruction?
|
384
384
|
record.destroy
|
385
|
-
|
385
|
+
elsif autosave != false
|
386
386
|
key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id
|
387
|
-
|
387
|
+
|
388
|
+
if (autosave && record.changed_for_autosave?) || new_record? || record_changed?(reflection, record, key)
|
388
389
|
unless reflection.through_reflection
|
389
390
|
record[reflection.foreign_key] = key
|
390
391
|
end
|
@@ -397,6 +398,11 @@ module ActiveRecord
|
|
397
398
|
end
|
398
399
|
end
|
399
400
|
|
401
|
+
# If the record is new or it has changed, returns true.
|
402
|
+
def record_changed?(reflection, record, key)
|
403
|
+
record.new_record? || record[reflection.foreign_key] != key || record.changed_attributes.include?(reflection.foreign_key)
|
404
|
+
end
|
405
|
+
|
400
406
|
# Saves the associated record if it's new or <tt>:autosave</tt> is enabled.
|
401
407
|
#
|
402
408
|
# In addition, it will destroy the association if it was marked for destruction.
|
@@ -128,7 +128,7 @@ module ActiveRecord
|
|
128
128
|
# record.credit_card_number = decrypt(record.credit_card_number)
|
129
129
|
# end
|
130
130
|
#
|
131
|
-
# alias_method :
|
131
|
+
# alias_method :after_initialize, :after_save
|
132
132
|
#
|
133
133
|
# private
|
134
134
|
# def encrypt(value)
|
@@ -163,7 +163,7 @@ module ActiveRecord
|
|
163
163
|
# record.send("#{@attribute}=", decrypt(record.send("#{@attribute}")))
|
164
164
|
# end
|
165
165
|
#
|
166
|
-
# alias_method :
|
166
|
+
# alias_method :after_initialize, :after_save
|
167
167
|
#
|
168
168
|
# private
|
169
169
|
# def encrypt(value)
|
@@ -398,7 +398,7 @@ module ActiveRecord
|
|
398
398
|
synchronize do
|
399
399
|
stale = Time.now - @dead_connection_timeout
|
400
400
|
connections.dup.each do |conn|
|
401
|
-
if conn.in_use? && stale > conn.last_use && !conn.
|
401
|
+
if conn.in_use? && stale > conn.last_use && !conn.active_threadsafe?
|
402
402
|
remove conn
|
403
403
|
end
|
404
404
|
end
|
@@ -21,6 +21,14 @@ module ActiveRecord
|
|
21
21
|
# Returns an array of record hashes with the column names as keys and
|
22
22
|
# column values as values.
|
23
23
|
def select_all(arel, name = nil, binds = [])
|
24
|
+
if arel.is_a?(Relation)
|
25
|
+
relation = arel
|
26
|
+
arel = relation.arel
|
27
|
+
if !binds || binds.empty?
|
28
|
+
binds = relation.bind_values
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
24
32
|
select(to_sql(arel, binds), name, binds)
|
25
33
|
end
|
26
34
|
|
@@ -41,13 +49,16 @@ module ActiveRecord
|
|
41
49
|
# Returns an array of the values of the first column in a select:
|
42
50
|
# select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
|
43
51
|
def select_values(arel, name = nil)
|
44
|
-
|
45
|
-
|
52
|
+
binds = []
|
53
|
+
if arel.is_a?(Relation)
|
54
|
+
arel, binds = arel.arel, arel.bind_values
|
55
|
+
end
|
56
|
+
select_rows(to_sql(arel, binds), name, binds).map(&:first)
|
46
57
|
end
|
47
58
|
|
48
59
|
# Returns an array of arrays containing the field values.
|
49
60
|
# Order is the same as that returned by +columns+.
|
50
|
-
def select_rows(sql, name = nil)
|
61
|
+
def select_rows(sql, name = nil, binds = [])
|
51
62
|
end
|
52
63
|
undef_method :select_rows
|
53
64
|
|
@@ -44,7 +44,9 @@ module ActiveRecord
|
|
44
44
|
# SQLite does not understand dates, so this method will convert a Date
|
45
45
|
# to a String.
|
46
46
|
def type_cast(value, column)
|
47
|
-
|
47
|
+
if value.respond_to?(:quoted_id) && value.respond_to?(:id)
|
48
|
+
return value.id
|
49
|
+
end
|
48
50
|
|
49
51
|
case value
|
50
52
|
when String, ActiveSupport::Multibyte::Chars
|
@@ -120,9 +120,9 @@ module ActiveRecord
|
|
120
120
|
# The name of the primary key, if one is to be added automatically.
|
121
121
|
# Defaults to +id+. If <tt>:id</tt> is false this option is ignored.
|
122
122
|
#
|
123
|
-
#
|
124
|
-
#
|
125
|
-
#
|
123
|
+
# Note that Active Record models will automatically detect their
|
124
|
+
# primary key. This can be avoided by using +self.primary_key=+ on the model
|
125
|
+
# to define the key explicitly.
|
126
126
|
#
|
127
127
|
# [<tt>:options</tt>]
|
128
128
|
# Any extra options you want appended to the table definition.
|
@@ -690,7 +690,7 @@ module ActiveRecord
|
|
690
690
|
|
691
691
|
column_type_sql
|
692
692
|
else
|
693
|
-
type
|
693
|
+
type.to_s
|
694
694
|
end
|
695
695
|
end
|
696
696
|
|
@@ -719,7 +719,7 @@ module ActiveRecord
|
|
719
719
|
# require the order columns appear in the SELECT.
|
720
720
|
#
|
721
721
|
# columns_for_distinct("posts.id", ["posts.created_at desc"])
|
722
|
-
def columns_for_distinct(columns, orders)
|
722
|
+
def columns_for_distinct(columns, orders) #:nodoc:
|
723
723
|
columns
|
724
724
|
end
|
725
725
|
|
@@ -741,6 +741,10 @@ module ActiveRecord
|
|
741
741
|
remove_column table_name, :created_at
|
742
742
|
end
|
743
743
|
|
744
|
+
def update_table_definition(table_name, base) #:nodoc:
|
745
|
+
Table.new(table_name, base)
|
746
|
+
end
|
747
|
+
|
744
748
|
protected
|
745
749
|
def add_index_sort_order(option_strings, column_names, options = {})
|
746
750
|
if options.is_a?(Hash) && order = options[:order]
|
@@ -873,10 +877,6 @@ module ActiveRecord
|
|
873
877
|
def create_alter_table(name)
|
874
878
|
AlterTable.new create_table_definition(name, false, {})
|
875
879
|
end
|
876
|
-
|
877
|
-
def update_table_definition(table_name, base)
|
878
|
-
Table.new(table_name, base)
|
879
|
-
end
|
880
880
|
end
|
881
881
|
end
|
882
882
|
end
|
@@ -39,6 +39,7 @@ module ActiveRecord
|
|
39
39
|
autoload :ClosedTransaction
|
40
40
|
autoload :RealTransaction
|
41
41
|
autoload :SavepointTransaction
|
42
|
+
autoload :TransactionState
|
42
43
|
end
|
43
44
|
|
44
45
|
# Active Record supports multiple database systems. AbstractAdapter and
|
@@ -325,6 +326,12 @@ module ActiveRecord
|
|
325
326
|
def active?
|
326
327
|
end
|
327
328
|
|
329
|
+
# Adapter should redefine this if it needs a threadsafe way to approximate
|
330
|
+
# if the connection is active
|
331
|
+
def active_threadsafe?
|
332
|
+
active?
|
333
|
+
end
|
334
|
+
|
328
335
|
# Disconnects from the database if already connected, and establishes a
|
329
336
|
# new connection with the database. Implementors should call super if they
|
330
337
|
# override the default implementation.
|
@@ -238,11 +238,19 @@ module ActiveRecord
|
|
238
238
|
end
|
239
239
|
end
|
240
240
|
|
241
|
-
def new_time(year, mon, mday, hour, min, sec, microsec)
|
241
|
+
def new_time(year, mon, mday, hour, min, sec, microsec, offset = nil)
|
242
242
|
# Treat 0000-00-00 00:00:00 as nil.
|
243
243
|
return nil if year.nil? || (year == 0 && mon == 0 && mday == 0)
|
244
244
|
|
245
|
-
|
245
|
+
if offset
|
246
|
+
time = Time.utc(year, mon, mday, hour, min, sec, microsec) rescue nil
|
247
|
+
return nil unless time
|
248
|
+
|
249
|
+
time -= offset
|
250
|
+
Base.default_timezone == :utc ? time : time.getlocal
|
251
|
+
else
|
252
|
+
Time.public_send(Base.default_timezone, year, mon, mday, hour, min, sec, microsec) rescue nil
|
253
|
+
end
|
246
254
|
end
|
247
255
|
|
248
256
|
def fast_string_to_date(string)
|
@@ -267,7 +275,7 @@ module ActiveRecord
|
|
267
275
|
time_hash = Date._parse(string)
|
268
276
|
time_hash[:sec_fraction] = microseconds(time_hash)
|
269
277
|
|
270
|
-
new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction))
|
278
|
+
new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset))
|
271
279
|
end
|
272
280
|
end
|
273
281
|
|