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

Juanjo Bazán Ben Woosley Aaron Patterson Jon Leighton
Juanjo Bazán

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.

Ben Woosley

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.

Aaron Patterson
Owner

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

Jon Leighton
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.

Aaron Patterson
Owner

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

Juanjo Bazán

OK, I will try to make those changes

Juanjo Bazán

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

Aaron Patterson
Owner

@jonleighton can you review?

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

Juanjo Bazán 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. Juanjo Bazán
This page is out of date. Refresh to see the latest.
2  activerecord/lib/active_record/querying.rb
View
@@ -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
6 activerecord/lib/active_record/relation.rb
View
@@ -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
13 activerecord/lib/active_record/relation/query_methods.rb
View
@@ -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
2  activerecord/test/cases/relation_test.rb
View
@@ -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
13 activerecord/test/cases/relations_test.rb
View
@@ -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.