activerecord 4.0.13 → 4.1.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +745 -2700
- data/README.rdoc +2 -2
- data/examples/performance.rb +30 -18
- data/examples/simple.rb +4 -4
- data/lib/active_record.rb +2 -6
- data/lib/active_record/aggregations.rb +2 -1
- data/lib/active_record/association_relation.rb +0 -4
- data/lib/active_record/associations.rb +87 -43
- data/lib/active_record/associations/alias_tracker.rb +1 -3
- data/lib/active_record/associations/association.rb +8 -16
- data/lib/active_record/associations/association_scope.rb +5 -16
- data/lib/active_record/associations/belongs_to_association.rb +34 -25
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +1 -1
- data/lib/active_record/associations/builder/association.rb +78 -54
- data/lib/active_record/associations/builder/belongs_to.rb +91 -58
- data/lib/active_record/associations/builder/collection_association.rb +47 -45
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +107 -25
- data/lib/active_record/associations/builder/has_many.rb +2 -2
- data/lib/active_record/associations/builder/has_one.rb +5 -7
- data/lib/active_record/associations/builder/singular_association.rb +6 -7
- data/lib/active_record/associations/collection_association.rb +68 -105
- data/lib/active_record/associations/collection_proxy.rb +12 -15
- data/lib/active_record/associations/has_many_association.rb +11 -9
- data/lib/active_record/associations/has_many_through_association.rb +16 -12
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +204 -165
- data/lib/active_record/associations/join_dependency/join_association.rb +43 -101
- data/lib/active_record/associations/join_dependency/join_base.rb +6 -8
- data/lib/active_record/associations/join_dependency/join_part.rb +18 -37
- data/lib/active_record/associations/join_helper.rb +2 -11
- data/lib/active_record/associations/preloader.rb +89 -34
- data/lib/active_record/associations/preloader/association.rb +43 -25
- data/lib/active_record/associations/preloader/collection_association.rb +2 -2
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/singular_association.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +58 -26
- data/lib/active_record/associations/singular_association.rb +6 -5
- data/lib/active_record/associations/through_association.rb +2 -2
- data/lib/active_record/attribute_assignment.rb +5 -2
- data/lib/active_record/attribute_methods.rb +45 -40
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -1
- data/lib/active_record/attribute_methods/dirty.rb +8 -22
- data/lib/active_record/attribute_methods/primary_key.rb +1 -7
- data/lib/active_record/attribute_methods/read.rb +55 -28
- data/lib/active_record/attribute_methods/serialization.rb +12 -33
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -13
- data/lib/active_record/attribute_methods/write.rb +37 -12
- data/lib/active_record/autosave_association.rb +207 -207
- data/lib/active_record/base.rb +5 -1
- data/lib/active_record/callbacks.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +2 -7
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +11 -22
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +12 -14
- data/lib/active_record/connection_adapters/abstract/quoting.rb +1 -5
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +84 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +9 -8
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +52 -83
- data/lib/active_record/connection_adapters/abstract/transaction.rb +0 -5
- data/lib/active_record/connection_adapters/abstract_adapter.rb +14 -97
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +58 -60
- data/lib/active_record/connection_adapters/column.rb +1 -35
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -2
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +3 -4
- data/lib/active_record/connection_adapters/mysql_adapter.rb +16 -15
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +24 -18
- data/lib/active_record/connection_adapters/postgresql/cast.rb +20 -16
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +23 -43
- data/lib/active_record/connection_adapters/postgresql/oid.rb +19 -12
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +28 -23
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +8 -30
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +92 -75
- data/lib/active_record/connection_adapters/schema_cache.rb +8 -29
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +31 -64
- data/lib/active_record/connection_handling.rb +2 -2
- data/lib/active_record/core.rb +22 -43
- data/lib/active_record/counter_cache.rb +7 -7
- data/lib/active_record/enum.rb +100 -0
- data/lib/active_record/errors.rb +10 -5
- data/lib/active_record/fixture_set/file.rb +2 -1
- data/lib/active_record/fixtures.rb +171 -74
- data/lib/active_record/inheritance.rb +16 -22
- data/lib/active_record/integration.rb +52 -1
- data/lib/active_record/locking/optimistic.rb +7 -2
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +5 -12
- data/lib/active_record/migration.rb +62 -46
- data/lib/active_record/migration/command_recorder.rb +7 -13
- data/lib/active_record/model_schema.rb +7 -14
- data/lib/active_record/nested_attributes.rb +10 -8
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +3 -3
- data/lib/active_record/persistence.rb +16 -34
- data/lib/active_record/querying.rb +14 -12
- data/lib/active_record/railtie.rb +0 -50
- data/lib/active_record/railties/databases.rake +12 -15
- data/lib/active_record/readonly_attributes.rb +0 -6
- data/lib/active_record/reflection.rb +189 -75
- data/lib/active_record/relation.rb +69 -94
- data/lib/active_record/relation/batches.rb +57 -23
- data/lib/active_record/relation/calculations.rb +36 -43
- data/lib/active_record/relation/delegation.rb +54 -39
- data/lib/active_record/relation/finder_methods.rb +107 -62
- data/lib/active_record/relation/merger.rb +7 -20
- data/lib/active_record/relation/predicate_builder.rb +57 -38
- data/lib/active_record/relation/predicate_builder/array_handler.rb +29 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/query_methods.rb +110 -98
- data/lib/active_record/relation/spawn_methods.rb +1 -2
- data/lib/active_record/result.rb +45 -6
- data/lib/active_record/runtime_registry.rb +5 -0
- data/lib/active_record/sanitization.rb +6 -8
- data/lib/active_record/schema_dumper.rb +16 -5
- data/lib/active_record/schema_migration.rb +24 -25
- data/lib/active_record/scoping/default.rb +5 -18
- data/lib/active_record/scoping/named.rb +8 -29
- data/lib/active_record/store.rb +56 -28
- data/lib/active_record/tasks/database_tasks.rb +8 -4
- data/lib/active_record/timestamp.rb +4 -4
- data/lib/active_record/transactions.rb +8 -10
- data/lib/active_record/validations/presence.rb +1 -1
- data/lib/active_record/validations/uniqueness.rb +1 -6
- data/lib/active_record/version.rb +1 -1
- data/lib/rails/generators/active_record.rb +2 -8
- data/lib/rails/generators/active_record/migration.rb +18 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +4 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +4 -0
- metadata +32 -45
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -65
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
- data/lib/active_record/tasks/firebird_database_tasks.rb +0 -56
- data/lib/active_record/tasks/oracle_database_tasks.rb +0 -45
- data/lib/active_record/tasks/sqlserver_database_tasks.rb +0 -48
- data/lib/active_record/test_case.rb +0 -102
@@ -1,5 +1,20 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
class PredicateBuilder # :nodoc:
|
3
|
+
@handlers = []
|
4
|
+
|
5
|
+
autoload :RelationHandler, 'active_record/relation/predicate_builder/relation_handler'
|
6
|
+
autoload :ArrayHandler, 'active_record/relation/predicate_builder/array_handler'
|
7
|
+
|
8
|
+
def self.resolve_column_aliases(klass, hash)
|
9
|
+
hash = hash.dup
|
10
|
+
hash.keys.grep(Symbol) do |key|
|
11
|
+
if klass.attribute_alias? key
|
12
|
+
hash[klass.attribute_alias(key)] = hash.delete key
|
13
|
+
end
|
14
|
+
end
|
15
|
+
hash
|
16
|
+
end
|
17
|
+
|
3
18
|
def self.build_from_hash(klass, attributes, default_table)
|
4
19
|
queries = []
|
5
20
|
|
@@ -40,9 +55,9 @@ module ActiveRecord
|
|
40
55
|
#
|
41
56
|
# For polymorphic relationships, find the foreign key and type:
|
42
57
|
# PriceEstimate.where(estimate_of: treasure)
|
43
|
-
if klass &&
|
44
|
-
if reflection.polymorphic?
|
45
|
-
queries << build(table[reflection.foreign_type],
|
58
|
+
if klass && reflection = klass.reflect_on_association(column.to_sym)
|
59
|
+
if reflection.polymorphic? && base_class = polymorphic_base_class_from_value(value)
|
60
|
+
queries << build(table[reflection.foreign_type], base_class)
|
46
61
|
end
|
47
62
|
|
48
63
|
column = reflection.foreign_key
|
@@ -52,6 +67,18 @@ module ActiveRecord
|
|
52
67
|
queries
|
53
68
|
end
|
54
69
|
|
70
|
+
def self.polymorphic_base_class_from_value(value)
|
71
|
+
case value
|
72
|
+
when Relation
|
73
|
+
value.klass.base_class
|
74
|
+
when Array
|
75
|
+
val = value.compact.first
|
76
|
+
val.class.base_class if val.is_a?(Base)
|
77
|
+
when Base
|
78
|
+
value.class.base_class
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
55
82
|
def self.references(attributes)
|
56
83
|
attributes.map do |key, value|
|
57
84
|
if value.is_a?(Hash)
|
@@ -63,44 +90,36 @@ module ActiveRecord
|
|
63
90
|
end.compact
|
64
91
|
end
|
65
92
|
|
93
|
+
# Define how a class is converted to Arel nodes when passed to +where+.
|
94
|
+
# The handler can be any object that responds to +call+, and will be used
|
95
|
+
# for any value that +===+ the class given. For example:
|
96
|
+
#
|
97
|
+
# MyCustomDateRange = Struct.new(:start, :end)
|
98
|
+
# handler = proc do |column, range|
|
99
|
+
# Arel::Nodes::Between.new(column,
|
100
|
+
# Arel::Nodes::And.new([range.start, range.end])
|
101
|
+
# )
|
102
|
+
# end
|
103
|
+
# ActiveRecord::PredicateBuilder.register_handler(MyCustomDateRange, handler)
|
104
|
+
def self.register_handler(klass, handler)
|
105
|
+
@handlers.unshift([klass, handler])
|
106
|
+
end
|
107
|
+
|
108
|
+
register_handler(BasicObject, ->(attribute, value) { attribute.eq(value) })
|
109
|
+
# FIXME: I think we need to deprecate this behavior
|
110
|
+
register_handler(Class, ->(attribute, value) { attribute.eq(value.name) })
|
111
|
+
register_handler(Base, ->(attribute, value) { attribute.eq(value.id) })
|
112
|
+
register_handler(Range, ->(attribute, value) { attribute.in(value) })
|
113
|
+
register_handler(Relation, RelationHandler.new)
|
114
|
+
register_handler(Array, ArrayHandler.new)
|
115
|
+
|
66
116
|
private
|
67
117
|
def self.build(attribute, value)
|
68
|
-
|
69
|
-
|
70
|
-
values = value.to_a.map {|x| x.is_a?(Base) ? x.id : x}
|
71
|
-
ranges, values = values.partition {|v| v.is_a?(Range)}
|
72
|
-
|
73
|
-
values_predicate = if values.include?(nil)
|
74
|
-
values = values.compact
|
75
|
-
|
76
|
-
case values.length
|
77
|
-
when 0
|
78
|
-
attribute.eq(nil)
|
79
|
-
when 1
|
80
|
-
attribute.eq(values.first).or(attribute.eq(nil))
|
81
|
-
else
|
82
|
-
attribute.in(values).or(attribute.eq(nil))
|
83
|
-
end
|
84
|
-
else
|
85
|
-
attribute.in(values)
|
86
|
-
end
|
118
|
+
handler_for(value).call(attribute, value)
|
119
|
+
end
|
87
120
|
|
88
|
-
|
89
|
-
|
90
|
-
array_predicates.inject { |composite, predicate| composite.or(predicate) }
|
91
|
-
when ActiveRecord::Relation
|
92
|
-
value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty?
|
93
|
-
attribute.in(value.arel.ast)
|
94
|
-
when Range
|
95
|
-
attribute.in(value)
|
96
|
-
when ActiveRecord::Base
|
97
|
-
attribute.eq(value.id)
|
98
|
-
when Class
|
99
|
-
# FIXME: I think we need to deprecate this behavior
|
100
|
-
attribute.eq(value.name)
|
101
|
-
else
|
102
|
-
attribute.eq(value)
|
103
|
-
end
|
121
|
+
def self.handler_for(object)
|
122
|
+
@handlers.detect { |klass, _| klass === object }.last
|
104
123
|
end
|
105
124
|
end
|
106
125
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class PredicateBuilder
|
3
|
+
class ArrayHandler # :nodoc:
|
4
|
+
def call(attribute, value)
|
5
|
+
values = value.map { |x| x.is_a?(Base) ? x.id : x }
|
6
|
+
ranges, values = values.partition { |v| v.is_a?(Range) }
|
7
|
+
|
8
|
+
values_predicate = if values.include?(nil)
|
9
|
+
values = values.compact
|
10
|
+
|
11
|
+
case values.length
|
12
|
+
when 0
|
13
|
+
attribute.eq(nil)
|
14
|
+
when 1
|
15
|
+
attribute.eq(values.first).or(attribute.eq(nil))
|
16
|
+
else
|
17
|
+
attribute.in(values).or(attribute.eq(nil))
|
18
|
+
end
|
19
|
+
else
|
20
|
+
attribute.in(values)
|
21
|
+
end
|
22
|
+
|
23
|
+
array_predicates = ranges.map { |range| attribute.in(range) }
|
24
|
+
array_predicates << values_predicate
|
25
|
+
array_predicates.inject { |composite, predicate| composite.or(predicate) }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class PredicateBuilder
|
3
|
+
class RelationHandler # :nodoc:
|
4
|
+
def call(attribute, value)
|
5
|
+
if value.select_values.empty?
|
6
|
+
value = value.select(value.klass.arel_table[value.klass.primary_key])
|
7
|
+
end
|
8
|
+
|
9
|
+
attribute.in(value.arel.ast)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -1,12 +1,9 @@
|
|
1
1
|
require 'active_support/core_ext/array/wrap'
|
2
|
-
require 'active_model/forbidden_attributes_protection'
|
3
2
|
|
4
3
|
module ActiveRecord
|
5
4
|
module QueryMethods
|
6
5
|
extend ActiveSupport::Concern
|
7
6
|
|
8
|
-
include ActiveModel::ForbiddenAttributesProtection
|
9
|
-
|
10
7
|
# WhereChain objects act as placeholder for queries in which #where does not have any parameter.
|
11
8
|
# In this case, #where must be chained with #not to return a new relation.
|
12
9
|
class WhereChain
|
@@ -103,6 +100,14 @@ module ActiveRecord
|
|
103
100
|
# firing an additional query. This will often result in a
|
104
101
|
# performance improvement over a simple +join+.
|
105
102
|
#
|
103
|
+
# You can also specify multiple relationships, like this:
|
104
|
+
#
|
105
|
+
# users = User.includes(:address, :friends)
|
106
|
+
#
|
107
|
+
# Loading nested relationships is possible using a Hash:
|
108
|
+
#
|
109
|
+
# users = User.includes(:address, friends: [:address, :followers])
|
110
|
+
#
|
106
111
|
# === conditions
|
107
112
|
#
|
108
113
|
# If you want to add conditions to your included models you'll have
|
@@ -114,14 +119,15 @@ module ActiveRecord
|
|
114
119
|
#
|
115
120
|
# User.includes(:posts).where('posts.name = ?', 'example').references(:posts)
|
116
121
|
def includes(*args)
|
117
|
-
check_if_method_has_arguments!(
|
122
|
+
check_if_method_has_arguments!(:includes, args)
|
118
123
|
spawn.includes!(*args)
|
119
124
|
end
|
120
125
|
|
121
126
|
def includes!(*args) # :nodoc:
|
122
|
-
args.reject!
|
127
|
+
args.reject!(&:blank?)
|
128
|
+
args.flatten!
|
123
129
|
|
124
|
-
self.includes_values
|
130
|
+
self.includes_values |= args
|
125
131
|
self
|
126
132
|
end
|
127
133
|
|
@@ -132,7 +138,7 @@ module ActiveRecord
|
|
132
138
|
# FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" =
|
133
139
|
# "users"."id"
|
134
140
|
def eager_load(*args)
|
135
|
-
check_if_method_has_arguments!(
|
141
|
+
check_if_method_has_arguments!(:eager_load, args)
|
136
142
|
spawn.eager_load!(*args)
|
137
143
|
end
|
138
144
|
|
@@ -146,7 +152,7 @@ module ActiveRecord
|
|
146
152
|
# User.preload(:posts)
|
147
153
|
# => SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1, 2, 3)
|
148
154
|
def preload(*args)
|
149
|
-
check_if_method_has_arguments!(
|
155
|
+
check_if_method_has_arguments!(:preload, args)
|
150
156
|
spawn.preload!(*args)
|
151
157
|
end
|
152
158
|
|
@@ -164,14 +170,15 @@ module ActiveRecord
|
|
164
170
|
# User.includes(:posts).where("posts.name = 'foo'").references(:posts)
|
165
171
|
# # => Query now knows the string references posts, so adds a JOIN
|
166
172
|
def references(*args)
|
167
|
-
check_if_method_has_arguments!(
|
173
|
+
check_if_method_has_arguments!(:references, args)
|
168
174
|
spawn.references!(*args)
|
169
175
|
end
|
170
176
|
|
171
177
|
def references!(*args) # :nodoc:
|
172
178
|
args.flatten!
|
179
|
+
args.map!(&:to_s)
|
173
180
|
|
174
|
-
self.references_values
|
181
|
+
self.references_values |= args
|
175
182
|
self
|
176
183
|
end
|
177
184
|
|
@@ -219,12 +226,14 @@ module ActiveRecord
|
|
219
226
|
to_a.select { |*block_args| yield(*block_args) }
|
220
227
|
else
|
221
228
|
raise ArgumentError, 'Call this with at least one field' if fields.empty?
|
222
|
-
spawn.
|
229
|
+
spawn.select!(*fields)
|
223
230
|
end
|
224
231
|
end
|
225
232
|
|
226
|
-
def
|
227
|
-
|
233
|
+
def select!(*fields) # :nodoc:
|
234
|
+
fields.flatten!
|
235
|
+
|
236
|
+
self.select_values += fields
|
228
237
|
self
|
229
238
|
end
|
230
239
|
|
@@ -244,7 +253,7 @@ module ActiveRecord
|
|
244
253
|
# User.group('name AS grouped_name, age')
|
245
254
|
# => [#<User id: 3, name: "Foo", age: 21, ...>, #<User id: 2, name: "Oscar", age: 21, ...>, #<User id: 5, name: "Foo", age: 23, ...>]
|
246
255
|
def group(*args)
|
247
|
-
check_if_method_has_arguments!(
|
256
|
+
check_if_method_has_arguments!(:group, args)
|
248
257
|
spawn.group!(*args)
|
249
258
|
end
|
250
259
|
|
@@ -275,24 +284,12 @@ module ActiveRecord
|
|
275
284
|
# User.order(:name, email: :desc)
|
276
285
|
# => SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC
|
277
286
|
def order(*args)
|
278
|
-
check_if_method_has_arguments!(
|
287
|
+
check_if_method_has_arguments!(:order, args)
|
279
288
|
spawn.order!(*args)
|
280
289
|
end
|
281
290
|
|
282
291
|
def order!(*args) # :nodoc:
|
283
|
-
args
|
284
|
-
validate_order_args args
|
285
|
-
|
286
|
-
references = args.reject { |arg| Arel::Node === arg }
|
287
|
-
references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
|
288
|
-
references!(references) if references.any?
|
289
|
-
|
290
|
-
# if a symbol is given we prepend the quoted table name
|
291
|
-
args = args.map! { |arg|
|
292
|
-
arg.is_a?(Symbol) ?
|
293
|
-
Arel::Nodes::Ascending.new(klass.arel_table[arg]) :
|
294
|
-
arg
|
295
|
-
}
|
292
|
+
preprocess_order_args(args)
|
296
293
|
|
297
294
|
self.order_values += args
|
298
295
|
self
|
@@ -308,13 +305,12 @@ module ActiveRecord
|
|
308
305
|
#
|
309
306
|
# generates a query with 'ORDER BY id ASC, name ASC'.
|
310
307
|
def reorder(*args)
|
311
|
-
check_if_method_has_arguments!(
|
308
|
+
check_if_method_has_arguments!(:reorder, args)
|
312
309
|
spawn.reorder!(*args)
|
313
310
|
end
|
314
311
|
|
315
312
|
def reorder!(*args) # :nodoc:
|
316
|
-
args
|
317
|
-
validate_order_args args
|
313
|
+
preprocess_order_args(args)
|
318
314
|
|
319
315
|
self.reordering_value = true
|
320
316
|
self.order_values = args
|
@@ -345,20 +341,27 @@ module ActiveRecord
|
|
345
341
|
# User.where(name: "John", active: true).unscope(where: :name)
|
346
342
|
# == User.where(active: true)
|
347
343
|
#
|
348
|
-
#
|
349
|
-
#
|
350
|
-
#
|
344
|
+
# This method is similar to <tt>except</tt>, but unlike
|
345
|
+
# <tt>except</tt>, it persists across merges:
|
346
|
+
#
|
347
|
+
# User.order('email').merge(User.except(:order))
|
348
|
+
# == User.order('email')
|
349
|
+
#
|
350
|
+
# User.order('email').merge(User.unscope(:order))
|
351
|
+
# == User.all
|
352
|
+
#
|
353
|
+
# This means it can be used in association definitions:
|
351
354
|
#
|
352
|
-
#
|
355
|
+
# has_many :comments, -> { unscope where: :trashed }
|
353
356
|
#
|
354
|
-
# will still have an order if it comes from the default_scope on Comment.
|
355
357
|
def unscope(*args)
|
356
|
-
check_if_method_has_arguments!(
|
358
|
+
check_if_method_has_arguments!(:unscope, args)
|
357
359
|
spawn.unscope!(*args)
|
358
360
|
end
|
359
361
|
|
360
362
|
def unscope!(*args) # :nodoc:
|
361
363
|
args.flatten!
|
364
|
+
self.unscope_values += args
|
362
365
|
|
363
366
|
args.each do |scope|
|
364
367
|
case scope
|
@@ -392,8 +395,12 @@ module ActiveRecord
|
|
392
395
|
# User.joins("LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id")
|
393
396
|
# => SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id
|
394
397
|
def joins(*args)
|
395
|
-
check_if_method_has_arguments!(
|
396
|
-
|
398
|
+
check_if_method_has_arguments!(:joins, args)
|
399
|
+
|
400
|
+
args.compact!
|
401
|
+
args.flatten!
|
402
|
+
|
403
|
+
spawn.joins!(*args)
|
397
404
|
end
|
398
405
|
|
399
406
|
def joins!(*args) # :nodoc:
|
@@ -420,7 +427,7 @@ module ActiveRecord
|
|
420
427
|
# === string
|
421
428
|
#
|
422
429
|
# A single string, without additional arguments, is passed to the query
|
423
|
-
# constructor as
|
430
|
+
# constructor as an SQL fragment, and used in the where clause of the query.
|
424
431
|
#
|
425
432
|
# Client.where("orders_count = '2'")
|
426
433
|
# # SELECT * from clients where orders_count = '2';
|
@@ -543,16 +550,25 @@ module ActiveRecord
|
|
543
550
|
if opts == :chain
|
544
551
|
WhereChain.new(self)
|
545
552
|
else
|
546
|
-
if Hash === opts
|
547
|
-
opts = sanitize_forbidden_attributes(opts)
|
548
|
-
references!(PredicateBuilder.references(opts))
|
549
|
-
end
|
553
|
+
references!(PredicateBuilder.references(opts)) if Hash === opts
|
550
554
|
|
551
555
|
self.where_values += build_where(opts, rest)
|
552
556
|
self
|
553
557
|
end
|
554
558
|
end
|
555
559
|
|
560
|
+
# Allows you to change a previously set where condition for a given attribute, instead of appending to that condition.
|
561
|
+
#
|
562
|
+
# Post.where(trashed: true).where(trashed: false) # => WHERE `trashed` = 1 AND `trashed` = 0
|
563
|
+
# Post.where(trashed: true).rewhere(trashed: false) # => WHERE `trashed` = 0
|
564
|
+
# Post.where(active: true).where(trashed: true).rewhere(trashed: false) # => WHERE `active` = 1 AND `trashed` = 0
|
565
|
+
#
|
566
|
+
# This is short-hand for unscope(where: conditions.keys).where(conditions). Note that unlike reorder, we're only unscoping
|
567
|
+
# the named conditions -- not the entire where statement.
|
568
|
+
def rewhere(conditions)
|
569
|
+
unscope(where: conditions.keys).where(conditions)
|
570
|
+
end
|
571
|
+
|
556
572
|
# Allows to specify a HAVING clause. Note that you can't use HAVING
|
557
573
|
# without also specifying a GROUP clause.
|
558
574
|
#
|
@@ -615,11 +631,12 @@ module ActiveRecord
|
|
615
631
|
self
|
616
632
|
end
|
617
633
|
|
618
|
-
# Returns a chainable relation with zero records
|
634
|
+
# Returns a chainable relation with zero records, specifically an
|
635
|
+
# instance of the <tt>ActiveRecord::NullRelation</tt> class.
|
619
636
|
#
|
620
|
-
# The returned
|
621
|
-
# object with defined null behavior and always returns an empty
|
622
|
-
# records without querying the database.
|
637
|
+
# The returned <tt>ActiveRecord::NullRelation</tt> inherits from Relation and implements the
|
638
|
+
# Null Object pattern. It is an object with defined null behavior and always returns an empty
|
639
|
+
# array of records without querying the database.
|
623
640
|
#
|
624
641
|
# Any subsequent condition chained to the returned relation will continue
|
625
642
|
# generating an empty relation and will not fire any query to the database.
|
@@ -639,7 +656,7 @@ module ActiveRecord
|
|
639
656
|
# when 'Reviewer'
|
640
657
|
# Post.published
|
641
658
|
# when 'Bad User'
|
642
|
-
# Post.none #
|
659
|
+
# Post.none # It can't be chained if [] is returned.
|
643
660
|
# end
|
644
661
|
# end
|
645
662
|
#
|
@@ -684,20 +701,14 @@ module ActiveRecord
|
|
684
701
|
end
|
685
702
|
|
686
703
|
def create_with!(value) # :nodoc:
|
687
|
-
|
688
|
-
value = sanitize_forbidden_attributes(value)
|
689
|
-
self.create_with_value = create_with_value.merge(value)
|
690
|
-
else
|
691
|
-
self.create_with_value = {}
|
692
|
-
end
|
693
|
-
|
704
|
+
self.create_with_value = value ? create_with_value.merge(value) : {}
|
694
705
|
self
|
695
706
|
end
|
696
707
|
|
697
708
|
# Specifies table from which the records will be fetched. For example:
|
698
709
|
#
|
699
710
|
# Topic.select('title').from('posts')
|
700
|
-
#
|
711
|
+
# # => SELECT title FROM posts
|
701
712
|
#
|
702
713
|
# Can accept other relation objects. For example:
|
703
714
|
#
|
@@ -783,9 +794,10 @@ module ActiveRecord
|
|
783
794
|
end
|
784
795
|
|
785
796
|
def extending!(*modules, &block) # :nodoc:
|
786
|
-
modules << Module.new(&block) if
|
797
|
+
modules << Module.new(&block) if block
|
798
|
+
modules.flatten!
|
787
799
|
|
788
|
-
self.extending_values += modules
|
800
|
+
self.extending_values += modules
|
789
801
|
extend(*extending_values) if extending_values.any?
|
790
802
|
|
791
803
|
self
|
@@ -805,7 +817,7 @@ module ActiveRecord
|
|
805
817
|
|
806
818
|
# Returns the Arel object associated with the relation.
|
807
819
|
def arel
|
808
|
-
@arel ||=
|
820
|
+
@arel ||= build_arel
|
809
821
|
end
|
810
822
|
|
811
823
|
# Like #arel, but ignores the default scope of the model.
|
@@ -816,12 +828,12 @@ module ActiveRecord
|
|
816
828
|
|
817
829
|
collapse_wheres(arel, (where_values - ['']).uniq)
|
818
830
|
|
819
|
-
arel.having(*having_values.uniq.reject
|
831
|
+
arel.having(*having_values.uniq.reject(&:blank?)) unless having_values.empty?
|
820
832
|
|
821
833
|
arel.take(connection.sanitize_limit(limit_value)) if limit_value
|
822
834
|
arel.skip(offset_value.to_i) if offset_value
|
823
835
|
|
824
|
-
arel.group(*group_values.uniq.reject
|
836
|
+
arel.group(*group_values.uniq.reject(&:blank?)) unless group_values.empty?
|
825
837
|
|
826
838
|
build_order(arel)
|
827
839
|
|
@@ -870,13 +882,11 @@ module ActiveRecord
|
|
870
882
|
end
|
871
883
|
|
872
884
|
def custom_join_ast(table, joins)
|
873
|
-
joins = joins.reject
|
885
|
+
joins = joins.reject(&:blank?)
|
874
886
|
|
875
887
|
return [] if joins.empty?
|
876
888
|
|
877
|
-
|
878
|
-
|
879
|
-
joins.map do |join|
|
889
|
+
joins.map! do |join|
|
880
890
|
case join
|
881
891
|
when Array
|
882
892
|
join = Arel.sql(join.join(' ')) if array_of_strings?(join)
|
@@ -909,6 +919,7 @@ module ActiveRecord
|
|
909
919
|
|
910
920
|
[@klass.send(:sanitize_sql, other.empty? ? opts : ([opts] + other))]
|
911
921
|
when Hash
|
922
|
+
opts = PredicateBuilder.resolve_column_aliases(klass, opts)
|
912
923
|
attributes = @klass.send(:expand_hash_conditions_for_aggregates, opts)
|
913
924
|
|
914
925
|
attributes.values.grep(ActiveRecord::Relation) do |rel|
|
@@ -940,7 +951,7 @@ module ActiveRecord
|
|
940
951
|
:string_join
|
941
952
|
when Hash, Symbol, Array
|
942
953
|
:association_join
|
943
|
-
when ActiveRecord::Associations::JoinDependency
|
954
|
+
when ActiveRecord::Associations::JoinDependency
|
944
955
|
:stashed_join
|
945
956
|
when Arel::Nodes::Join
|
946
957
|
:join_node
|
@@ -952,7 +963,7 @@ module ActiveRecord
|
|
952
963
|
association_joins = buckets[:association_join] || []
|
953
964
|
stashed_association_joins = buckets[:stashed_join] || []
|
954
965
|
join_nodes = (buckets[:join_node] || []).uniq
|
955
|
-
string_joins = (buckets[:string_join] || []).map
|
966
|
+
string_joins = (buckets[:string_join] || []).map(&:strip).uniq
|
956
967
|
|
957
968
|
join_list = join_nodes + custom_join_ast(manager, string_joins)
|
958
969
|
|
@@ -962,23 +973,17 @@ module ActiveRecord
|
|
962
973
|
join_list
|
963
974
|
)
|
964
975
|
|
965
|
-
join_dependency.
|
976
|
+
joins = join_dependency.join_constraints stashed_association_joins
|
966
977
|
|
967
|
-
|
978
|
+
joins.each { |join| manager.from(join) }
|
968
979
|
|
969
|
-
|
970
|
-
join_dependency.join_associations.each do |association|
|
971
|
-
association.join_to(manager)
|
972
|
-
end
|
973
|
-
|
974
|
-
manager.join_sources.concat join_list
|
980
|
+
manager.join_sources.concat(join_list)
|
975
981
|
|
976
982
|
manager
|
977
983
|
end
|
978
984
|
|
979
985
|
def build_select(arel, selects)
|
980
986
|
unless selects.empty?
|
981
|
-
@implicit_readonly = false
|
982
987
|
arel.project(*selects)
|
983
988
|
else
|
984
989
|
arel.project(@klass.arel_table[Arel.star])
|
@@ -993,16 +998,10 @@ module ActiveRecord
|
|
993
998
|
when Arel::Nodes::Ordering
|
994
999
|
o.reverse
|
995
1000
|
when String
|
996
|
-
o.to_s.split(',').
|
1001
|
+
o.to_s.split(',').map! do |s|
|
997
1002
|
s.strip!
|
998
1003
|
s.gsub!(/\sasc\Z/i, ' DESC') || s.gsub!(/\sdesc\Z/i, ' ASC') || s.concat(' DESC')
|
999
1004
|
end
|
1000
|
-
when Symbol
|
1001
|
-
{ o => :desc }
|
1002
|
-
when Hash
|
1003
|
-
o.each_with_object({}) do |(field, dir), memo|
|
1004
|
-
memo[field] = (dir == :asc ? :desc : :asc )
|
1005
|
-
end
|
1006
1005
|
else
|
1007
1006
|
o
|
1008
1007
|
end
|
@@ -1010,35 +1009,48 @@ module ActiveRecord
|
|
1010
1009
|
end
|
1011
1010
|
|
1012
1011
|
def array_of_strings?(o)
|
1013
|
-
o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)}
|
1012
|
+
o.is_a?(Array) && o.all? { |obj| obj.is_a?(String) }
|
1014
1013
|
end
|
1015
1014
|
|
1016
1015
|
def build_order(arel)
|
1017
|
-
orders = order_values
|
1016
|
+
orders = order_values.uniq
|
1017
|
+
orders.reject!(&:blank?)
|
1018
1018
|
orders = reverse_sql_order(orders) if reverse_order_value
|
1019
1019
|
|
1020
|
-
orders = orders.uniq.reject(&:blank?).flat_map do |order|
|
1021
|
-
case order
|
1022
|
-
when Symbol
|
1023
|
-
table[order].asc
|
1024
|
-
when Hash
|
1025
|
-
order.map { |field, dir| table[field].send(dir) }
|
1026
|
-
else
|
1027
|
-
order
|
1028
|
-
end
|
1029
|
-
end
|
1030
|
-
|
1031
1020
|
arel.order(*orders) unless orders.empty?
|
1032
1021
|
end
|
1033
1022
|
|
1034
1023
|
def validate_order_args(args)
|
1035
|
-
args.
|
1024
|
+
args.grep(Hash) do |h|
|
1036
1025
|
unless (h.values - [:asc, :desc]).empty?
|
1037
1026
|
raise ArgumentError, 'Direction should be :asc or :desc'
|
1038
1027
|
end
|
1039
1028
|
end
|
1040
1029
|
end
|
1041
1030
|
|
1031
|
+
def preprocess_order_args(order_args)
|
1032
|
+
order_args.flatten!
|
1033
|
+
validate_order_args(order_args)
|
1034
|
+
|
1035
|
+
references = order_args.grep(String)
|
1036
|
+
references.map! { |arg| arg =~ /^([a-zA-Z]\w*)\.(\w+)/ && $1 }.compact!
|
1037
|
+
references!(references) if references.any?
|
1038
|
+
|
1039
|
+
# if a symbol is given we prepend the quoted table name
|
1040
|
+
order_args.map! do |arg|
|
1041
|
+
case arg
|
1042
|
+
when Symbol
|
1043
|
+
table[arg].asc
|
1044
|
+
when Hash
|
1045
|
+
arg.map { |field, dir|
|
1046
|
+
table[field].send(dir)
|
1047
|
+
}
|
1048
|
+
else
|
1049
|
+
arg
|
1050
|
+
end
|
1051
|
+
end.flatten!
|
1052
|
+
end
|
1053
|
+
|
1042
1054
|
# Checks to make sure that the arguments are not blank. Note that if some
|
1043
1055
|
# blank-like object were initially passed into the query method, then this
|
1044
1056
|
# method will not raise an error.
|