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
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
module ActiveRecord
|
3
2
|
module ConnectionAdapters
|
4
3
|
class SchemaCache
|
@@ -12,15 +11,15 @@ module ActiveRecord
|
|
12
11
|
@columns_hash = {}
|
13
12
|
@primary_keys = {}
|
14
13
|
@tables = {}
|
15
|
-
prepare_default_proc
|
16
14
|
end
|
17
15
|
|
18
16
|
def primary_keys(table_name)
|
19
|
-
@primary_keys[table_name]
|
17
|
+
@primary_keys[table_name] ||= table_exists?(table_name) ? connection.primary_key(table_name) : nil
|
20
18
|
end
|
21
19
|
|
22
20
|
# A cached lookup for table existence.
|
23
21
|
def table_exists?(name)
|
22
|
+
prepare_tables if @tables.empty?
|
24
23
|
return @tables[name] if @tables.key? name
|
25
24
|
|
26
25
|
@tables[name] = connection.table_exists?(name)
|
@@ -29,9 +28,9 @@ module ActiveRecord
|
|
29
28
|
# Add internal cache for table with +table_name+.
|
30
29
|
def add(table_name)
|
31
30
|
if table_exists?(table_name)
|
32
|
-
|
33
|
-
|
34
|
-
|
31
|
+
primary_keys(table_name)
|
32
|
+
columns(table_name)
|
33
|
+
columns_hash(table_name)
|
35
34
|
end
|
36
35
|
end
|
37
36
|
|
@@ -40,14 +39,16 @@ module ActiveRecord
|
|
40
39
|
end
|
41
40
|
|
42
41
|
# Get the columns for a table
|
43
|
-
def columns(
|
44
|
-
@columns[
|
42
|
+
def columns(table_name)
|
43
|
+
@columns[table_name] ||= connection.columns(table_name)
|
45
44
|
end
|
46
45
|
|
47
46
|
# Get the columns for a table as a hash, key is the column name
|
48
47
|
# value is the column object.
|
49
|
-
def columns_hash(
|
50
|
-
@columns_hash[
|
48
|
+
def columns_hash(table_name)
|
49
|
+
@columns_hash[table_name] ||= Hash[columns(table_name).map { |col|
|
50
|
+
[col.name, col]
|
51
|
+
}]
|
51
52
|
end
|
52
53
|
|
53
54
|
# Clears out internal caches
|
@@ -76,33 +77,18 @@ module ActiveRecord
|
|
76
77
|
def marshal_dump
|
77
78
|
# if we get current version during initialization, it happens stack over flow.
|
78
79
|
@version = ActiveRecord::Migrator.current_version
|
79
|
-
[@version
|
80
|
-
Hash[val]
|
81
|
-
}
|
80
|
+
[@version, @columns, @columns_hash, @primary_keys, @tables]
|
82
81
|
end
|
83
82
|
|
84
83
|
def marshal_load(array)
|
85
84
|
@version, @columns, @columns_hash, @primary_keys, @tables = array
|
86
|
-
prepare_default_proc
|
87
85
|
end
|
88
86
|
|
89
87
|
private
|
90
88
|
|
91
|
-
|
92
|
-
|
93
|
-
h[table_name] = connection.columns(table_name)
|
94
|
-
end
|
95
|
-
|
96
|
-
@columns_hash.default_proc = Proc.new do |h, table_name|
|
97
|
-
h[table_name] = Hash[columns(table_name).map { |col|
|
98
|
-
[col.name, col]
|
99
|
-
}]
|
89
|
+
def prepare_tables
|
90
|
+
connection.tables.each { |table| @tables[table] = true }
|
100
91
|
end
|
101
|
-
|
102
|
-
@primary_keys.default_proc = Proc.new do |h, table_name|
|
103
|
-
h[table_name] = table_exists?(table_name) ? connection.primary_key(table_name) : nil
|
104
|
-
end
|
105
|
-
end
|
106
92
|
end
|
107
93
|
end
|
108
94
|
end
|
@@ -14,9 +14,9 @@ module ActiveRecord
|
|
14
14
|
raise ArgumentError, "No database file specified. Missing argument: database"
|
15
15
|
end
|
16
16
|
|
17
|
-
# Allow database path relative to Rails.root, but only if
|
18
|
-
#
|
19
|
-
#
|
17
|
+
# Allow database path relative to Rails.root, but only if the database
|
18
|
+
# path is not the special path that tells sqlite to build a database only
|
19
|
+
# in memory.
|
20
20
|
if ':memory:' != config[:database]
|
21
21
|
config[:database] = File.expand_path(config[:database], Rails.root) if defined?(Rails.root)
|
22
22
|
dirname = File.dirname(config[:database])
|
@@ -30,24 +30,32 @@ module ActiveRecord
|
|
30
30
|
|
31
31
|
db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout]
|
32
32
|
|
33
|
-
ConnectionAdapters::SQLite3Adapter.new(db, logger, config)
|
33
|
+
ConnectionAdapters::SQLite3Adapter.new(db, logger, nil, config)
|
34
34
|
rescue Errno::ENOENT => error
|
35
35
|
if error.message.include?("No such file or directory")
|
36
|
-
raise ActiveRecord::NoDatabaseError.new(error.message)
|
36
|
+
raise ActiveRecord::NoDatabaseError.new(error.message, error)
|
37
37
|
else
|
38
|
-
raise
|
38
|
+
raise
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
42
42
|
|
43
43
|
module ConnectionAdapters #:nodoc:
|
44
|
-
class
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
44
|
+
class SQLite3Binary < Type::Binary # :nodoc:
|
45
|
+
def cast_value(value)
|
46
|
+
if value.encoding != Encoding::ASCII_8BIT
|
47
|
+
value = value.force_encoding(Encoding::ASCII_8BIT)
|
48
|
+
end
|
49
|
+
value
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class SQLite3String < Type::String # :nodoc:
|
54
|
+
def type_cast_for_database(value)
|
55
|
+
if value.is_a?(::String) && value.encoding == Encoding::ASCII_8BIT
|
56
|
+
value.encode(Encoding::UTF_8)
|
57
|
+
else
|
58
|
+
super
|
51
59
|
end
|
52
60
|
end
|
53
61
|
end
|
@@ -63,13 +71,12 @@ module ActiveRecord
|
|
63
71
|
|
64
72
|
NATIVE_DATABASE_TYPES = {
|
65
73
|
primary_key: 'INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL',
|
66
|
-
string: { name: "varchar"
|
74
|
+
string: { name: "varchar" },
|
67
75
|
text: { name: "text" },
|
68
76
|
integer: { name: "integer" },
|
69
77
|
float: { name: "float" },
|
70
78
|
decimal: { name: "decimal" },
|
71
79
|
datetime: { name: "datetime" },
|
72
|
-
timestamp: { name: "datetime" },
|
73
80
|
time: { name: "time" },
|
74
81
|
date: { name: "date" },
|
75
82
|
binary: { name: "blob" },
|
@@ -123,11 +130,7 @@ module ActiveRecord
|
|
123
130
|
end
|
124
131
|
end
|
125
132
|
|
126
|
-
|
127
|
-
include Arel::Visitors::BindVisitor
|
128
|
-
end
|
129
|
-
|
130
|
-
def initialize(connection, logger, config)
|
133
|
+
def initialize(connection, logger, connection_options, config)
|
131
134
|
super(connection, logger)
|
132
135
|
|
133
136
|
@active = nil
|
@@ -135,11 +138,12 @@ module ActiveRecord
|
|
135
138
|
self.class.type_cast_config_to_integer(config.fetch(:statement_limit) { 1000 }))
|
136
139
|
@config = config
|
137
140
|
|
141
|
+
@visitor = Arel::Visitors::SQLite.new self
|
142
|
+
|
138
143
|
if self.class.type_cast_config_to_boolean(config.fetch(:prepared_statements) { true })
|
139
144
|
@prepared_statements = true
|
140
|
-
@visitor = Arel::Visitors::SQLite.new self
|
141
145
|
else
|
142
|
-
@
|
146
|
+
@prepared_statements = false
|
143
147
|
end
|
144
148
|
end
|
145
149
|
|
@@ -225,10 +229,19 @@ module ActiveRecord
|
|
225
229
|
|
226
230
|
# QUOTING ==================================================
|
227
231
|
|
228
|
-
def
|
229
|
-
|
230
|
-
|
231
|
-
"x'#{
|
232
|
+
def _quote(value) # :nodoc:
|
233
|
+
case value
|
234
|
+
when Type::Binary::Data
|
235
|
+
"x'#{value.hex}'"
|
236
|
+
else
|
237
|
+
super
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def _type_cast(value) # :nodoc:
|
242
|
+
case value
|
243
|
+
when BigDecimal
|
244
|
+
value.to_f
|
232
245
|
else
|
233
246
|
super
|
234
247
|
end
|
@@ -256,24 +269,11 @@ module ActiveRecord
|
|
256
269
|
end
|
257
270
|
end
|
258
271
|
|
259
|
-
def type_cast(value, column) # :nodoc:
|
260
|
-
return value.to_f if BigDecimal === value
|
261
|
-
return super unless String === value
|
262
|
-
return super unless column && value
|
263
|
-
|
264
|
-
value = super
|
265
|
-
if column.type == :string && value.encoding == Encoding::ASCII_8BIT
|
266
|
-
logger.error "Binary data inserted for `string` type on column `#{column.name}`" if logger
|
267
|
-
value = value.encode Encoding::UTF_8
|
268
|
-
end
|
269
|
-
value
|
270
|
-
end
|
271
|
-
|
272
272
|
# DATABASE STATEMENTS ======================================
|
273
273
|
|
274
274
|
def explain(arel, binds = [])
|
275
275
|
sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}"
|
276
|
-
ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN',
|
276
|
+
ExplainPrettyPrinter.new.pp(exec_query(sql, 'EXPLAIN', []))
|
277
277
|
end
|
278
278
|
|
279
279
|
class ExplainPrettyPrinter
|
@@ -385,7 +385,7 @@ module ActiveRecord
|
|
385
385
|
table_name && tables(nil, table_name).any?
|
386
386
|
end
|
387
387
|
|
388
|
-
# Returns an array of +
|
388
|
+
# Returns an array of +Column+ objects for the table specified by +table_name+.
|
389
389
|
def columns(table_name) #:nodoc:
|
390
390
|
table_structure(table_name).map do |field|
|
391
391
|
case field["dflt_value"]
|
@@ -397,7 +397,9 @@ module ActiveRecord
|
|
397
397
|
field["dflt_value"] = $1.gsub('""', '"')
|
398
398
|
end
|
399
399
|
|
400
|
-
|
400
|
+
sql_type = field['type']
|
401
|
+
cast_type = lookup_cast_type(sql_type)
|
402
|
+
new_column(field['name'], field['dflt_value'], cast_type, sql_type, field['notnull'].to_i == 0)
|
401
403
|
end
|
402
404
|
end
|
403
405
|
|
@@ -498,14 +500,19 @@ module ActiveRecord
|
|
498
500
|
end
|
499
501
|
|
500
502
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
alter_table(table_name, :rename => {column_name.to_s => new_column_name.to_s})
|
505
|
-
rename_column_indexes(table_name, column_name, new_column_name)
|
503
|
+
column = column_for(table_name, column_name)
|
504
|
+
alter_table(table_name, rename: {column.name => new_column_name.to_s})
|
505
|
+
rename_column_indexes(table_name, column.name, new_column_name)
|
506
506
|
end
|
507
507
|
|
508
508
|
protected
|
509
|
+
|
510
|
+
def initialize_type_map(m)
|
511
|
+
super
|
512
|
+
m.register_type(/binary/i, SQLite3Binary.new)
|
513
|
+
register_class_with_limit m, %r(char)i, SQLite3String
|
514
|
+
end
|
515
|
+
|
509
516
|
def select(sql, name = nil, binds = []) #:nodoc:
|
510
517
|
exec_query(sql, name, binds)
|
511
518
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module ConnectionHandling
|
3
|
-
RAILS_ENV = -> {
|
3
|
+
RAILS_ENV = -> { Rails.env if defined?(Rails) }
|
4
4
|
DEFAULT_ENV = -> { RAILS_ENV.call || "default_env" }
|
5
5
|
|
6
6
|
# Establishes the connection to the database. Accepts a hash as input where
|
data/lib/active_record/core.rb
CHANGED
@@ -16,7 +16,6 @@ module ActiveRecord
|
|
16
16
|
mattr_accessor :logger, instance_writer: false
|
17
17
|
|
18
18
|
##
|
19
|
-
# :singleton-method:
|
20
19
|
# Contains the database configuration - as is typically stored in config/database.yml -
|
21
20
|
# as a Hash.
|
22
21
|
#
|
@@ -85,6 +84,7 @@ module ActiveRecord
|
|
85
84
|
mattr_accessor :dump_schema_after_migration, instance_writer: false
|
86
85
|
self.dump_schema_after_migration = true
|
87
86
|
|
87
|
+
# :nodoc:
|
88
88
|
mattr_accessor :maintain_test_schema, instance_accessor: false
|
89
89
|
|
90
90
|
def self.disable_implicit_join_references=(value)
|
@@ -93,6 +93,7 @@ module ActiveRecord
|
|
93
93
|
end
|
94
94
|
|
95
95
|
class_attribute :default_connection_handler, instance_writer: false
|
96
|
+
class_attribute :find_by_statement_cache
|
96
97
|
|
97
98
|
def self.connection_handler
|
98
99
|
ActiveRecord::RuntimeRegistry.connection_handler || default_connection_handler
|
@@ -106,7 +107,79 @@ module ActiveRecord
|
|
106
107
|
end
|
107
108
|
|
108
109
|
module ClassMethods
|
110
|
+
def allocate
|
111
|
+
define_attribute_methods
|
112
|
+
super
|
113
|
+
end
|
114
|
+
|
115
|
+
def initialize_find_by_cache
|
116
|
+
self.find_by_statement_cache = {}.extend(Mutex_m)
|
117
|
+
end
|
118
|
+
|
119
|
+
def inherited(child_class)
|
120
|
+
child_class.initialize_find_by_cache
|
121
|
+
super
|
122
|
+
end
|
123
|
+
|
124
|
+
def find(*ids)
|
125
|
+
# We don't have cache keys for this stuff yet
|
126
|
+
return super unless ids.length == 1
|
127
|
+
return super if block_given? ||
|
128
|
+
primary_key.nil? ||
|
129
|
+
default_scopes.any? ||
|
130
|
+
columns_hash.include?(inheritance_column) ||
|
131
|
+
ids.first.kind_of?(Array)
|
132
|
+
|
133
|
+
id = ids.first
|
134
|
+
if ActiveRecord::Base === id
|
135
|
+
id = id.id
|
136
|
+
ActiveSupport::Deprecation.warn "You are passing an instance of ActiveRecord::Base to `find`." \
|
137
|
+
"Please pass the id of the object by calling `.id`"
|
138
|
+
end
|
139
|
+
key = primary_key
|
140
|
+
|
141
|
+
s = find_by_statement_cache[key] || find_by_statement_cache.synchronize {
|
142
|
+
find_by_statement_cache[key] ||= StatementCache.create(connection) { |params|
|
143
|
+
where(key => params.bind).limit(1)
|
144
|
+
}
|
145
|
+
}
|
146
|
+
record = s.execute([id], self, connection).first
|
147
|
+
unless record
|
148
|
+
raise RecordNotFound, "Couldn't find #{name} with '#{primary_key}'=#{id}"
|
149
|
+
end
|
150
|
+
record
|
151
|
+
end
|
152
|
+
|
153
|
+
def find_by(*args)
|
154
|
+
return super if current_scope || args.length > 1 || reflect_on_all_aggregations.any?
|
155
|
+
|
156
|
+
hash = args.first
|
157
|
+
|
158
|
+
return super if hash.values.any? { |v|
|
159
|
+
v.nil? || Array === v || Hash === v
|
160
|
+
}
|
161
|
+
|
162
|
+
key = hash.keys
|
163
|
+
|
164
|
+
klass = self
|
165
|
+
s = find_by_statement_cache[key] || find_by_statement_cache.synchronize {
|
166
|
+
find_by_statement_cache[key] ||= StatementCache.create(connection) { |params|
|
167
|
+
wheres = key.each_with_object({}) { |param,o|
|
168
|
+
o[param] = params.bind
|
169
|
+
}
|
170
|
+
klass.where(wheres).limit(1)
|
171
|
+
}
|
172
|
+
}
|
173
|
+
begin
|
174
|
+
s.execute(hash.values, self, connection).first
|
175
|
+
rescue TypeError => e
|
176
|
+
raise ActiveRecord::StatementInvalid.new(e.message, e)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
109
180
|
def initialize_generated_modules
|
181
|
+
super
|
182
|
+
|
110
183
|
generated_association_methods
|
111
184
|
end
|
112
185
|
|
@@ -180,16 +253,12 @@ module ActiveRecord
|
|
180
253
|
# # Instantiates a single new object
|
181
254
|
# User.new(first_name: 'Jamie')
|
182
255
|
def initialize(attributes = nil, options = {})
|
183
|
-
|
184
|
-
defaults.each { |k, v| defaults[k] = v.dup if v.duplicable? }
|
185
|
-
|
186
|
-
@attributes = self.class.initialize_attributes(defaults)
|
187
|
-
@column_types_override = nil
|
188
|
-
@column_types = self.class.column_types
|
256
|
+
@attributes = self.class.default_attributes.dup
|
189
257
|
|
190
258
|
init_internals
|
191
259
|
initialize_internals_callback
|
192
260
|
|
261
|
+
self.class.define_attribute_methods
|
193
262
|
# +options+ argument is only needed to make protected_attributes gem easier to hook.
|
194
263
|
# Remove it when we drop support to this gem.
|
195
264
|
init_attributes(attributes, options) if attributes
|
@@ -209,13 +278,11 @@ module ActiveRecord
|
|
209
278
|
# post.init_with('attributes' => { 'title' => 'hello world' })
|
210
279
|
# post.title # => 'hello world'
|
211
280
|
def init_with(coder)
|
212
|
-
@attributes
|
213
|
-
@column_types_override = coder['column_types']
|
214
|
-
@column_types = self.class.column_types
|
281
|
+
@attributes = coder['attributes']
|
215
282
|
|
216
283
|
init_internals
|
217
284
|
|
218
|
-
@new_record =
|
285
|
+
@new_record = coder['new_record']
|
219
286
|
|
220
287
|
self.class.define_attribute_methods
|
221
288
|
|
@@ -253,17 +320,13 @@ module ActiveRecord
|
|
253
320
|
|
254
321
|
##
|
255
322
|
def initialize_dup(other) # :nodoc:
|
256
|
-
|
257
|
-
self.class.
|
258
|
-
|
259
|
-
@attributes = cloned_attributes
|
260
|
-
@attributes[self.class.primary_key] = nil
|
323
|
+
@attributes = @attributes.dup
|
324
|
+
@attributes.reset(self.class.primary_key)
|
261
325
|
|
262
326
|
run_callbacks(:initialize) unless _initialize_callbacks.empty?
|
263
327
|
|
264
328
|
@aggregation_cache = {}
|
265
329
|
@association_cache = {}
|
266
|
-
@attributes_cache = {}
|
267
330
|
|
268
331
|
@new_record = true
|
269
332
|
@destroyed = false
|
@@ -284,7 +347,10 @@ module ActiveRecord
|
|
284
347
|
# Post.new.encode_with(coder)
|
285
348
|
# coder # => {"attributes" => {"id" => nil, ... }}
|
286
349
|
def encode_with(coder)
|
287
|
-
|
350
|
+
# FIXME: Remove this when we better serialize attributes
|
351
|
+
coder['raw_attributes'] = attributes_before_type_cast
|
352
|
+
coder['attributes'] = @attributes
|
353
|
+
coder['new_record'] = new_record?
|
288
354
|
end
|
289
355
|
|
290
356
|
# Returns true if +comparison_object+ is the same exact object, or +comparison_object+
|
@@ -307,7 +373,11 @@ module ActiveRecord
|
|
307
373
|
# Delegates to id in order to allow two records of the same type and id to work with something like:
|
308
374
|
# [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
|
309
375
|
def hash
|
310
|
-
id
|
376
|
+
if id
|
377
|
+
id.hash
|
378
|
+
else
|
379
|
+
super
|
380
|
+
end
|
311
381
|
end
|
312
382
|
|
313
383
|
# Clone and freeze the attributes hash such that associations are still
|
@@ -363,6 +433,29 @@ module ActiveRecord
|
|
363
433
|
"#<#{self.class} #{inspection}>"
|
364
434
|
end
|
365
435
|
|
436
|
+
# Takes a PP and prettily prints this record to it, allowing you to get a nice result from `pp record`
|
437
|
+
# when pp is required.
|
438
|
+
def pretty_print(pp)
|
439
|
+
pp.object_address_group(self) do
|
440
|
+
if defined?(@attributes) && @attributes
|
441
|
+
column_names = self.class.column_names.select { |name| has_attribute?(name) || new_record? }
|
442
|
+
pp.seplist(column_names, proc { pp.text ',' }) do |column_name|
|
443
|
+
column_value = read_attribute(column_name)
|
444
|
+
pp.breakable ' '
|
445
|
+
pp.group(1) do
|
446
|
+
pp.text column_name
|
447
|
+
pp.text ':'
|
448
|
+
pp.breakable
|
449
|
+
pp.pp column_value
|
450
|
+
end
|
451
|
+
end
|
452
|
+
else
|
453
|
+
pp.breakable ' '
|
454
|
+
pp.text 'not initialized'
|
455
|
+
end
|
456
|
+
end
|
457
|
+
end
|
458
|
+
|
366
459
|
# Returns a hash of the given methods with their names as keys and returned values as values.
|
367
460
|
def slice(*methods)
|
368
461
|
Hash[methods.map! { |method| [method, public_send(method)] }].with_indifferent_access
|
@@ -426,12 +519,10 @@ module ActiveRecord
|
|
426
519
|
end
|
427
520
|
|
428
521
|
def init_internals
|
429
|
-
|
430
|
-
@attributes[pk] = nil unless @attributes.key?(pk)
|
522
|
+
@attributes.ensure_initialized(self.class.primary_key)
|
431
523
|
|
432
524
|
@aggregation_cache = {}
|
433
525
|
@association_cache = {}
|
434
|
-
@attributes_cache = {}
|
435
526
|
@readonly = false
|
436
527
|
@destroyed = false
|
437
528
|
@marked_for_destruction = false
|
@@ -451,5 +542,11 @@ module ActiveRecord
|
|
451
542
|
def init_attributes(attributes, options)
|
452
543
|
assign_attributes(attributes)
|
453
544
|
end
|
545
|
+
|
546
|
+
def thaw
|
547
|
+
if frozen?
|
548
|
+
@attributes = @attributes.dup
|
549
|
+
end
|
550
|
+
end
|
454
551
|
end
|
455
552
|
end
|
@@ -11,7 +11,7 @@ module ActiveRecord
|
|
11
11
|
# ==== Parameters
|
12
12
|
#
|
13
13
|
# * +id+ - The id of the object you wish to reset a counter on.
|
14
|
-
# * +counters+ - One or more association counters to reset
|
14
|
+
# * +counters+ - One or more association counters to reset. Association name or counter name can be given.
|
15
15
|
#
|
16
16
|
# ==== Examples
|
17
17
|
#
|
@@ -19,9 +19,14 @@ module ActiveRecord
|
|
19
19
|
# Post.reset_counters(1, :comments)
|
20
20
|
def reset_counters(id, *counters)
|
21
21
|
object = find(id)
|
22
|
-
counters.each do |
|
23
|
-
has_many_association = _reflect_on_association(
|
24
|
-
|
22
|
+
counters.each do |counter_association|
|
23
|
+
has_many_association = _reflect_on_association(counter_association.to_sym)
|
24
|
+
unless has_many_association
|
25
|
+
has_many = reflect_on_all_associations(:has_many)
|
26
|
+
has_many_association = has_many.find { |association| association.counter_cache_column && association.counter_cache_column.to_sym == counter_association.to_sym }
|
27
|
+
counter_association = has_many_association.plural_name if has_many_association
|
28
|
+
end
|
29
|
+
raise ArgumentError, "'#{self.name}' has no association called '#{counter_association}'" unless has_many_association
|
25
30
|
|
26
31
|
if has_many_association.is_a? ActiveRecord::Reflection::ThroughReflection
|
27
32
|
has_many_association = has_many_association.through_reflection
|
@@ -29,11 +34,11 @@ module ActiveRecord
|
|
29
34
|
|
30
35
|
foreign_key = has_many_association.foreign_key.to_s
|
31
36
|
child_class = has_many_association.klass
|
32
|
-
reflection = child_class._reflections.values.find { |e|
|
37
|
+
reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
|
33
38
|
counter_name = reflection.counter_cache_column
|
34
39
|
|
35
40
|
stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
|
36
|
-
arel_table[counter_name] => object.send(
|
41
|
+
arel_table[counter_name] => object.send(counter_association).count(:all)
|
37
42
|
}, primary_key)
|
38
43
|
connection.update stmt
|
39
44
|
end
|
@@ -117,5 +122,54 @@ module ActiveRecord
|
|
117
122
|
update_counters(id, counter_name => -1)
|
118
123
|
end
|
119
124
|
end
|
125
|
+
|
126
|
+
protected
|
127
|
+
|
128
|
+
def actually_destroyed?
|
129
|
+
@_actually_destroyed
|
130
|
+
end
|
131
|
+
|
132
|
+
def clear_destroy_state
|
133
|
+
@_actually_destroyed = nil
|
134
|
+
end
|
135
|
+
|
136
|
+
private
|
137
|
+
|
138
|
+
def _create_record(*)
|
139
|
+
id = super
|
140
|
+
|
141
|
+
each_counter_cached_associations do |association|
|
142
|
+
if send(association.reflection.name)
|
143
|
+
association.increment_counters
|
144
|
+
@_after_create_counter_called = true
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
id
|
149
|
+
end
|
150
|
+
|
151
|
+
def destroy_row
|
152
|
+
affected_rows = super
|
153
|
+
|
154
|
+
if affected_rows > 0
|
155
|
+
each_counter_cached_associations do |association|
|
156
|
+
foreign_key = association.reflection.foreign_key.to_sym
|
157
|
+
unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
|
158
|
+
if send(association.reflection.name)
|
159
|
+
association.decrement_counters
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
affected_rows
|
166
|
+
end
|
167
|
+
|
168
|
+
def each_counter_cached_associations
|
169
|
+
_reflections.each do |name, reflection|
|
170
|
+
yield association(name) if reflection.belongs_to? && reflection.counter_cache_column
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
120
174
|
end
|
121
175
|
end
|
data/lib/active_record/enum.rb
CHANGED
@@ -27,8 +27,10 @@ module ActiveRecord
|
|
27
27
|
# conversation.status # => nil
|
28
28
|
#
|
29
29
|
# Scopes based on the allowed values of the enum field will be provided
|
30
|
-
# as well. With the above example
|
31
|
-
#
|
30
|
+
# as well. With the above example:
|
31
|
+
#
|
32
|
+
# Conversation.active
|
33
|
+
# Conversation.archived
|
32
34
|
#
|
33
35
|
# You can set the default value from the database declaration, like:
|
34
36
|
#
|
@@ -68,7 +70,7 @@ module ActiveRecord
|
|
68
70
|
# Where conditions on an enum attribute must use the ordinal value of an enum.
|
69
71
|
module Enum
|
70
72
|
def self.extended(base) # :nodoc:
|
71
|
-
base.class_attribute(:defined_enums
|
73
|
+
base.class_attribute(:defined_enums)
|
72
74
|
base.defined_enums = {}
|
73
75
|
end
|
74
76
|
|
@@ -138,19 +140,16 @@ module ActiveRecord
|
|
138
140
|
@_enum_methods_module ||= begin
|
139
141
|
mod = Module.new do
|
140
142
|
private
|
141
|
-
def save_changed_attribute(attr_name,
|
143
|
+
def save_changed_attribute(attr_name, old)
|
142
144
|
if (mapping = self.class.defined_enums[attr_name.to_s])
|
145
|
+
value = read_attribute(attr_name)
|
143
146
|
if attribute_changed?(attr_name)
|
144
|
-
old = changed_attributes[attr_name]
|
145
|
-
|
146
147
|
if mapping[old] == value
|
147
|
-
|
148
|
+
clear_attribute_changes([attr_name])
|
148
149
|
end
|
149
150
|
else
|
150
|
-
old = clone_attribute_value(:read_attribute, attr_name)
|
151
|
-
|
152
151
|
if old != value
|
153
|
-
|
152
|
+
set_attribute_was(attr_name, mapping.key(old))
|
154
153
|
end
|
155
154
|
end
|
156
155
|
else
|