activerecord 5.1.7 → 5.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 +372 -765
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -5
- data/examples/performance.rb +2 -0
- data/examples/simple.rb +2 -0
- data/lib/active_record/aggregations.rb +6 -5
- data/lib/active_record/association_relation.rb +4 -2
- data/lib/active_record/associations/alias_tracker.rb +19 -27
- data/lib/active_record/associations/association.rb +16 -27
- data/lib/active_record/associations/association_scope.rb +38 -50
- data/lib/active_record/associations/belongs_to_association.rb +20 -10
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +4 -7
- data/lib/active_record/associations/builder/association.rb +4 -7
- data/lib/active_record/associations/builder/belongs_to.rb +4 -5
- data/lib/active_record/associations/builder/collection_association.rb +1 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +3 -1
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +2 -0
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +43 -35
- data/lib/active_record/associations/collection_proxy.rb +12 -15
- data/lib/active_record/associations/foreign_association.rb +2 -0
- data/lib/active_record/associations/has_many_association.rb +3 -1
- data/lib/active_record/associations/has_many_through_association.rb +7 -18
- data/lib/active_record/associations/has_one_association.rb +4 -1
- data/lib/active_record/associations/has_one_through_association.rb +8 -7
- data/lib/active_record/associations/join_dependency/join_association.rb +17 -56
- data/lib/active_record/associations/join_dependency/join_base.rb +9 -8
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -9
- data/lib/active_record/associations/join_dependency.rb +23 -43
- data/lib/active_record/associations/preloader/association.rb +45 -61
- data/lib/active_record/associations/preloader/through_association.rb +71 -79
- data/lib/active_record/associations/preloader.rb +17 -37
- data/lib/active_record/associations/singular_association.rb +14 -10
- data/lib/active_record/associations/through_association.rb +25 -10
- data/lib/active_record/associations.rb +31 -54
- data/lib/active_record/attribute_assignment.rb +2 -5
- data/lib/active_record/attribute_decorators.rb +3 -2
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -0
- data/lib/active_record/attribute_methods/dirty.rb +25 -214
- data/lib/active_record/attribute_methods/primary_key.rb +7 -6
- data/lib/active_record/attribute_methods/query.rb +2 -0
- data/lib/active_record/attribute_methods/read.rb +8 -2
- data/lib/active_record/attribute_methods/serialization.rb +23 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +6 -8
- data/lib/active_record/attribute_methods/write.rb +21 -9
- data/lib/active_record/attribute_methods.rb +65 -24
- data/lib/active_record/attributes.rb +6 -5
- data/lib/active_record/autosave_association.rb +8 -11
- data/lib/active_record/base.rb +2 -0
- data/lib/active_record/callbacks.rb +8 -10
- data/lib/active_record/coders/json.rb +2 -0
- data/lib/active_record/coders/yaml_column.rb +2 -0
- data/lib/active_record/collection_cache_key.rb +11 -7
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +111 -38
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +157 -29
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +7 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +13 -32
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +2 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +14 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +57 -2
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +31 -53
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +158 -78
- data/lib/active_record/connection_adapters/abstract/transaction.rb +45 -9
- data/lib/active_record/connection_adapters/abstract_adapter.rb +81 -96
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +111 -183
- data/lib/active_record/connection_adapters/column.rb +3 -1
- data/lib/active_record/connection_adapters/connection_specification.rb +17 -3
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +11 -2
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +9 -10
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +5 -3
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +7 -10
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +30 -30
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +106 -1
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -2
- data/lib/active_record/connection_adapters/postgresql/column.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +6 -0
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +3 -11
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +4 -6
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +4 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +18 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +19 -25
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +24 -11
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +20 -13
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +246 -110
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +58 -82
- data/lib/active_record/connection_adapters/schema_cache.rb +4 -2
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +18 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +2 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +6 -15
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +3 -2
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +71 -1
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +80 -90
- data/lib/active_record/connection_adapters/statement_pool.rb +2 -0
- data/lib/active_record/connection_handling.rb +4 -2
- data/lib/active_record/core.rb +39 -60
- data/lib/active_record/counter_cache.rb +15 -12
- data/lib/active_record/define_callbacks.rb +5 -3
- data/lib/active_record/dynamic_matchers.rb +9 -9
- data/lib/active_record/enum.rb +17 -13
- data/lib/active_record/errors.rb +54 -21
- data/lib/active_record/explain.rb +3 -1
- data/lib/active_record/explain_registry.rb +2 -0
- data/lib/active_record/explain_subscriber.rb +2 -0
- data/lib/active_record/fixture_set/file.rb +2 -0
- data/lib/active_record/fixtures.rb +67 -60
- data/lib/active_record/gem_version.rb +4 -2
- data/lib/active_record/inheritance.rb +49 -19
- data/lib/active_record/integration.rb +58 -19
- data/lib/active_record/internal_metadata.rb +2 -0
- data/lib/active_record/legacy_yaml_adapter.rb +3 -1
- data/lib/active_record/locking/optimistic.rb +14 -17
- data/lib/active_record/locking/pessimistic.rb +9 -6
- data/lib/active_record/log_subscriber.rb +43 -0
- data/lib/active_record/migration/command_recorder.rb +11 -9
- data/lib/active_record/migration/compatibility.rb +40 -2
- data/lib/active_record/migration/join_table.rb +2 -0
- data/lib/active_record/migration.rb +189 -139
- data/lib/active_record/model_schema.rb +16 -21
- data/lib/active_record/nested_attributes.rb +18 -6
- data/lib/active_record/no_touching.rb +3 -1
- data/lib/active_record/null_relation.rb +2 -0
- data/lib/active_record/persistence.rb +166 -16
- data/lib/active_record/query_cache.rb +11 -6
- data/lib/active_record/querying.rb +3 -1
- data/lib/active_record/railtie.rb +61 -3
- data/lib/active_record/railties/console_sandbox.rb +2 -0
- data/lib/active_record/railties/controller_runtime.rb +2 -0
- data/lib/active_record/railties/databases.rake +46 -36
- data/lib/active_record/readonly_attributes.rb +3 -2
- data/lib/active_record/reflection.rb +110 -192
- data/lib/active_record/relation/batches/batch_enumerator.rb +2 -0
- data/lib/active_record/relation/batches.rb +20 -5
- data/lib/active_record/relation/calculations.rb +30 -8
- data/lib/active_record/relation/delegation.rb +15 -27
- data/lib/active_record/relation/finder_methods.rb +75 -78
- data/lib/active_record/relation/from_clause.rb +2 -8
- data/lib/active_record/relation/merger.rb +51 -20
- data/lib/active_record/relation/predicate_builder/array_handler.rb +10 -7
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +2 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +12 -1
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +26 -9
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +6 -0
- data/lib/active_record/relation/predicate_builder.rb +53 -78
- data/lib/active_record/relation/query_attribute.rb +26 -2
- data/lib/active_record/relation/query_methods.rb +89 -88
- data/lib/active_record/relation/record_fetch_warning.rb +2 -0
- data/lib/active_record/relation/spawn_methods.rb +3 -1
- data/lib/active_record/relation/where_clause.rb +65 -68
- data/lib/active_record/relation/where_clause_factory.rb +5 -48
- data/lib/active_record/relation.rb +95 -208
- data/lib/active_record/result.rb +2 -0
- data/lib/active_record/runtime_registry.rb +2 -0
- data/lib/active_record/sanitization.rb +129 -121
- data/lib/active_record/schema.rb +4 -2
- data/lib/active_record/schema_dumper.rb +36 -26
- data/lib/active_record/schema_migration.rb +2 -0
- data/lib/active_record/scoping/default.rb +6 -7
- data/lib/active_record/scoping/named.rb +21 -7
- data/lib/active_record/scoping.rb +9 -8
- data/lib/active_record/secure_token.rb +2 -0
- data/lib/active_record/serialization.rb +2 -0
- data/lib/active_record/statement_cache.rb +22 -12
- data/lib/active_record/store.rb +3 -1
- data/lib/active_record/suppressor.rb +2 -0
- data/lib/active_record/table_metadata.rb +12 -3
- data/lib/active_record/tasks/database_tasks.rb +26 -15
- data/lib/active_record/tasks/mysql_database_tasks.rb +9 -48
- data/lib/active_record/tasks/postgresql_database_tasks.rb +10 -2
- data/lib/active_record/tasks/sqlite_database_tasks.rb +25 -3
- data/lib/active_record/timestamp.rb +5 -12
- data/lib/active_record/touch_later.rb +2 -0
- data/lib/active_record/transactions.rb +9 -7
- data/lib/active_record/translation.rb +2 -0
- data/lib/active_record/type/adapter_specific_registry.rb +2 -0
- data/lib/active_record/type/date.rb +2 -0
- data/lib/active_record/type/date_time.rb +2 -0
- data/lib/active_record/type/decimal_without_scale.rb +2 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +2 -0
- data/lib/active_record/type/internal/timezone.rb +2 -0
- data/lib/active_record/type/json.rb +30 -0
- data/lib/active_record/type/serialized.rb +2 -4
- data/lib/active_record/type/text.rb +2 -0
- data/lib/active_record/type/time.rb +2 -0
- data/lib/active_record/type/type_map.rb +2 -0
- data/lib/active_record/type/unsigned_integer.rb +2 -0
- data/lib/active_record/type.rb +4 -1
- data/lib/active_record/type_caster/connection.rb +2 -0
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/type_caster.rb +2 -0
- data/lib/active_record/validations/absence.rb +2 -0
- data/lib/active_record/validations/associated.rb +2 -0
- data/lib/active_record/validations/length.rb +2 -0
- data/lib/active_record/validations/presence.rb +2 -0
- data/lib/active_record/validations/uniqueness.rb +35 -5
- data/lib/active_record/validations.rb +2 -0
- data/lib/active_record/version.rb +2 -0
- data/lib/active_record.rb +11 -4
- data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
- data/lib/rails/generators/active_record/{model/templates/application_record.rb → application_record/templates/application_record.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +3 -1
- data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +0 -0
- data/lib/rails/generators/active_record/migration.rb +2 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +2 -23
- data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +0 -0
- data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
- data/lib/rails/generators/active_record.rb +3 -1
- metadata +24 -36
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -15
- data/lib/active_record/associations/preloader/collection_association.rb +0 -17
- data/lib/active_record/associations/preloader/has_many.rb +0 -15
- data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
- data/lib/active_record/associations/preloader/has_one.rb +0 -15
- data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
- data/lib/active_record/associations/preloader/singular_association.rb +0 -18
- data/lib/active_record/attribute/user_provided_default.rb +0 -30
- data/lib/active_record/attribute.rb +0 -240
- data/lib/active_record/attribute_mutation_tracker.rb +0 -122
- data/lib/active_record/attribute_set/builder.rb +0 -126
- data/lib/active_record/attribute_set/yaml_encoder.rb +0 -41
- data/lib/active_record/attribute_set.rb +0 -113
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -10
- data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +0 -88
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +0 -59
- data/lib/active_record/type/internal/abstract_json.rb +0 -37
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Calculations
|
3
5
|
# Count the records.
|
@@ -37,7 +39,16 @@ module ActiveRecord
|
|
37
39
|
# Note: not all valid {Relation#select}[rdoc-ref:QueryMethods#select] expressions are valid #count expressions. The specifics differ
|
38
40
|
# between databases. In invalid cases, an error from the database is thrown.
|
39
41
|
def count(column_name = nil)
|
40
|
-
|
42
|
+
if block_given?
|
43
|
+
unless column_name.nil?
|
44
|
+
ActiveSupport::Deprecation.warn \
|
45
|
+
"When `count' is called with a block, it ignores other arguments. " \
|
46
|
+
"This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
|
47
|
+
end
|
48
|
+
|
49
|
+
return super()
|
50
|
+
end
|
51
|
+
|
41
52
|
calculate(:count, column_name)
|
42
53
|
end
|
43
54
|
|
@@ -73,7 +84,16 @@ module ActiveRecord
|
|
73
84
|
#
|
74
85
|
# Person.sum(:age) # => 4562
|
75
86
|
def sum(column_name = nil)
|
76
|
-
|
87
|
+
if block_given?
|
88
|
+
unless column_name.nil?
|
89
|
+
ActiveSupport::Deprecation.warn \
|
90
|
+
"When `sum' is called with a block, it ignores other arguments. " \
|
91
|
+
"This behavior is now deprecated and will result in an ArgumentError in Rails 6.0."
|
92
|
+
end
|
93
|
+
|
94
|
+
return super()
|
95
|
+
end
|
96
|
+
|
77
97
|
calculate(:sum, column_name)
|
78
98
|
end
|
79
99
|
|
@@ -110,7 +130,7 @@ module ActiveRecord
|
|
110
130
|
# end
|
111
131
|
def calculate(operation, column_name)
|
112
132
|
if has_include?(column_name)
|
113
|
-
relation =
|
133
|
+
relation = apply_join_dependency
|
114
134
|
|
115
135
|
if operation.to_s.downcase == "count"
|
116
136
|
relation.distinct!
|
@@ -167,13 +187,15 @@ module ActiveRecord
|
|
167
187
|
end
|
168
188
|
|
169
189
|
if has_include?(column_names.first)
|
170
|
-
|
190
|
+
relation = apply_join_dependency
|
191
|
+
relation.pluck(*column_names)
|
171
192
|
else
|
193
|
+
enforce_raw_sql_whitelist(column_names)
|
172
194
|
relation = spawn
|
173
195
|
relation.select_values = column_names.map { |cn|
|
174
196
|
@klass.has_attribute?(cn) || @klass.attribute_alias?(cn) ? arel_attribute(cn) : cn
|
175
197
|
}
|
176
|
-
result = klass.connection.select_all(relation.arel, nil
|
198
|
+
result = skip_query_cache_if_necessary { klass.connection.select_all(relation.arel, nil) }
|
177
199
|
result.cast_values(klass.attribute_types)
|
178
200
|
end
|
179
201
|
end
|
@@ -220,7 +242,7 @@ module ActiveRecord
|
|
220
242
|
def aggregate_column(column_name)
|
221
243
|
return column_name if Arel::Expressions === column_name
|
222
244
|
|
223
|
-
if @klass.has_attribute?(column_name
|
245
|
+
if @klass.has_attribute?(column_name) || @klass.attribute_alias?(column_name)
|
224
246
|
@klass.arel_attribute(column_name)
|
225
247
|
else
|
226
248
|
Arel.sql(column_name == :all ? "*" : column_name.to_s)
|
@@ -257,7 +279,7 @@ module ActiveRecord
|
|
257
279
|
query_builder = relation.arel
|
258
280
|
end
|
259
281
|
|
260
|
-
result = @klass.connection.select_all(query_builder, nil
|
282
|
+
result = skip_query_cache_if_necessary { @klass.connection.select_all(query_builder, nil) }
|
261
283
|
row = result.first
|
262
284
|
value = row && row.values.first
|
263
285
|
type = result.column_types.fetch(column_alias) do
|
@@ -308,7 +330,7 @@ module ActiveRecord
|
|
308
330
|
relation.group_values = group_fields
|
309
331
|
relation.select_values = select_values
|
310
332
|
|
311
|
-
calculated_data = @klass.connection.select_all(relation, nil
|
333
|
+
calculated_data = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, nil) }
|
312
334
|
|
313
335
|
if association
|
314
336
|
key_ids = calculated_data.collect { |row| row[group_aliases.first] }
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module Delegation # :nodoc:
|
3
5
|
module DelegateCache # :nodoc:
|
@@ -36,13 +38,12 @@ module ActiveRecord
|
|
36
38
|
# may vary depending on the klass of a relation, so we create a subclass of Relation
|
37
39
|
# for each different klass, and the delegations are compiled into that subclass only.
|
38
40
|
|
39
|
-
delegate :to_xml, :encode_with, :length, :each, :uniq, :
|
40
|
-
:[], :&, :|, :+, :-, :sample, :reverse, :compact, :in_groups, :in_groups_of,
|
41
|
+
delegate :to_xml, :encode_with, :length, :each, :uniq, :join,
|
42
|
+
:[], :&, :|, :+, :-, :sample, :reverse, :rotate, :compact, :in_groups, :in_groups_of,
|
41
43
|
:to_sentence, :to_formatted_s, :as_json,
|
42
|
-
:shuffle, :split, :index, to: :records
|
44
|
+
:shuffle, :split, :slice, :index, :rindex, to: :records
|
43
45
|
|
44
|
-
delegate :
|
45
|
-
:connection, :columns_hash, to: :klass
|
46
|
+
delegate :primary_key, :connection, to: :klass
|
46
47
|
|
47
48
|
module ClassSpecificRelation # :nodoc:
|
48
49
|
extend ActiveSupport::Concern
|
@@ -73,13 +74,6 @@ module ActiveRecord
|
|
73
74
|
end
|
74
75
|
end
|
75
76
|
end
|
76
|
-
|
77
|
-
def delegate(method, opts = {})
|
78
|
-
@delegation_mutex.synchronize do
|
79
|
-
return if method_defined?(method)
|
80
|
-
super
|
81
|
-
end
|
82
|
-
end
|
83
77
|
end
|
84
78
|
|
85
79
|
private
|
@@ -88,8 +82,14 @@ module ActiveRecord
|
|
88
82
|
if @klass.respond_to?(method)
|
89
83
|
self.class.delegate_to_scoped_klass(method)
|
90
84
|
scoping { @klass.public_send(method, *args, &block) }
|
85
|
+
elsif @delegate_to_klass && @klass.respond_to?(method, true)
|
86
|
+
ActiveSupport::Deprecation.warn \
|
87
|
+
"Delegating missing #{method} method to #{@klass}. " \
|
88
|
+
"Accessibility of private/protected class methods in :scope is deprecated and will be removed in Rails 6.0."
|
89
|
+
@klass.send(method, *args, &block)
|
91
90
|
elsif arel.respond_to?(method)
|
92
|
-
|
91
|
+
ActiveSupport::Deprecation.warn \
|
92
|
+
"Delegating #{method} to arel is deprecated and will be removed in Rails 6.0."
|
93
93
|
arel.public_send(method, *args, &block)
|
94
94
|
else
|
95
95
|
super
|
@@ -109,21 +109,9 @@ module ActiveRecord
|
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
112
|
-
def respond_to_missing?(method, include_private = false)
|
113
|
-
super || @klass.respond_to?(method, include_private) ||
|
114
|
-
arel.respond_to?(method, include_private)
|
115
|
-
end
|
116
|
-
|
117
112
|
private
|
118
|
-
|
119
|
-
|
120
|
-
if @klass.respond_to?(method)
|
121
|
-
scoping { @klass.public_send(method, *args, &block) }
|
122
|
-
elsif arel.respond_to?(method)
|
123
|
-
arel.public_send(method, *args, &block)
|
124
|
-
else
|
125
|
-
super
|
126
|
-
end
|
113
|
+
def respond_to_missing?(method, _)
|
114
|
+
super || @klass.respond_to?(method) || arel.respond_to?(method)
|
127
115
|
end
|
128
116
|
end
|
129
117
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/core_ext/string/filters"
|
2
4
|
|
3
5
|
module ActiveRecord
|
@@ -16,9 +18,10 @@ module ActiveRecord
|
|
16
18
|
# Person.find([1]) # returns an array for the object with ID = 1
|
17
19
|
# Person.where("administrator = 1").order("created_on DESC").find(1)
|
18
20
|
#
|
19
|
-
# NOTE: The returned records
|
20
|
-
#
|
21
|
-
#
|
21
|
+
# NOTE: The returned records are in the same order as the ids you provide.
|
22
|
+
# If you want the results to be sorted by database, you can use ActiveRecord::QueryMethods#where
|
23
|
+
# method and provide an explicit ActiveRecord::QueryMethods#order option.
|
24
|
+
# But ActiveRecord::QueryMethods#where method doesn't raise ActiveRecord::RecordNotFound.
|
22
25
|
#
|
23
26
|
# ==== Find with lock
|
24
27
|
#
|
@@ -86,7 +89,7 @@ module ActiveRecord
|
|
86
89
|
where(arg, *args).take!
|
87
90
|
rescue ::RangeError
|
88
91
|
raise RecordNotFound.new("Couldn't find #{@klass.name} with an out of range value",
|
89
|
-
@klass.name)
|
92
|
+
@klass.name, @klass.primary_key)
|
90
93
|
end
|
91
94
|
|
92
95
|
# Gives a record (or N records if a parameter is supplied) without any implied
|
@@ -145,10 +148,9 @@ module ActiveRecord
|
|
145
148
|
#
|
146
149
|
# [#<Person id:4>, #<Person id:3>, #<Person id:2>]
|
147
150
|
def last(limit = nil)
|
148
|
-
return find_last(limit) if loaded? ||
|
151
|
+
return find_last(limit) if loaded? || has_limit_or_offset?
|
149
152
|
|
150
|
-
result = limit(limit)
|
151
|
-
result.order!(arel_attribute(primary_key)) if order_values.empty? && primary_key
|
153
|
+
result = ordered_relation.limit(limit)
|
152
154
|
result = result.reverse_order!
|
153
155
|
|
154
156
|
limit ? result.reverse : result.first
|
@@ -283,7 +285,7 @@ module ActiveRecord
|
|
283
285
|
# * Hash - Finds the record that matches these +find+-style conditions
|
284
286
|
# (such as <tt>{name: 'David'}</tt>).
|
285
287
|
# * +false+ - Returns always +false+.
|
286
|
-
# * No args - Returns +false+ if the
|
288
|
+
# * No args - Returns +false+ if the relation is empty, +true+ otherwise.
|
287
289
|
#
|
288
290
|
# For more information about specifying conditions as a hash or array,
|
289
291
|
# see the Conditions section in the introduction to ActiveRecord::Base.
|
@@ -299,6 +301,7 @@ module ActiveRecord
|
|
299
301
|
# Person.exists?(name: 'David')
|
300
302
|
# Person.exists?(false)
|
301
303
|
# Person.exists?
|
304
|
+
# Person.where(name: 'Spartacus', rating: 4).exists?
|
302
305
|
def exists?(conditions = :none)
|
303
306
|
if Base === conditions
|
304
307
|
raise ArgumentError, <<-MSG.squish
|
@@ -309,14 +312,14 @@ module ActiveRecord
|
|
309
312
|
|
310
313
|
return false if !conditions || limit_value == 0
|
311
314
|
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
315
|
+
if eager_loading?
|
316
|
+
relation = apply_join_dependency(eager_loading: false)
|
317
|
+
return relation.exists?(conditions)
|
318
|
+
end
|
316
319
|
|
317
|
-
relation = construct_relation_for_exists(
|
320
|
+
relation = construct_relation_for_exists(conditions)
|
318
321
|
|
319
|
-
connection.select_value(relation, "#{name} Exists"
|
322
|
+
skip_query_cache_if_necessary { connection.select_value(relation.arel, "#{name} Exists") } ? true : false
|
320
323
|
rescue ::RangeError
|
321
324
|
false
|
322
325
|
end
|
@@ -329,23 +332,23 @@ module ActiveRecord
|
|
329
332
|
# of results obtained should be provided in the +result_size+ argument and
|
330
333
|
# the expected number of results should be provided in the +expected_size+
|
331
334
|
# argument.
|
332
|
-
def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key) # :nodoc:
|
333
|
-
conditions = arel.where_sql(@klass
|
335
|
+
def raise_record_not_found_exception!(ids = nil, result_size = nil, expected_size = nil, key = primary_key, not_found_ids = nil) # :nodoc:
|
336
|
+
conditions = arel.where_sql(@klass)
|
334
337
|
conditions = " [#{conditions}]" if conditions
|
335
338
|
name = @klass.name
|
336
339
|
|
337
340
|
if ids.nil?
|
338
|
-
error = "Couldn't find #{name}"
|
341
|
+
error = "Couldn't find #{name}".dup
|
339
342
|
error << " with#{conditions}" if conditions
|
340
|
-
raise RecordNotFound.new(error, name)
|
343
|
+
raise RecordNotFound.new(error, name, key)
|
341
344
|
elsif Array(ids).size == 1
|
342
345
|
error = "Couldn't find #{name} with '#{key}'=#{ids}#{conditions}"
|
343
346
|
raise RecordNotFound.new(error, name, key, ids)
|
344
347
|
else
|
345
|
-
error = "Couldn't find all #{name.pluralize} with '#{key}': "
|
346
|
-
error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})"
|
347
|
-
|
348
|
-
raise RecordNotFound.new(error, name,
|
348
|
+
error = "Couldn't find all #{name.pluralize} with '#{key}': ".dup
|
349
|
+
error << "(#{ids.join(", ")})#{conditions} (found #{result_size} results, but was looking for #{expected_size})."
|
350
|
+
error << " Couldn't find #{name.pluralize(not_found_ids.size)} with #{key.to_s.pluralize(not_found_ids.size)} #{not_found_ids.join(', ')}." if not_found_ids
|
351
|
+
raise RecordNotFound.new(error, name, key, ids)
|
349
352
|
end
|
350
353
|
end
|
351
354
|
|
@@ -355,31 +358,12 @@ module ActiveRecord
|
|
355
358
|
offset_value || 0
|
356
359
|
end
|
357
360
|
|
358
|
-
def
|
359
|
-
|
360
|
-
# any joins already present in `self`, so pass them in
|
361
|
-
#
|
362
|
-
# failing to do so means that in cases like activerecord/test/cases/associations/inner_join_association_test.rb:136
|
363
|
-
# incorrect SQL is generated. In that case, the join dependency for
|
364
|
-
# SpecialCategorizations is constructed without knowledge of the
|
365
|
-
# preexisting join in joins_values to categorizations (by way of
|
366
|
-
# the `has_many :through` for categories).
|
367
|
-
#
|
368
|
-
join_dependency = construct_join_dependency(joins_values)
|
369
|
-
|
370
|
-
aliases = join_dependency.aliases
|
371
|
-
relation = select aliases.columns
|
372
|
-
relation = apply_join_dependency(relation, join_dependency)
|
373
|
-
|
374
|
-
yield relation, join_dependency
|
375
|
-
end
|
376
|
-
|
377
|
-
def construct_relation_for_exists(relation, conditions)
|
378
|
-
relation = relation.except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
|
361
|
+
def construct_relation_for_exists(conditions)
|
362
|
+
relation = except(:select, :distinct, :order)._select!(ONE_AS_ONE).limit!(1)
|
379
363
|
|
380
364
|
case conditions
|
381
365
|
when Array, Hash
|
382
|
-
relation.where!(conditions)
|
366
|
+
relation.where!(conditions)
|
383
367
|
else
|
384
368
|
relation.where!(primary_key => conditions) unless conditions == :none
|
385
369
|
end
|
@@ -387,37 +371,43 @@ module ActiveRecord
|
|
387
371
|
relation
|
388
372
|
end
|
389
373
|
|
390
|
-
def construct_join_dependency
|
374
|
+
def construct_join_dependency
|
391
375
|
including = eager_load_values + includes_values
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
apply_join_dependency(self, construct_join_dependency(joins_values))
|
376
|
+
joins = joins_values.select { |join| join.is_a?(Arel::Nodes::Join) }
|
377
|
+
ActiveRecord::Associations::JoinDependency.new(
|
378
|
+
klass, table, including, alias_tracker(joins)
|
379
|
+
)
|
397
380
|
end
|
398
381
|
|
399
|
-
def apply_join_dependency(
|
400
|
-
|
382
|
+
def apply_join_dependency(eager_loading: true)
|
383
|
+
join_dependency = construct_join_dependency
|
384
|
+
relation = except(:includes, :eager_load, :preload).joins!(join_dependency)
|
401
385
|
|
402
|
-
if using_limitable_reflections?(join_dependency.reflections)
|
403
|
-
|
404
|
-
else
|
405
|
-
if relation.limit_value
|
386
|
+
if eager_loading && !using_limitable_reflections?(join_dependency.reflections)
|
387
|
+
if has_limit_or_offset?
|
406
388
|
limited_ids = limited_ids_for(relation)
|
407
389
|
limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
|
408
390
|
end
|
409
|
-
relation.
|
391
|
+
relation.limit_value = relation.offset_value = nil
|
392
|
+
end
|
393
|
+
|
394
|
+
if block_given?
|
395
|
+
relation._select!(join_dependency.aliases.columns)
|
396
|
+
yield relation, join_dependency
|
397
|
+
else
|
398
|
+
relation
|
410
399
|
end
|
411
400
|
end
|
412
401
|
|
413
402
|
def limited_ids_for(relation)
|
414
403
|
values = @klass.connection.columns_for_distinct(
|
415
|
-
|
404
|
+
connection.column_name_from_arel_node(arel_attribute(primary_key)),
|
405
|
+
relation.order_values
|
406
|
+
)
|
416
407
|
|
417
408
|
relation = relation.except(:select).select(values).distinct!
|
418
|
-
arel = relation.arel
|
419
409
|
|
420
|
-
id_rows = @klass.connection.select_all(arel, "SQL"
|
410
|
+
id_rows = skip_query_cache_if_necessary { @klass.connection.select_all(relation.arel, "SQL") }
|
421
411
|
id_rows.map { |row| row[primary_key] }
|
422
412
|
end
|
423
413
|
|
@@ -429,13 +419,16 @@ module ActiveRecord
|
|
429
419
|
raise UnknownPrimaryKey.new(@klass) if primary_key.nil?
|
430
420
|
|
431
421
|
expects_array = ids.first.kind_of?(Array)
|
432
|
-
return
|
422
|
+
return ids.first if expects_array && ids.first.empty?
|
433
423
|
|
434
424
|
ids = ids.flatten.compact.uniq
|
435
425
|
|
426
|
+
model_name = @klass.name
|
427
|
+
|
436
428
|
case ids.size
|
437
429
|
when 0
|
438
|
-
|
430
|
+
error_message = "Couldn't find #{model_name} without an ID"
|
431
|
+
raise RecordNotFound.new(error_message, model_name, primary_key)
|
439
432
|
when 1
|
440
433
|
result = find_one(ids.first)
|
441
434
|
expects_array ? [ result ] : result
|
@@ -443,7 +436,8 @@ module ActiveRecord
|
|
443
436
|
find_some(ids)
|
444
437
|
end
|
445
438
|
rescue ::RangeError
|
446
|
-
|
439
|
+
error_message = "Couldn't find #{model_name} with an out of range ID"
|
440
|
+
raise RecordNotFound.new(error_message, model_name, primary_key, ids)
|
447
441
|
end
|
448
442
|
|
449
443
|
def find_one(id)
|
@@ -525,13 +519,13 @@ module ActiveRecord
|
|
525
519
|
if loaded?
|
526
520
|
records[index, limit] || []
|
527
521
|
else
|
528
|
-
relation =
|
529
|
-
|
530
|
-
|
531
|
-
|
522
|
+
relation = ordered_relation
|
523
|
+
|
524
|
+
if limit_value
|
525
|
+
limit = [limit_value - index, limit].min
|
532
526
|
end
|
533
527
|
|
534
|
-
if
|
528
|
+
if limit > 0
|
535
529
|
relation = relation.offset(offset_index + index) unless index.zero?
|
536
530
|
relation.limit(limit).to_a
|
537
531
|
else
|
@@ -544,23 +538,26 @@ module ActiveRecord
|
|
544
538
|
if loaded?
|
545
539
|
records[-index]
|
546
540
|
else
|
547
|
-
relation =
|
548
|
-
|
541
|
+
relation = ordered_relation
|
542
|
+
|
543
|
+
if equal?(relation) || has_limit_or_offset?
|
544
|
+
relation.records[-index]
|
549
545
|
else
|
550
|
-
|
546
|
+
relation.last(index)[-index]
|
551
547
|
end
|
552
|
-
|
553
|
-
relation.to_a[-index]
|
554
|
-
# TODO: can be made more performant on large result sets by
|
555
|
-
# for instance, last(index)[-index] (which would require
|
556
|
-
# refactoring the last(n) finder method to make test suite pass),
|
557
|
-
# or by using a combination of reverse_order, limit, and offset,
|
558
|
-
# e.g., reverse_order.offset(index-1).first
|
559
548
|
end
|
560
549
|
end
|
561
550
|
|
562
551
|
def find_last(limit)
|
563
552
|
limit ? records.last(limit) : records.last
|
564
553
|
end
|
554
|
+
|
555
|
+
def ordered_relation
|
556
|
+
if order_values.empty? && primary_key
|
557
|
+
order(arel_attribute(primary_key).asc)
|
558
|
+
else
|
559
|
+
self
|
560
|
+
end
|
561
|
+
end
|
565
562
|
end
|
566
563
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
class Relation
|
3
5
|
class FromClause # :nodoc:
|
@@ -8,14 +10,6 @@ module ActiveRecord
|
|
8
10
|
@name = name
|
9
11
|
end
|
10
12
|
|
11
|
-
def binds
|
12
|
-
if value.is_a?(Relation)
|
13
|
-
value.bound_attributes
|
14
|
-
else
|
15
|
-
[]
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
13
|
def merge(other)
|
20
14
|
self
|
21
15
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/core_ext/hash/keys"
|
2
4
|
|
3
5
|
module ActiveRecord
|
@@ -21,7 +23,11 @@ module ActiveRecord
|
|
21
23
|
# build a relation to merge in rather than directly merging
|
22
24
|
# the values.
|
23
25
|
def other
|
24
|
-
other = Relation.create(
|
26
|
+
other = Relation.create(
|
27
|
+
relation.klass,
|
28
|
+
table: relation.table,
|
29
|
+
predicate_builder: relation.predicate_builder
|
30
|
+
)
|
25
31
|
hash.each { |k, v|
|
26
32
|
if k == :joins
|
27
33
|
if Hash === v
|
@@ -50,7 +56,7 @@ module ActiveRecord
|
|
50
56
|
|
51
57
|
NORMAL_VALUES = Relation::VALUE_METHODS -
|
52
58
|
Relation::CLAUSE_METHODS -
|
53
|
-
[:includes, :preload, :joins, :order, :reverse_order, :lock, :create_with, :reordering] # :nodoc:
|
59
|
+
[:includes, :preload, :joins, :left_outer_joins, :order, :reverse_order, :lock, :create_with, :reordering] # :nodoc:
|
54
60
|
|
55
61
|
def normal_values
|
56
62
|
NORMAL_VALUES
|
@@ -77,6 +83,7 @@ module ActiveRecord
|
|
77
83
|
merge_clauses
|
78
84
|
merge_preloads
|
79
85
|
merge_joins
|
86
|
+
merge_outer_joins
|
80
87
|
|
81
88
|
relation
|
82
89
|
end
|
@@ -110,21 +117,43 @@ module ActiveRecord
|
|
110
117
|
if other.klass == relation.klass
|
111
118
|
relation.joins!(*other.joins_values)
|
112
119
|
else
|
113
|
-
|
120
|
+
alias_tracker = nil
|
121
|
+
joins_dependency = other.joins_values.map do |join|
|
114
122
|
case join
|
115
123
|
when Hash, Symbol, Array
|
116
|
-
|
124
|
+
alias_tracker ||= other.alias_tracker
|
125
|
+
ActiveRecord::Associations::JoinDependency.new(
|
126
|
+
other.klass, other.table, join, alias_tracker
|
127
|
+
)
|
117
128
|
else
|
118
|
-
|
129
|
+
join
|
119
130
|
end
|
120
131
|
end
|
121
132
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
relation.joins! rest
|
133
|
+
relation.joins!(*joins_dependency)
|
134
|
+
end
|
135
|
+
end
|
126
136
|
|
127
|
-
|
137
|
+
def merge_outer_joins
|
138
|
+
return if other.left_outer_joins_values.blank?
|
139
|
+
|
140
|
+
if other.klass == relation.klass
|
141
|
+
relation.left_outer_joins!(*other.left_outer_joins_values)
|
142
|
+
else
|
143
|
+
alias_tracker = nil
|
144
|
+
joins_dependency = other.left_outer_joins_values.map do |join|
|
145
|
+
case join
|
146
|
+
when Hash, Symbol, Array
|
147
|
+
alias_tracker ||= other.alias_tracker
|
148
|
+
ActiveRecord::Associations::JoinDependency.new(
|
149
|
+
other.klass, other.table, join, alias_tracker
|
150
|
+
)
|
151
|
+
else
|
152
|
+
join
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
relation.left_outer_joins!(*joins_dependency)
|
128
157
|
end
|
129
158
|
end
|
130
159
|
|
@@ -132,19 +161,17 @@ module ActiveRecord
|
|
132
161
|
if other.reordering_value
|
133
162
|
# override any order specified in the original relation
|
134
163
|
relation.reorder! other.order_values
|
135
|
-
elsif other.order_values
|
164
|
+
elsif other.order_values.any?
|
136
165
|
# merge in order_values from relation
|
137
166
|
relation.order! other.order_values
|
138
167
|
end
|
139
168
|
|
140
|
-
|
169
|
+
extensions = other.extensions - relation.extensions
|
170
|
+
relation.extending!(*extensions) if extensions.any?
|
141
171
|
end
|
142
172
|
|
143
173
|
def merge_single_values
|
144
|
-
if
|
145
|
-
relation.from_clause = other.from_clause
|
146
|
-
end
|
147
|
-
relation.lock_value ||= other.lock_value
|
174
|
+
relation.lock_value ||= other.lock_value if other.lock_value
|
148
175
|
|
149
176
|
unless other.create_with_value.blank?
|
150
177
|
relation.create_with_value = (relation.create_with_value || {}).merge(other.create_with_value)
|
@@ -152,11 +179,15 @@ module ActiveRecord
|
|
152
179
|
end
|
153
180
|
|
154
181
|
def merge_clauses
|
155
|
-
|
156
|
-
|
157
|
-
other_clause = other.get_value(method)
|
158
|
-
relation.set_value(method, clause.merge(other_clause))
|
182
|
+
if relation.from_clause.empty? && !other.from_clause.empty?
|
183
|
+
relation.from_clause = other.from_clause
|
159
184
|
end
|
185
|
+
|
186
|
+
where_clause = relation.where_clause.merge(other.where_clause)
|
187
|
+
relation.where_clause = where_clause unless where_clause.empty?
|
188
|
+
|
189
|
+
having_clause = relation.having_clause.merge(other.having_clause)
|
190
|
+
relation.having_clause = having_clause unless having_clause.empty?
|
160
191
|
end
|
161
192
|
end
|
162
193
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
class PredicateBuilder
|
3
5
|
class ArrayHandler # :nodoc:
|
@@ -6,18 +8,21 @@ module ActiveRecord
|
|
6
8
|
end
|
7
9
|
|
8
10
|
def call(attribute, value)
|
11
|
+
return attribute.in([]) if value.empty?
|
12
|
+
|
9
13
|
values = value.map { |x| x.is_a?(Base) ? x.id : x }
|
10
14
|
nils, values = values.partition(&:nil?)
|
11
|
-
|
12
|
-
return attribute.in([]) if values.empty? && nils.empty?
|
13
|
-
|
14
15
|
ranges, values = values.partition { |v| v.is_a?(Range) }
|
15
16
|
|
16
17
|
values_predicate =
|
17
18
|
case values.length
|
18
19
|
when 0 then NullPredicate
|
19
20
|
when 1 then predicate_builder.build(attribute, values.first)
|
20
|
-
else
|
21
|
+
else
|
22
|
+
bind_values = values.map do |v|
|
23
|
+
predicate_builder.build_bind_attribute(attribute.name, v)
|
24
|
+
end
|
25
|
+
attribute.in(bind_values)
|
21
26
|
end
|
22
27
|
|
23
28
|
unless nils.empty?
|
@@ -26,11 +31,9 @@ module ActiveRecord
|
|
26
31
|
|
27
32
|
array_predicates = ranges.map { |range| predicate_builder.build(attribute, range) }
|
28
33
|
array_predicates.unshift(values_predicate)
|
29
|
-
array_predicates.inject
|
34
|
+
array_predicates.inject(&:or)
|
30
35
|
end
|
31
36
|
|
32
|
-
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
33
|
-
# Workaround for Ruby 2.2 "private attribute?" warning.
|
34
37
|
protected
|
35
38
|
|
36
39
|
attr_reader :predicate_builder
|