1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/activerecord/test/cases/relation/mutation_test.rb
Jon Leighton 64b9e93bb5 Fix ActiveRecord::Relation#unscope
I'm pretty confused about the addition of this method. The documentation
says that it was intended to allow the removal of values from the
default scope (in contrast to #except). However it behaves exactly the
same as except: https://gist.github.com/jonleighton/7537008 (other than
having a slightly enhanced syntax).

The removal of the default scope is allowed by
94924dc32b, which was not a change we
could make until 4.1 due to the need to deprecate things. However after
that change #unscope still gives us nothing that #except doesn't already
give us.

However there *is* a desire to be able to unscope stuff in a way that
persists across merges, which would allow associations to be defined
which unscope stuff from the default scope of the associated model. E.g.

  has_many :comments, -> { unscope where: :trashed }

So that's what this change implements. I've also corrected the
documentation. I removed the guide references to #except as I think
unscope really supercedes #except now.

While we're here, there's also a potential desire to be able to write
this:

  has_many :comments, -> { unscoped }

However, it doesn't make sense and would not be straightforward to
implement. While with #unscope we're specifying exactly what we want to
be removed from the relation, with "unscoped" we're just saying that we
want it to not have some things which were added earlier on by the
default scope. However in the case of an association, we surely don't
want *all* conditions to be removed, otherwise the above would just
become "SELECT * FROM comments" with no foreign key constraint.

To make the above work, we'd have to somehow tag the relation values
which get added when evaluating the default scope in order to
differentiate them from other relation values. Which is way too much
complexity and therefore not worth it when most use cases can be
satisfied with unscope.

Closes #10643, #11061.
2013-11-20 22:23:16 +00:00

144 lines
4.2 KiB
Ruby

require 'cases/helper'
require 'models/post'
module ActiveRecord
class RelationMutationTest < ActiveSupport::TestCase
class FakeKlass < Struct.new(:table_name, :name)
extend ActiveRecord::Delegation::DelegateCache
inherited self
def connection
Post.connection
end
def relation_delegate_class(klass)
self.class.relation_delegate_class(klass)
end
end
def relation
@relation ||= Relation.new FakeKlass.new('posts'), Post.arel_table
end
(Relation::MULTI_VALUE_METHODS - [:references, :extending, :order, :unscope]).each do |method|
test "##{method}!" do
assert relation.public_send("#{method}!", :foo).equal?(relation)
assert_equal [:foo], relation.public_send("#{method}_values")
end
end
test '#order!' do
assert relation.order!('name ASC').equal?(relation)
assert_equal ['name ASC'], relation.order_values
end
test '#order! with symbol prepends the table name' do
assert relation.order!(:name).equal?(relation)
node = relation.order_values.first
assert node.ascending?
assert_equal :name, node.expr.name
assert_equal "posts", node.expr.relation.name
end
test '#order! on non-string does not attempt regexp match for references' do
obj = Object.new
obj.expects(:=~).never
assert relation.order!(obj)
assert_equal [obj], relation.order_values
end
test '#references!' do
assert relation.references!(:foo).equal?(relation)
assert relation.references_values.include?('foo')
end
test 'extending!' do
mod, mod2 = Module.new, Module.new
assert relation.extending!(mod).equal?(relation)
assert_equal [mod], relation.extending_values
assert relation.is_a?(mod)
relation.extending!(mod2)
assert_equal [mod, mod2], relation.extending_values
end
test 'extending! with empty args' do
relation.extending!
assert_equal [], relation.extending_values
end
(Relation::SINGLE_VALUE_METHODS - [:from, :lock, :reordering, :reverse_order, :create_with]).each do |method|
test "##{method}!" do
assert relation.public_send("#{method}!", :foo).equal?(relation)
assert_equal :foo, relation.public_send("#{method}_value")
end
end
test '#from!' do
assert relation.from!('foo').equal?(relation)
assert_equal ['foo', nil], relation.from_value
end
test '#lock!' do
assert relation.lock!('foo').equal?(relation)
assert_equal 'foo', relation.lock_value
end
test '#reorder!' do
relation = self.relation.order('foo')
assert relation.reorder!('bar').equal?(relation)
assert_equal ['bar'], relation.order_values
assert relation.reordering_value
end
test '#reorder! with symbol prepends the table name' do
assert relation.reorder!(:name).equal?(relation)
node = relation.order_values.first
assert node.ascending?
assert_equal :name, node.expr.name
assert_equal "posts", node.expr.relation.name
end
test 'reverse_order!' do
assert relation.reverse_order!.equal?(relation)
assert relation.reverse_order_value
relation.reverse_order!
assert !relation.reverse_order_value
end
test 'create_with!' do
assert relation.create_with!(foo: 'bar').equal?(relation)
assert_equal({foo: 'bar'}, relation.create_with_value)
end
test 'test_merge!' do
assert relation.merge!(where: :foo).equal?(relation)
assert_equal [:foo], relation.where_values
end
test 'merge with a proc' do
assert_equal [:foo], relation.merge(-> { where(:foo) }).where_values
end
test 'none!' do
assert relation.none!.equal?(relation)
assert_equal [NullRelation], relation.extending_values
assert relation.is_a?(NullRelation)
end
test 'distinct!' do
relation.distinct! :foo
assert_equal :foo, relation.distinct_value
assert_equal :foo, relation.uniq_value # deprecated access
end
test 'uniq! was replaced by distinct!' do
relation.uniq! :foo
assert_equal :foo, relation.distinct_value
assert_equal :foo, relation.uniq_value # deprecated access
end
end
end