activerecord 4.1.0 → 4.2.0
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 +776 -1330
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/association_relation.rb +4 -0
- data/lib/active_record/associations/alias_tracker.rb +14 -13
- data/lib/active_record/associations/association.rb +2 -2
- data/lib/active_record/associations/association_scope.rb +83 -43
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- 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/has_and_belongs_to_many.rb +9 -6
- 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 +66 -29
- data/lib/active_record/associations/collection_proxy.rb +22 -26
- data/lib/active_record/associations/has_many_association.rb +65 -18
- data/lib/active_record/associations/has_many_through_association.rb +55 -27
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +19 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +20 -12
- data/lib/active_record/associations/preloader/association.rb +34 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +49 -59
- data/lib/active_record/associations/singular_association.rb +25 -4
- data/lib/active_record/associations/through_association.rb +23 -14
- data/lib/active_record/associations.rb +171 -42
- data/lib/active_record/attribute.rb +149 -0
- data/lib/active_record/attribute_assignment.rb +18 -10
- 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 +98 -44
- data/lib/active_record/attribute_methods/primary_key.rb +14 -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 +37 -147
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +34 -28
- data/lib/active_record/attribute_methods/write.rb +14 -21
- data/lib/active_record/attribute_methods.rb +67 -94
- data/lib/active_record/attribute_set/builder.rb +86 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attributes.rb +139 -0
- data/lib/active_record/autosave_association.rb +45 -38
- data/lib/active_record/base.rb +10 -20
- data/lib/active_record/callbacks.rb +7 -7
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +78 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +38 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -55
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +126 -54
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +198 -64
- data/lib/active_record/connection_adapters/abstract/transaction.rb +126 -114
- data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -55
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +240 -135
- data/lib/active_record/connection_adapters/column.rb +28 -239
- data/lib/active_record/connection_adapters/connection_specification.rb +16 -25
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -22
- data/lib/active_record/connection_adapters/mysql_adapter.rb +65 -149
- 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 -27
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -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 +79 -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 +97 -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/oid.rb +29 -374
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +55 -135
- 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 +127 -38
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +220 -466
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -61
- data/lib/active_record/connection_handling.rb +3 -3
- data/lib/active_record/core.rb +143 -32
- data/lib/active_record/counter_cache.rb +60 -7
- data/lib/active_record/enum.rb +10 -11
- data/lib/active_record/errors.rb +49 -27
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +56 -70
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +35 -17
- data/lib/active_record/log_subscriber.rb +1 -1
- 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 +52 -49
- data/lib/active_record/model_schema.rb +49 -57
- data/lib/active_record/nested_attributes.rb +7 -7
- data/lib/active_record/null_relation.rb +19 -5
- data/lib/active_record/persistence.rb +50 -31
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +14 -11
- data/lib/active_record/railties/databases.rake +56 -54
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +286 -102
- data/lib/active_record/relation/batches.rb +0 -1
- data/lib/active_record/relation/calculations.rb +39 -31
- data/lib/active_record/relation/delegation.rb +2 -2
- data/lib/active_record/relation/finder_methods.rb +80 -36
- data/lib/active_record/relation/merger.rb +25 -30
- data/lib/active_record/relation/predicate_builder/array_handler.rb +31 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +11 -10
- data/lib/active_record/relation/query_methods.rb +141 -55
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +69 -30
- 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 +58 -26
- data/lib/active_record/schema_migration.rb +11 -0
- data/lib/active_record/scoping/default.rb +8 -7
- 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 +19 -10
- data/lib/active_record/tasks/database_tasks.rb +73 -7
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +11 -9
- data/lib/active_record/transactions.rb +37 -21
- 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 +30 -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/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
- data/lib/active_record/type/integer.rb +55 -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 +56 -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 +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +6 -4
- data/lib/active_record/validations/uniqueness.rb +11 -17
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +3 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +4 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +65 -10
- 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 =
|
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,12 +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
|
-
|
33
|
-
reflection = belongs_to.find { |e| e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
|
37
|
+
reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
|
34
38
|
counter_name = reflection.counter_cache_column
|
35
39
|
|
36
40
|
stmt = unscoped.where(arel_table[primary_key].eq(object.id)).arel.compile_update({
|
37
|
-
arel_table[counter_name] => object.send(
|
41
|
+
arel_table[counter_name] => object.send(counter_association).count(:all)
|
38
42
|
}, primary_key)
|
39
43
|
connection.update stmt
|
40
44
|
end
|
@@ -118,5 +122,54 @@ module ActiveRecord
|
|
118
122
|
update_counters(id, counter_name => -1)
|
119
123
|
end
|
120
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
|
+
|
121
174
|
end
|
122
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
|
#
|
@@ -67,12 +69,12 @@ module ActiveRecord
|
|
67
69
|
#
|
68
70
|
# Where conditions on an enum attribute must use the ordinal value of an enum.
|
69
71
|
module Enum
|
70
|
-
def self.extended(base)
|
72
|
+
def self.extended(base) # :nodoc:
|
71
73
|
base.class_attribute(:defined_enums)
|
72
74
|
base.defined_enums = {}
|
73
75
|
end
|
74
76
|
|
75
|
-
def inherited(base)
|
77
|
+
def inherited(base) # :nodoc:
|
76
78
|
base.defined_enums = defined_enums.deep_dup
|
77
79
|
super
|
78
80
|
end
|
@@ -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
|
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(record)
|
75
|
+
@record = record
|
76
|
+
super()
|
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
|
@@ -225,6 +240,13 @@ module ActiveRecord
|
|
225
240
|
class ImmutableRelation < ActiveRecordError
|
226
241
|
end
|
227
242
|
|
243
|
+
# TransactionIsolationError will be raised under the following conditions:
|
244
|
+
#
|
245
|
+
# * The adapter does not support setting the isolation level
|
246
|
+
# * You are joining an existing open transaction
|
247
|
+
# * You are creating a nested (savepoint) transaction
|
248
|
+
#
|
249
|
+
# The mysql, mysql2 and postgresql adapters support setting the transaction isolation level.
|
228
250
|
class TransactionIsolationError < ActiveRecordError
|
229
251
|
end
|
230
252
|
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.
|
@@ -124,7 +126,7 @@ module ActiveRecord
|
|
124
126
|
# that is included in <tt>ActiveRecord::FixtureSet.context_class</tt>.
|
125
127
|
#
|
126
128
|
# - define a helper method in `test_helper.rb`
|
127
|
-
#
|
129
|
+
# module FixtureFileHelpers
|
128
130
|
# def file_sha(path)
|
129
131
|
# Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
|
130
132
|
# end
|
@@ -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
|
|
@@ -549,9 +551,13 @@ module ActiveRecord
|
|
549
551
|
end
|
550
552
|
|
551
553
|
# Returns a consistent, platform-independent identifier for +label+.
|
552
|
-
#
|
553
|
-
def self.identify(label)
|
554
|
-
|
554
|
+
# Integer identifiers are values less than 2^30. UUIDs are RFC 4122 version 5 SHA-1 hashes.
|
555
|
+
def self.identify(label, column_type = :integer)
|
556
|
+
if column_type == :uuid
|
557
|
+
Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, label.to_s)
|
558
|
+
else
|
559
|
+
Zlib.crc32(label.to_s) % MAX_ID
|
560
|
+
end
|
555
561
|
end
|
556
562
|
|
557
563
|
# Superclass for the evaluation contexts used by ERB fixtures.
|
@@ -559,6 +565,10 @@ module ActiveRecord
|
|
559
565
|
@context_class ||= Class.new
|
560
566
|
end
|
561
567
|
|
568
|
+
def self.update_all_loaded_fixtures(fixtures_map) # :nodoc:
|
569
|
+
all_loaded_fixtures.update(fixtures_map)
|
570
|
+
end
|
571
|
+
|
562
572
|
attr_reader :table_name, :name, :fixtures, :model_class, :config
|
563
573
|
|
564
574
|
def initialize(connection, name, class_name, path, config = ActiveRecord::Base)
|
@@ -567,10 +577,6 @@ module ActiveRecord
|
|
567
577
|
@config = config
|
568
578
|
@model_class = nil
|
569
579
|
|
570
|
-
if class_name.is_a?(String)
|
571
|
-
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.")
|
572
|
-
end
|
573
|
-
|
574
580
|
if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
|
575
581
|
@model_class = class_name
|
576
582
|
else
|
@@ -627,12 +633,12 @@ module ActiveRecord
|
|
627
633
|
|
628
634
|
# interpolate the fixture label
|
629
635
|
row.each do |key, value|
|
630
|
-
row[key] =
|
636
|
+
row[key] = value.gsub("$LABEL", label) if value.is_a?(String)
|
631
637
|
end
|
632
638
|
|
633
639
|
# generate a primary key if necessary
|
634
640
|
if has_primary_key_column? && !row.include?(primary_key_name)
|
635
|
-
row[primary_key_name] = ActiveRecord::FixtureSet.identify(label)
|
641
|
+
row[primary_key_name] = ActiveRecord::FixtureSet.identify(label, primary_key_type)
|
636
642
|
end
|
637
643
|
|
638
644
|
# If STI is used, find the correct subclass for association reflection
|
@@ -643,19 +649,20 @@ module ActiveRecord
|
|
643
649
|
model_class
|
644
650
|
end
|
645
651
|
|
646
|
-
reflection_class.
|
652
|
+
reflection_class._reflections.each_value do |association|
|
647
653
|
case association.macro
|
648
654
|
when :belongs_to
|
649
655
|
# Do not replace association name with association foreign key if they are named the same
|
650
656
|
fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
|
651
657
|
|
652
658
|
if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
|
653
|
-
if association.
|
659
|
+
if association.polymorphic? && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
|
654
660
|
# support polymorphic belongs_to as "label (Type)"
|
655
661
|
row[association.foreign_type] = $1
|
656
662
|
end
|
657
663
|
|
658
|
-
|
664
|
+
fk_type = association.active_record.columns_hash[fk_name].type
|
665
|
+
row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type)
|
659
666
|
end
|
660
667
|
when :has_many
|
661
668
|
if association.options[:through]
|
@@ -682,6 +689,10 @@ module ActiveRecord
|
|
682
689
|
def name
|
683
690
|
@association.name
|
684
691
|
end
|
692
|
+
|
693
|
+
def primary_key_type
|
694
|
+
@association.klass.column_types[@association.klass.primary_key].type
|
695
|
+
end
|
685
696
|
end
|
686
697
|
|
687
698
|
class HasManyThroughProxy < ReflectionProxy # :nodoc:
|
@@ -699,17 +710,22 @@ module ActiveRecord
|
|
699
710
|
@primary_key_name ||= model_class && model_class.primary_key
|
700
711
|
end
|
701
712
|
|
713
|
+
def primary_key_type
|
714
|
+
@primary_key_type ||= model_class && model_class.column_types[model_class.primary_key].type
|
715
|
+
end
|
716
|
+
|
702
717
|
def add_join_records(rows, row, association)
|
703
718
|
# This is the case when the join table has no fixtures file
|
704
719
|
if (targets = row.delete(association.name.to_s))
|
705
|
-
table_name
|
706
|
-
|
707
|
-
|
720
|
+
table_name = association.join_table
|
721
|
+
column_type = association.primary_key_type
|
722
|
+
lhs_key = association.lhs_key
|
723
|
+
rhs_key = association.rhs_key
|
708
724
|
|
709
725
|
targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
|
710
726
|
rows[table_name].concat targets.map { |target|
|
711
727
|
{ lhs_key => row[primary_key_name],
|
712
|
-
rhs_key => ActiveRecord::FixtureSet.identify(target) }
|
728
|
+
rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) }
|
713
729
|
}
|
714
730
|
end
|
715
731
|
end
|
@@ -790,7 +806,9 @@ module ActiveRecord
|
|
790
806
|
|
791
807
|
def find
|
792
808
|
if model_class
|
793
|
-
model_class.
|
809
|
+
model_class.unscoped do
|
810
|
+
model_class.find(fixture[model_class.primary_key])
|
811
|
+
end
|
794
812
|
else
|
795
813
|
raise FixtureClassNotFound, "No class attached to find."
|
796
814
|
end
|
@@ -854,34 +872,9 @@ module ActiveRecord
|
|
854
872
|
end
|
855
873
|
|
856
874
|
self.fixture_table_names |= fixture_set_names
|
857
|
-
require_fixture_classes(fixture_set_names, self.config)
|
858
875
|
setup_fixture_accessors(fixture_set_names)
|
859
876
|
end
|
860
877
|
|
861
|
-
def try_to_load_dependency(file_name)
|
862
|
-
require_dependency file_name
|
863
|
-
rescue LoadError => e
|
864
|
-
# Let's hope the developer has included it
|
865
|
-
# Let's warn in case this is a subdependency, otherwise
|
866
|
-
# subdependency error messages are totally cryptic
|
867
|
-
if ActiveRecord::Base.logger
|
868
|
-
ActiveRecord::Base.logger.warn("Unable to load #{file_name}, underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
|
869
|
-
end
|
870
|
-
end
|
871
|
-
|
872
|
-
def require_fixture_classes(fixture_set_names = nil, config = ActiveRecord::Base)
|
873
|
-
if fixture_set_names
|
874
|
-
fixture_set_names = fixture_set_names.map { |n| n.to_s }
|
875
|
-
else
|
876
|
-
fixture_set_names = fixture_table_names
|
877
|
-
end
|
878
|
-
|
879
|
-
fixture_set_names.each do |file_name|
|
880
|
-
file_name = file_name.singularize if config.pluralize_table_names
|
881
|
-
try_to_load_dependency(file_name)
|
882
|
-
end
|
883
|
-
end
|
884
|
-
|
885
878
|
def setup_fixture_accessors(fixture_set_names = nil)
|
886
879
|
fixture_set_names = Array(fixture_set_names || fixture_table_names)
|
887
880
|
methods = Module.new do
|
@@ -958,7 +951,7 @@ module ActiveRecord
|
|
958
951
|
end
|
959
952
|
|
960
953
|
# Instantiate fixtures for every test if requested.
|
961
|
-
instantiate_fixtures
|
954
|
+
instantiate_fixtures if use_instantiated_fixtures
|
962
955
|
end
|
963
956
|
|
964
957
|
def teardown_fixtures
|
@@ -985,16 +978,9 @@ module ActiveRecord
|
|
985
978
|
Hash[fixtures.map { |f| [f.name, f] }]
|
986
979
|
end
|
987
980
|
|
988
|
-
|
989
|
-
@@required_fixture_classes = false
|
990
|
-
|
991
|
-
def instantiate_fixtures(config)
|
981
|
+
def instantiate_fixtures
|
992
982
|
if pre_loaded_fixtures
|
993
983
|
raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
|
994
|
-
unless @@required_fixture_classes
|
995
|
-
self.class.require_fixture_classes ActiveRecord::FixtureSet.all_loaded_fixtures.keys, config
|
996
|
-
@@required_fixture_classes = true
|
997
|
-
end
|
998
984
|
ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
|
999
985
|
else
|
1000
986
|
raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
|
@@ -1,12 +1,12 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
# Returns the version of the currently loaded
|
2
|
+
# Returns the version of the currently loaded Active Record as a <tt>Gem::Version</tt>
|
3
3
|
def self.gem_version
|
4
4
|
Gem::Version.new VERSION::STRING
|
5
5
|
end
|
6
6
|
|
7
7
|
module VERSION
|
8
8
|
MAJOR = 4
|
9
|
-
MINOR =
|
9
|
+
MINOR = 2
|
10
10
|
TINY = 0
|
11
11
|
PRE = nil
|
12
12
|
|