aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib/active_record/relation
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/lib/active_record/relation')
-rw-r--r--activerecord/lib/active_record/relation/delegation.rb16
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb28
-rw-r--r--activerecord/lib/active_record/relation/spawn_methods.rb10
3 files changed, 35 insertions, 19 deletions
diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb
index 27de313d05..b1333f110c 100644
--- a/activerecord/lib/active_record/relation/delegation.rb
+++ b/activerecord/lib/active_record/relation/delegation.rb
@@ -36,13 +36,8 @@ module ActiveRecord
# may vary depending on the klass of a relation, so we create a subclass of Relation
# for each different klass, and the delegations are compiled into that subclass only.
- BLACKLISTED_ARRAY_METHODS = [
- :compact!, :flatten!, :reject!, :reverse!, :rotate!, :map!,
- :shuffle!, :slice!, :sort!, :sort_by!, :delete_if,
- :keep_if, :pop, :shift, :delete_at, :select!
- ].to_set # :nodoc:
-
- delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join, to: :to_a
+ delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join,
+ :[], :&, :|, :+, :-, :sample, :reverse, :compact, to: :to_a
delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key,
:connection, :columns_hash, :to => :klass
@@ -114,21 +109,14 @@ module ActiveRecord
def respond_to?(method, include_private = false)
super || @klass.respond_to?(method, include_private) ||
- array_delegable?(method) ||
arel.respond_to?(method, include_private)
end
protected
- def array_delegable?(method)
- Array.method_defined?(method) && BLACKLISTED_ARRAY_METHODS.exclude?(method)
- end
-
def method_missing(method, *args, &block)
if @klass.respond_to?(method)
scoping { @klass.public_send(method, *args, &block) }
- elsif array_delegable?(method)
- to_a.public_send(method, *args, &block)
elsif arel.respond_to?(method)
arel.public_send(method, *args, &block)
else
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index 2dc52982c9..dbecb842b5 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -13,6 +13,8 @@ module ActiveRecord
# WhereChain objects act as placeholder for queries in which #where does not have any parameter.
# In this case, #where must be chained with #not to return a new relation.
class WhereChain
+ include ActiveModel::ForbiddenAttributesProtection
+
def initialize(scope)
@scope = scope
end
@@ -41,6 +43,8 @@ module ActiveRecord
# User.where.not(name: "Jon", role: "admin")
# # SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'
def not(opts, *rest)
+ opts = sanitize_forbidden_attributes(opts)
+
where_clause = @scope.send(:where_clause_factory).build(opts, rest)
@scope.references!(PredicateBuilder.references(opts)) if Hash === opts
@@ -407,10 +411,30 @@ module ActiveRecord
self
end
- # Performs a joins on +args+:
+ # Performs a joins on +args+. The given symbol(s) should match the name of
+ # the association(s).
#
# User.joins(:posts)
- # # SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
+ # # SELECT "users".*
+ # # FROM "users"
+ # # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
+ #
+ # Multiple joins:
+ #
+ # User.joins(:posts, :account)
+ # # SELECT "users".*
+ # # FROM "users"
+ # # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
+ # # INNER JOIN "accounts" ON "accounts"."id" = "users"."account_id"
+ #
+ # Nested joins:
+ #
+ # User.joins(posts: [:comments])
+ # # SELECT "users".*
+ # # FROM "users"
+ # # INNER JOIN "posts" ON "posts"."user_id" = "users"."id"
+ # # INNER JOIN "comments" "comments_posts"
+ # # ON "comments_posts"."post_id" = "posts"."id"
#
# You can use strings in order to customize your joins:
#
diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb
index 5c3318651a..67d7f83cb4 100644
--- a/activerecord/lib/active_record/relation/spawn_methods.rb
+++ b/activerecord/lib/active_record/relation/spawn_methods.rb
@@ -12,6 +12,7 @@ module ActiveRecord
# Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an ActiveRecord::Relation.
# Returns an array representing the intersection of the resulting records with <tt>other</tt>, if <tt>other</tt> is an array.
+ #
# Post.where(published: true).joins(:comments).merge( Comment.where(spam: false) )
# # Performs a single join query with both where conditions.
#
@@ -37,11 +38,14 @@ module ActiveRecord
end
def merge!(other) # :nodoc:
- if !other.is_a?(Relation) && other.respond_to?(:to_proc)
+ if other.is_a?(Hash)
+ Relation::HashMerger.new(self, other).merge
+ elsif other.is_a?(Relation)
+ Relation::Merger.new(self, other).merge
+ elsif other.respond_to?(:to_proc)
instance_exec(&other)
else
- klass = other.is_a?(Hash) ? Relation::HashMerger : Relation::Merger
- klass.new(self, other).merge
+ raise ArgumentError, "#{other.inspect} is not an ActiveRecord::Relation"
end
end