Skip to content

Commit

Permalink
Added none query method to return zero records.
Browse files Browse the repository at this point in the history
And added NullRelation class implementing the null object pattern for the `Relation` class.
  • Loading branch information
xuanxu committed Jan 31, 2012
1 parent bb842e8 commit 8270e4a
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 1 deletion.
13 changes: 13 additions & 0 deletions activerecord/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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:
Expand Down
1 change: 1 addition & 0 deletions activerecord/lib/active_record.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ module ActiveRecord
autoload :AutosaveAssociation

autoload :Relation
autoload :NullRelation

autoload_under 'relation' do
autoload :QueryMethods
Expand Down
10 changes: 10 additions & 0 deletions activerecord/lib/active_record/null_relation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-

module ActiveRecord
# = Active Record Null Relation
class NullRelation < Relation
def exec_queries
@records = []
end
end
end
2 changes: 1 addition & 1 deletion activerecord/lib/active_record/querying.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
33 changes: 33 additions & 0 deletions activerecord/lib/active_record/relation/query_methods.rb
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,39 @@ def lock(locks = true)
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
Expand Down
13 changes: 13 additions & 0 deletions activerecord/test/cases/relations_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,19 @@ def test_select_with_block
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
Expand Down

0 comments on commit 8270e4a

Please sign in to comment.