activerecord 5.0.7.2 → 5.1.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 +5 -5
- data/CHANGELOG.md +389 -2252
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/examples/performance.rb +28 -28
- data/examples/simple.rb +3 -3
- data/lib/active_record.rb +20 -20
- data/lib/active_record/aggregations.rb +244 -244
- data/lib/active_record/association_relation.rb +5 -5
- data/lib/active_record/associations.rb +1579 -1569
- data/lib/active_record/associations/alias_tracker.rb +1 -1
- data/lib/active_record/associations/association.rb +23 -15
- data/lib/active_record/associations/association_scope.rb +83 -81
- data/lib/active_record/associations/belongs_to_association.rb +0 -1
- data/lib/active_record/associations/builder/belongs_to.rb +16 -14
- data/lib/active_record/associations/builder/collection_association.rb +1 -2
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +27 -27
- data/lib/active_record/associations/collection_association.rb +74 -241
- data/lib/active_record/associations/collection_proxy.rb +144 -70
- data/lib/active_record/associations/has_many_association.rb +15 -19
- data/lib/active_record/associations/has_many_through_association.rb +12 -5
- data/lib/active_record/associations/has_one_association.rb +22 -28
- data/lib/active_record/associations/has_one_through_association.rb +5 -1
- data/lib/active_record/associations/join_dependency.rb +117 -115
- data/lib/active_record/associations/join_dependency/join_association.rb +16 -13
- data/lib/active_record/associations/join_dependency/join_base.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/preloader.rb +94 -94
- data/lib/active_record/associations/preloader/association.rb +87 -64
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -2
- data/lib/active_record/associations/preloader/collection_association.rb +6 -6
- data/lib/active_record/associations/preloader/has_many.rb +0 -2
- data/lib/active_record/associations/preloader/singular_association.rb +6 -8
- data/lib/active_record/associations/preloader/through_association.rb +34 -41
- data/lib/active_record/associations/singular_association.rb +8 -25
- data/lib/active_record/associations/through_association.rb +3 -6
- data/lib/active_record/attribute.rb +98 -71
- data/lib/active_record/attribute/user_provided_default.rb +4 -2
- data/lib/active_record/attribute_assignment.rb +61 -61
- data/lib/active_record/attribute_decorators.rb +35 -13
- data/lib/active_record/attribute_methods.rb +56 -65
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
- data/lib/active_record/attribute_methods/dirty.rb +216 -34
- data/lib/active_record/attribute_methods/primary_key.rb +78 -73
- data/lib/active_record/attribute_methods/read.rb +39 -35
- data/lib/active_record/attribute_methods/serialization.rb +7 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +35 -58
- data/lib/active_record/attribute_methods/write.rb +36 -30
- data/lib/active_record/attribute_mutation_tracker.rb +53 -10
- data/lib/active_record/attribute_set.rb +9 -6
- data/lib/active_record/attribute_set/builder.rb +41 -49
- data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
- data/lib/active_record/attributes.rb +21 -21
- data/lib/active_record/autosave_association.rb +13 -13
- data/lib/active_record/base.rb +24 -22
- data/lib/active_record/callbacks.rb +52 -14
- data/lib/active_record/coders/yaml_column.rb +9 -11
- data/lib/active_record/collection_cache_key.rb +6 -17
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +320 -278
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -34
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +44 -57
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +9 -19
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +78 -79
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +99 -93
- data/lib/active_record/connection_adapters/abstract/transaction.rb +1 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +156 -128
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +424 -382
- data/lib/active_record/connection_adapters/column.rb +27 -5
- data/lib/active_record/connection_adapters/connection_specification.rb +128 -118
- data/lib/active_record/connection_adapters/mysql/column.rb +6 -31
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +45 -43
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +22 -22
- data/lib/active_record/connection_adapters/mysql/quoting.rb +6 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +16 -19
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +49 -31
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +5 -6
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +24 -26
- data/lib/active_record/connection_adapters/postgresql/column.rb +1 -28
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -35
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +9 -9
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
- data/lib/active_record/connection_adapters/postgresql/oid/{rails_5_1_point.rb → legacy_point.rb} +9 -16
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +28 -8
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +28 -30
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +51 -51
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +38 -36
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +37 -24
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +19 -23
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +161 -170
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/utils.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +179 -152
- data/lib/active_record/connection_adapters/schema_cache.rb +16 -7
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +3 -3
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +16 -20
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +1 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +28 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +187 -130
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -7
- data/lib/active_record/connection_handling.rb +14 -26
- data/lib/active_record/core.rb +110 -93
- data/lib/active_record/counter_cache.rb +62 -13
- data/lib/active_record/define_callbacks.rb +20 -0
- data/lib/active_record/dynamic_matchers.rb +80 -79
- data/lib/active_record/enum.rb +8 -6
- data/lib/active_record/errors.rb +58 -15
- data/lib/active_record/explain.rb +1 -2
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +7 -4
- data/lib/active_record/fixture_set/file.rb +11 -8
- data/lib/active_record/fixtures.rb +66 -53
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +93 -79
- data/lib/active_record/integration.rb +7 -7
- data/lib/active_record/internal_metadata.rb +3 -16
- data/lib/active_record/legacy_yaml_adapter.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +64 -56
- data/lib/active_record/locking/pessimistic.rb +10 -1
- data/lib/active_record/log_subscriber.rb +29 -29
- data/lib/active_record/migration.rb +155 -172
- data/lib/active_record/migration/command_recorder.rb +94 -94
- data/lib/active_record/migration/compatibility.rb +76 -37
- data/lib/active_record/migration/join_table.rb +6 -6
- data/lib/active_record/model_schema.rb +85 -119
- data/lib/active_record/nested_attributes.rb +200 -199
- data/lib/active_record/null_relation.rb +10 -33
- data/lib/active_record/persistence.rb +45 -38
- data/lib/active_record/query_cache.rb +4 -8
- data/lib/active_record/querying.rb +2 -3
- data/lib/active_record/railtie.rb +16 -17
- data/lib/active_record/railties/controller_runtime.rb +6 -2
- data/lib/active_record/railties/databases.rake +125 -140
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +2 -2
- data/lib/active_record/reflection.rb +79 -96
- data/lib/active_record/relation.rb +72 -115
- data/lib/active_record/relation/batches.rb +87 -58
- data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
- data/lib/active_record/relation/calculations.rb +154 -160
- data/lib/active_record/relation/delegation.rb +30 -29
- data/lib/active_record/relation/finder_methods.rb +195 -226
- data/lib/active_record/relation/merger.rb +58 -62
- data/lib/active_record/relation/predicate_builder.rb +92 -89
- data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -5
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +23 -23
- data/lib/active_record/relation/predicate_builder/base_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +0 -8
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +12 -10
- data/lib/active_record/relation/predicate_builder/range_handler.rb +0 -8
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +247 -295
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +4 -5
- data/lib/active_record/relation/where_clause.rb +79 -65
- data/lib/active_record/relation/where_clause_factory.rb +47 -8
- data/lib/active_record/result.rb +29 -31
- data/lib/active_record/runtime_registry.rb +3 -3
- data/lib/active_record/sanitization.rb +182 -197
- data/lib/active_record/schema.rb +3 -3
- data/lib/active_record/schema_dumper.rb +14 -37
- data/lib/active_record/schema_migration.rb +3 -3
- data/lib/active_record/scoping.rb +9 -10
- data/lib/active_record/scoping/default.rb +87 -91
- data/lib/active_record/scoping/named.rb +16 -28
- data/lib/active_record/secure_token.rb +2 -2
- data/lib/active_record/statement_cache.rb +13 -15
- data/lib/active_record/store.rb +31 -32
- data/lib/active_record/suppressor.rb +2 -1
- data/lib/active_record/table_metadata.rb +9 -5
- data/lib/active_record/tasks/database_tasks.rb +72 -65
- data/lib/active_record/tasks/mysql_database_tasks.rb +75 -72
- data/lib/active_record/tasks/postgresql_database_tasks.rb +53 -48
- data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
- data/lib/active_record/timestamp.rb +39 -25
- data/lib/active_record/touch_later.rb +1 -2
- data/lib/active_record/transactions.rb +98 -110
- data/lib/active_record/type.rb +17 -13
- data/lib/active_record/type/adapter_specific_registry.rb +46 -42
- data/lib/active_record/type/decimal_without_scale.rb +9 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
- data/lib/active_record/type/serialized.rb +8 -8
- data/lib/active_record/type/text.rb +9 -0
- data/lib/active_record/type/time.rb +0 -1
- data/lib/active_record/type/type_map.rb +11 -15
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type_caster.rb +2 -2
- data/lib/active_record/type_caster/connection.rb +8 -6
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/validations.rb +4 -4
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/presence.rb +2 -2
- data/lib/active_record/validations/uniqueness.rb +8 -39
- data/lib/active_record/version.rb +1 -1
- data/lib/rails/generators/active_record.rb +4 -4
- data/lib/rails/generators/active_record/migration.rb +2 -2
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
- metadata +22 -13
- data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
@@ -12,13 +12,21 @@ module ActiveRecord
|
|
12
12
|
#
|
13
13
|
# * +id+ - The id of the object you wish to reset a counter on.
|
14
14
|
# * +counters+ - One or more association counters to reset. Association name or counter name can be given.
|
15
|
+
# * <tt>:touch</tt> - Touch timestamp columns when updating.
|
16
|
+
# Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
|
17
|
+
# touch that column or an array of symbols to touch just those ones.
|
15
18
|
#
|
16
19
|
# ==== Examples
|
17
20
|
#
|
18
|
-
# # For Post with id #1
|
21
|
+
# # For the Post with id #1, reset the comments_count
|
19
22
|
# Post.reset_counters(1, :comments)
|
20
|
-
|
23
|
+
#
|
24
|
+
# # Like above, but also touch the +updated_at+ and/or +updated_on+
|
25
|
+
# # attributes.
|
26
|
+
# Post.reset_counters(1, :comments, touch: true)
|
27
|
+
def reset_counters(id, *counters, touch: nil)
|
21
28
|
object = find(id)
|
29
|
+
|
22
30
|
counters.each do |counter_association|
|
23
31
|
has_many_association = _reflect_on_association(counter_association)
|
24
32
|
unless has_many_association
|
@@ -26,7 +34,7 @@ module ActiveRecord
|
|
26
34
|
has_many_association = has_many.find { |association| association.counter_cache_column && association.counter_cache_column.to_sym == counter_association.to_sym }
|
27
35
|
counter_association = has_many_association.plural_name if has_many_association
|
28
36
|
end
|
29
|
-
raise ArgumentError, "'#{
|
37
|
+
raise ArgumentError, "'#{name}' has no association called '#{counter_association}'" unless has_many_association
|
30
38
|
|
31
39
|
if has_many_association.is_a? ActiveRecord::Reflection::ThroughReflection
|
32
40
|
has_many_association = has_many_association.through_reflection
|
@@ -37,10 +45,12 @@ module ActiveRecord
|
|
37
45
|
reflection = child_class._reflections.values.find { |e| e.belongs_to? && e.foreign_key.to_s == foreign_key && e.options[:counter_cache].present? }
|
38
46
|
counter_name = reflection.counter_cache_column
|
39
47
|
|
40
|
-
|
41
|
-
|
42
|
-
|
48
|
+
updates = { counter_name.to_sym => object.send(counter_association).count(:all) }
|
49
|
+
updates.merge!(touch_updates(touch)) if touch
|
50
|
+
|
51
|
+
unscoped.where(primary_key => object.id).update_all(updates)
|
43
52
|
end
|
53
|
+
|
44
54
|
return true
|
45
55
|
end
|
46
56
|
|
@@ -55,6 +65,9 @@ module ActiveRecord
|
|
55
65
|
# * +id+ - The id of the object you wish to update a counter on or an array of ids.
|
56
66
|
# * +counters+ - A Hash containing the names of the fields
|
57
67
|
# to update as keys and the amount to update the field by as values.
|
68
|
+
# * <tt>:touch</tt> option - Touch timestamp columns when updating.
|
69
|
+
# Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
|
70
|
+
# touch that column or an array of symbols to touch just those ones.
|
58
71
|
#
|
59
72
|
# ==== Examples
|
60
73
|
#
|
@@ -73,14 +86,30 @@ module ActiveRecord
|
|
73
86
|
# # UPDATE posts
|
74
87
|
# # SET comment_count = COALESCE(comment_count, 0) + 1
|
75
88
|
# # WHERE id IN (10, 15)
|
89
|
+
#
|
90
|
+
# # For the Posts with id of 10 and 15, increment the comment_count by 1
|
91
|
+
# # and update the updated_at value for each counter.
|
92
|
+
# Post.update_counters [10, 15], comment_count: 1, touch: true
|
93
|
+
# # Executes the following SQL:
|
94
|
+
# # UPDATE posts
|
95
|
+
# # SET comment_count = COALESCE(comment_count, 0) + 1,
|
96
|
+
# # `updated_at` = '2016-10-13T09:59:23-05:00'
|
97
|
+
# # WHERE id IN (10, 15)
|
76
98
|
def update_counters(id, counters)
|
99
|
+
touch = counters.delete(:touch)
|
100
|
+
|
77
101
|
updates = counters.map do |counter_name, value|
|
78
|
-
operator = value < 0 ?
|
102
|
+
operator = value < 0 ? "-" : "+"
|
79
103
|
quoted_column = connection.quote_column_name(counter_name)
|
80
104
|
"#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}"
|
81
105
|
end
|
82
106
|
|
83
|
-
|
107
|
+
if touch
|
108
|
+
touch_updates = touch_updates(touch)
|
109
|
+
updates << sanitize_sql_for_assignment(touch_updates) unless touch_updates.empty?
|
110
|
+
end
|
111
|
+
|
112
|
+
unscoped.where(primary_key => id).update_all updates.join(", ")
|
84
113
|
end
|
85
114
|
|
86
115
|
# Increment a numeric field by one, via a direct SQL update.
|
@@ -94,13 +123,20 @@ module ActiveRecord
|
|
94
123
|
#
|
95
124
|
# * +counter_name+ - The name of the field that should be incremented.
|
96
125
|
# * +id+ - The id of the object that should be incremented or an array of ids.
|
126
|
+
# * <tt>:touch</tt> - Touch timestamp columns when updating.
|
127
|
+
# Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
|
128
|
+
# touch that column or an array of symbols to touch just those ones.
|
97
129
|
#
|
98
130
|
# ==== Examples
|
99
131
|
#
|
100
132
|
# # Increment the posts_count column for the record with an id of 5
|
101
133
|
# DiscussionBoard.increment_counter(:posts_count, 5)
|
102
|
-
|
103
|
-
|
134
|
+
#
|
135
|
+
# # Increment the posts_count column for the record with an id of 5
|
136
|
+
# # and update the updated_at value.
|
137
|
+
# DiscussionBoard.increment_counter(:posts_count, 5, touch: true)
|
138
|
+
def increment_counter(counter_name, id, touch: nil)
|
139
|
+
update_counters(id, counter_name => 1, touch: touch)
|
104
140
|
end
|
105
141
|
|
106
142
|
# Decrement a numeric field by one, via a direct SQL update.
|
@@ -112,14 +148,28 @@ module ActiveRecord
|
|
112
148
|
#
|
113
149
|
# * +counter_name+ - The name of the field that should be decremented.
|
114
150
|
# * +id+ - The id of the object that should be decremented or an array of ids.
|
151
|
+
# * <tt>:touch</tt> - Touch timestamp columns when updating.
|
152
|
+
# Pass +true+ to touch +updated_at+ and/or +updated_on+. Pass a symbol to
|
153
|
+
# touch that column or an array of symbols to touch just those ones.
|
115
154
|
#
|
116
155
|
# ==== Examples
|
117
156
|
#
|
118
157
|
# # Decrement the posts_count column for the record with an id of 5
|
119
158
|
# DiscussionBoard.decrement_counter(:posts_count, 5)
|
120
|
-
|
121
|
-
|
159
|
+
#
|
160
|
+
# # Decrement the posts_count column for the record with an id of 5
|
161
|
+
# # and update the updated_at value.
|
162
|
+
# DiscussionBoard.decrement_counter(:posts_count, 5, touch: true)
|
163
|
+
def decrement_counter(counter_name, id, touch: nil)
|
164
|
+
update_counters(id, counter_name => -1, touch: touch)
|
122
165
|
end
|
166
|
+
|
167
|
+
private
|
168
|
+
def touch_updates(touch)
|
169
|
+
touch = timestamp_attributes_for_update_in_model if touch == true
|
170
|
+
touch_time = current_time_from_proper_timezone
|
171
|
+
Array(touch).map { |column| [ column, touch_time ] }.to_h
|
172
|
+
end
|
123
173
|
end
|
124
174
|
|
125
175
|
private
|
@@ -159,6 +209,5 @@ module ActiveRecord
|
|
159
209
|
yield association(name.to_sym) if reflection.belongs_to? && reflection.counter_cache_column
|
160
210
|
end
|
161
211
|
end
|
162
|
-
|
163
212
|
end
|
164
213
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
# This module exists because `ActiveRecord::AttributeMethods::Dirty` needs to
|
3
|
+
# define callbacks, but continue to have its version of `save` be the super
|
4
|
+
# method of `ActiveRecord::Callbacks`. This will be removed when the removal
|
5
|
+
# of deprecated code removes this need.
|
6
|
+
module DefineCallbacks
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
module ClassMethods # :nodoc:
|
10
|
+
include ActiveModel::Callbacks
|
11
|
+
end
|
12
|
+
|
13
|
+
included do
|
14
|
+
include ActiveModel::Validations::Callbacks
|
15
|
+
|
16
|
+
define_model_callbacks :initialize, :find, :touch, only: :after
|
17
|
+
define_model_callbacks :save, :create, :update, :destroy
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
|
+
|
1
2
|
module ActiveRecord
|
2
3
|
module DynamicMatchers #:nodoc:
|
3
|
-
def
|
4
|
+
def respond_to_missing?(name, include_private = false)
|
4
5
|
if self == Base
|
5
6
|
super
|
6
7
|
else
|
@@ -11,111 +12,111 @@ module ActiveRecord
|
|
11
12
|
|
12
13
|
private
|
13
14
|
|
14
|
-
|
15
|
-
|
15
|
+
def method_missing(name, *arguments, &block)
|
16
|
+
match = Method.match(self, name)
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
18
|
+
if match && match.valid?
|
19
|
+
match.define
|
20
|
+
send(name, *arguments, &block)
|
21
|
+
else
|
22
|
+
super
|
23
|
+
end
|
22
24
|
end
|
23
|
-
end
|
24
25
|
|
25
|
-
|
26
|
-
|
26
|
+
class Method
|
27
|
+
@matchers = []
|
27
28
|
|
28
|
-
|
29
|
-
|
29
|
+
class << self
|
30
|
+
attr_reader :matchers
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
def match(model, name)
|
33
|
+
klass = matchers.find { |k| k.pattern.match?(name) }
|
34
|
+
klass.new(model, name) if klass
|
35
|
+
end
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
|
37
|
+
def pattern
|
38
|
+
@pattern ||= /\A#{prefix}_([_a-zA-Z]\w*)#{suffix}\Z/
|
39
|
+
end
|
39
40
|
|
40
|
-
|
41
|
-
|
42
|
-
|
41
|
+
def prefix
|
42
|
+
raise NotImplementedError
|
43
|
+
end
|
43
44
|
|
44
|
-
|
45
|
-
|
45
|
+
def suffix
|
46
|
+
""
|
47
|
+
end
|
46
48
|
end
|
47
|
-
end
|
48
49
|
|
49
|
-
|
50
|
+
attr_reader :model, :name, :attribute_names
|
50
51
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
52
|
+
def initialize(model, name)
|
53
|
+
@model = model
|
54
|
+
@name = name.to_s
|
55
|
+
@attribute_names = @name.match(self.class.pattern)[1].split("_and_")
|
56
|
+
@attribute_names.map! { |n| @model.attribute_aliases[n] || n }
|
57
|
+
end
|
57
58
|
|
58
|
-
|
59
|
-
|
60
|
-
|
59
|
+
def valid?
|
60
|
+
attribute_names.all? { |name| model.columns_hash[name] || model.reflect_on_aggregation(name.to_sym) }
|
61
|
+
end
|
61
62
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
63
|
+
def define
|
64
|
+
model.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
65
|
+
def self.#{name}(#{signature})
|
66
|
+
#{body}
|
67
|
+
end
|
68
|
+
CODE
|
69
|
+
end
|
69
70
|
|
70
|
-
|
71
|
+
private
|
71
72
|
|
72
|
-
|
73
|
-
|
74
|
-
|
73
|
+
def body
|
74
|
+
"#{finder}(#{attributes_hash})"
|
75
|
+
end
|
75
76
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
77
|
+
# The parameters in the signature may have reserved Ruby words, in order
|
78
|
+
# to prevent errors, we start each param name with `_`.
|
79
|
+
def signature
|
80
|
+
attribute_names.map { |name| "_#{name}" }.join(", ")
|
81
|
+
end
|
81
82
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
83
|
+
# Given that the parameters starts with `_`, the finder needs to use the
|
84
|
+
# same parameter name.
|
85
|
+
def attributes_hash
|
86
|
+
"{" + attribute_names.map { |name| ":#{name} => _#{name}" }.join(",") + "}"
|
87
|
+
end
|
87
88
|
|
88
|
-
|
89
|
-
|
89
|
+
def finder
|
90
|
+
raise NotImplementedError
|
91
|
+
end
|
90
92
|
end
|
91
|
-
end
|
92
93
|
|
93
|
-
|
94
|
-
|
94
|
+
class FindBy < Method
|
95
|
+
Method.matchers << self
|
95
96
|
|
96
|
-
|
97
|
-
|
98
|
-
|
97
|
+
def self.prefix
|
98
|
+
"find_by"
|
99
|
+
end
|
99
100
|
|
100
|
-
|
101
|
-
|
101
|
+
def finder
|
102
|
+
"find_by"
|
103
|
+
end
|
102
104
|
end
|
103
|
-
end
|
104
105
|
|
105
|
-
|
106
|
-
|
106
|
+
class FindByBang < Method
|
107
|
+
Method.matchers << self
|
107
108
|
|
108
|
-
|
109
|
-
|
110
|
-
|
109
|
+
def self.prefix
|
110
|
+
"find_by"
|
111
|
+
end
|
111
112
|
|
112
|
-
|
113
|
-
|
114
|
-
|
113
|
+
def self.suffix
|
114
|
+
"!"
|
115
|
+
end
|
115
116
|
|
116
|
-
|
117
|
-
|
117
|
+
def finder
|
118
|
+
"find_by!"
|
119
|
+
end
|
118
120
|
end
|
119
|
-
end
|
120
121
|
end
|
121
122
|
end
|
data/lib/active_record/enum.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "active_support/core_ext/object/deep_dup"
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
# Declare an enum attribute where the values map to integers in the database,
|
@@ -140,9 +140,11 @@ module ActiveRecord
|
|
140
140
|
end
|
141
141
|
end
|
142
142
|
|
143
|
+
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
144
|
+
# Workaround for Ruby 2.2 "private attribute?" warning.
|
143
145
|
protected
|
144
146
|
|
145
|
-
|
147
|
+
attr_reader :name, :mapping, :subtype
|
146
148
|
end
|
147
149
|
|
148
150
|
def enum(definitions)
|
@@ -216,18 +218,18 @@ module ActiveRecord
|
|
216
218
|
|
217
219
|
def detect_enum_conflict!(enum_name, method_name, klass_method = false)
|
218
220
|
if klass_method && dangerous_class_method?(method_name)
|
219
|
-
raise_conflict_error(enum_name, method_name, type:
|
221
|
+
raise_conflict_error(enum_name, method_name, type: "class")
|
220
222
|
elsif !klass_method && dangerous_attribute_method?(method_name)
|
221
223
|
raise_conflict_error(enum_name, method_name)
|
222
224
|
elsif !klass_method && method_defined_within?(method_name, _enum_methods_module, Module)
|
223
|
-
raise_conflict_error(enum_name, method_name, source:
|
225
|
+
raise_conflict_error(enum_name, method_name, source: "another enum")
|
224
226
|
end
|
225
227
|
end
|
226
228
|
|
227
|
-
def raise_conflict_error(enum_name, method_name, type:
|
229
|
+
def raise_conflict_error(enum_name, method_name, type: "instance", source: "Active Record")
|
228
230
|
raise ArgumentError, ENUM_CONFLICT_MESSAGE % {
|
229
231
|
enum: enum_name,
|
230
|
-
klass:
|
232
|
+
klass: name,
|
231
233
|
type: type,
|
232
234
|
method: method_name,
|
233
235
|
source: source
|
data/lib/active_record/errors.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
|
3
2
|
# = Active Record Errors
|
4
3
|
#
|
5
4
|
# Generic Active Record exception class.
|
@@ -44,7 +43,7 @@ module ActiveRecord
|
|
44
43
|
|
45
44
|
# Raised when connection to the database could not been established (for example when
|
46
45
|
# {ActiveRecord::Base.connection=}[rdoc-ref:ConnectionHandling#connection]
|
47
|
-
# is given a nil object).
|
46
|
+
# is given a +nil+ object).
|
48
47
|
class ConnectionNotEstablished < ActiveRecordError
|
49
48
|
end
|
50
49
|
|
@@ -96,20 +95,9 @@ module ActiveRecord
|
|
96
95
|
#
|
97
96
|
# Wraps the underlying database error as +cause+.
|
98
97
|
class StatementInvalid < ActiveRecordError
|
99
|
-
|
100
|
-
def initialize(message = nil, original_exception = nil)
|
101
|
-
if original_exception
|
102
|
-
ActiveSupport::Deprecation.warn("Passing #original_exception is deprecated and has no effect. " \
|
103
|
-
"Exceptions will automatically capture the original exception.", caller)
|
104
|
-
end
|
105
|
-
|
98
|
+
def initialize(message = nil)
|
106
99
|
super(message || $!.try(:message))
|
107
100
|
end
|
108
|
-
|
109
|
-
def original_exception
|
110
|
-
ActiveSupport::Deprecation.warn("#original_exception is deprecated. Use #cause instead.", caller)
|
111
|
-
cause
|
112
|
-
end
|
113
101
|
end
|
114
102
|
|
115
103
|
# Defunct wrapper class kept for compatibility.
|
@@ -125,10 +113,46 @@ module ActiveRecord
|
|
125
113
|
class InvalidForeignKey < WrappedDatabaseException
|
126
114
|
end
|
127
115
|
|
116
|
+
# Raised when a foreign key constraint cannot be added because the column type does not match the referenced column type.
|
117
|
+
class MismatchedForeignKey < StatementInvalid
|
118
|
+
def initialize(adapter = nil, message: nil, table: nil, foreign_key: nil, target_table: nil, primary_key: nil)
|
119
|
+
@adapter = adapter
|
120
|
+
if table
|
121
|
+
msg = <<-EOM.strip_heredoc
|
122
|
+
Column `#{foreign_key}` on table `#{table}` has a type of `#{column_type(table, foreign_key)}`.
|
123
|
+
This does not match column `#{primary_key}` on `#{target_table}`, which has type `#{column_type(target_table, primary_key)}`.
|
124
|
+
To resolve this issue, change the type of the `#{foreign_key}` column on `#{table}` to be :integer. (For example `t.integer #{foreign_key}`).
|
125
|
+
EOM
|
126
|
+
else
|
127
|
+
msg = <<-EOM
|
128
|
+
There is a mismatch between the foreign key and primary key column types.
|
129
|
+
Verify that the foreign key column type and the primary key of the associated table match types.
|
130
|
+
EOM
|
131
|
+
end
|
132
|
+
if message
|
133
|
+
msg << "\nOriginal message: #{message}"
|
134
|
+
end
|
135
|
+
super(msg)
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
def column_type(table, column)
|
140
|
+
@adapter.columns(table).detect { |c| c.name == column }.sql_type
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Raised when a record cannot be inserted or updated because it would violate a not null constraint.
|
145
|
+
class NotNullViolation < StatementInvalid
|
146
|
+
end
|
147
|
+
|
128
148
|
# Raised when a record cannot be inserted or updated because a value too long for a column type.
|
129
149
|
class ValueTooLong < StatementInvalid
|
130
150
|
end
|
131
151
|
|
152
|
+
# Raised when values that executed are out of range.
|
153
|
+
class RangeError < StatementInvalid
|
154
|
+
end
|
155
|
+
|
132
156
|
# Raised when number of bind variables in statement given to +:condition+ key
|
133
157
|
# (for example, when using {ActiveRecord::Base.find}[rdoc-ref:FinderMethods#find] method)
|
134
158
|
# does not match number of expected values supplied.
|
@@ -166,7 +190,6 @@ module ActiveRecord
|
|
166
190
|
super("Stale object error.")
|
167
191
|
end
|
168
192
|
end
|
169
|
-
|
170
193
|
end
|
171
194
|
|
172
195
|
# Raised when association is being configured improperly or user tries to use
|
@@ -285,6 +308,26 @@ module ActiveRecord
|
|
285
308
|
class TransactionIsolationError < ActiveRecordError
|
286
309
|
end
|
287
310
|
|
311
|
+
# TransactionRollbackError will be raised when a transaction is rolled
|
312
|
+
# back by the database due to a serialization failure or a deadlock.
|
313
|
+
#
|
314
|
+
# See the following:
|
315
|
+
#
|
316
|
+
# * http://www.postgresql.org/docs/current/static/transaction-iso.html
|
317
|
+
# * https://dev.mysql.com/doc/refman/5.7/en/error-messages-server.html#error_er_lock_deadlock
|
318
|
+
class TransactionRollbackError < StatementInvalid
|
319
|
+
end
|
320
|
+
|
321
|
+
# SerializationFailure will be raised when a transaction is rolled
|
322
|
+
# back by the database due to a serialization failure.
|
323
|
+
class SerializationFailure < TransactionRollbackError
|
324
|
+
end
|
325
|
+
|
326
|
+
# Deadlocked will be raised when a transaction is rolled
|
327
|
+
# back by the database when a deadlock is encountered.
|
328
|
+
class Deadlocked < TransactionRollbackError
|
329
|
+
end
|
330
|
+
|
288
331
|
# IrreversibleOrderError is raised when a relation's order is too complex for
|
289
332
|
# +reverse_order+ to automatically reverse.
|
290
333
|
class IrreversibleOrderError < ActiveRecordError
|