diff --git a/lib/mongo/errors.rb b/lib/mongo/errors.rb
index 60bd370..365c2ef 100644
--- a/lib/mongo/errors.rb
+++ b/lib/mongo/errors.rb
@@ -23,4 +23,7 @@ module Mongo
# Raised when an invalid name is used.
class InvalidName < RuntimeError; end
+
+ # Raised when the client supplies an invalid values for a sorting.
+ class InvalidSortValueError < RuntimeError; end
end
diff --git a/lib/mongo/message/query_message.rb b/lib/mongo/message/query_message.rb
index b53307a..8778605 100644
--- a/lib/mongo/message/query_message.rb
+++ b/lib/mongo/message/query_message.rb
@@ -16,11 +16,12 @@
require 'mongo/message/message'
require 'mongo/message/opcodes'
+require 'mongo/util/conversions'
require 'mongo/util/ordered_hash'
module Mongo
-
class QueryMessage < Message
+ include Mongo::Conversions
attr_reader :query
@@ -37,24 +38,14 @@ module Mongo
sel = OrderedHash.new
sel['query'] = query.selector
if query.order_by && query.order_by.length > 0
- sel['orderby'] = case query.order_by
- when String
- {query.order_by => 1}
- when Array
- h = OrderedHash.new
- query.order_by.each { |ob|
- case ob
- when String
- h[ob] = 1
- when Hash # should have one entry; will handle all
- ob.each { |k,v| h[k] = v }
- else
- raise "illegal query order_by value #{query.order_by.inspect}"
- end
- }
- h
+ order_by = query.order_by
+ sel['orderby'] = case order_by
+ when String then string_as_sort_parameters(order_by)
+ when Symbol then symbol_as_sort_parameters(order_by)
+ when Array then array_as_sort_parameters(order_by)
when Hash # Should be an ordered hash, but this message doesn't care
- query.order_by
+ warn_if_deprecated(order_by)
+ order_by
else
raise "illegal order_by: is a #{query.order_by.class.name}, must be String, Array, Hash, or OrderedHash"
end
diff --git a/lib/mongo/util/conversions.rb b/lib/mongo/util/conversions.rb
new file mode 100644
index 0000000..c75fc2d
--- /dev/null
+++ b/lib/mongo/util/conversions.rb
@@ -0,0 +1,110 @@
+# --
+# Copyright (C) 2008-2009 10gen Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ++
+module Mongo #:nodoc:
+
+ # Utility module to include when needing to convert certain types of
+ # objects to mongo-friendly parameters.
+ module Conversions
+
+ ASCENDING = ["ascending", "asc", "1"]
+ DESCENDING = ["descending", "desc", "-1"]
+
+ # Converts the supplied +Array+ to a +Hash+ to pass to mongo as
+ # sorting parameters. The returned +Hash+ will vary depending
+ # on whether the passed +Array+ is one or two dimensional.
+ #
+ # Example:
+ #
+ # *DEPRECATED
+ #
+ # array_as_sort_parameters(["field1", "field2"]) =>
+ # { "field1" => "1", "field2" => "1" }
+ #
+ # *New Syntax:
+ #
+ # array_as_sort_parameters([["field1", :asc], ["field2", :desc]]) =>
+ # { "field1" => 1, "field2" => -1}
+ def array_as_sort_parameters(value)
+ warn_if_deprecated(value)
+ order_by = OrderedHash.new
+ value.each do |param|
+ if (param.class.name == "String")
+ order_by[param] = 1
+ else
+ order_by[param[0]] = sort_value(param[1]) unless param[1].nil?
+ end
+ end
+ order_by
+ end
+
+ # Converts the supplied +String+ to a +Hash+ to pass to mongo as
+ # a sorting parameter with ascending order. If the +String+
+ # is empty then an empty +Hash+ will be returned.
+ #
+ # Example:
+ #
+ # *DEPRECATED
+ #
+ # string_as_sort_parameters("field") => { "field" => 1 }
+ # string_as_sort_parameters("") => {}
+ def string_as_sort_parameters(value)
+ warn_if_deprecated(value)
+ return {} if value.empty?
+ { value => 1 }
+ end
+
+ # Converts the supplied +Symbol+ to a +Hash+ to pass to mongo as
+ # a sorting parameter with ascending order.
+ #
+ # Example:
+ #
+ # *DEPRECATED
+ #
+ # symbol_as_sort_parameters(:field) => { "field" => 1 }
+ def symbol_as_sort_parameters(value)
+ warn_if_deprecated(value)
+ { value.to_s => 1 }
+ end
+
+ # Converts the +String+, +Symbol+, or +Integer+ to the
+ # corresponding sort value in MongoDB.
+ #
+ # Valid conversions (case-insensitive):
+ #
+ # ascending, asc, :ascending, :asc, 1 => 1
+ # descending, desc, :descending, :desc, -1 => -1
+ #
+ # If the value is invalid then an error will be raised.
+ def sort_value(value)
+ val = value.to_s.downcase
+ return 1 if ASCENDING.include?(val)
+ return -1 if DESCENDING.include?(val)
+ raise InvalidSortValueError.new(
+ "#{self} was supplied as a sort value when acceptable values are: " +
+ "ascending, asc, :ascending, :asc, 1, descending, desc, :descending, :desc, -1.")
+ end
+
+ # This is the method to call when the client needs to be warned of
+ # deprecation in the way sorting parameters are supplied.
+ def warn_if_deprecated(value)
+ unless value.is_a?(Array) && value.first.is_a?(Array)
+ warn("\nSorting has been deprecated in favor of a new syntax: \n" +
+ " :sort => [['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]")
+ end
+ end
+
+ end
+end
\ No newline at end of file
diff --git a/test/test_conversions.rb b/test/test_conversions.rb
new file mode 100644
index 0000000..8d5a91f
--- /dev/null
+++ b/test/test_conversions.rb
@@ -0,0 +1,121 @@
+$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
+require 'mongo/errors'
+require 'mongo/util/conversions'
+require 'mongo/util/ordered_hash'
+require 'test/unit'
+
+class ConversionsTest < Test::Unit::TestCase
+ include Mongo::Conversions
+
+ def test_array_as_sort_parameters_with_array_of_strings
+ params = array_as_sort_parameters(["field1", "field2"])
+ assert_equal({ "field1" => 1, "field2" => 1 }, params)
+ end
+
+ def test_array_as_sort_parameters_with_array_of_string_and_values
+ params = array_as_sort_parameters([["field1", :asc], ["field2", :desc]])
+ assert_equal({ "field1" => 1, "field2" => -1 }, params)
+ end
+
+ def test_string_as_sort_parameters_with_string
+ params = string_as_sort_parameters("field")
+ assert_equal({ "field" => 1 }, params)
+ end
+
+ def test_string_as_sort_parameters_with_empty_string
+ params = string_as_sort_parameters("")
+ assert_equal({}, params)
+ end
+
+ def test_symbol_as_sort_parameters
+ params = symbol_as_sort_parameters(:field)
+ assert_equal({ "field" => 1 }, params)
+ end
+
+ def test_sort_value_when_value_is_one
+ assert_equal 1, sort_value(1)
+ end
+
+ def test_sort_value_when_value_is_one_as_a_string
+ assert_equal 1, sort_value("1")
+ end
+
+ def test_sort_value_when_value_is_negative_one
+ assert_equal -1, sort_value(-1)
+ end
+
+ def test_sort_value_when_value_is_negative_one_as_a_string
+ assert_equal -1, sort_value("-1")
+ end
+
+ def test_sort_value_when_value_is_ascending
+ assert_equal 1, sort_value("ascending")
+ end
+
+ def test_sort_value_when_value_is_asc
+ assert_equal 1, sort_value("asc")
+ end
+
+ def test_sort_value_when_value_is_uppercase_ascending
+ assert_equal 1, sort_value("ASCENDING")
+ end
+
+ def test_sort_value_when_value_is_uppercase_asc
+ assert_equal 1, sort_value("ASC")
+ end
+
+ def test_sort_value_when_value_is_symbol_ascending
+ assert_equal 1, sort_value(:ascending)
+ end
+
+ def test_sort_value_when_value_is_symbol_asc
+ assert_equal 1, sort_value(:asc)
+ end
+
+ def test_sort_value_when_value_is_symbol_uppercase_ascending
+ assert_equal 1, sort_value(:ASCENDING)
+ end
+
+ def test_sort_value_when_value_is_symbol_uppercase_asc
+ assert_equal 1, sort_value(:ASC)
+ end
+
+ def test_sort_value_when_value_is_descending
+ assert_equal -1, sort_value("descending")
+ end
+
+ def test_sort_value_when_value_is_desc
+ assert_equal -1, sort_value("desc")
+ end
+
+ def test_sort_value_when_value_is_uppercase_descending
+ assert_equal -1, sort_value("DESCENDING")
+ end
+
+ def test_sort_value_when_value_is_uppercase_desc
+ assert_equal -1, sort_value("DESC")
+ end
+
+ def test_sort_value_when_value_is_symbol_descending
+ assert_equal -1, sort_value(:descending)
+ end
+
+ def test_sort_value_when_value_is_symbol_desc
+ assert_equal -1, sort_value(:desc)
+ end
+
+ def test_sort_value_when_value_is_uppercase_symbol_descending
+ assert_equal -1, sort_value(:DESCENDING)
+ end
+
+ def test_sort_value_when_value_is_uppercase_symbol_desc
+ assert_equal -1, sort_value(:DESC)
+ end
+
+ def test_sort_value_when_value_is_invalid
+ assert_raise Mongo::InvalidSortValueError do
+ sort_value(2)
+ end
+ end
+
+end
\ No newline at end of file