activerecord 4.2.0 → 5.0.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 +1537 -789
- data/MIT-LICENSE +2 -2
- data/README.rdoc +7 -8
- data/examples/performance.rb +2 -3
- data/examples/simple.rb +0 -1
- data/lib/active_record/aggregations.rb +37 -23
- data/lib/active_record/association_relation.rb +16 -3
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +23 -9
- data/lib/active_record/associations/association_scope.rb +74 -102
- data/lib/active_record/associations/belongs_to_association.rb +26 -29
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +43 -18
- data/lib/active_record/associations/builder/collection_association.rb +12 -20
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +22 -15
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +11 -6
- data/lib/active_record/associations/builder/singular_association.rb +3 -10
- data/lib/active_record/associations/collection_association.rb +61 -33
- data/lib/active_record/associations/collection_proxy.rb +81 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +21 -57
- data/lib/active_record/associations/has_many_through_association.rb +15 -45
- data/lib/active_record/associations/has_one_association.rb +13 -5
- data/lib/active_record/associations/join_dependency/join_association.rb +20 -8
- data/lib/active_record/associations/join_dependency.rb +37 -21
- data/lib/active_record/associations/preloader/association.rb +51 -53
- data/lib/active_record/associations/preloader/collection_association.rb +0 -6
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/through_association.rb +27 -14
- data/lib/active_record/associations/preloader.rb +18 -8
- data/lib/active_record/associations/singular_association.rb +8 -8
- data/lib/active_record/associations/through_association.rb +22 -9
- data/lib/active_record/associations.rb +321 -212
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute.rb +79 -15
- data/lib/active_record/attribute_assignment.rb +20 -141
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods/before_type_cast.rb +6 -1
- data/lib/active_record/attribute_methods/dirty.rb +51 -81
- data/lib/active_record/attribute_methods/primary_key.rb +2 -2
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +31 -59
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +65 -14
- data/lib/active_record/attribute_methods/write.rb +14 -38
- data/lib/active_record/attribute_methods.rb +70 -45
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set/builder.rb +37 -15
- data/lib/active_record/attribute_set.rb +34 -3
- data/lib/active_record/attributes.rb +199 -73
- data/lib/active_record/autosave_association.rb +73 -25
- data/lib/active_record/base.rb +35 -27
- data/lib/active_record/callbacks.rb +39 -43
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +40 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +457 -181
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -9
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +4 -4
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +246 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +438 -136
- data/lib/active_record/connection_adapters/abstract/transaction.rb +53 -40
- data/lib/active_record/connection_adapters/abstract_adapter.rb +166 -66
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +429 -335
- data/lib/active_record/connection_adapters/column.rb +28 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -177
- data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +11 -73
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +27 -56
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -13
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +17 -5
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +26 -18
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +248 -154
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +258 -170
- data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +150 -209
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +38 -15
- data/lib/active_record/core.rb +109 -114
- data/lib/active_record/counter_cache.rb +14 -25
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +115 -79
- data/lib/active_record/errors.rb +88 -48
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +2 -2
- data/lib/active_record/fixture_set/file.rb +26 -5
- data/lib/active_record/fixtures.rb +84 -46
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +32 -40
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/internal_metadata.rb +56 -0
- data/lib/active_record/legacy_yaml_adapter.rb +46 -0
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +27 -25
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +43 -21
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/migration.rb +372 -114
- data/lib/active_record/model_schema.rb +128 -38
- data/lib/active_record/nested_attributes.rb +71 -32
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +124 -80
- data/lib/active_record/query_cache.rb +15 -18
- data/lib/active_record/querying.rb +10 -9
- data/lib/active_record/railtie.rb +28 -19
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +67 -51
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +318 -139
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/batches.rb +139 -34
- data/lib/active_record/relation/calculations.rb +80 -102
- data/lib/active_record/relation/delegation.rb +7 -20
- data/lib/active_record/relation/finder_methods.rb +167 -97
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +38 -41
- data/lib/active_record/relation/predicate_builder/array_handler.rb +12 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +124 -82
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +323 -257
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +11 -10
- data/lib/active_record/relation/where_clause.rb +174 -0
- data/lib/active_record/relation/where_clause_factory.rb +38 -0
- data/lib/active_record/relation.rb +176 -115
- data/lib/active_record/result.rb +4 -3
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +95 -66
- data/lib/active_record/schema.rb +26 -22
- data/lib/active_record/schema_dumper.rb +62 -38
- data/lib/active_record/schema_migration.rb +11 -17
- data/lib/active_record/scoping/default.rb +24 -9
- data/lib/active_record/scoping/named.rb +49 -28
- data/lib/active_record/scoping.rb +32 -15
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +2 -4
- data/lib/active_record/statement_cache.rb +16 -14
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +68 -0
- data/lib/active_record/tasks/database_tasks.rb +59 -42
- data/lib/active_record/tasks/mysql_database_tasks.rb +32 -26
- data/lib/active_record/tasks/postgresql_database_tasks.rb +29 -9
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +20 -9
- data/lib/active_record/touch_later.rb +58 -0
- data/lib/active_record/transactions.rb +159 -67
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -41
- data/lib/active_record/type/date_time.rb +2 -38
- data/lib/active_record/type/hash_lookup_type_map.rb +8 -2
- data/lib/active_record/type/internal/abstract_json.rb +29 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +21 -14
- data/lib/active_record/type/time.rb +10 -16
- data/lib/active_record/type/type_map.rb +4 -4
- data/lib/active_record/type.rb +66 -17
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/validations/absence.rb +23 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +11 -12
- data/lib/active_record/validations/uniqueness.rb +29 -18
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record.rb +9 -2
- data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -6
- data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -7
- data/lib/rails/generators/active_record/migration.rb +7 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +32 -15
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +60 -34
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -30
- data/lib/active_record/type/decimal.rb +0 -40
- data/lib/active_record/type/decimal_without_scale.rb +0 -11
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -55
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -36
- data/lib/active_record/type/text.rb +0 -11
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/unsigned_integer.rb +0 -15
- data/lib/active_record/type/value.rb +0 -101
@@ -1,38 +1,39 @@
|
|
1
|
-
|
2
|
-
require 'arel/collectors/bind'
|
1
|
+
require "arel/collectors/bind"
|
3
2
|
|
4
3
|
module ActiveRecord
|
5
|
-
# = Active Record Relation
|
4
|
+
# = Active Record \Relation
|
6
5
|
class Relation
|
7
6
|
MULTI_VALUE_METHODS = [:includes, :eager_load, :preload, :select, :group,
|
8
|
-
:order, :joins, :
|
7
|
+
:order, :joins, :left_joins, :left_outer_joins, :references,
|
9
8
|
:extending, :unscope]
|
10
9
|
|
11
|
-
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :
|
12
|
-
:reverse_order, :distinct, :create_with
|
10
|
+
SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :reordering,
|
11
|
+
:reverse_order, :distinct, :create_with]
|
12
|
+
CLAUSE_METHODS = [:where, :having, :from]
|
13
13
|
INVALID_METHODS_FOR_DELETE_ALL = [:limit, :distinct, :offset, :group, :having]
|
14
14
|
|
15
|
-
VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS
|
15
|
+
VALUE_METHODS = MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS
|
16
16
|
|
17
|
+
include Enumerable
|
17
18
|
include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
|
18
19
|
|
19
|
-
attr_reader :table, :klass, :loaded
|
20
|
+
attr_reader :table, :klass, :loaded, :predicate_builder
|
20
21
|
alias :model :klass
|
21
22
|
alias :loaded? :loaded
|
22
23
|
|
23
|
-
def initialize(klass, table, values = {})
|
24
|
+
def initialize(klass, table, predicate_builder, values = {})
|
24
25
|
@klass = klass
|
25
26
|
@table = table
|
26
27
|
@values = values
|
27
28
|
@offsets = {}
|
28
29
|
@loaded = false
|
30
|
+
@predicate_builder = predicate_builder
|
29
31
|
end
|
30
32
|
|
31
33
|
def initialize_copy(other)
|
32
34
|
# This method is a hot spot, so for now, use Hash[] to dup the hash.
|
33
35
|
# https://bugs.ruby-lang.org/issues/7166
|
34
36
|
@values = Hash[@values]
|
35
|
-
@values[:bind] = @values[:bind].dup if @values.key? :bind
|
36
37
|
reset
|
37
38
|
end
|
38
39
|
|
@@ -44,9 +45,9 @@ module ActiveRecord
|
|
44
45
|
k.name == primary_key
|
45
46
|
}]
|
46
47
|
|
47
|
-
if !primary_key_value &&
|
48
|
-
primary_key_value =
|
49
|
-
values[klass.
|
48
|
+
if !primary_key_value && klass.prefetch_primary_key?
|
49
|
+
primary_key_value = klass.next_sequence_value
|
50
|
+
values[arel_attribute(klass.primary_key)] = primary_key_value
|
50
51
|
end
|
51
52
|
end
|
52
53
|
|
@@ -80,7 +81,7 @@ module ActiveRecord
|
|
80
81
|
end
|
81
82
|
|
82
83
|
relation = scope.where(@klass.primary_key => (id_was || id))
|
83
|
-
bvs = binds + relation.
|
84
|
+
bvs = binds + relation.bound_attributes
|
84
85
|
um = relation
|
85
86
|
.arel
|
86
87
|
.compile_update(substitutes, @klass.primary_key)
|
@@ -93,21 +94,25 @@ module ActiveRecord
|
|
93
94
|
end
|
94
95
|
|
95
96
|
def substitute_values(values) # :nodoc:
|
96
|
-
binds =
|
97
|
-
|
98
|
-
end
|
97
|
+
binds = []
|
98
|
+
substitutes = []
|
99
99
|
|
100
|
-
|
101
|
-
|
100
|
+
values.each do |arel_attr, value|
|
101
|
+
binds.push QueryAttribute.new(arel_attr.name, value, klass.type_for_attribute(arel_attr.name))
|
102
|
+
substitutes.push [arel_attr, Arel::Nodes::BindParam.new]
|
102
103
|
end
|
103
104
|
|
104
105
|
[substitutes, binds]
|
105
106
|
end
|
106
107
|
|
108
|
+
def arel_attribute(name) # :nodoc:
|
109
|
+
klass.arel_attribute(name, table)
|
110
|
+
end
|
111
|
+
|
107
112
|
# Initializes new record from relation while maintaining the current
|
108
113
|
# scope.
|
109
114
|
#
|
110
|
-
# Expects arguments in the same format as
|
115
|
+
# Expects arguments in the same format as {ActiveRecord::Base.new}[rdoc-ref:Core.new].
|
111
116
|
#
|
112
117
|
# users = User.where(name: 'DHH')
|
113
118
|
# user = users.new # => #<User id: nil, name: "DHH", created_at: nil, updated_at: nil>
|
@@ -125,28 +130,32 @@ module ActiveRecord
|
|
125
130
|
# Tries to create a new record with the same scoped attributes
|
126
131
|
# defined in the relation. Returns the initialized object if validation fails.
|
127
132
|
#
|
128
|
-
# Expects arguments in the same format as
|
133
|
+
# Expects arguments in the same format as
|
134
|
+
# {ActiveRecord::Base.create}[rdoc-ref:Persistence::ClassMethods#create].
|
129
135
|
#
|
130
136
|
# ==== Examples
|
137
|
+
#
|
131
138
|
# users = User.where(name: 'Oscar')
|
132
|
-
# users.create # #<User id: 3, name: "
|
139
|
+
# users.create # => #<User id: 3, name: "Oscar", ...>
|
133
140
|
#
|
134
141
|
# users.create(name: 'fxn')
|
135
|
-
# users.create # #<User id: 4, name: "fxn", ...>
|
142
|
+
# users.create # => #<User id: 4, name: "fxn", ...>
|
136
143
|
#
|
137
144
|
# users.create { |user| user.name = 'tenderlove' }
|
138
|
-
# # #<User id: 5, name: "tenderlove", ...>
|
145
|
+
# # => #<User id: 5, name: "tenderlove", ...>
|
139
146
|
#
|
140
147
|
# users.create(name: nil) # validation on name
|
141
|
-
# # #<User id: nil, name: nil, ...>
|
148
|
+
# # => #<User id: nil, name: nil, ...>
|
142
149
|
def create(*args, &block)
|
143
150
|
scoping { @klass.create(*args, &block) }
|
144
151
|
end
|
145
152
|
|
146
|
-
# Similar to #create, but calls
|
147
|
-
#
|
153
|
+
# Similar to #create, but calls
|
154
|
+
# {create!}[rdoc-ref:Persistence::ClassMethods#create!]
|
155
|
+
# on the base class. Raises an exception if a validation error occurs.
|
148
156
|
#
|
149
|
-
# Expects arguments in the same format as
|
157
|
+
# Expects arguments in the same format as
|
158
|
+
# {ActiveRecord::Base.create!}[rdoc-ref:Persistence::ClassMethods#create!].
|
150
159
|
def create!(*args, &block)
|
151
160
|
scoping { @klass.create!(*args, &block) }
|
152
161
|
end
|
@@ -180,7 +189,7 @@ module ActiveRecord
|
|
180
189
|
# User.create_with(last_name: 'Johansson').find_or_create_by(first_name: 'Scarlett')
|
181
190
|
# # => #<User id: 2, first_name: "Scarlett", last_name: "Johansson">
|
182
191
|
#
|
183
|
-
# This method accepts a block, which is passed down to
|
192
|
+
# This method accepts a block, which is passed down to #create. The last example
|
184
193
|
# above can be alternatively written this way:
|
185
194
|
#
|
186
195
|
# # Find the first user named "Scarlett" or create a new one with a
|
@@ -192,7 +201,7 @@ module ActiveRecord
|
|
192
201
|
#
|
193
202
|
# This method always returns a record, but if creation was attempted and
|
194
203
|
# failed due to validation errors it won't be persisted, you get what
|
195
|
-
#
|
204
|
+
# #create returns in such situation.
|
196
205
|
#
|
197
206
|
# Please note *this method is not atomic*, it runs first a SELECT, and if
|
198
207
|
# there are no results an INSERT is attempted. If there are other threads
|
@@ -204,7 +213,9 @@ module ActiveRecord
|
|
204
213
|
# constraint an exception may be raised, just retry:
|
205
214
|
#
|
206
215
|
# begin
|
207
|
-
# CreditAccount.
|
216
|
+
# CreditAccount.transaction(requires_new: true) do
|
217
|
+
# CreditAccount.find_or_create_by(user_id: user.id)
|
218
|
+
# end
|
208
219
|
# rescue ActiveRecord::RecordNotUnique
|
209
220
|
# retry
|
210
221
|
# end
|
@@ -213,13 +224,15 @@ module ActiveRecord
|
|
213
224
|
find_by(attributes) || create(attributes, &block)
|
214
225
|
end
|
215
226
|
|
216
|
-
# Like
|
227
|
+
# Like #find_or_create_by, but calls
|
228
|
+
# {create!}[rdoc-ref:Persistence::ClassMethods#create!] so an exception
|
217
229
|
# is raised if the created record is invalid.
|
218
230
|
def find_or_create_by!(attributes, &block)
|
219
231
|
find_by(attributes) || create!(attributes, &block)
|
220
232
|
end
|
221
233
|
|
222
|
-
# Like
|
234
|
+
# Like #find_or_create_by, but calls {new}[rdoc-ref:Core#new]
|
235
|
+
# instead of {create}[rdoc-ref:Persistence::ClassMethods#create].
|
223
236
|
def find_or_initialize_by(attributes, &block)
|
224
237
|
find_by(attributes) || new(attributes, &block)
|
225
238
|
end
|
@@ -240,17 +253,21 @@ module ActiveRecord
|
|
240
253
|
|
241
254
|
# Converts relation objects to Array.
|
242
255
|
def to_a
|
256
|
+
records.dup
|
257
|
+
end
|
258
|
+
|
259
|
+
def records # :nodoc:
|
243
260
|
load
|
244
261
|
@records
|
245
262
|
end
|
246
263
|
|
247
264
|
# Serializes the relation objects Array.
|
248
265
|
def encode_with(coder)
|
249
|
-
coder.represent_seq(nil,
|
266
|
+
coder.represent_seq(nil, records)
|
250
267
|
end
|
251
268
|
|
252
269
|
def as_json(options = nil) #:nodoc:
|
253
|
-
|
270
|
+
records.as_json(options)
|
254
271
|
end
|
255
272
|
|
256
273
|
# Returns size of the records.
|
@@ -270,22 +287,54 @@ module ActiveRecord
|
|
270
287
|
end
|
271
288
|
end
|
272
289
|
|
290
|
+
# Returns true if there are no records.
|
291
|
+
def none?
|
292
|
+
return super if block_given?
|
293
|
+
empty?
|
294
|
+
end
|
295
|
+
|
273
296
|
# Returns true if there are any records.
|
274
297
|
def any?
|
275
|
-
if block_given?
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
298
|
+
return super if block_given?
|
299
|
+
!empty?
|
300
|
+
end
|
301
|
+
|
302
|
+
# Returns true if there is exactly one record.
|
303
|
+
def one?
|
304
|
+
return super if block_given?
|
305
|
+
limit_value ? records.one? : size == 1
|
280
306
|
end
|
281
307
|
|
282
308
|
# Returns true if there is more than one record.
|
283
309
|
def many?
|
284
|
-
if block_given?
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
310
|
+
return super if block_given?
|
311
|
+
limit_value ? records.many? : size > 1
|
312
|
+
end
|
313
|
+
|
314
|
+
# Returns a cache key that can be used to identify the records fetched by
|
315
|
+
# this query. The cache key is built with a fingerprint of the sql query,
|
316
|
+
# the number of records matched by the query and a timestamp of the last
|
317
|
+
# updated record. When a new record comes to match the query, or any of
|
318
|
+
# the existing records is updated or deleted, the cache key changes.
|
319
|
+
#
|
320
|
+
# Product.where("name like ?", "%Cosmic Encounter%").cache_key
|
321
|
+
# # => "products/query-1850ab3d302391b85b8693e941286659-1-20150714212553907087000"
|
322
|
+
#
|
323
|
+
# If the collection is loaded, the method will iterate through the records
|
324
|
+
# to generate the timestamp, otherwise it will trigger one SQL query like:
|
325
|
+
#
|
326
|
+
# SELECT COUNT(*), MAX("products"."updated_at") FROM "products" WHERE (name like '%Cosmic Encounter%')
|
327
|
+
#
|
328
|
+
# You can also pass a custom timestamp column to fetch the timestamp of the
|
329
|
+
# last updated record.
|
330
|
+
#
|
331
|
+
# Product.where("name like ?", "%Game%").cache_key(:last_reviewed_at)
|
332
|
+
#
|
333
|
+
# You can customize the strategy to generate the key on a per model basis
|
334
|
+
# overriding ActiveRecord::Base#collection_cache_key.
|
335
|
+
def cache_key(timestamp_column = :updated_at)
|
336
|
+
@cache_keys ||= {}
|
337
|
+
@cache_keys[timestamp_column] ||= @klass.collection_cache_key(self, timestamp_column)
|
289
338
|
end
|
290
339
|
|
291
340
|
# Scope all queries to the current scope.
|
@@ -306,9 +355,8 @@ module ActiveRecord
|
|
306
355
|
|
307
356
|
# Updates all records in the current relation with details given. This method constructs a single SQL UPDATE
|
308
357
|
# statement and sends it straight to the database. It does not instantiate the involved models and it does not
|
309
|
-
# trigger Active Record callbacks or validations.
|
310
|
-
#
|
311
|
-
# database.
|
358
|
+
# trigger Active Record callbacks or validations. However, values passed to #update_all will still go through
|
359
|
+
# Active Record's normal type casting and serialization.
|
312
360
|
#
|
313
361
|
# ==== Parameters
|
314
362
|
#
|
@@ -327,22 +375,21 @@ module ActiveRecord
|
|
327
375
|
def update_all(updates)
|
328
376
|
raise ArgumentError, "Empty list of attributes to change" if updates.blank?
|
329
377
|
|
330
|
-
stmt = Arel::UpdateManager.new
|
378
|
+
stmt = Arel::UpdateManager.new
|
331
379
|
|
332
380
|
stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates))
|
333
381
|
stmt.table(table)
|
334
|
-
stmt.key = table[primary_key]
|
335
382
|
|
336
383
|
if joins_values.any?
|
337
|
-
@klass.connection.join_to_update(stmt, arel)
|
384
|
+
@klass.connection.join_to_update(stmt, arel, arel_attribute(primary_key))
|
338
385
|
else
|
386
|
+
stmt.key = arel_attribute(primary_key)
|
339
387
|
stmt.take(arel.limit)
|
340
388
|
stmt.order(*arel.orders)
|
341
389
|
stmt.wheres = arel.constraints
|
342
390
|
end
|
343
391
|
|
344
|
-
|
345
|
-
@klass.connection.update stmt, 'SQL', bvs
|
392
|
+
@klass.connection.update stmt, 'SQL', bound_attributes
|
346
393
|
end
|
347
394
|
|
348
395
|
# Updates an object (or multiple objects) and saves it to the database, if validations pass.
|
@@ -361,20 +408,39 @@ module ActiveRecord
|
|
361
408
|
# # Updates multiple records
|
362
409
|
# people = { 1 => { "first_name" => "David" }, 2 => { "first_name" => "Jeremy" } }
|
363
410
|
# Person.update(people.keys, people.values)
|
364
|
-
|
411
|
+
#
|
412
|
+
# # Updates multiple records from the result of a relation
|
413
|
+
# people = Person.where(group: 'expert')
|
414
|
+
# people.update(group: 'masters')
|
415
|
+
#
|
416
|
+
# Note: Updating a large number of records will run an
|
417
|
+
# UPDATE query for each record, which may cause a performance
|
418
|
+
# issue. So if it is not needed to run callbacks for each update, it is
|
419
|
+
# preferred to use #update_all for updating all records using
|
420
|
+
# a single query.
|
421
|
+
def update(id = :all, attributes)
|
365
422
|
if id.is_a?(Array)
|
366
423
|
id.map.with_index { |one_id, idx| update(one_id, attributes[idx]) }
|
424
|
+
elsif id == :all
|
425
|
+
records.each { |record| record.update(attributes) }
|
367
426
|
else
|
427
|
+
if ActiveRecord::Base === id
|
428
|
+
id = id.id
|
429
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
430
|
+
You are passing an instance of ActiveRecord::Base to `update`.
|
431
|
+
Please pass the id of the object by calling `.id`.
|
432
|
+
MSG
|
433
|
+
end
|
368
434
|
object = find(id)
|
369
435
|
object.update(attributes)
|
370
436
|
object
|
371
437
|
end
|
372
438
|
end
|
373
439
|
|
374
|
-
# Destroys the records
|
375
|
-
# record and calling its
|
376
|
-
# executed (including <tt>:dependent</tt> association options).
|
377
|
-
# collection of objects that were destroyed; each will be frozen, to
|
440
|
+
# Destroys the records by instantiating each
|
441
|
+
# record and calling its {#destroy}[rdoc-ref:Persistence#destroy] method.
|
442
|
+
# Each object's callbacks are executed (including <tt>:dependent</tt> association options).
|
443
|
+
# Returns the collection of objects that were destroyed; each will be frozen, to
|
378
444
|
# reflect that no changes should be made (since they can't be persisted).
|
379
445
|
#
|
380
446
|
# Note: Instantiation, callback execution, and deletion of each
|
@@ -382,31 +448,26 @@ module ActiveRecord
|
|
382
448
|
# once. It generates at least one SQL +DELETE+ query per record (or
|
383
449
|
# possibly more, to enforce your callbacks). If you want to delete many
|
384
450
|
# rows quickly, without concern for their associations or callbacks, use
|
385
|
-
#
|
386
|
-
#
|
387
|
-
# ==== Parameters
|
388
|
-
#
|
389
|
-
# * +conditions+ - A string, array, or hash that specifies which records
|
390
|
-
# to destroy. If omitted, all records are destroyed. See the
|
391
|
-
# Conditions section in the introduction to ActiveRecord::Base for
|
392
|
-
# more information.
|
451
|
+
# #delete_all instead.
|
393
452
|
#
|
394
453
|
# ==== Examples
|
395
454
|
#
|
396
|
-
# Person.destroy_all("last_login < '2004-04-04'")
|
397
|
-
# Person.destroy_all(status: "inactive")
|
398
455
|
# Person.where(age: 0..18).destroy_all
|
399
456
|
def destroy_all(conditions = nil)
|
400
457
|
if conditions
|
458
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
459
|
+
Passing conditions to destroy_all is deprecated and will be removed in Rails 5.1.
|
460
|
+
To achieve the same use where(conditions).destroy_all.
|
461
|
+
MESSAGE
|
401
462
|
where(conditions).destroy_all
|
402
463
|
else
|
403
|
-
|
464
|
+
records.each(&:destroy).tap { reset }
|
404
465
|
end
|
405
466
|
end
|
406
467
|
|
407
468
|
# Destroy an object (or multiple objects) that has the given id. The object is instantiated first,
|
408
469
|
# therefore all callbacks and filters are fired off before the object is deleted. This method is
|
409
|
-
# less efficient than
|
470
|
+
# less efficient than #delete but allows cleanup methods and other actions to be run.
|
410
471
|
#
|
411
472
|
# This essentially finds the object (or multiple objects) with the given id, creates a new object
|
412
473
|
# from the attributes, and then calls destroy on it.
|
@@ -431,22 +492,21 @@ module ActiveRecord
|
|
431
492
|
end
|
432
493
|
end
|
433
494
|
|
434
|
-
# Deletes the records
|
435
|
-
# first, and hence not calling the
|
436
|
-
#
|
437
|
-
#
|
495
|
+
# Deletes the records without instantiating the records
|
496
|
+
# first, and hence not calling the {#destroy}[rdoc-ref:Persistence#destroy]
|
497
|
+
# method nor invoking callbacks.
|
498
|
+
# This is a single SQL DELETE statement that goes straight to the database, much more
|
499
|
+
# efficient than #destroy_all. Be careful with relations though, in particular
|
438
500
|
# <tt>:dependent</tt> rules defined on associations are not honored. Returns the
|
439
501
|
# number of rows affected.
|
440
502
|
#
|
441
|
-
# Post.delete_all("person_id = 5 AND (category = 'Something' OR category = 'Else')")
|
442
|
-
# Post.delete_all(["person_id = ? AND (category = ? OR category = ?)", 5, 'Something', 'Else'])
|
443
503
|
# Post.where(person_id: 5).where(category: ['Something', 'Else']).delete_all
|
444
504
|
#
|
445
505
|
# Both calls delete the affected posts all at once with a single DELETE statement.
|
446
506
|
# If you need to destroy dependent associations or call your <tt>before_*</tt> or
|
447
|
-
# +after_destroy+ callbacks, use the
|
507
|
+
# +after_destroy+ callbacks, use the #destroy_all method instead.
|
448
508
|
#
|
449
|
-
# If an invalid method is supplied,
|
509
|
+
# If an invalid method is supplied, #delete_all raises an ActiveRecordError:
|
450
510
|
#
|
451
511
|
# Post.limit(100).delete_all
|
452
512
|
# # => ActiveRecord::ActiveRecordError: delete_all doesn't support limit
|
@@ -454,8 +514,10 @@ module ActiveRecord
|
|
454
514
|
invalid_methods = INVALID_METHODS_FOR_DELETE_ALL.select { |method|
|
455
515
|
if MULTI_VALUE_METHODS.include?(method)
|
456
516
|
send("#{method}_values").any?
|
457
|
-
|
517
|
+
elsif SINGLE_VALUE_METHODS.include?(method)
|
458
518
|
send("#{method}_value")
|
519
|
+
elsif CLAUSE_METHODS.include?(method)
|
520
|
+
send("#{method}_clause").any?
|
459
521
|
end
|
460
522
|
}
|
461
523
|
if invalid_methods.any?
|
@@ -463,18 +525,22 @@ module ActiveRecord
|
|
463
525
|
end
|
464
526
|
|
465
527
|
if conditions
|
528
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.squish)
|
529
|
+
Passing conditions to delete_all is deprecated and will be removed in Rails 5.1.
|
530
|
+
To achieve the same use where(conditions).delete_all.
|
531
|
+
MESSAGE
|
466
532
|
where(conditions).delete_all
|
467
533
|
else
|
468
|
-
stmt = Arel::DeleteManager.new
|
534
|
+
stmt = Arel::DeleteManager.new
|
469
535
|
stmt.from(table)
|
470
536
|
|
471
537
|
if joins_values.any?
|
472
|
-
@klass.connection.join_to_delete(stmt, arel,
|
538
|
+
@klass.connection.join_to_delete(stmt, arel, arel_attribute(primary_key))
|
473
539
|
else
|
474
540
|
stmt.wheres = arel.constraints
|
475
541
|
end
|
476
542
|
|
477
|
-
affected = @klass.connection.delete(stmt, 'SQL',
|
543
|
+
affected = @klass.connection.delete(stmt, 'SQL', bound_attributes)
|
478
544
|
|
479
545
|
reset
|
480
546
|
affected
|
@@ -489,7 +555,7 @@ module ActiveRecord
|
|
489
555
|
# You can delete multiple rows at once by passing an Array of <tt>id</tt>s.
|
490
556
|
#
|
491
557
|
# Note: Although it is often much faster than the alternative,
|
492
|
-
#
|
558
|
+
# #destroy, skipping callbacks might bypass business logic in
|
493
559
|
# your application that ensures referential integrity or performs other
|
494
560
|
# essential jobs.
|
495
561
|
#
|
@@ -525,7 +591,7 @@ module ActiveRecord
|
|
525
591
|
def reset
|
526
592
|
@last = @to_sql = @order_clause = @scope_for_create = @arel = @loaded = nil
|
527
593
|
@should_eager_load = @join_dependency = nil
|
528
|
-
@records = []
|
594
|
+
@records = [].freeze
|
529
595
|
@offsets = {}
|
530
596
|
self
|
531
597
|
end
|
@@ -544,10 +610,10 @@ module ActiveRecord
|
|
544
610
|
find_with_associations { |rel| relation = rel }
|
545
611
|
end
|
546
612
|
|
547
|
-
|
548
|
-
binds = (
|
549
|
-
binds.map! { |
|
550
|
-
collect = visitor.accept(arel.ast, Arel::Collectors::Bind.new)
|
613
|
+
binds = relation.bound_attributes
|
614
|
+
binds = connection.prepare_binds_for_database(binds)
|
615
|
+
binds.map! { |value| connection.quote(value) }
|
616
|
+
collect = visitor.accept(relation.arel.ast, Arel::Collectors::Bind.new)
|
551
617
|
collect.substitute_binds(binds).join
|
552
618
|
end
|
553
619
|
end
|
@@ -557,22 +623,7 @@ module ActiveRecord
|
|
557
623
|
# User.where(name: 'Oscar').where_values_hash
|
558
624
|
# # => {name: "Oscar"}
|
559
625
|
def where_values_hash(relation_table_name = table_name)
|
560
|
-
|
561
|
-
node.left.relation.name == relation_table_name
|
562
|
-
}
|
563
|
-
|
564
|
-
binds = Hash[bind_values.find_all(&:first).map { |column, v| [column.name, v] }]
|
565
|
-
|
566
|
-
Hash[equalities.map { |where|
|
567
|
-
name = where.left.name
|
568
|
-
[name, binds.fetch(name.to_s) {
|
569
|
-
case where.right
|
570
|
-
when Array then where.right.map(&:val)
|
571
|
-
else
|
572
|
-
where.right.val
|
573
|
-
end
|
574
|
-
}]
|
575
|
-
}]
|
626
|
+
where_clause.to_h(relation_table_name)
|
576
627
|
end
|
577
628
|
|
578
629
|
def scope_for_create
|
@@ -594,31 +645,34 @@ module ActiveRecord
|
|
594
645
|
includes_values & joins_values
|
595
646
|
end
|
596
647
|
|
597
|
-
#
|
598
|
-
#
|
648
|
+
# {#uniq}[rdoc-ref:QueryMethods#uniq] and
|
649
|
+
# {#uniq!}[rdoc-ref:QueryMethods#uniq!] are silently deprecated.
|
650
|
+
# #uniq_value delegates to #distinct_value to maintain backwards compatibility.
|
651
|
+
# Use #distinct_value instead.
|
599
652
|
def uniq_value
|
600
653
|
distinct_value
|
601
654
|
end
|
655
|
+
deprecate uniq_value: :distinct_value
|
602
656
|
|
603
657
|
# Compares two relations for equality.
|
604
658
|
def ==(other)
|
605
659
|
case other
|
606
660
|
when Associations::CollectionProxy, AssociationRelation
|
607
|
-
self == other.
|
661
|
+
self == other.records
|
608
662
|
when Relation
|
609
663
|
other.to_sql == to_sql
|
610
664
|
when Array
|
611
|
-
|
665
|
+
records == other
|
612
666
|
end
|
613
667
|
end
|
614
668
|
|
615
669
|
def pretty_print(q)
|
616
|
-
q.pp(self.
|
670
|
+
q.pp(self.records)
|
617
671
|
end
|
618
672
|
|
619
673
|
# Returns true if relation is blank.
|
620
674
|
def blank?
|
621
|
-
|
675
|
+
records.blank?
|
622
676
|
end
|
623
677
|
|
624
678
|
def values
|
@@ -626,16 +680,23 @@ module ActiveRecord
|
|
626
680
|
end
|
627
681
|
|
628
682
|
def inspect
|
629
|
-
entries =
|
683
|
+
entries = records.take([limit_value, 11].compact.min).map!(&:inspect)
|
630
684
|
entries[10] = '...' if entries.size == 11
|
631
685
|
|
632
686
|
"#<#{self.class.name} [#{entries.join(', ')}]>"
|
633
687
|
end
|
634
688
|
|
689
|
+
protected
|
690
|
+
|
691
|
+
def load_records(records)
|
692
|
+
@records = records.freeze
|
693
|
+
@loaded = true
|
694
|
+
end
|
695
|
+
|
635
696
|
private
|
636
697
|
|
637
698
|
def exec_queries
|
638
|
-
@records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel,
|
699
|
+
@records = eager_loading? ? find_with_associations.freeze : @klass.find_by_sql(arel, bound_attributes).freeze
|
639
700
|
|
640
701
|
preload = preload_values
|
641
702
|
preload += includes_values unless eager_loading?
|
@@ -644,7 +705,7 @@ module ActiveRecord
|
|
644
705
|
preloader.preload @records, associations
|
645
706
|
end
|
646
707
|
|
647
|
-
@records.each
|
708
|
+
@records.each(&:readonly!) if readonly_value
|
648
709
|
|
649
710
|
@loaded = true
|
650
711
|
@records
|
@@ -666,7 +727,7 @@ module ActiveRecord
|
|
666
727
|
joined_tables += [table.name, table.table_alias]
|
667
728
|
|
668
729
|
# always convert table names to downcase as in Oracle quoted table names are in uppercase
|
669
|
-
joined_tables = joined_tables.flatten.compact.map
|
730
|
+
joined_tables = joined_tables.flatten.compact.map(&:downcase).uniq
|
670
731
|
|
671
732
|
(references_values - joined_tables).any?
|
672
733
|
end
|
@@ -675,7 +736,7 @@ module ActiveRecord
|
|
675
736
|
return [] if string.blank?
|
676
737
|
# always convert table names to downcase as in Oracle quoted table names are in uppercase
|
677
738
|
# ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries
|
678
|
-
string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map
|
739
|
+
string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map(&:downcase).uniq - ['raw_sql_']
|
679
740
|
end
|
680
741
|
end
|
681
742
|
end
|
data/lib/active_record/result.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
###
|
3
|
-
# This class encapsulates a
|
4
|
-
#
|
3
|
+
# This class encapsulates a result returned from calling
|
4
|
+
# {#exec_query}[rdoc-ref:ConnectionAdapters::DatabaseStatements#exec_query]
|
5
|
+
# on any database connection adapter. For example:
|
5
6
|
#
|
6
7
|
# result = ActiveRecord::Base.connection.exec_query('SELECT id, title, body FROM posts')
|
7
8
|
# result # => #<ActiveRecord::Result:0xdeadbeef>
|
@@ -81,7 +82,7 @@ module ActiveRecord
|
|
81
82
|
def cast_values(type_overrides = {}) # :nodoc:
|
82
83
|
types = columns.map { |name| column_type(name, type_overrides) }
|
83
84
|
result = rows.map do |values|
|
84
|
-
types.zip(values).map { |type, value| type.
|
85
|
+
types.zip(values).map { |type, value| type.deserialize(value) }
|
85
86
|
end
|
86
87
|
|
87
88
|
columns.one? ? result.map!(&:first) : result
|
@@ -7,7 +7,7 @@ module ActiveRecord
|
|
7
7
|
#
|
8
8
|
# returns the connection handler local to the current thread.
|
9
9
|
#
|
10
|
-
# See the documentation of
|
10
|
+
# See the documentation of ActiveSupport::PerThreadRegistry
|
11
11
|
# for further details.
|
12
12
|
class RuntimeRegistry # :nodoc:
|
13
13
|
extend ActiveSupport::PerThreadRegistry
|