From 8270e4a8ce8337fca98030953922e5992b06a3dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juanjo=20Baza=CC=81n?= Date: Mon, 30 Jan 2012 21:36:47 +0100 Subject: Added `none` query method to return zero records. And added NullRelation class implementing the null object pattern for the `Relation` class. --- activerecord/CHANGELOG.md | 13 +++++++++ activerecord/lib/active_record.rb | 1 + activerecord/lib/active_record/null_relation.rb | 10 +++++++ activerecord/lib/active_record/querying.rb | 2 +- .../lib/active_record/relation/query_methods.rb | 33 ++++++++++++++++++++++ activerecord/test/cases/relations_test.rb | 13 +++++++++ 6 files changed, 71 insertions(+), 1 deletion(-) create mode 100644 activerecord/lib/active_record/null_relation.rb 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 -- cgit v1.2.3