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,4 +1,3 @@
|
|
1
|
-
require 'active_support/deprecation'
|
2
1
|
require 'active_support/core_ext/string/filters'
|
3
2
|
|
4
3
|
module ActiveRecord
|
@@ -6,7 +5,7 @@ module ActiveRecord
|
|
6
5
|
ONE_AS_ONE = '1 AS one'
|
7
6
|
|
8
7
|
# Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]).
|
9
|
-
# If
|
8
|
+
# If one or more records can not be found for the requested ids, then RecordNotFound will be raised. If the primary key
|
10
9
|
# is an integer, find by id coerces its arguments using +to_i+.
|
11
10
|
#
|
12
11
|
# Person.find(1) # returns the object for ID = 1
|
@@ -17,10 +16,8 @@ module ActiveRecord
|
|
17
16
|
# Person.find([1]) # returns an array for the object with ID = 1
|
18
17
|
# Person.where("administrator = 1").order("created_on DESC").find(1)
|
19
18
|
#
|
20
|
-
# <tt>ActiveRecord::RecordNotFound</tt> will be raised if one or more ids are not found.
|
21
|
-
#
|
22
19
|
# NOTE: The returned records may not be in the same order as the ids you
|
23
|
-
# provide since database rows are unordered. You'd need to provide an explicit
|
20
|
+
# provide since database rows are unordered. You'd need to provide an explicit QueryMethods#order
|
24
21
|
# option if you want the results are sorted.
|
25
22
|
#
|
26
23
|
# ==== Find with lock
|
@@ -37,7 +34,7 @@ module ActiveRecord
|
|
37
34
|
# person.save!
|
38
35
|
# end
|
39
36
|
#
|
40
|
-
# ==== Variations of
|
37
|
+
# ==== Variations of #find
|
41
38
|
#
|
42
39
|
# Person.where(name: 'Spartacus', rating: 4)
|
43
40
|
# # returns a chainable list (which can be empty).
|
@@ -45,13 +42,13 @@ module ActiveRecord
|
|
45
42
|
# Person.find_by(name: 'Spartacus', rating: 4)
|
46
43
|
# # returns the first item or nil.
|
47
44
|
#
|
48
|
-
# Person.
|
45
|
+
# Person.find_or_initialize_by(name: 'Spartacus', rating: 4)
|
49
46
|
# # returns the first item or returns a new instance (requires you call .save to persist against the database).
|
50
47
|
#
|
51
|
-
# Person.
|
52
|
-
# # returns the first item or creates it and returns it
|
48
|
+
# Person.find_or_create_by(name: 'Spartacus', rating: 4)
|
49
|
+
# # returns the first item or creates it and returns it.
|
53
50
|
#
|
54
|
-
# ==== Alternatives for
|
51
|
+
# ==== Alternatives for #find
|
55
52
|
#
|
56
53
|
# Person.where(name: 'Spartacus', rating: 4).exists?(conditions = :none)
|
57
54
|
# # returns a boolean indicating if any record with the given conditions exist.
|
@@ -60,16 +57,13 @@ module ActiveRecord
|
|
60
57
|
# # returns a chainable list of instances with only the mentioned fields.
|
61
58
|
#
|
62
59
|
# Person.where(name: 'Spartacus', rating: 4).ids
|
63
|
-
# # returns an Array of ids
|
60
|
+
# # returns an Array of ids.
|
64
61
|
#
|
65
62
|
# Person.where(name: 'Spartacus', rating: 4).pluck(:field1, :field2)
|
66
|
-
# # returns an Array of the required fields
|
63
|
+
# # returns an Array of the required fields.
|
67
64
|
def find(*args)
|
68
|
-
if block_given?
|
69
|
-
|
70
|
-
else
|
71
|
-
find_with_ids(*args)
|
72
|
-
end
|
65
|
+
return super if block_given?
|
66
|
+
find_with_ids(*args)
|
73
67
|
end
|
74
68
|
|
75
69
|
# Finds the first record matching the specified conditions. There
|
@@ -80,18 +74,19 @@ module ActiveRecord
|
|
80
74
|
#
|
81
75
|
# Post.find_by name: 'Spartacus', rating: 4
|
82
76
|
# Post.find_by "published_at < ?", 2.weeks.ago
|
83
|
-
def find_by(*args)
|
84
|
-
where(*args).take
|
77
|
+
def find_by(arg, *args)
|
78
|
+
where(arg, *args).take
|
85
79
|
rescue RangeError
|
86
80
|
nil
|
87
81
|
end
|
88
82
|
|
89
|
-
# Like
|
90
|
-
# an
|
91
|
-
def find_by!(*args)
|
92
|
-
where(*args).take!
|
83
|
+
# Like #find_by, except that if no record is found, raises
|
84
|
+
# an ActiveRecord::RecordNotFound error.
|
85
|
+
def find_by!(arg, *args)
|
86
|
+
where(arg, *args).take!
|
93
87
|
rescue RangeError
|
94
|
-
raise RecordNotFound
|
88
|
+
raise RecordNotFound.new("Couldn't find #{@klass.name} with an out of range value",
|
89
|
+
@klass.name)
|
95
90
|
end
|
96
91
|
|
97
92
|
# Gives a record (or N records if a parameter is supplied) without any implied
|
@@ -105,43 +100,31 @@ module ActiveRecord
|
|
105
100
|
limit ? limit(limit).to_a : find_take
|
106
101
|
end
|
107
102
|
|
108
|
-
# Same as
|
109
|
-
# is found. Note that
|
103
|
+
# Same as #take but raises ActiveRecord::RecordNotFound if no record
|
104
|
+
# is found. Note that #take! accepts no arguments.
|
110
105
|
def take!
|
111
|
-
take or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
|
106
|
+
take or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
|
112
107
|
end
|
113
108
|
|
114
109
|
# Find the first record (or first N records if a parameter is supplied).
|
115
110
|
# If no order is defined it will order by primary key.
|
116
111
|
#
|
117
|
-
# Person.first # returns the first object fetched by SELECT * FROM people
|
112
|
+
# Person.first # returns the first object fetched by SELECT * FROM people ORDER BY people.id LIMIT 1
|
118
113
|
# Person.where(["user_name = ?", user_name]).first
|
119
114
|
# Person.where(["user_name = :u", { u: user_name }]).first
|
120
115
|
# Person.order("created_on DESC").offset(5).first
|
121
|
-
# Person.first(3) # returns the first three objects fetched by SELECT * FROM people LIMIT 3
|
122
|
-
#
|
123
|
-
# ==== Rails 3
|
124
|
-
#
|
125
|
-
# Person.first # SELECT "people".* FROM "people" LIMIT 1
|
126
|
-
#
|
127
|
-
# NOTE: Rails 3 may not order this query by the primary key and the order
|
128
|
-
# will depend on the database implementation. In order to ensure that behavior,
|
129
|
-
# use <tt>User.order(:id).first</tt> instead.
|
130
|
-
#
|
131
|
-
# ==== Rails 4
|
132
|
-
#
|
133
|
-
# Person.first # SELECT "people".* FROM "people" ORDER BY "people"."id" ASC LIMIT 1
|
116
|
+
# Person.first(3) # returns the first three objects fetched by SELECT * FROM people ORDER BY people.id LIMIT 3
|
134
117
|
#
|
135
118
|
def first(limit = nil)
|
136
119
|
if limit
|
137
|
-
|
120
|
+
find_nth_with_limit_and_offset(0, limit, offset: offset_index)
|
138
121
|
else
|
139
|
-
find_nth
|
122
|
+
find_nth 0
|
140
123
|
end
|
141
124
|
end
|
142
125
|
|
143
|
-
# Same as
|
144
|
-
# is found. Note that
|
126
|
+
# Same as #first but raises ActiveRecord::RecordNotFound if no record
|
127
|
+
# is found. Note that #first! accepts no arguments.
|
145
128
|
def first!
|
146
129
|
find_nth! 0
|
147
130
|
end
|
@@ -162,21 +145,27 @@ module ActiveRecord
|
|
162
145
|
#
|
163
146
|
# [#<Person id:4>, #<Person id:3>, #<Person id:2>]
|
164
147
|
def last(limit = nil)
|
165
|
-
if
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
148
|
+
return find_last(limit) if loaded? || limit_value
|
149
|
+
|
150
|
+
result = limit(limit || 1)
|
151
|
+
result.order!(arel_attribute(primary_key)) if order_values.empty? && primary_key
|
152
|
+
result = result.reverse_order!
|
153
|
+
|
154
|
+
limit ? result.reverse : result.first
|
155
|
+
rescue ActiveRecord::IrreversibleOrderError
|
156
|
+
ActiveSupport::Deprecation.warn(<<-WARNING.squish)
|
157
|
+
Finding a last element by loading the relation when SQL ORDER
|
158
|
+
can not be reversed is deprecated.
|
159
|
+
Rails 5.1 will raise ActiveRecord::IrreversibleOrderError in this case.
|
160
|
+
Please call `to_a.last` if you still want to load the relation.
|
161
|
+
WARNING
|
162
|
+
find_last(limit)
|
174
163
|
end
|
175
164
|
|
176
|
-
# Same as
|
177
|
-
# is found. Note that
|
165
|
+
# Same as #last but raises ActiveRecord::RecordNotFound if no record
|
166
|
+
# is found. Note that #last! accepts no arguments.
|
178
167
|
def last!
|
179
|
-
last or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]")
|
168
|
+
last or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
|
180
169
|
end
|
181
170
|
|
182
171
|
# Find the second record.
|
@@ -186,10 +175,10 @@ module ActiveRecord
|
|
186
175
|
# Person.offset(3).second # returns the second object from OFFSET 3 (which is OFFSET 4)
|
187
176
|
# Person.where(["user_name = :u", { u: user_name }]).second
|
188
177
|
def second
|
189
|
-
find_nth
|
178
|
+
find_nth 1
|
190
179
|
end
|
191
180
|
|
192
|
-
# Same as
|
181
|
+
# Same as #second but raises ActiveRecord::RecordNotFound if no record
|
193
182
|
# is found.
|
194
183
|
def second!
|
195
184
|
find_nth! 1
|
@@ -202,10 +191,10 @@ module ActiveRecord
|
|
202
191
|
# Person.offset(3).third # returns the third object from OFFSET 3 (which is OFFSET 5)
|
203
192
|
# Person.where(["user_name = :u", { u: user_name }]).third
|
204
193
|
def third
|
205
|
-
find_nth
|
194
|
+
find_nth 2
|
206
195
|
end
|
207
196
|
|
208
|
-
# Same as
|
197
|
+
# Same as #third but raises ActiveRecord::RecordNotFound if no record
|
209
198
|
# is found.
|
210
199
|
def third!
|
211
200
|
find_nth! 2
|
@@ -218,10 +207,10 @@ module ActiveRecord
|
|
218
207
|
# Person.offset(3).fourth # returns the fourth object from OFFSET 3 (which is OFFSET 6)
|
219
208
|
# Person.where(["user_name = :u", { u: user_name }]).fourth
|
220
209
|
def fourth
|
221
|
-
find_nth
|
210
|
+
find_nth 3
|
222
211
|
end
|
223
212
|
|
224
|
-
# Same as
|
213
|
+
# Same as #fourth but raises ActiveRecord::RecordNotFound if no record
|
225
214
|
# is found.
|
226
215
|
def fourth!
|
227
216
|
find_nth! 3
|
@@ -234,10 +223,10 @@ module ActiveRecord
|
|
234
223
|
# Person.offset(3).fifth # returns the fifth object from OFFSET 3 (which is OFFSET 7)
|
235
224
|
# Person.where(["user_name = :u", { u: user_name }]).fifth
|
236
225
|
def fifth
|
237
|
-
find_nth
|
226
|
+
find_nth 4
|
238
227
|
end
|
239
228
|
|
240
|
-
# Same as
|
229
|
+
# Same as #fifth but raises ActiveRecord::RecordNotFound if no record
|
241
230
|
# is found.
|
242
231
|
def fifth!
|
243
232
|
find_nth! 4
|
@@ -250,17 +239,49 @@ module ActiveRecord
|
|
250
239
|
# Person.offset(3).forty_two # returns the forty-second object from OFFSET 3 (which is OFFSET 44)
|
251
240
|
# Person.where(["user_name = :u", { u: user_name }]).forty_two
|
252
241
|
def forty_two
|
253
|
-
find_nth
|
242
|
+
find_nth 41
|
254
243
|
end
|
255
244
|
|
256
|
-
# Same as
|
245
|
+
# Same as #forty_two but raises ActiveRecord::RecordNotFound if no record
|
257
246
|
# is found.
|
258
247
|
def forty_two!
|
259
248
|
find_nth! 41
|
260
249
|
end
|
261
250
|
|
262
|
-
#
|
263
|
-
#
|
251
|
+
# Find the third-to-last record.
|
252
|
+
# If no order is defined it will order by primary key.
|
253
|
+
#
|
254
|
+
# Person.third_to_last # returns the third-to-last object fetched by SELECT * FROM people
|
255
|
+
# Person.offset(3).third_to_last # returns the third-to-last object from OFFSET 3
|
256
|
+
# Person.where(["user_name = :u", { u: user_name }]).third_to_last
|
257
|
+
def third_to_last
|
258
|
+
find_nth_from_last 3
|
259
|
+
end
|
260
|
+
|
261
|
+
# Same as #third_to_last but raises ActiveRecord::RecordNotFound if no record
|
262
|
+
# is found.
|
263
|
+
def third_to_last!
|
264
|
+
find_nth_from_last 3 or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
|
265
|
+
end
|
266
|
+
|
267
|
+
# Find the second-to-last record.
|
268
|
+
# If no order is defined it will order by primary key.
|
269
|
+
#
|
270
|
+
# Person.second_to_last # returns the second-to-last object fetched by SELECT * FROM people
|
271
|
+
# Person.offset(3).second_to_last # returns the second-to-last object from OFFSET 3
|
272
|
+
# Person.where(["user_name = :u", { u: user_name }]).second_to_last
|
273
|
+
def second_to_last
|
274
|
+
find_nth_from_last 2
|
275
|
+
end
|
276
|
+
|
277
|
+
# Same as #second_to_last but raises ActiveRecord::RecordNotFound if no record
|
278
|
+
# is found.
|
279
|
+
def second_to_last!
|
280
|
+
find_nth_from_last 2 or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
|
281
|
+
end
|
282
|
+
|
283
|
+
# Returns true if a record exists in the table that matches the +id+ or
|
284
|
+
# conditions given, or false otherwise. The argument can take six forms:
|
264
285
|
#
|
265
286
|
# * Integer - Finds the record with this primary key.
|
266
287
|
# * String - Finds the record with a primary key corresponding to this
|
@@ -273,7 +294,7 @@ module ActiveRecord
|
|
273
294
|
# * No args - Returns +false+ if the table is empty, +true+ otherwise.
|
274
295
|
#
|
275
296
|
# For more information about specifying conditions as a hash or array,
|
276
|
-
# see the Conditions section in the introduction to
|
297
|
+
# see the Conditions section in the introduction to ActiveRecord::Base.
|
277
298
|
#
|
278
299
|
# Note: You can't pass in a condition as a string (like <tt>name =
|
279
300
|
# 'Jamie'</tt>), since it would be sanitized and then queried against
|
@@ -291,7 +312,7 @@ module ActiveRecord
|
|
291
312
|
conditions = conditions.id
|
292
313
|
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
293
314
|
You are passing an instance of ActiveRecord::Base to `exists?`.
|
294
|
-
Please pass the id of the object by calling `.id
|
315
|
+
Please pass the id of the object by calling `.id`.
|
295
316
|
MSG
|
296
317
|
end
|
297
318
|
|
@@ -307,15 +328,15 @@ module ActiveRecord
|
|
307
328
|
relation = relation.where(conditions)
|
308
329
|
else
|
309
330
|
unless conditions == :none
|
310
|
-
relation = where(primary_key => conditions)
|
331
|
+
relation = relation.where(primary_key => conditions)
|
311
332
|
end
|
312
333
|
end
|
313
334
|
|
314
|
-
connection.select_value(relation, "#{name} Exists", relation.
|
335
|
+
connection.select_value(relation, "#{name} Exists", relation.bound_attributes) ? true : false
|
315
336
|
end
|
316
337
|
|
317
338
|
# This method is called whenever no records are found with either a single
|
318
|
-
# id or multiple ids and raises
|
339
|
+
# id or multiple ids and raises an ActiveRecord::RecordNotFound exception.
|
319
340
|
#
|
320
341
|
# The error message is different depending on whether a single id or
|
321
342
|
# multiple ids are provided. If multiple ids are provided, then the number
|
@@ -323,7 +344,7 @@ module ActiveRecord
|
|
323
344
|
# the expected number of results should be provided in the +expected_size+
|
324
345
|
# argument.
|
325
346
|
def raise_record_not_found_exception!(ids, result_size, expected_size) #:nodoc:
|
326
|
-
conditions = arel.where_sql
|
347
|
+
conditions = arel.where_sql(@klass.arel_engine)
|
327
348
|
conditions = " [#{conditions}]" if conditions
|
328
349
|
|
329
350
|
if Array(ids).size == 1
|
@@ -365,7 +386,7 @@ module ActiveRecord
|
|
365
386
|
[]
|
366
387
|
else
|
367
388
|
arel = relation.arel
|
368
|
-
rows = connection.select_all(arel, 'SQL',
|
389
|
+
rows = connection.select_all(arel, 'SQL', relation.bound_attributes)
|
369
390
|
join_dependency.instantiate(rows, aliases)
|
370
391
|
end
|
371
392
|
end
|
@@ -379,7 +400,7 @@ module ActiveRecord
|
|
379
400
|
def construct_relation_for_association_calculations
|
380
401
|
from = arel.froms.first
|
381
402
|
if Arel::Table === from
|
382
|
-
apply_join_dependency(self, construct_join_dependency)
|
403
|
+
apply_join_dependency(self, construct_join_dependency(joins_values))
|
383
404
|
else
|
384
405
|
# FIXME: as far as I can tell, `from` will always be an Arel::Table.
|
385
406
|
# There are no tests that test this branch, but presumably it's
|
@@ -397,7 +418,7 @@ module ActiveRecord
|
|
397
418
|
else
|
398
419
|
if relation.limit_value
|
399
420
|
limited_ids = limited_ids_for(relation)
|
400
|
-
limited_ids.empty? ? relation.none! : relation.where!(
|
421
|
+
limited_ids.empty? ? relation.none! : relation.where!(primary_key => limited_ids)
|
401
422
|
end
|
402
423
|
relation.except(:limit, :offset)
|
403
424
|
end
|
@@ -410,12 +431,12 @@ module ActiveRecord
|
|
410
431
|
relation = relation.except(:select).select(values).distinct!
|
411
432
|
arel = relation.arel
|
412
433
|
|
413
|
-
id_rows = @klass.connection.select_all(arel, 'SQL',
|
434
|
+
id_rows = @klass.connection.select_all(arel, 'SQL', relation.bound_attributes)
|
414
435
|
id_rows.map {|row| row[primary_key]}
|
415
436
|
end
|
416
437
|
|
417
438
|
def using_limitable_reflections?(reflections)
|
418
|
-
reflections.none?
|
439
|
+
reflections.none?(&:collection?)
|
419
440
|
end
|
420
441
|
|
421
442
|
protected
|
@@ -446,7 +467,7 @@ module ActiveRecord
|
|
446
467
|
id = id.id
|
447
468
|
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
448
469
|
You are passing an instance of ActiveRecord::Base to `find`.
|
449
|
-
Please pass the id of the object by calling `.id
|
470
|
+
Please pass the id of the object by calling `.id`.
|
450
471
|
MSG
|
451
472
|
end
|
452
473
|
|
@@ -459,6 +480,8 @@ module ActiveRecord
|
|
459
480
|
end
|
460
481
|
|
461
482
|
def find_some(ids)
|
483
|
+
return find_some_ordered(ids) unless order_values.present?
|
484
|
+
|
462
485
|
result = where(primary_key => ids).to_a
|
463
486
|
|
464
487
|
expected_size =
|
@@ -480,49 +503,96 @@ module ActiveRecord
|
|
480
503
|
end
|
481
504
|
end
|
482
505
|
|
506
|
+
def find_some_ordered(ids)
|
507
|
+
ids = ids.slice(offset_value || 0, limit_value || ids.size) || []
|
508
|
+
|
509
|
+
result = except(:limit, :offset).where(primary_key => ids).records
|
510
|
+
|
511
|
+
if result.size == ids.size
|
512
|
+
pk_type = @klass.type_for_attribute(primary_key)
|
513
|
+
|
514
|
+
records_by_id = result.index_by(&:id)
|
515
|
+
ids.map { |id| records_by_id.fetch(pk_type.cast(id)) }
|
516
|
+
else
|
517
|
+
raise_record_not_found_exception!(ids, result.size, ids.size)
|
518
|
+
end
|
519
|
+
end
|
520
|
+
|
483
521
|
def find_take
|
484
522
|
if loaded?
|
485
523
|
@records.first
|
486
524
|
else
|
487
|
-
@take ||= limit(1).
|
525
|
+
@take ||= limit(1).records.first
|
488
526
|
end
|
489
527
|
end
|
490
528
|
|
491
|
-
def find_nth(index, offset)
|
529
|
+
def find_nth(index, offset = nil)
|
530
|
+
# TODO: once the offset argument is removed we rely on offset_index
|
531
|
+
# within find_nth_with_limit, rather than pass it in via
|
532
|
+
# find_nth_with_limit_and_offset
|
533
|
+
if offset
|
534
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
535
|
+
Passing an offset argument to find_nth is deprecated,
|
536
|
+
please use Relation#offset instead.
|
537
|
+
MSG
|
538
|
+
end
|
492
539
|
if loaded?
|
493
540
|
@records[index]
|
494
541
|
else
|
495
|
-
offset
|
496
|
-
@offsets[offset] ||=
|
542
|
+
offset ||= offset_index
|
543
|
+
@offsets[offset + index] ||= find_nth_with_limit_and_offset(index, 1, offset: offset).first
|
497
544
|
end
|
498
545
|
end
|
499
546
|
|
500
547
|
def find_nth!(index)
|
501
|
-
find_nth(index
|
548
|
+
find_nth(index) or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]")
|
502
549
|
end
|
503
550
|
|
504
|
-
def find_nth_with_limit(
|
551
|
+
def find_nth_with_limit(index, limit)
|
552
|
+
# TODO: once the offset argument is removed from find_nth,
|
553
|
+
# find_nth_with_limit_and_offset can be merged into this method
|
505
554
|
relation = if order_values.empty? && primary_key
|
506
|
-
order(
|
555
|
+
order(arel_attribute(primary_key).asc)
|
507
556
|
else
|
508
557
|
self
|
509
558
|
end
|
510
559
|
|
511
|
-
relation = relation.offset(
|
560
|
+
relation = relation.offset(index) unless index.zero?
|
512
561
|
relation.limit(limit).to_a
|
513
562
|
end
|
514
563
|
|
515
|
-
def
|
564
|
+
def find_nth_from_last(index)
|
565
|
+
if loaded?
|
566
|
+
@records[-index]
|
567
|
+
else
|
568
|
+
relation = if order_values.empty? && primary_key
|
569
|
+
order(arel_attribute(primary_key).asc)
|
570
|
+
else
|
571
|
+
self
|
572
|
+
end
|
573
|
+
|
574
|
+
relation.to_a[-index]
|
575
|
+
# TODO: can be made more performant on large result sets by
|
576
|
+
# for instance, last(index)[-index] (which would require
|
577
|
+
# refactoring the last(n) finder method to make test suite pass),
|
578
|
+
# or by using a combination of reverse_order, limit, and offset,
|
579
|
+
# e.g., reverse_order.offset(index-1).first
|
580
|
+
end
|
581
|
+
end
|
582
|
+
|
583
|
+
private
|
584
|
+
|
585
|
+
def find_nth_with_limit_and_offset(index, limit, offset:) # :nodoc:
|
516
586
|
if loaded?
|
517
|
-
@records
|
587
|
+
@records[index, limit]
|
518
588
|
else
|
519
|
-
|
520
|
-
|
521
|
-
to_a.last
|
522
|
-
else
|
523
|
-
reverse_order.limit(1).to_a.first
|
524
|
-
end
|
589
|
+
index += offset
|
590
|
+
find_nth_with_limit(index, limit)
|
525
591
|
end
|
526
592
|
end
|
593
|
+
|
594
|
+
def find_last(limit)
|
595
|
+
limit ? records.last(limit) : records.last
|
596
|
+
end
|
527
597
|
end
|
528
598
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class Relation
|
3
|
+
class FromClause # :nodoc:
|
4
|
+
attr_reader :value, :name
|
5
|
+
|
6
|
+
def initialize(value, name)
|
7
|
+
@value = value
|
8
|
+
@name = name
|
9
|
+
end
|
10
|
+
|
11
|
+
def binds
|
12
|
+
if value.is_a?(Relation)
|
13
|
+
value.bound_attributes
|
14
|
+
else
|
15
|
+
[]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def merge(other)
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def empty?
|
24
|
+
value.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.empty
|
28
|
+
@empty ||= new(nil, nil)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -1,5 +1,4 @@
|
|
1
1
|
require 'active_support/core_ext/hash/keys'
|
2
|
-
require "set"
|
3
2
|
|
4
3
|
module ActiveRecord
|
5
4
|
class Relation
|
@@ -22,7 +21,7 @@ module ActiveRecord
|
|
22
21
|
# build a relation to merge in rather than directly merging
|
23
22
|
# the values.
|
24
23
|
def other
|
25
|
-
other = Relation.create(relation.klass, relation.table)
|
24
|
+
other = Relation.create(relation.klass, relation.table, relation.predicate_builder)
|
26
25
|
hash.each { |k, v|
|
27
26
|
if k == :joins
|
28
27
|
if Hash === v
|
@@ -49,9 +48,9 @@ module ActiveRecord
|
|
49
48
|
@other = other
|
50
49
|
end
|
51
50
|
|
52
|
-
NORMAL_VALUES = Relation::
|
53
|
-
Relation::
|
54
|
-
[:
|
51
|
+
NORMAL_VALUES = Relation::VALUE_METHODS -
|
52
|
+
Relation::CLAUSE_METHODS -
|
53
|
+
[:includes, :preload, :joins, :order, :reverse_order, :lock, :create_with, :reordering] # :nodoc:
|
55
54
|
|
56
55
|
def normal_values
|
57
56
|
NORMAL_VALUES
|
@@ -75,6 +74,8 @@ module ActiveRecord
|
|
75
74
|
|
76
75
|
merge_multi_values
|
77
76
|
merge_single_values
|
77
|
+
merge_clauses
|
78
|
+
merge_preloads
|
78
79
|
merge_joins
|
79
80
|
|
80
81
|
relation
|
@@ -82,6 +83,27 @@ module ActiveRecord
|
|
82
83
|
|
83
84
|
private
|
84
85
|
|
86
|
+
def merge_preloads
|
87
|
+
return if other.preload_values.empty? && other.includes_values.empty?
|
88
|
+
|
89
|
+
if other.klass == relation.klass
|
90
|
+
relation.preload!(*other.preload_values) unless other.preload_values.empty?
|
91
|
+
relation.includes!(other.includes_values) unless other.includes_values.empty?
|
92
|
+
else
|
93
|
+
reflection = relation.klass.reflect_on_all_associations.find do |r|
|
94
|
+
r.class_name == other.klass.name
|
95
|
+
end || return
|
96
|
+
|
97
|
+
unless other.preload_values.empty?
|
98
|
+
relation.preload! reflection.name => other.preload_values
|
99
|
+
end
|
100
|
+
|
101
|
+
unless other.includes_values.empty?
|
102
|
+
relation.includes! reflection.name => other.includes_values
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
85
107
|
def merge_joins
|
86
108
|
return if other.joins_values.blank?
|
87
109
|
|
@@ -107,20 +129,6 @@ module ActiveRecord
|
|
107
129
|
end
|
108
130
|
|
109
131
|
def merge_multi_values
|
110
|
-
lhs_wheres = relation.where_values
|
111
|
-
rhs_wheres = other.where_values
|
112
|
-
|
113
|
-
lhs_binds = relation.bind_values
|
114
|
-
rhs_binds = other.bind_values
|
115
|
-
|
116
|
-
removed, kept = partition_overwrites(lhs_wheres, rhs_wheres)
|
117
|
-
|
118
|
-
where_values = kept + rhs_wheres
|
119
|
-
bind_values = filter_binds(lhs_binds, removed) + rhs_binds
|
120
|
-
|
121
|
-
relation.where_values = where_values
|
122
|
-
relation.bind_values = bind_values
|
123
|
-
|
124
132
|
if other.reordering_value
|
125
133
|
# override any order specified in the original relation
|
126
134
|
relation.reorder! other.order_values
|
@@ -133,36 +141,25 @@ module ActiveRecord
|
|
133
141
|
end
|
134
142
|
|
135
143
|
def merge_single_values
|
136
|
-
relation.
|
137
|
-
|
144
|
+
if relation.from_clause.empty?
|
145
|
+
relation.from_clause = other.from_clause
|
146
|
+
end
|
147
|
+
relation.lock_value ||= other.lock_value
|
138
148
|
|
139
149
|
unless other.create_with_value.blank?
|
140
150
|
relation.create_with_value = (relation.create_with_value || {}).merge(other.create_with_value)
|
141
151
|
end
|
142
152
|
end
|
143
153
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
set = Set.new removed_wheres.map { |x| x.left.name.to_s }
|
148
|
-
lhs_binds.dup.delete_if { |col,_| set.include? col.name }
|
154
|
+
CLAUSE_METHOD_NAMES = CLAUSE_METHODS.map do |name|
|
155
|
+
["#{name}_clause", "#{name}_clause="]
|
149
156
|
end
|
150
157
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
return [[], lhs_wheres]
|
157
|
-
end
|
158
|
-
|
159
|
-
nodes = rhs_wheres.find_all do |w|
|
160
|
-
w.respond_to?(:operator) && w.operator == :==
|
161
|
-
end
|
162
|
-
seen = Set.new(nodes) { |node| node.left }
|
163
|
-
|
164
|
-
lhs_wheres.partition do |w|
|
165
|
-
w.respond_to?(:operator) && w.operator == :== && seen.include?(w.left)
|
158
|
+
def merge_clauses
|
159
|
+
CLAUSE_METHOD_NAMES.each do |(reader, writer)|
|
160
|
+
clause = relation.send(reader)
|
161
|
+
other_clause = other.send(reader)
|
162
|
+
relation.send(writer, clause.merge(other_clause))
|
166
163
|
end
|
167
164
|
end
|
168
165
|
end
|