aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activerecord/CHANGELOG.md13
-rw-r--r--activerecord/lib/active_record.rb1
-rw-r--r--activerecord/lib/active_record/null_relation.rb10
-rw-r--r--activerecord/lib/active_record/querying.rb2
-rw-r--r--activerecord/lib/active_record/relation/query_methods.rb33
-rw-r--r--activerecord/test/cases/relations_test.rb13
6 files changed, 71 insertions, 1 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md
index e9e97f5d62..69cf1193b6 100644
--- a/activerecord/CHANGELOG.md
+++ b/activerecord/CHANGELOG.md
@@ -1,5 +1,18 @@
## Rails 4.0.0 (unreleased) ##
+* Implemented ActiveRecord::Relation#none method
+
+ The `none` method returns a chainable relation with zero records
+ (an instance of the NullRelation class).
+
+ Any subsequent condition chained to the returned relation will continue
+ generating an empty relation and will not fire any query to the database.
+
+ *Juanjo Bazán*
+
+* Added the `ActiveRecord::NullRelation` class implementing the null
+ object pattern for the Relation class. *Juanjo Bazán*
+
* Added deprecation for the `:dependent => :restrict` association option.
Please note:
diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb
index 78e958442f..73c8a06ab7 100644
--- a/activerecord/lib/active_record.rb
+++ b/activerecord/lib/active_record.rb
@@ -43,6 +43,7 @@ module ActiveRecord
autoload :AutosaveAssociation
autoload :Relation
+ autoload :NullRelation
autoload_under 'relation' do
autoload :QueryMethods
diff --git a/activerecord/lib/active_record/null_relation.rb b/activerecord/lib/active_record/null_relation.rb
new file mode 100644
index 0000000000..60c37ac2b7
--- /dev/null
+++ b/activerecord/lib/active_record/null_relation.rb
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+
+module ActiveRecord
+ # = Active Record Null Relation
+ class NullRelation < Relation
+ def exec_queries
+ @records = []
+ end
+ end
+end \ No newline at end of file
diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb
index 94e34e1bd4..c3acec0f25 100644
--- a/activerecord/lib/active_record/querying.rb
+++ b/activerecord/lib/active_record/querying.rb
@@ -8,7 +8,7 @@ module ActiveRecord
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
diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb
index a8ae7208fc..6a28d7b155 100644
--- a/activerecord/lib/active_record/relation/query_methods.rb
+++ b/activerecord/lib/active_record/relation/query_methods.rb
@@ -196,6 +196,39 @@ module ActiveRecord
relation
end
+ # Returns a chainable relation with zero records, specifically an
+ # instance of the NullRelation class.
+ #
+ # The returned NullRelation inherits from Relation and implements the
+ # Null Object pattern so it is an object with defined null behavior:
+ # it always returns an empty array of records and avoids any database query.
+ #
+ # Any subsequent condition chained to the returned relation will continue
+ # generating an empty relation and will not fire any query to the database.
+ #
+ # Used in cases where is needed a method or a scope that could return zero
+ # results but the response has to be chainable.
+ #
+ # For example:
+ #
+ # @posts = current_user.visible_posts.where(:name => params[:name])
+ # # => the visible_post method response has to be a chainable Relation
+ #
+ # def visible_posts
+ # case role
+ # if 'Country Manager'
+ # Post.where(:country => country)
+ # if 'Reviewer'
+ # Post.published
+ # if 'Bad User'
+ # Post.none # => returning [] instead breaks the previous code
+ # end
+ # end
+ #
+ def none
+ NullRelation.new(@klass, @table)
+ end
+
def readonly(value = true)
relation = clone
relation.readonly_value = value
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 5e19465253..0471d03f3b 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -215,6 +215,19 @@ class RelationTest < ActiveRecord::TestCase
assert_equal [2, 4, 6, 8, 10], even_ids.sort
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_joins_with_nil_argument
assert_nothing_raised { DependentFirm.joins(nil).first }
end