activerecord 4.1.15 → 4.2.11.3
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 +5 -5
- data/CHANGELOG.md +1162 -1792
- data/README.rdoc +15 -10
- data/lib/active_record.rb +4 -0
- data/lib/active_record/aggregations.rb +15 -8
- data/lib/active_record/association_relation.rb +13 -0
- data/lib/active_record/associations.rb +158 -49
- data/lib/active_record/associations/alias_tracker.rb +3 -12
- data/lib/active_record/associations/association.rb +16 -4
- data/lib/active_record/associations/association_scope.rb +83 -38
- data/lib/active_record/associations/belongs_to_association.rb +28 -10
- data/lib/active_record/associations/builder/association.rb +15 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/collection_association.rb +5 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +63 -27
- data/lib/active_record/associations/collection_proxy.rb +29 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +83 -22
- data/lib/active_record/associations/has_many_through_association.rb +49 -26
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +26 -13
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/preloader.rb +36 -26
- data/lib/active_record/associations/preloader/association.rb +14 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/singular_association.rb +17 -2
- data/lib/active_record/associations/through_association.rb +5 -12
- data/lib/active_record/attribute.rb +163 -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.rb +56 -94
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +107 -43
- data/lib/active_record/attribute_methods/primary_key.rb +7 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +16 -150
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
- data/lib/active_record/attribute_methods/write.rb +9 -24
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +19 -12
- data/lib/active_record/base.rb +13 -24
- data/lib/active_record/callbacks.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
- data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
- data/lib/active_record/connection_adapters/column.rb +29 -240
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
- 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 +40 -25
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -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 +15 -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 +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -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 +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +163 -39
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -11
- data/lib/active_record/errors.rb +53 -30
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixtures.rb +55 -69
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locking/optimistic.rb +46 -26
- data/lib/active_record/migration.rb +71 -46
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/model_schema.rb +52 -58
- data/lib/active_record/nested_attributes.rb +5 -5
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +46 -26
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +18 -11
- data/lib/active_record/railties/databases.rake +50 -51
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +273 -114
- data/lib/active_record/relation.rb +57 -25
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +41 -37
- data/lib/active_record/relation/finder_methods.rb +70 -47
- data/lib/active_record/relation/merger.rb +39 -29
- data/lib/active_record/relation/predicate_builder.rb +16 -8
- data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
- data/lib/active_record/relation/query_methods.rb +114 -65
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +59 -28
- data/lib/active_record/schema_migration.rb +5 -4
- data/lib/active_record/scoping/default.rb +6 -4
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +61 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +32 -17
- data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +53 -27
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/integer.rb +59 -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 +62 -0
- data/lib/active_record/type/string.rb +40 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +110 -0
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +5 -3
- data/lib/active_record/validations/uniqueness.rb +25 -29
- 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 +66 -11
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -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)
|
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.to_sym) 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
@@ -18,17 +18,18 @@ module ActiveRecord
|
|
18
18
|
# conversation.archived? # => true
|
19
19
|
# conversation.status # => "archived"
|
20
20
|
#
|
21
|
-
# # conversation.
|
21
|
+
# # conversation.status = 1
|
22
22
|
# conversation.status = "archived"
|
23
23
|
#
|
24
|
-
# # conversation.update! status: nil
|
25
24
|
# conversation.status = nil
|
26
25
|
# conversation.status.nil? # => true
|
27
26
|
# conversation.status # => nil
|
28
27
|
#
|
29
28
|
# Scopes based on the allowed values of the enum field will be provided
|
30
|
-
# as well. With the above example
|
31
|
-
#
|
29
|
+
# as well. With the above example:
|
30
|
+
#
|
31
|
+
# Conversation.active
|
32
|
+
# Conversation.archived
|
32
33
|
#
|
33
34
|
# You can set the default value from the database declaration, like:
|
34
35
|
#
|
@@ -138,19 +139,16 @@ module ActiveRecord
|
|
138
139
|
@_enum_methods_module ||= begin
|
139
140
|
mod = Module.new do
|
140
141
|
private
|
141
|
-
def save_changed_attribute(attr_name,
|
142
|
+
def save_changed_attribute(attr_name, old)
|
142
143
|
if (mapping = self.class.defined_enums[attr_name.to_s])
|
144
|
+
value = _read_attribute(attr_name)
|
143
145
|
if attribute_changed?(attr_name)
|
144
|
-
old = changed_attributes[attr_name]
|
145
|
-
|
146
146
|
if mapping[old] == value
|
147
|
-
|
147
|
+
clear_attribute_changes([attr_name])
|
148
148
|
end
|
149
149
|
else
|
150
|
-
old = clone_attribute_value(:read_attribute, attr_name)
|
151
|
-
|
152
150
|
if old != value
|
153
|
-
|
151
|
+
set_attribute_was(attr_name, mapping.key(old))
|
154
152
|
end
|
155
153
|
end
|
156
154
|
else
|
data/lib/active_record/errors.rb
CHANGED
@@ -30,17 +30,18 @@ module ActiveRecord
|
|
30
30
|
class SerializationTypeMismatch < ActiveRecordError
|
31
31
|
end
|
32
32
|
|
33
|
-
# Raised when adapter not specified on connection (or configuration file
|
34
|
-
# misses adapter field).
|
33
|
+
# Raised when adapter not specified on connection (or configuration file
|
34
|
+
# +config/database.yml+ misses adapter field).
|
35
35
|
class AdapterNotSpecified < ActiveRecordError
|
36
36
|
end
|
37
37
|
|
38
|
-
# Raised when Active Record cannot find database adapter specified in
|
38
|
+
# Raised when Active Record cannot find database adapter specified in
|
39
|
+
# +config/database.yml+ or programmatically.
|
39
40
|
class AdapterNotFound < ActiveRecordError
|
40
41
|
end
|
41
42
|
|
42
|
-
# Raised when connection to the database could not been established (for
|
43
|
-
# is given a nil object).
|
43
|
+
# Raised when connection to the database could not been established (for
|
44
|
+
# example when +connection=+ is given a nil object).
|
44
45
|
class ConnectionNotEstablished < ActiveRecordError
|
45
46
|
end
|
46
47
|
|
@@ -51,10 +52,29 @@ module ActiveRecord
|
|
51
52
|
# Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be
|
52
53
|
# saved because record is invalid.
|
53
54
|
class RecordNotSaved < ActiveRecordError
|
55
|
+
attr_reader :record
|
56
|
+
|
57
|
+
def initialize(message, record = nil)
|
58
|
+
@record = record
|
59
|
+
super(message)
|
60
|
+
end
|
54
61
|
end
|
55
62
|
|
56
63
|
# Raised by ActiveRecord::Base.destroy! when a call to destroy would return false.
|
64
|
+
#
|
65
|
+
# begin
|
66
|
+
# complex_operation_that_internally_calls_destroy!
|
67
|
+
# rescue ActiveRecord::RecordNotDestroyed => invalid
|
68
|
+
# puts invalid.record.errors
|
69
|
+
# end
|
70
|
+
#
|
57
71
|
class RecordNotDestroyed < ActiveRecordError
|
72
|
+
attr_reader :record
|
73
|
+
|
74
|
+
def initialize(message, record = nil)
|
75
|
+
@record = record
|
76
|
+
super(message)
|
77
|
+
end
|
58
78
|
end
|
59
79
|
|
60
80
|
# Superclass for all database execution errors.
|
@@ -82,35 +102,26 @@ module ActiveRecord
|
|
82
102
|
class InvalidForeignKey < WrappedDatabaseException
|
83
103
|
end
|
84
104
|
|
85
|
-
# Raised when number of bind variables in statement given to
|
86
|
-
# when using +find+ method)
|
87
|
-
#
|
105
|
+
# Raised when number of bind variables in statement given to +:condition+ key
|
106
|
+
# (for example, when using +find+ method) does not match number of expected
|
107
|
+
# values supplied.
|
88
108
|
#
|
89
|
-
# For example,
|
109
|
+
# For example, when there are two placeholders with only one value supplied:
|
90
110
|
#
|
91
111
|
# Location.where("lat = ? AND lng = ?", 53.7362)
|
92
|
-
#
|
93
|
-
# two placeholders are given but only one variable to fill them.
|
94
112
|
class PreparedStatementInvalid < ActiveRecordError
|
95
113
|
end
|
96
114
|
|
97
|
-
# Raised when a given database does not exist
|
98
|
-
class NoDatabaseError <
|
99
|
-
def initialize(message)
|
100
|
-
super extend_message(message)
|
101
|
-
end
|
102
|
-
|
103
|
-
# can be over written to add additional error information.
|
104
|
-
def extend_message(message)
|
105
|
-
message
|
106
|
-
end
|
115
|
+
# Raised when a given database does not exist.
|
116
|
+
class NoDatabaseError < StatementInvalid
|
107
117
|
end
|
108
118
|
|
109
119
|
# Raised on attempt to save stale record. Record is stale when it's being saved in another query after
|
110
120
|
# instantiation, for example, when two users edit the same wiki page and one starts editing and saves
|
111
121
|
# the page before the other.
|
112
122
|
#
|
113
|
-
# Read more about optimistic locking in ActiveRecord::Locking module
|
123
|
+
# Read more about optimistic locking in ActiveRecord::Locking module
|
124
|
+
# documentation.
|
114
125
|
class StaleObjectError < ActiveRecordError
|
115
126
|
attr_reader :record, :attempted_action
|
116
127
|
|
@@ -122,8 +133,9 @@ module ActiveRecord
|
|
122
133
|
|
123
134
|
end
|
124
135
|
|
125
|
-
# Raised when association is being configured improperly or
|
126
|
-
#
|
136
|
+
# Raised when association is being configured improperly or user tries to use
|
137
|
+
# offset and limit together with +has_many+ or +has_and_belongs_to_many+
|
138
|
+
# associations.
|
127
139
|
class ConfigurationError < ActiveRecordError
|
128
140
|
end
|
129
141
|
|
@@ -161,7 +173,8 @@ module ActiveRecord
|
|
161
173
|
class Rollback < ActiveRecordError
|
162
174
|
end
|
163
175
|
|
164
|
-
# Raised when attribute has a name reserved by Active Record (when attribute
|
176
|
+
# Raised when attribute has a name reserved by Active Record (when attribute
|
177
|
+
# has name of one of Active Record instance methods).
|
165
178
|
class DangerousAttributeError < ActiveRecordError
|
166
179
|
end
|
167
180
|
|
@@ -173,16 +186,17 @@ module ActiveRecord
|
|
173
186
|
def initialize(record, attribute)
|
174
187
|
@record = record
|
175
188
|
@attribute = attribute.to_s
|
176
|
-
super("unknown attribute
|
189
|
+
super("unknown attribute '#{attribute}' for #{@record.class}.")
|
177
190
|
end
|
178
191
|
|
179
192
|
end
|
180
193
|
|
181
194
|
# Raised when an error occurred while doing a mass assignment to an attribute through the
|
182
|
-
#
|
195
|
+
# +attributes=+ method. The exception has an +attribute+ property that is the name of the
|
183
196
|
# offending attribute.
|
184
197
|
class AttributeAssignmentError < ActiveRecordError
|
185
198
|
attr_reader :exception, :attribute
|
199
|
+
|
186
200
|
def initialize(message, exception, attribute)
|
187
201
|
super(message)
|
188
202
|
@exception = exception
|
@@ -195,6 +209,7 @@ module ActiveRecord
|
|
195
209
|
# objects, each corresponding to the error while assigning to an attribute.
|
196
210
|
class MultiparameterAssignmentErrors < ActiveRecordError
|
197
211
|
attr_reader :errors
|
212
|
+
|
198
213
|
def initialize(errors)
|
199
214
|
@errors = errors
|
200
215
|
end
|
@@ -204,11 +219,12 @@ module ActiveRecord
|
|
204
219
|
class UnknownPrimaryKey < ActiveRecordError
|
205
220
|
attr_reader :model
|
206
221
|
|
207
|
-
def initialize(model)
|
208
|
-
|
222
|
+
def initialize(model, description = nil)
|
223
|
+
message = "Unknown primary key for table #{model.table_name} in model #{model}."
|
224
|
+
message += "\n#{description}" if description
|
225
|
+
super(message)
|
209
226
|
@model = model
|
210
227
|
end
|
211
|
-
|
212
228
|
end
|
213
229
|
|
214
230
|
# Raised when a relation cannot be mutated because it's already loaded.
|
@@ -225,6 +241,13 @@ module ActiveRecord
|
|
225
241
|
class ImmutableRelation < ActiveRecordError
|
226
242
|
end
|
227
243
|
|
244
|
+
# TransactionIsolationError will be raised under the following conditions:
|
245
|
+
#
|
246
|
+
# * The adapter does not support setting the isolation level
|
247
|
+
# * You are joining an existing open transaction
|
248
|
+
# * You are creating a nested (savepoint) transaction
|
249
|
+
#
|
250
|
+
# The mysql, mysql2 and postgresql adapters support setting the transaction isolation level.
|
228
251
|
class TransactionIsolationError < ActiveRecordError
|
229
252
|
end
|
230
253
|
end
|
@@ -19,7 +19,7 @@ module ActiveRecord
|
|
19
19
|
# On the other hand, we want to monitor the performance of our real database
|
20
20
|
# queries, not the performance of the access to the query cache.
|
21
21
|
IGNORED_PAYLOADS = %w(SCHEMA EXPLAIN CACHE)
|
22
|
-
EXPLAINED_SQLS = /\A\s*(select|update|delete|insert)\b/i
|
22
|
+
EXPLAINED_SQLS = /\A\s*(with|select|update|delete|insert)\b/i
|
23
23
|
def ignore_payload?(payload)
|
24
24
|
payload[:exception] || IGNORED_PAYLOADS.include?(payload[:name]) || payload[:sql] !~ EXPLAINED_SQLS
|
25
25
|
end
|
@@ -2,6 +2,7 @@ require 'erb'
|
|
2
2
|
require 'yaml'
|
3
3
|
require 'zlib'
|
4
4
|
require 'active_support/dependencies'
|
5
|
+
require 'active_support/core_ext/digest/uuid'
|
5
6
|
require 'active_record/fixture_set/file'
|
6
7
|
require 'active_record/errors'
|
7
8
|
|
@@ -14,9 +15,10 @@ module ActiveRecord
|
|
14
15
|
# They are stored in YAML files, one file per model, which are placed in the directory
|
15
16
|
# appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically
|
16
17
|
# configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/</tt>).
|
17
|
-
# The fixture file ends with the
|
18
|
-
# <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>).
|
19
|
-
#
|
18
|
+
# The fixture file ends with the +.yml+ file extension, for example:
|
19
|
+
# <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>).
|
20
|
+
#
|
21
|
+
# The format of a fixture file looks like this:
|
20
22
|
#
|
21
23
|
# rubyonrails:
|
22
24
|
# id: 1
|
@@ -32,7 +34,7 @@ module ActiveRecord
|
|
32
34
|
# is followed by an indented list of key/value pairs in the "key: value" format. Records are
|
33
35
|
# separated by a blank line for your viewing pleasure.
|
34
36
|
#
|
35
|
-
# Note
|
37
|
+
# Note: Fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
|
36
38
|
# See http://yaml.org/type/omap.html
|
37
39
|
# for the specification. You will need ordered fixtures when you have foreign key constraints
|
38
40
|
# on keys in the same table. This is commonly needed for tree structures. Example:
|
@@ -60,8 +62,8 @@ module ActiveRecord
|
|
60
62
|
# end
|
61
63
|
# end
|
62
64
|
#
|
63
|
-
# By default,
|
64
|
-
# so this test will succeed.
|
65
|
+
# By default, +test_helper.rb+ will load all of your fixtures into your test
|
66
|
+
# database, so this test will succeed.
|
65
67
|
#
|
66
68
|
# The testing environment will automatically load the all fixtures into the database before each
|
67
69
|
# test. To ensure consistent data, the environment deletes the fixtures before running the load.
|
@@ -179,6 +181,9 @@ module ActiveRecord
|
|
179
181
|
# * Stable, autogenerated IDs
|
180
182
|
# * Label references for associations (belongs_to, has_one, has_many)
|
181
183
|
# * HABTM associations as inline lists
|
184
|
+
#
|
185
|
+
# There are some more advanced features available even if the id is specified:
|
186
|
+
#
|
182
187
|
# * Autofilled timestamp columns
|
183
188
|
# * Fixture label interpolation
|
184
189
|
# * Support for YAML defaults
|
@@ -361,6 +366,7 @@ module ActiveRecord
|
|
361
366
|
# geeksomnia:
|
362
367
|
# name: Geeksomnia's Account
|
363
368
|
# subdomain: $LABEL
|
369
|
+
# email: $LABEL@email.com
|
364
370
|
#
|
365
371
|
# Also, sometimes (like when porting older join table fixtures) you'll need
|
366
372
|
# to be able to get a hold of the identifier for a given label. ERB
|
@@ -372,8 +378,9 @@ module ActiveRecord
|
|
372
378
|
#
|
373
379
|
# == Support for YAML defaults
|
374
380
|
#
|
375
|
-
# You
|
376
|
-
#
|
381
|
+
# You can set and reuse defaults in your fixtures YAML file.
|
382
|
+
# This is the same technique used in the +database.yml+ file to specify
|
383
|
+
# defaults:
|
377
384
|
#
|
378
385
|
# DEFAULTS: &DEFAULTS
|
379
386
|
# created_on: <%= 3.weeks.ago.to_s(:db) %>
|
@@ -389,7 +396,8 @@ module ActiveRecord
|
|
389
396
|
# Any fixture labeled "DEFAULTS" is safely ignored.
|
390
397
|
class FixtureSet
|
391
398
|
#--
|
392
|
-
# An instance of FixtureSet is normally stored in a single YAML file and
|
399
|
+
# An instance of FixtureSet is normally stored in a single YAML file and
|
400
|
+
# possibly in a folder with the same name.
|
393
401
|
#++
|
394
402
|
|
395
403
|
MAX_ID = 2 ** 30 - 1
|
@@ -459,13 +467,7 @@ module ActiveRecord
|
|
459
467
|
@config = config
|
460
468
|
|
461
469
|
# Remove string values that aren't constants or subclasses of AR
|
462
|
-
@class_names.delete_if { |
|
463
|
-
unless klass.is_a? Class
|
464
|
-
klass = klass.safe_constantize
|
465
|
-
ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name to `set_fixture_class` will be removed in Rails 4.2. Use the class itself instead.")
|
466
|
-
end
|
467
|
-
!insert_class(@class_names, k, klass)
|
468
|
-
}
|
470
|
+
@class_names.delete_if { |klass_name, klass| !insert_class(@class_names, klass_name, klass) }
|
469
471
|
end
|
470
472
|
|
471
473
|
def [](fs_name)
|
@@ -516,14 +518,14 @@ module ActiveRecord
|
|
516
518
|
::File.join(fixtures_directory, fs_name))
|
517
519
|
end
|
518
520
|
|
519
|
-
|
521
|
+
update_all_loaded_fixtures fixtures_map
|
520
522
|
|
521
523
|
connection.transaction(:requires_new => true) do
|
522
524
|
fixture_sets.each do |fs|
|
523
525
|
conn = fs.model_class.respond_to?(:connection) ? fs.model_class.connection : connection
|
524
526
|
table_rows = fs.table_rows
|
525
527
|
|
526
|
-
table_rows.
|
528
|
+
table_rows.each_key do |table|
|
527
529
|
conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
|
528
530
|
end
|
529
531
|
|
@@ -547,9 +549,13 @@ module ActiveRecord
|
|
547
549
|
end
|
548
550
|
|
549
551
|
# Returns a consistent, platform-independent identifier for +label+.
|
550
|
-
#
|
551
|
-
def self.identify(label)
|
552
|
-
|
552
|
+
# Integer identifiers are values less than 2^30. UUIDs are RFC 4122 version 5 SHA-1 hashes.
|
553
|
+
def self.identify(label, column_type = :integer)
|
554
|
+
if column_type == :uuid
|
555
|
+
Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, label.to_s)
|
556
|
+
else
|
557
|
+
Zlib.crc32(label.to_s) % MAX_ID
|
558
|
+
end
|
553
559
|
end
|
554
560
|
|
555
561
|
# Superclass for the evaluation contexts used by ERB fixtures.
|
@@ -557,6 +563,10 @@ module ActiveRecord
|
|
557
563
|
@context_class ||= Class.new
|
558
564
|
end
|
559
565
|
|
566
|
+
def self.update_all_loaded_fixtures(fixtures_map) # :nodoc:
|
567
|
+
all_loaded_fixtures.update(fixtures_map)
|
568
|
+
end
|
569
|
+
|
560
570
|
attr_reader :table_name, :name, :fixtures, :model_class, :config
|
561
571
|
|
562
572
|
def initialize(connection, name, class_name, path, config = ActiveRecord::Base)
|
@@ -565,10 +575,6 @@ module ActiveRecord
|
|
565
575
|
@config = config
|
566
576
|
@model_class = nil
|
567
577
|
|
568
|
-
if class_name.is_a?(String)
|
569
|
-
ActiveSupport::Deprecation.warn("The ability to pass in strings as a class name to `FixtureSet.new` will be removed in Rails 4.2. Use the class itself instead.")
|
570
|
-
end
|
571
|
-
|
572
578
|
if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
|
573
579
|
@model_class = class_name
|
574
580
|
else
|
@@ -625,12 +631,12 @@ module ActiveRecord
|
|
625
631
|
|
626
632
|
# interpolate the fixture label
|
627
633
|
row.each do |key, value|
|
628
|
-
row[key] =
|
634
|
+
row[key] = value.gsub("$LABEL", label.to_s) if value.is_a?(String)
|
629
635
|
end
|
630
636
|
|
631
637
|
# generate a primary key if necessary
|
632
638
|
if has_primary_key_column? && !row.include?(primary_key_name)
|
633
|
-
row[primary_key_name] = ActiveRecord::FixtureSet.identify(label)
|
639
|
+
row[primary_key_name] = ActiveRecord::FixtureSet.identify(label, primary_key_type)
|
634
640
|
end
|
635
641
|
|
636
642
|
# If STI is used, find the correct subclass for association reflection
|
@@ -641,19 +647,20 @@ module ActiveRecord
|
|
641
647
|
model_class
|
642
648
|
end
|
643
649
|
|
644
|
-
reflection_class._reflections.
|
650
|
+
reflection_class._reflections.each_value do |association|
|
645
651
|
case association.macro
|
646
652
|
when :belongs_to
|
647
653
|
# Do not replace association name with association foreign key if they are named the same
|
648
654
|
fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
|
649
655
|
|
650
656
|
if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
|
651
|
-
if association.
|
657
|
+
if association.polymorphic? && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
|
652
658
|
# support polymorphic belongs_to as "label (Type)"
|
653
659
|
row[association.foreign_type] = $1
|
654
660
|
end
|
655
661
|
|
656
|
-
|
662
|
+
fk_type = reflection_class.columns_hash[fk_name].type
|
663
|
+
row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type)
|
657
664
|
end
|
658
665
|
when :has_many
|
659
666
|
if association.options[:through]
|
@@ -680,6 +687,10 @@ module ActiveRecord
|
|
680
687
|
def name
|
681
688
|
@association.name
|
682
689
|
end
|
690
|
+
|
691
|
+
def primary_key_type
|
692
|
+
@association.klass.column_types[@association.klass.primary_key].type
|
693
|
+
end
|
683
694
|
end
|
684
695
|
|
685
696
|
class HasManyThroughProxy < ReflectionProxy # :nodoc:
|
@@ -701,17 +712,22 @@ module ActiveRecord
|
|
701
712
|
@primary_key_name ||= model_class && model_class.primary_key
|
702
713
|
end
|
703
714
|
|
715
|
+
def primary_key_type
|
716
|
+
@primary_key_type ||= model_class && model_class.column_types[model_class.primary_key].type
|
717
|
+
end
|
718
|
+
|
704
719
|
def add_join_records(rows, row, association)
|
705
720
|
# This is the case when the join table has no fixtures file
|
706
721
|
if (targets = row.delete(association.name.to_s))
|
707
|
-
table_name
|
708
|
-
|
709
|
-
|
722
|
+
table_name = association.join_table
|
723
|
+
column_type = association.primary_key_type
|
724
|
+
lhs_key = association.lhs_key
|
725
|
+
rhs_key = association.rhs_key
|
710
726
|
|
711
727
|
targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
|
712
728
|
rows[table_name].concat targets.map { |target|
|
713
729
|
{ lhs_key => row[primary_key_name],
|
714
|
-
rhs_key => ActiveRecord::FixtureSet.identify(target) }
|
730
|
+
rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) }
|
715
731
|
}
|
716
732
|
end
|
717
733
|
end
|
@@ -792,7 +808,9 @@ module ActiveRecord
|
|
792
808
|
|
793
809
|
def find
|
794
810
|
if model_class
|
795
|
-
model_class.unscoped
|
811
|
+
model_class.unscoped do
|
812
|
+
model_class.find(fixture[model_class.primary_key])
|
813
|
+
end
|
796
814
|
else
|
797
815
|
raise FixtureClassNotFound, "No class attached to find."
|
798
816
|
end
|
@@ -856,34 +874,9 @@ module ActiveRecord
|
|
856
874
|
end
|
857
875
|
|
858
876
|
self.fixture_table_names |= fixture_set_names
|
859
|
-
require_fixture_classes(fixture_set_names, self.config)
|
860
877
|
setup_fixture_accessors(fixture_set_names)
|
861
878
|
end
|
862
879
|
|
863
|
-
def try_to_load_dependency(file_name)
|
864
|
-
require_dependency file_name
|
865
|
-
rescue LoadError => e
|
866
|
-
unless fixture_class_names.key?(file_name.pluralize)
|
867
|
-
if ActiveRecord::Base.logger
|
868
|
-
ActiveRecord::Base.logger.warn("Unable to load #{file_name}, make sure you added it to ActiveSupport::TestCase.set_fixture_class")
|
869
|
-
ActiveRecord::Base.logger.warn("underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
|
870
|
-
end
|
871
|
-
end
|
872
|
-
end
|
873
|
-
|
874
|
-
def require_fixture_classes(fixture_set_names = nil, config = ActiveRecord::Base)
|
875
|
-
if fixture_set_names
|
876
|
-
fixture_set_names = fixture_set_names.map { |n| n.to_s }
|
877
|
-
else
|
878
|
-
fixture_set_names = fixture_table_names
|
879
|
-
end
|
880
|
-
|
881
|
-
fixture_set_names.each do |file_name|
|
882
|
-
file_name = file_name.singularize if config.pluralize_table_names
|
883
|
-
try_to_load_dependency(file_name)
|
884
|
-
end
|
885
|
-
end
|
886
|
-
|
887
880
|
def setup_fixture_accessors(fixture_set_names = nil)
|
888
881
|
fixture_set_names = Array(fixture_set_names || fixture_table_names)
|
889
882
|
methods = Module.new do
|
@@ -960,7 +953,7 @@ module ActiveRecord
|
|
960
953
|
end
|
961
954
|
|
962
955
|
# Instantiate fixtures for every test if requested.
|
963
|
-
instantiate_fixtures
|
956
|
+
instantiate_fixtures if use_instantiated_fixtures
|
964
957
|
end
|
965
958
|
|
966
959
|
def teardown_fixtures
|
@@ -987,16 +980,9 @@ module ActiveRecord
|
|
987
980
|
Hash[fixtures.map { |f| [f.name, f] }]
|
988
981
|
end
|
989
982
|
|
990
|
-
|
991
|
-
@@required_fixture_classes = false
|
992
|
-
|
993
|
-
def instantiate_fixtures(config)
|
983
|
+
def instantiate_fixtures
|
994
984
|
if pre_loaded_fixtures
|
995
985
|
raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
|
996
|
-
unless @@required_fixture_classes
|
997
|
-
self.class.require_fixture_classes ActiveRecord::FixtureSet.all_loaded_fixtures.keys, config
|
998
|
-
@@required_fixture_classes = true
|
999
|
-
end
|
1000
986
|
ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
|
1001
987
|
else
|
1002
988
|
raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
|