Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

none query method #4609

Closed
wants to merge 1 commit into from

4 participants

@xuanxu

Here is a none method to return an empty relation from a AR model (i.e Post.none), as previously discussed here: #4548

It prevents the query hitting the database, is chainable and makes any subsequent relation empty, so Post.none.where(:author_id => 1) is also empty.

@Empact

You might want to just update the code in the original pull request, to keep the conversation together there.

BTW I'm +1 on this idea. .where('1 = 0') is a lame hack.

@tenderlove
Owner

I'm OK with this, but I'd like more opinions. @jonleighton ?

@jonleighton
Collaborator

I'm okay with this too, but I think it would be better if:

  • The rationale was explained in the documentation (also explained in terms of the 'null object pattern' as that's what this is)
  • There was a CHANGELOG entry
  • Ideally I'd prefer if we used polymorphism. I.e. if Relation#none returned an instance of a NullRelation that implemented this behaviour, rather than having to have a @none_value var and special code in exec_queries.

Thanks.

@tenderlove
Owner

I agree with @jonleighton. @xuanxu can you make those changes, and we'll merge it in. Thanks for the contribution! :-)

@xuanxu

OK, I will try to make those changes

@xuanxu

@tenderlove Done!: #4768
¿should I backport it to 3.2?

@tenderlove
Owner

@jonleighton can you review?

@xuanxu no, we will not backport this (as it is a new feature).

@xuanxu xuanxu closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Jan 23, 2012
  1. @xuanxu
This page is out of date. Refresh to see the latest.
View
2  activerecord/lib/active_record/querying.rb
@@ -8,7 +8,7 @@ module Querying
delegate :find_each, :find_in_batches, :to => :scoped
delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins,
:where, :preload, :eager_load, :includes, :from, :lock, :readonly,
- :having, :create_with, :uniq, :references, :to => :scoped
+ :having, :create_with, :uniq, :references, :none, :to => :scoped
delegate :count, :average, :minimum, :maximum, :sum, :calculate, :pluck, :to => :scoped
# Executes a custom SQL query against your database and returns all the results. The results will
View
6 activerecord/lib/active_record/relation.rb
@@ -8,7 +8,7 @@ class Relation
JoinOperation = Struct.new(:relation, :join_class, :on)
ASSOCIATION_METHODS = [:includes, :eager_load, :preload]
MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having, :bind, :references]
- SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reordering, :reverse_order, :uniq]
+ SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :from, :reordering, :reverse_order, :uniq, :none]
include FinderMethods, Calculations, SpawnMethods, QueryMethods, Batches, Explain, Delegation
@@ -166,7 +166,9 @@ def exec_queries
default_scoped = with_default_scope
- if default_scoped.equal?(self)
+ if @none_value
+ @records = []
+ elsif default_scoped.equal?(self)
@records = if @readonly_value.nil? && !@klass.locking_enabled?
eager_loading? ? find_with_associations : @klass.find_by_sql(arel, @bind_values)
else
View
13 activerecord/lib/active_record/relation/query_methods.rb
@@ -10,7 +10,7 @@ module QueryMethods
:where_values, :having_values, :bind_values,
:limit_value, :offset_value, :lock_value, :readonly_value, :create_with_value,
:from_value, :reordering_value, :reverse_order_value,
- :uniq_value, :references_values
+ :uniq_value, :references_values, :none_value
def includes(*args)
args.reject! {|a| a.blank? }
@@ -196,6 +196,17 @@ def lock(locks = true)
relation
end
+ # Returns a chainable empty relation with zero records. It prevents the
+ # execution of the query.
+ #
+ # Any subsequent condition chained to the relation will continue
+ # generating an empty relation and will not fire any query to the database.
+ def none
+ relation = clone
+ relation.none_value = true
+ relation
+ end
+
def readonly(value = true)
relation = clone
relation.readonly_value = value
View
2  activerecord/test/cases/relation_test.rb
@@ -20,7 +20,7 @@ def test_construction
end
def test_single_values
- assert_equal [:limit, :offset, :lock, :readonly, :from, :reordering, :reverse_order, :uniq].map(&:to_s).sort,
+ assert_equal [:limit, :offset, :lock, :readonly, :from, :reordering, :reverse_order, :uniq, :none].map(&:to_s).sort,
Relation::SINGLE_VALUE_METHODS.map(&:to_s).sort
end
View
13 activerecord/test/cases/relations_test.rb
@@ -210,6 +210,19 @@ def test_finding_with_group
assert_equal 4, developers.map(&:salary).uniq.size
end
+ def test_none
+ assert_no_queries do
+ assert_equal [], Developer.none
+ assert_equal [], Developer.scoped.none
+ end
+ end
+
+ def test_none_chainable
+ assert_no_queries do
+ assert_equal [], Developer.none.where(:name => 'David')
+ end
+ end
+
def test_select_with_block
even_ids = Developer.scoped.select {|d| d.id % 2 == 0 }.map(&:id)
assert_equal [2, 4, 6, 8, 10], even_ids.sort
Something went wrong with that request. Please try again.