activerecord 4.0.5 → 4.0.6.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 +7 -0
- data/CHANGELOG.md +198 -0
- data/lib/active_record/association_relation.rb +4 -0
- data/lib/active_record/associations.rb +7 -1
- data/lib/active_record/associations/builder/belongs_to.rb +3 -4
- data/lib/active_record/associations/collection_association.rb +5 -5
- data/lib/active_record/associations/collection_proxy.rb +4 -2
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +1 -1
- data/lib/active_record/associations/has_many_association.rb +4 -4
- data/lib/active_record/associations/has_many_through_association.rb +5 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +1 -1
- data/lib/active_record/associations/preloader.rb +6 -27
- data/lib/active_record/associations/preloader/association.rb +1 -1
- data/lib/active_record/associations/singular_association.rb +3 -3
- data/lib/active_record/attribute_methods.rb +1 -0
- data/lib/active_record/attribute_methods/dirty.rb +2 -2
- data/lib/active_record/attribute_methods/serialization.rb +16 -5
- data/lib/active_record/base.rb +1 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +9 -12
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +6 -0
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +5 -5
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +9 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +10 -5
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +7 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +6 -3
- data/lib/active_record/connection_handling.rb +2 -2
- data/lib/active_record/core.rb +1 -0
- data/lib/active_record/locking/optimistic.rb +1 -1
- data/lib/active_record/migration.rb +1 -1
- data/lib/active_record/null_relation.rb +9 -3
- data/lib/active_record/persistence.rb +9 -7
- data/lib/active_record/railties/databases.rake +2 -1
- data/lib/active_record/reflection.rb +3 -3
- data/lib/active_record/relation.rb +4 -2
- data/lib/active_record/relation/merger.rb +10 -2
- data/lib/active_record/relation/query_methods.rb +2 -2
- data/lib/active_record/scoping/default.rb +3 -3
- data/lib/active_record/store.rb +16 -4
- data/lib/active_record/test_case.rb +6 -0
- data/lib/active_record/timestamp.rb +2 -2
- data/lib/active_record/transactions.rb +1 -1
- data/lib/active_record/version.rb +1 -1
- metadata +27 -37
@@ -18,11 +18,11 @@ module ActiveRecord
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def create(attributes = {}, &block)
|
21
|
-
|
21
|
+
_create_record(attributes, &block)
|
22
22
|
end
|
23
23
|
|
24
24
|
def create!(attributes = {}, &block)
|
25
|
-
|
25
|
+
_create_record(attributes, true, &block)
|
26
26
|
end
|
27
27
|
|
28
28
|
def build(attributes = {})
|
@@ -51,7 +51,7 @@ module ActiveRecord
|
|
51
51
|
replace(record)
|
52
52
|
end
|
53
53
|
|
54
|
-
def
|
54
|
+
def _create_record(attributes, raise_error = false)
|
55
55
|
record = build_record(attributes)
|
56
56
|
yield(record) if block_given?
|
57
57
|
saved = record.save
|
@@ -161,6 +161,7 @@ module ActiveRecord
|
|
161
161
|
# this is probably horribly slow, but should only happen at most once for a given AR class
|
162
162
|
attribute_method.bind(self).call(*args, &block)
|
163
163
|
else
|
164
|
+
return super unless respond_to_missing?(method, true)
|
164
165
|
send(method, *args, &block)
|
165
166
|
end
|
166
167
|
else
|
@@ -70,11 +70,11 @@ module ActiveRecord
|
|
70
70
|
super(attr, value)
|
71
71
|
end
|
72
72
|
|
73
|
-
def
|
73
|
+
def _update_record(*)
|
74
74
|
partial_writes? ? super(keys_for_partial_write) : super
|
75
75
|
end
|
76
76
|
|
77
|
-
def
|
77
|
+
def _create_record(*)
|
78
78
|
partial_writes? ? super(keys_for_partial_write) : super
|
79
79
|
end
|
80
80
|
|
@@ -27,7 +27,8 @@ module ActiveRecord
|
|
27
27
|
# ==== Parameters
|
28
28
|
#
|
29
29
|
# * +attr_name+ - The field name that should be serialized.
|
30
|
-
# * +
|
30
|
+
# * +class_name_or_coder+ - Optional, a coder object, which responds to `.load` / `.dump`
|
31
|
+
# or a class name that the object type should be equal to.
|
31
32
|
#
|
32
33
|
# ==== Example
|
33
34
|
#
|
@@ -35,13 +36,23 @@ module ActiveRecord
|
|
35
36
|
# class User < ActiveRecord::Base
|
36
37
|
# serialize :preferences
|
37
38
|
# end
|
38
|
-
|
39
|
+
#
|
40
|
+
# # Serialize preferences using JSON as coder.
|
41
|
+
# class User < ActiveRecord::Base
|
42
|
+
# serialize :preferences, JSON
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# # Serialize preferences as Hash using YAML coder.
|
46
|
+
# class User < ActiveRecord::Base
|
47
|
+
# serialize :preferences, Hash
|
48
|
+
# end
|
49
|
+
def serialize(attr_name, class_name_or_coder = Object)
|
39
50
|
include Behavior
|
40
51
|
|
41
|
-
coder = if [:load, :dump].all? { |x|
|
42
|
-
|
52
|
+
coder = if [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
|
53
|
+
class_name_or_coder
|
43
54
|
else
|
44
|
-
Coders::YAMLColumn.new(
|
55
|
+
Coders::YAMLColumn.new(class_name_or_coder)
|
45
56
|
end
|
46
57
|
|
47
58
|
# merge new serialized attribute and create new hash to ensure that each class in inheritance hierarchy
|
data/lib/active_record/base.rb
CHANGED
@@ -305,8 +305,8 @@ module ActiveRecord #:nodoc:
|
|
305
305
|
include Locking::Optimistic
|
306
306
|
include Locking::Pessimistic
|
307
307
|
include AttributeMethods
|
308
|
-
include Callbacks
|
309
308
|
include Timestamp
|
309
|
+
include Callbacks
|
310
310
|
include Associations
|
311
311
|
include ActiveModel::SecurePassword
|
312
312
|
include AutosaveAssociation
|
@@ -302,11 +302,11 @@ module ActiveRecord
|
|
302
302
|
run_callbacks(:save) { super }
|
303
303
|
end
|
304
304
|
|
305
|
-
def
|
305
|
+
def _create_record #:nodoc:
|
306
306
|
run_callbacks(:create) { super }
|
307
307
|
end
|
308
308
|
|
309
|
-
def
|
309
|
+
def _update_record(*) #:nodoc:
|
310
310
|
run_callbacks(:update) { super }
|
311
311
|
end
|
312
312
|
end
|
@@ -21,14 +21,7 @@ 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
|
-
|
25
|
-
relation = arel
|
26
|
-
arel = relation.arel
|
27
|
-
if !binds || binds.empty?
|
28
|
-
binds = relation.bind_values
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
24
|
+
arel, binds = binds_from_relation arel, binds
|
32
25
|
select(to_sql(arel, binds), name, binds)
|
33
26
|
end
|
34
27
|
|
@@ -49,10 +42,7 @@ module ActiveRecord
|
|
49
42
|
# Returns an array of the values of the first column in a select:
|
50
43
|
# select_values("SELECT id FROM companies LIMIT 3") => [1,2,3]
|
51
44
|
def select_values(arel, name = nil)
|
52
|
-
binds = []
|
53
|
-
if arel.is_a?(Relation)
|
54
|
-
arel, binds = arel.arel, arel.bind_values
|
55
|
-
end
|
45
|
+
arel, binds = binds_from_relation arel, []
|
56
46
|
select_rows(to_sql(arel, binds), name, binds).map(&:first)
|
57
47
|
end
|
58
48
|
|
@@ -396,6 +386,13 @@ module ActiveRecord
|
|
396
386
|
row = result.rows.first
|
397
387
|
row && row.first
|
398
388
|
end
|
389
|
+
|
390
|
+
def binds_from_relation(relation, binds)
|
391
|
+
if relation.is_a?(Relation) && binds.blank?
|
392
|
+
relation, binds = relation.arel, relation.bind_values
|
393
|
+
end
|
394
|
+
[relation, binds]
|
395
|
+
end
|
399
396
|
end
|
400
397
|
end
|
401
398
|
end
|
@@ -15,7 +15,7 @@ module ActiveRecord
|
|
15
15
|
# are typically created by methods in TableDefinition, and added to the
|
16
16
|
# +columns+ attribute of said TableDefinition object, in order to be used
|
17
17
|
# for generating a number of table creation or table changing SQL statements.
|
18
|
-
class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :primary_key) #:nodoc:
|
18
|
+
class ColumnDefinition < Struct.new(:name, :type, :limit, :precision, :scale, :default, :null, :first, :after, :primary_key, :sql_type) #:nodoc:
|
19
19
|
def string_to_binary(value)
|
20
20
|
value
|
21
21
|
end
|
@@ -869,6 +869,12 @@ module ActiveRecord
|
|
869
869
|
end
|
870
870
|
end
|
871
871
|
|
872
|
+
def quote_value(value, column)
|
873
|
+
column.sql_type ||= type_to_sql(column.type, column.limit, column.precision, column.scale)
|
874
|
+
|
875
|
+
quote(value, column)
|
876
|
+
end
|
877
|
+
|
872
878
|
private
|
873
879
|
def create_table_definition(name, temporary, options)
|
874
880
|
TableDefinition.new native_database_types, name, temporary, options
|
@@ -743,22 +743,22 @@ module ActiveRecord
|
|
743
743
|
end
|
744
744
|
|
745
745
|
def configure_connection
|
746
|
-
variables = @config
|
746
|
+
variables = @config.fetch(:variables, {}).stringify_keys
|
747
747
|
|
748
748
|
# By default, MySQL 'where id is null' selects the last inserted id.
|
749
749
|
# Turn this off. http://dev.rubyonrails.org/ticket/6778
|
750
|
-
variables[
|
750
|
+
variables['sql_auto_is_null'] = 0
|
751
751
|
|
752
752
|
# Increase timeout so the server doesn't disconnect us.
|
753
753
|
wait_timeout = @config[:wait_timeout]
|
754
754
|
wait_timeout = 2147483 unless wait_timeout.is_a?(Fixnum)
|
755
|
-
variables[
|
755
|
+
variables['wait_timeout'] = self.class.type_cast_config_to_integer(wait_timeout)
|
756
756
|
|
757
757
|
# Make MySQL reject illegal values rather than truncating or blanking them, see
|
758
758
|
# http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html#sqlmode_strict_all_tables
|
759
759
|
# If the user has provided another value for sql_mode, don't replace it.
|
760
|
-
if strict_mode? && !variables.has_key?(
|
761
|
-
variables[
|
760
|
+
if strict_mode? && !variables.has_key?('sql_mode')
|
761
|
+
variables['sql_mode'] = 'STRICT_ALL_TABLES'
|
762
762
|
end
|
763
763
|
|
764
764
|
# NAMES does not have an equals sign, see
|
@@ -143,10 +143,7 @@ module ActiveRecord
|
|
143
143
|
fields.each_with_index do |fname, i|
|
144
144
|
ftype = result.ftype i
|
145
145
|
fmod = result.fmod i
|
146
|
-
types[fname] =
|
147
|
-
warn "unknown OID: #{fname}(#{oid}) (#{sql})"
|
148
|
-
OID::Identity.new
|
149
|
-
}
|
146
|
+
types[fname] = get_oid_type(ftype, fmod, fname)
|
150
147
|
end
|
151
148
|
|
152
149
|
ret = ActiveRecord::Result.new(fields, result.values, types)
|
@@ -167,6 +167,15 @@ module ActiveRecord
|
|
167
167
|
end
|
168
168
|
result
|
169
169
|
end
|
170
|
+
|
171
|
+
# Does not quote function default values for UUID columns
|
172
|
+
def quote_default_value(value, column) #:nodoc:
|
173
|
+
if column.type == :uuid && value =~ /\(\)/
|
174
|
+
value
|
175
|
+
else
|
176
|
+
quote(value, column)
|
177
|
+
end
|
178
|
+
end
|
170
179
|
end
|
171
180
|
end
|
172
181
|
end
|
@@ -185,13 +185,15 @@ module ActiveRecord
|
|
185
185
|
def columns(table_name)
|
186
186
|
# Limit, precision, and scale are all handled by the superclass.
|
187
187
|
column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod|
|
188
|
-
oid =
|
189
|
-
OID::Identity.new
|
190
|
-
}
|
188
|
+
oid = get_oid_type(oid.to_i, fmod.to_i, column_name)
|
191
189
|
PostgreSQLColumn.new(column_name, default, oid, type, notnull == 'f')
|
192
190
|
end
|
193
191
|
end
|
194
192
|
|
193
|
+
def column_for(table_name, column_name) #:nodoc:
|
194
|
+
columns(table_name).detect { |c| c.name == column_name.to_s }
|
195
|
+
end
|
196
|
+
|
195
197
|
# Returns the current database name.
|
196
198
|
def current_database
|
197
199
|
query('select current_database()', 'SCHEMA')[0][0]
|
@@ -408,13 +410,16 @@ module ActiveRecord
|
|
408
410
|
# Changes the default value of a table column.
|
409
411
|
def change_column_default(table_name, column_name, default)
|
410
412
|
clear_cache!
|
411
|
-
|
413
|
+
column = column_for(table_name, column_name)
|
414
|
+
|
415
|
+
execute "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} SET DEFAULT #{quote_default_value(default, column)}" if column
|
412
416
|
end
|
413
417
|
|
414
418
|
def change_column_null(table_name, column_name, null, default = nil)
|
415
419
|
clear_cache!
|
416
420
|
unless null || default.nil?
|
417
|
-
|
421
|
+
column = column_for(table_name, column_name)
|
422
|
+
execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote_default_value(default, column)} WHERE #{quote_column_name(column_name)} IS NULL") if column
|
418
423
|
end
|
419
424
|
execute("ALTER TABLE #{quote_table_name(table_name)} ALTER #{quote_column_name(column_name)} #{null ? 'DROP' : 'SET'} NOT NULL")
|
420
425
|
end
|
@@ -749,6 +749,13 @@ module ActiveRecord
|
|
749
749
|
|
750
750
|
private
|
751
751
|
|
752
|
+
def get_oid_type(oid, fmod, column_name)
|
753
|
+
OID::TYPE_MAP.fetch(oid, fmod) {
|
754
|
+
warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String."
|
755
|
+
OID::TYPE_MAP[oid] = OID::Identity.new
|
756
|
+
}
|
757
|
+
end
|
758
|
+
|
752
759
|
def reload_type_map
|
753
760
|
OID::TYPE_MAP.clear
|
754
761
|
initialize_type_map
|
@@ -297,9 +297,12 @@ module ActiveRecord
|
|
297
297
|
# Don't cache statements if they are not prepared
|
298
298
|
if without_prepared_statement?(binds)
|
299
299
|
stmt = @connection.prepare(sql)
|
300
|
-
|
301
|
-
|
302
|
-
|
300
|
+
begin
|
301
|
+
cols = stmt.columns
|
302
|
+
records = stmt.to_a
|
303
|
+
ensure
|
304
|
+
stmt.close
|
305
|
+
end
|
303
306
|
stmt = records
|
304
307
|
else
|
305
308
|
cache = @statements[sql] ||= {
|
@@ -15,14 +15,14 @@ module ActiveRecord
|
|
15
15
|
# Example for SQLite database:
|
16
16
|
#
|
17
17
|
# ActiveRecord::Base.establish_connection(
|
18
|
-
# adapter: "
|
18
|
+
# adapter: "sqlite3",
|
19
19
|
# database: "path/to/dbfile"
|
20
20
|
# )
|
21
21
|
#
|
22
22
|
# Also accepts keys as strings (for parsing from YAML for example):
|
23
23
|
#
|
24
24
|
# ActiveRecord::Base.establish_connection(
|
25
|
-
# "adapter" => "
|
25
|
+
# "adapter" => "sqlite3",
|
26
26
|
# "database" => "path/to/dbfile"
|
27
27
|
# )
|
28
28
|
#
|
data/lib/active_record/core.rb
CHANGED
@@ -66,7 +66,7 @@ module ActiveRecord
|
|
66
66
|
send(lock_col + '=', previous_lock_value + 1)
|
67
67
|
end
|
68
68
|
|
69
|
-
def
|
69
|
+
def _update_record(attribute_names = @attributes.keys) #:nodoc:
|
70
70
|
return super unless locking_enabled?
|
71
71
|
return 0 if attribute_names.empty?
|
72
72
|
|
@@ -620,7 +620,7 @@ module ActiveRecord
|
|
620
620
|
|
621
621
|
say_with_time "#{method}(#{arg_list})" do
|
622
622
|
unless @connection.respond_to? :revert
|
623
|
-
unless arguments.empty? ||
|
623
|
+
unless arguments.empty? || [:execute, :enable_extension, :disable_extension].include?(method)
|
624
624
|
arguments[0] = Migrator.proper_table_name(arguments.first)
|
625
625
|
arguments[1] = Migrator.proper_table_name(arguments.second) if method == :rename_table
|
626
626
|
end
|
@@ -43,15 +43,21 @@ module ActiveRecord
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def count(*)
|
46
|
-
|
46
|
+
calculate :count, nil
|
47
47
|
end
|
48
48
|
|
49
49
|
def sum(*)
|
50
50
|
0
|
51
51
|
end
|
52
52
|
|
53
|
-
def calculate(
|
54
|
-
|
53
|
+
def calculate(operation, _column_name, _options = {})
|
54
|
+
# TODO: Remove _options argument as soon we remove support to
|
55
|
+
# activerecord-deprecated_finders.
|
56
|
+
if operation == :count
|
57
|
+
group_values.any? ? Hash.new : 0
|
58
|
+
else
|
59
|
+
nil
|
60
|
+
end
|
55
61
|
end
|
56
62
|
|
57
63
|
def exists?(_id = false)
|
@@ -37,7 +37,7 @@ module ActiveRecord
|
|
37
37
|
end
|
38
38
|
|
39
39
|
# Given an attributes hash, +instantiate+ returns a new instance of
|
40
|
-
# the appropriate class.
|
40
|
+
# the appropriate class. Accepts only keys as strings.
|
41
41
|
#
|
42
42
|
# For example, +Post.all+ may return Comments, Messages, and Emails
|
43
43
|
# by storing the record's subclass in a +type+ attribute. By calling
|
@@ -46,10 +46,10 @@ module ActiveRecord
|
|
46
46
|
#
|
47
47
|
# See +ActiveRecord::Inheritance#discriminate_class_for_record+ to see
|
48
48
|
# how this "single-table" inheritance mapping is implemented.
|
49
|
-
def instantiate(
|
50
|
-
klass = discriminate_class_for_record(
|
49
|
+
def instantiate(attributes, column_types = {})
|
50
|
+
klass = discriminate_class_for_record(attributes)
|
51
51
|
column_types = klass.decorate_columns(column_types.dup)
|
52
|
-
klass.allocate.init_with('attributes' =>
|
52
|
+
klass.allocate.init_with('attributes' => attributes, 'column_types' => column_types)
|
53
53
|
end
|
54
54
|
|
55
55
|
private
|
@@ -448,6 +448,8 @@ module ActiveRecord
|
|
448
448
|
@changed_attributes.except!(*changes.keys)
|
449
449
|
primary_key = self.class.primary_key
|
450
450
|
self.class.unscoped.where(primary_key => self[primary_key]).update_all(changes) == 1
|
451
|
+
else
|
452
|
+
true
|
451
453
|
end
|
452
454
|
end
|
453
455
|
|
@@ -475,13 +477,13 @@ module ActiveRecord
|
|
475
477
|
|
476
478
|
def create_or_update
|
477
479
|
raise ReadOnlyRecord if readonly?
|
478
|
-
result = new_record? ?
|
480
|
+
result = new_record? ? _create_record : _update_record
|
479
481
|
result != false
|
480
482
|
end
|
481
483
|
|
482
484
|
# Updates the associated record with values matching those of the instance attributes.
|
483
485
|
# Returns the number of affected rows.
|
484
|
-
def
|
486
|
+
def _update_record(attribute_names = @attributes.keys)
|
485
487
|
attributes_with_values = arel_attributes_with_values_for_update(attribute_names)
|
486
488
|
if attributes_with_values.empty?
|
487
489
|
0
|
@@ -504,7 +506,7 @@ module ActiveRecord
|
|
504
506
|
|
505
507
|
# Creates a record with values matching those of the instance attributes
|
506
508
|
# and returns its id.
|
507
|
-
def
|
509
|
+
def _create_record(attribute_names = @attributes.keys)
|
508
510
|
attributes_values = arel_attributes_with_values_for_create(attribute_names)
|
509
511
|
|
510
512
|
new_id = self.class.unscoped.insert attributes_values
|
@@ -287,7 +287,8 @@ db_namespace = namespace :db do
|
|
287
287
|
current_config = ActiveRecord::Tasks::DatabaseTasks.current_config
|
288
288
|
ActiveRecord::Tasks::DatabaseTasks.structure_dump(current_config, filename)
|
289
289
|
|
290
|
-
if ActiveRecord::Base.connection.supports_migrations?
|
290
|
+
if ActiveRecord::Base.connection.supports_migrations? &&
|
291
|
+
ActiveRecord::SchemaMigration.table_exists?
|
291
292
|
File.open(filename, "a") do |f|
|
292
293
|
f.puts ActiveRecord::Base.connection.dump_schema_information
|
293
294
|
end
|