mongo 0.18 → 0.18.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +12 -4
- data/Rakefile +25 -7
- data/lib/mongo.rb +24 -15
- data/lib/mongo/admin.rb +0 -2
- data/lib/mongo/collection.rb +9 -9
- data/lib/mongo/connection.rb +5 -5
- data/lib/mongo/cursor.rb +4 -7
- data/lib/mongo/db.rb +0 -3
- data/lib/mongo/errors.rb +3 -0
- data/lib/mongo/gridfs/chunk.rb +0 -1
- data/lib/mongo/types/objectid.rb +6 -0
- data/lib/mongo/util/bson_c.rb +18 -0
- data/lib/mongo/util/{bson.rb → bson_ruby.rb} +110 -117
- data/lib/mongo/util/byte_buffer.rb +23 -0
- data/test/test_admin.rb +1 -3
- data/test/test_bson.rb +105 -70
- data/test/test_byte_buffer.rb +1 -3
- data/test/test_chunk.rb +1 -3
- data/test/test_collection.rb +11 -17
- data/test/test_connection.rb +1 -3
- data/test/test_conversions.rb +1 -2
- data/test/test_cursor.rb +1 -3
- data/test/test_db.rb +1 -3
- data/test/test_db_api.rb +1 -3
- data/test/test_db_connection.rb +1 -3
- data/test/test_grid_store.rb +1 -3
- data/test/test_helper.rb +5 -3
- data/test/test_objectid.rb +7 -3
- data/test/test_ordered_hash.rb +1 -3
- data/test/test_round_trip.rb +8 -14
- data/test/test_slave_connection.rb +1 -3
- data/test/unit/connection_test.rb +17 -1
- metadata +6 -7
    
        data/README.rdoc
    CHANGED
    
    | @@ -254,16 +254,23 @@ Random cursor fun facts: | |
| 254 254 |  | 
| 255 255 | 
             
            = Testing
         | 
| 256 256 |  | 
| 257 | 
            -
            If you have the source code, you can run the tests.
         | 
| 257 | 
            +
            If you have the source code, you can run the tests. There's a separate rake task for testing with 
         | 
| 258 | 
            +
            the mongo_ext c extension enabled.
         | 
| 258 259 |  | 
| 259 | 
            -
              $ rake test
         | 
| 260 | 
            +
              $ rake test:c
         | 
| 260 261 |  | 
| 261 | 
            -
             | 
| 262 | 
            -
             | 
| 262 | 
            +
            Or, to test without the extension:
         | 
| 263 | 
            +
             | 
| 264 | 
            +
              $ rake test:ruby
         | 
| 265 | 
            +
             | 
| 266 | 
            +
            These will run both unit and functional tests. To run these tests alone:
         | 
| 263 267 |  | 
| 264 268 | 
             
              $ rake test:unit
         | 
| 265 269 | 
             
              $ rake test:functional
         | 
| 266 270 |  | 
| 271 | 
            +
            To run any individual rake tasks with the C extenson enabled, just pass C_EXT=true to the task:
         | 
| 272 | 
            +
             | 
| 273 | 
            +
              $ rake test:unit C_EXT=true
         | 
| 267 274 |  | 
| 268 275 | 
             
            If you want to test replica pairs, you can run the following tests
         | 
| 269 276 | 
             
            individually:
         | 
| @@ -276,6 +283,7 @@ It's also possible to test replica pairs with connection pooling: | |
| 276 283 |  | 
| 277 284 | 
             
              $ rake test:pooled_pair_insert
         | 
| 278 285 |  | 
| 286 | 
            +
            ===Shoulda and Mocha
         | 
| 279 287 |  | 
| 280 288 | 
             
            All tests now require shoulda and mocha.  You can install these gems as
         | 
| 281 289 | 
             
            follows:
         | 
    
        data/Rakefile
    CHANGED
    
    | @@ -10,19 +10,37 @@ rescue LoadError | |
| 10 10 | 
             
            end
         | 
| 11 11 | 
             
            require 'rbconfig'
         | 
| 12 12 | 
             
            include Config
         | 
| 13 | 
            +
            ENV['TEST_MODE'] = 'TRUE'
         | 
| 13 14 |  | 
| 14 15 | 
             
            gem_command = "gem"
         | 
| 15 16 | 
             
            gem_command = "gem1.9" if $0.match(/1\.9$/) # use gem1.9 if we used rake1.9
         | 
| 16 17 |  | 
| 17 | 
            -
            # NOTE: the functional tests assume MongoDB is running.
         | 
| 18 18 | 
             
            desc "Test the MongoDB Ruby driver."
         | 
| 19 19 | 
             
            task :test do
         | 
| 20 | 
            -
               | 
| 21 | 
            -
               | 
| 22 | 
            -
               | 
| 20 | 
            +
              puts "\nThis option has changed."
         | 
| 21 | 
            +
              puts "\nTo test the driver with the c-extensions:\nrake test:c\n"
         | 
| 22 | 
            +
              puts "To test the pure ruby driver: \nrake test:ruby"
         | 
| 23 23 | 
             
            end
         | 
| 24 24 |  | 
| 25 | 
            -
            namespace :test do | 
| 25 | 
            +
            namespace :test do
         | 
| 26 | 
            +
             | 
| 27 | 
            +
              desc "Test the driver with the c extension enabled."
         | 
| 28 | 
            +
              task :c do
         | 
| 29 | 
            +
                ENV['C_EXT'] = 'TRUE'
         | 
| 30 | 
            +
                Rake::Task['test:unit'].invoke
         | 
| 31 | 
            +
                Rake::Task['test:functional'].invoke
         | 
| 32 | 
            +
                Rake::Task['test:pooled_threading'].invoke
         | 
| 33 | 
            +
                ENV['C_EXT'] = nil
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              desc "Test the driver using pure ruby (no c extension)"
         | 
| 37 | 
            +
              task :ruby do
         | 
| 38 | 
            +
                ENV['C_EXT'] = nil
         | 
| 39 | 
            +
                Rake::Task['test:unit'].invoke
         | 
| 40 | 
            +
                Rake::Task['test:functional'].invoke
         | 
| 41 | 
            +
                Rake::Task['test:pooled_threading'].invoke
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
             | 
| 26 44 | 
             
              Rake::TestTask.new(:unit) do |t|
         | 
| 27 45 | 
             
                t.test_files = FileList['test/unit/*_test.rb']
         | 
| 28 46 | 
             
                t.verbose    = true
         | 
| @@ -79,7 +97,7 @@ namespace :gem do | |
| 79 97 | 
             
              task :install do
         | 
| 80 98 | 
             
                sh <<EOS
         | 
| 81 99 | 
             
            #{gem_command} build mongo-ruby-driver.gemspec &&
         | 
| 82 | 
            -
                 | 
| 100 | 
            +
                #{gem_command} install mongo-*.gem &&
         | 
| 83 101 | 
             
                rm mongo-*.gem
         | 
| 84 102 | 
             
            EOS
         | 
| 85 103 | 
             
              end
         | 
| @@ -88,7 +106,7 @@ EOS | |
| 88 106 | 
             
              task :install_extensions do
         | 
| 89 107 | 
             
                sh <<EOS
         | 
| 90 108 | 
             
            #{gem_command} build mongo-extensions.gemspec &&
         | 
| 91 | 
            -
                 | 
| 109 | 
            +
                #{gem_command} install mongo_ext-*.gem &&
         | 
| 92 110 | 
             
                rm mongo_ext-*.gem
         | 
| 93 111 | 
             
            EOS
         | 
| 94 112 | 
             
              end
         | 
    
        data/lib/mongo.rb
    CHANGED
    
    | @@ -1,5 +1,28 @@ | |
| 1 1 | 
             
            $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
         | 
| 2 2 |  | 
| 3 | 
            +
            module Mongo
         | 
| 4 | 
            +
              ASCENDING = 1
         | 
| 5 | 
            +
              DESCENDING = -1
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              VERSION = "0.18.1"
         | 
| 8 | 
            +
            end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            begin
         | 
| 11 | 
            +
                # Need this for running test with and without c ext in Ruby 1.9.
         | 
| 12 | 
            +
                raise LoadError if ENV['TEST_MODE'] && !ENV['C_EXT']
         | 
| 13 | 
            +
                require 'mongo_ext/cbson'
         | 
| 14 | 
            +
                raise LoadError unless defined?(CBson::VERSION) && CBson::VERSION == Mongo::VERSION
         | 
| 15 | 
            +
                require 'mongo/util/bson_c'
         | 
| 16 | 
            +
                BSON            = BSON_C
         | 
| 17 | 
            +
              rescue LoadError
         | 
| 18 | 
            +
                require 'mongo/util/bson_ruby'
         | 
| 19 | 
            +
                BSON            = BSON_RUBY
         | 
| 20 | 
            +
                warn "\n**Notice: C extension not loaded. This is required for optimum MongoDB Ruby driver performance."
         | 
| 21 | 
            +
                warn "  You can install the extension as follows:\n  gem install mongo_ext\n"
         | 
| 22 | 
            +
                warn "  If you continue to receive this message after installing, make sure that the"
         | 
| 23 | 
            +
                warn "  mongo_ext gem is in your load path and that the mongo_ext and mongo gems are of the same version.\n"
         | 
| 24 | 
            +
            end 
         | 
| 25 | 
            +
             | 
| 3 26 | 
             
            require 'mongo/types/binary'
         | 
| 4 27 | 
             
            require 'mongo/types/code'
         | 
| 5 28 | 
             
            require 'mongo/types/dbref'
         | 
| @@ -9,6 +32,7 @@ require 'mongo/types/regexp_of_holding' | |
| 9 32 | 
             
            require 'mongo/util/support'
         | 
| 10 33 | 
             
            require 'mongo/util/conversions'
         | 
| 11 34 | 
             
            require 'mongo/util/server_version'
         | 
| 35 | 
            +
            require 'mongo/util/bson_ruby'
         | 
| 12 36 |  | 
| 13 37 | 
             
            require 'mongo/errors'
         | 
| 14 38 | 
             
            require 'mongo/constants'
         | 
| @@ -18,18 +42,3 @@ require 'mongo/cursor' | |
| 18 42 | 
             
            require 'mongo/collection'
         | 
| 19 43 | 
             
            require 'mongo/admin'
         | 
| 20 44 |  | 
| 21 | 
            -
            begin
         | 
| 22 | 
            -
              require 'mongo_ext/cbson'
         | 
| 23 | 
            -
                BSON_SERIALIZER = CBson
         | 
| 24 | 
            -
              rescue LoadError
         | 
| 25 | 
            -
                BSON_SERIALIZER = BSON
         | 
| 26 | 
            -
                warn "\n**Notice: C extension not detected. This is required for optimum MongoDB Ruby driver performance."
         | 
| 27 | 
            -
                warn "  You can install the extension as follows:\n  gem install mongo_ext\n"
         | 
| 28 | 
            -
            end
         | 
| 29 | 
            -
             | 
| 30 | 
            -
            module Mongo
         | 
| 31 | 
            -
              ASCENDING = 1
         | 
| 32 | 
            -
              DESCENDING = -1
         | 
| 33 | 
            -
             | 
| 34 | 
            -
              VERSION = "0.18"
         | 
| 35 | 
            -
            end
         | 
    
        data/lib/mongo/admin.rb
    CHANGED
    
    
    
        data/lib/mongo/collection.rb
    CHANGED
    
    | @@ -33,8 +33,8 @@ module Mongo | |
| 33 33 | 
             
                  if name.empty? or name.include? ".."
         | 
| 34 34 | 
             
                    raise InvalidName, "collection names cannot be empty"
         | 
| 35 35 | 
             
                  end
         | 
| 36 | 
            -
                  if name.include? "$" | 
| 37 | 
            -
                    raise InvalidName, "collection names must not contain '$'"
         | 
| 36 | 
            +
                  if name.include? "$"
         | 
| 37 | 
            +
                    raise InvalidName, "collection names must not contain '$'" unless name =~ /((^\$cmd)|(oplog\.\$main))/
         | 
| 38 38 | 
             
                  end
         | 
| 39 39 | 
             
                  if name.match(/^\./) or name.match(/\.$/)
         | 
| 40 40 | 
             
                    raise InvalidName, "collection names must not start or end with '.'"
         | 
| @@ -216,9 +216,9 @@ module Mongo | |
| 216 216 | 
             
                def remove(selector={})
         | 
| 217 217 | 
             
                  message = ByteBuffer.new
         | 
| 218 218 | 
             
                  message.put_int(0)
         | 
| 219 | 
            -
                   | 
| 219 | 
            +
                  BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{@name}")
         | 
| 220 220 | 
             
                  message.put_int(0)
         | 
| 221 | 
            -
                  message.put_array( | 
| 221 | 
            +
                  message.put_array(BSON.serialize(selector, false).unpack("C*"))
         | 
| 222 222 | 
             
                  @connection.send_message(Mongo::Constants::OP_DELETE, message,
         | 
| 223 223 | 
             
                    "db.#{@db.name}.remove(#{selector.inspect})")
         | 
| 224 224 | 
             
                end
         | 
| @@ -243,13 +243,13 @@ module Mongo | |
| 243 243 | 
             
                def update(selector, document, options={})
         | 
| 244 244 | 
             
                  message = ByteBuffer.new
         | 
| 245 245 | 
             
                  message.put_int(0)
         | 
| 246 | 
            -
                   | 
| 246 | 
            +
                  BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{@name}")
         | 
| 247 247 | 
             
                  update_options  = 0
         | 
| 248 248 | 
             
                  update_options += 1 if options[:upsert]
         | 
| 249 249 | 
             
                  update_options += 2 if options[:multi]
         | 
| 250 250 | 
             
                  message.put_int(update_options)
         | 
| 251 | 
            -
                  message.put_array( | 
| 252 | 
            -
                  message.put_array( | 
| 251 | 
            +
                  message.put_array(BSON.serialize(selector, false).unpack("C*"))
         | 
| 252 | 
            +
                  message.put_array(BSON.serialize(document, false).unpack("C*"))
         | 
| 253 253 | 
             
                  if options[:safe]
         | 
| 254 254 | 
             
                    @connection.send_message_with_safe_check(Mongo::Constants::OP_UPDATE, message, @db.name,
         | 
| 255 255 | 
             
                      "db.#{@name}.update(#{selector.inspect}, #{document.inspect})")
         | 
| @@ -507,8 +507,8 @@ EOS | |
| 507 507 | 
             
                def insert_documents(documents, collection_name=@name, check_keys=true, safe=false)
         | 
| 508 508 | 
             
                  message = ByteBuffer.new
         | 
| 509 509 | 
             
                  message.put_int(0)
         | 
| 510 | 
            -
                   | 
| 511 | 
            -
                  documents.each { |doc| message.put_array( | 
| 510 | 
            +
                  BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{collection_name}")
         | 
| 511 | 
            +
                  documents.each { |doc| message.put_array(BSON.serialize(doc, check_keys).unpack("C*")) }
         | 
| 512 512 | 
             
                  if safe
         | 
| 513 513 | 
             
                    @connection.send_message_with_safe_check(Mongo::Constants::OP_INSERT, message, @db.name,
         | 
| 514 514 | 
             
                      "db.#{collection_name}.insert(#{documents.inspect})")
         | 
    
        data/lib/mongo/connection.rb
    CHANGED
    
    | @@ -96,7 +96,7 @@ module Mongo | |
| 96 96 | 
             
                #                  :right => ["db2.example.com", 27017]}, nil,
         | 
| 97 97 | 
             
                #                  :pool_size => 20, :timeout => 5)
         | 
| 98 98 | 
             
                def initialize(pair_or_host=nil, port=nil, options={})
         | 
| 99 | 
            -
                  @nodes = format_pair(pair_or_host)
         | 
| 99 | 
            +
                  @nodes = format_pair(pair_or_host, port)
         | 
| 100 100 |  | 
| 101 101 | 
             
                  # Host and port of current master.
         | 
| 102 102 | 
             
                  @host = @port = nil
         | 
| @@ -459,7 +459,7 @@ module Mongo | |
| 459 459 | 
             
                    buf.put_array(receive_message_on_socket(size - 4, sock).unpack("C*"), 4)
         | 
| 460 460 | 
             
                    number_remaining -= 1
         | 
| 461 461 | 
             
                    buf.rewind
         | 
| 462 | 
            -
                    docs << BSON. | 
| 462 | 
            +
                    docs << BSON.deserialize(buf)
         | 
| 463 463 | 
             
                  end
         | 
| 464 464 | 
             
                  [docs, number_received, cursor_id]
         | 
| 465 465 | 
             
                end
         | 
| @@ -467,10 +467,10 @@ module Mongo | |
| 467 467 | 
             
                def last_error_message(db_name)
         | 
| 468 468 | 
             
                  message = ByteBuffer.new
         | 
| 469 469 | 
             
                  message.put_int(0)
         | 
| 470 | 
            -
                   | 
| 470 | 
            +
                  BSON_RUBY.serialize_cstr(message, "#{db_name}.$cmd")
         | 
| 471 471 | 
             
                  message.put_int(0)
         | 
| 472 472 | 
             
                  message.put_int(-1)
         | 
| 473 | 
            -
                  message.put_array( | 
| 473 | 
            +
                  message.put_array(BSON.serialize({:getlasterror => 1}, false).unpack("C*"))
         | 
| 474 474 | 
             
                  add_message_headers(Mongo::Constants::OP_QUERY, message)
         | 
| 475 475 | 
             
                end
         | 
| 476 476 |  | 
| @@ -524,7 +524,7 @@ module Mongo | |
| 524 524 | 
             
                ## Private helper methods
         | 
| 525 525 |  | 
| 526 526 | 
             
                # Returns an array of host-port pairs.
         | 
| 527 | 
            -
                def format_pair(pair_or_host)
         | 
| 527 | 
            +
                def format_pair(pair_or_host, port)
         | 
| 528 528 | 
             
                  case pair_or_host
         | 
| 529 529 | 
             
                    when String
         | 
| 530 530 | 
             
                      [[pair_or_host, port ? port.to_i : DEFAULT_PORT]]
         | 
    
        data/lib/mongo/cursor.rb
    CHANGED
    
    | @@ -12,9 +12,6 @@ | |
| 12 12 | 
             
            # See the License for the specific language governing permissions and
         | 
| 13 13 | 
             
            # limitations under the License.
         | 
| 14 14 |  | 
| 15 | 
            -
            require 'mongo/util/byte_buffer'
         | 
| 16 | 
            -
            require 'mongo/util/bson'
         | 
| 17 | 
            -
             | 
| 18 15 | 
             
            module Mongo
         | 
| 19 16 |  | 
| 20 17 | 
             
              # A cursor over query results. Returned objects are hashes.
         | 
| @@ -289,7 +286,7 @@ module Mongo | |
| 289 286 |  | 
| 290 287 | 
             
                  # DB name.
         | 
| 291 288 | 
             
                  db_name = @admin ? 'admin' : @db.name
         | 
| 292 | 
            -
                   | 
| 289 | 
            +
                  BSON_RUBY.serialize_cstr(message, "#{db_name}.#{@collection.name}")
         | 
| 293 290 |  | 
| 294 291 | 
             
                  # Number of results to return; db decides for now.
         | 
| 295 292 | 
             
                  message.put_int(0)
         | 
| @@ -320,15 +317,15 @@ module Mongo | |
| 320 317 | 
             
                  message = ByteBuffer.new
         | 
| 321 318 | 
             
                  message.put_int(query_opts)
         | 
| 322 319 | 
             
                  db_name = @admin ? 'admin' : @db.name
         | 
| 323 | 
            -
                   | 
| 320 | 
            +
                  BSON_RUBY.serialize_cstr(message, "#{db_name}.#{@collection.name}")
         | 
| 324 321 | 
             
                  message.put_int(@skip)
         | 
| 325 322 | 
             
                  message.put_int(@limit)
         | 
| 326 323 | 
             
                  selector = @selector
         | 
| 327 324 | 
             
                  if query_contains_special_fields?
         | 
| 328 325 | 
             
                    selector = selector_with_special_query_fields
         | 
| 329 326 | 
             
                  end
         | 
| 330 | 
            -
                  message.put_array( | 
| 331 | 
            -
                  message.put_array( | 
| 327 | 
            +
                  message.put_array(BSON.serialize(selector, false).unpack("C*"))
         | 
| 328 | 
            +
                  message.put_array(BSON.serialize(@fields, false).unpack("C*")) if @fields
         | 
| 332 329 | 
             
                  message
         | 
| 333 330 | 
             
                end
         | 
| 334 331 |  | 
    
        data/lib/mongo/db.rb
    CHANGED
    
    
    
        data/lib/mongo/errors.rb
    CHANGED
    
    | @@ -27,6 +27,9 @@ module Mongo | |
| 27 27 | 
             
              # Raised when invalid arguments are sent to Mongo Ruby methods.
         | 
| 28 28 | 
             
              class MongoArgumentError < MongoRubyError; end
         | 
| 29 29 |  | 
| 30 | 
            +
              # Raised when given a string is not valid utf-8 (Ruby 1.8 only).
         | 
| 31 | 
            +
              class InvalidStringEncoding < MongoRubyError; end
         | 
| 32 | 
            +
             | 
| 30 33 | 
             
              # Raised on failures in connection to the database server.
         | 
| 31 34 | 
             
              class ConnectionError < MongoRubyError; end
         | 
| 32 35 |  | 
    
        data/lib/mongo/gridfs/chunk.rb
    CHANGED
    
    
    
        data/lib/mongo/types/objectid.rb
    CHANGED
    
    | @@ -124,6 +124,12 @@ module Mongo | |
| 124 124 | 
             
                  legacy
         | 
| 125 125 | 
             
                end
         | 
| 126 126 |  | 
| 127 | 
            +
                # Returns the utc time at which this ObjectID was generated. This may
         | 
| 128 | 
            +
                # be used in lieu of a created_at timestamp.
         | 
| 129 | 
            +
                def generation_time
         | 
| 130 | 
            +
                  Time.at(@data.pack("C4").unpack("N")[0])
         | 
| 131 | 
            +
                end
         | 
| 132 | 
            +
             | 
| 127 133 | 
             
                private
         | 
| 128 134 |  | 
| 129 135 | 
             
                begin
         | 
| @@ -0,0 +1,18 @@ | |
| 1 | 
            +
            # A thin wrapper for the CBson class
         | 
| 2 | 
            +
            class BSON_C
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              def self.serialize(obj, check_keys=false)
         | 
| 5 | 
            +
                ByteBuffer.new(CBson.serialize(obj, check_keys))
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              def self.deserialize(buf=nil)
         | 
| 9 | 
            +
                if buf.is_a? String
         | 
| 10 | 
            +
                  to_deserialize = ByteBuffer.new(buf) if buf
         | 
| 11 | 
            +
                else
         | 
| 12 | 
            +
                  buf = ByteBuffer.new(buf.to_a) if buf
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
                buf.rewind
         | 
| 15 | 
            +
                CBson.deserialize(buf.to_s)
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
            end
         | 
| @@ -22,8 +22,8 @@ require 'mongo/types/dbref' | |
| 22 22 | 
             
            require 'mongo/types/objectid'
         | 
| 23 23 | 
             
            require 'mongo/types/regexp_of_holding'
         | 
| 24 24 |  | 
| 25 | 
            -
            # A BSON seralizer/deserializer.
         | 
| 26 | 
            -
            class  | 
| 25 | 
            +
            # A BSON seralizer/deserializer in pure Ruby.
         | 
| 26 | 
            +
            class BSON_RUBY
         | 
| 27 27 |  | 
| 28 28 | 
             
              include Mongo
         | 
| 29 29 |  | 
| @@ -49,64 +49,70 @@ class BSON | |
| 49 49 | 
             
              NUMBER_LONG = 18
         | 
| 50 50 | 
             
              MAXKEY = 127
         | 
| 51 51 |  | 
| 52 | 
            +
              def initialize
         | 
| 53 | 
            +
                @buf = ByteBuffer.new
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
             | 
| 52 56 | 
             
              if RUBY_VERSION >= '1.9'
         | 
| 53 57 | 
             
                def self.to_utf8(str)
         | 
| 54 58 | 
             
                  str.encode("utf-8")
         | 
| 55 59 | 
             
                end
         | 
| 56 60 | 
             
              else
         | 
| 57 61 | 
             
                def self.to_utf8(str)
         | 
| 58 | 
            -
                   | 
| 62 | 
            +
                  begin
         | 
| 63 | 
            +
                  str.unpack("U*")
         | 
| 64 | 
            +
                  rescue => ex
         | 
| 65 | 
            +
                    raise InvalidStringEncoding, "String not valid utf-8: #{str}"
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
                  str
         | 
| 59 68 | 
             
                end
         | 
| 60 69 | 
             
              end
         | 
| 61 70 |  | 
| 62 71 | 
             
              def self.serialize_cstr(buf, val)
         | 
| 63 | 
            -
                buf.put_array(to_utf8(val.to_s).unpack("C*")  | 
| 64 | 
            -
              end
         | 
| 65 | 
            -
             | 
| 66 | 
            -
              def initialize()
         | 
| 67 | 
            -
                @buf = ByteBuffer.new
         | 
| 72 | 
            +
                buf.put_array(to_utf8(val.to_s).unpack("C*") << 0)
         | 
| 68 73 | 
             
              end
         | 
| 69 74 |  | 
| 70 75 | 
             
              def to_a
         | 
| 71 76 | 
             
                @buf.to_a
         | 
| 72 77 | 
             
              end
         | 
| 73 78 |  | 
| 79 | 
            +
              def to_s
         | 
| 80 | 
            +
                @buf.to_s
         | 
| 81 | 
            +
              end
         | 
| 82 | 
            +
             | 
| 74 83 | 
             
              # Serializes an object.
         | 
| 75 | 
            -
              # Implemented to ensure an API compatible with BSON extension. | 
| 76 | 
            -
              def self.serialize(obj, check_keys)
         | 
| 84 | 
            +
              # Implemented to ensure an API compatible with BSON extension.
         | 
| 85 | 
            +
              def self.serialize(obj, check_keys=false)
         | 
| 77 86 | 
             
                new.serialize(obj, check_keys)
         | 
| 78 87 | 
             
              end
         | 
| 79 88 |  | 
| 80 | 
            -
               | 
| 81 | 
            -
                 | 
| 82 | 
            -
             | 
| 83 | 
            -
             | 
| 84 | 
            -
             | 
| 85 | 
            -
             | 
| 86 | 
            -
                def serialize(obj, check_keys=false)
         | 
| 87 | 
            -
                  raise "Document is null" unless obj
         | 
| 88 | 
            -
             | 
| 89 | 
            -
                  @buf.rewind
         | 
| 90 | 
            -
                  # put in a placeholder for the total size
         | 
| 91 | 
            -
                  @buf.put_int(0)
         | 
| 92 | 
            -
             | 
| 93 | 
            -
                  # Write key/value pairs. Always write _id first if it exists.
         | 
| 94 | 
            -
                  if obj.has_key? '_id'
         | 
| 95 | 
            -
                    serialize_key_value('_id', obj['_id'], check_keys)
         | 
| 96 | 
            -
                  elsif obj.has_key? :_id
         | 
| 97 | 
            -
                    serialize_key_value('_id', obj[:_id], check_keys)
         | 
| 98 | 
            -
                  end
         | 
| 89 | 
            +
              def self.deserialize(buf=nil)
         | 
| 90 | 
            +
                new.deserialize(buf)
         | 
| 91 | 
            +
              end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
              def serialize(obj, check_keys=false)
         | 
| 94 | 
            +
                raise "Document is null" unless obj
         | 
| 99 95 |  | 
| 100 | 
            -
             | 
| 96 | 
            +
                @buf.rewind
         | 
| 97 | 
            +
                # put in a placeholder for the total size
         | 
| 98 | 
            +
                @buf.put_int(0)
         | 
| 101 99 |  | 
| 102 | 
            -
             | 
| 103 | 
            -
             | 
| 104 | 
            -
                   | 
| 100 | 
            +
                # Write key/value pairs. Always write _id first if it exists.
         | 
| 101 | 
            +
                if obj.has_key? '_id'
         | 
| 102 | 
            +
                  serialize_key_value('_id', obj['_id'], check_keys)
         | 
| 103 | 
            +
                elsif obj.has_key? :_id
         | 
| 104 | 
            +
                  serialize_key_value('_id', obj[:_id], check_keys)
         | 
| 105 105 | 
             
                end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                obj.each {|k, v| serialize_key_value(k, v, check_keys) unless k == '_id' || k == :_id }
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                serialize_eoo_element(@buf)
         | 
| 110 | 
            +
                @buf.put_int(@buf.size, 0)
         | 
| 111 | 
            +
                self
         | 
| 106 112 | 
             
              end
         | 
| 107 113 |  | 
| 108 114 | 
             
              # Returns the array stored in the buffer.
         | 
| 109 | 
            -
              # Implemented to ensure an API compatible with BSON extension. | 
| 115 | 
            +
              # Implemented to ensure an API compatible with BSON extension.
         | 
| 110 116 | 
             
              def unpack(arg)
         | 
| 111 117 | 
             
                @buf.to_a
         | 
| 112 118 | 
             
              end
         | 
| @@ -154,93 +160,80 @@ class BSON | |
| 154 160 | 
             
                end
         | 
| 155 161 | 
             
              end
         | 
| 156 162 |  | 
| 157 | 
            -
               | 
| 158 | 
            -
                 | 
| 159 | 
            -
                 | 
| 160 | 
            -
             | 
| 161 | 
            -
             | 
| 162 | 
            -
             | 
| 163 | 
            -
             | 
| 164 | 
            -
                  end
         | 
| 165 | 
            -
                  @buf.rewind
         | 
| 166 | 
            -
                  CBson.deserialize(@buf.to_s)
         | 
| 163 | 
            +
              def deserialize(buf=nil)
         | 
| 164 | 
            +
                # If buf is nil, use @buf, assumed to contain already-serialized BSON.
         | 
| 165 | 
            +
                # This is only true during testing.
         | 
| 166 | 
            +
                if buf.is_a? String
         | 
| 167 | 
            +
                  @buf = ByteBuffer.new(buf) if buf
         | 
| 168 | 
            +
                else
         | 
| 169 | 
            +
                  @buf = ByteBuffer.new(buf.to_a) if buf
         | 
| 167 170 | 
             
                end
         | 
| 168 | 
            -
             | 
| 169 | 
            -
                 | 
| 170 | 
            -
             | 
| 171 | 
            -
             | 
| 172 | 
            -
                   | 
| 173 | 
            -
             | 
| 171 | 
            +
                @buf.rewind
         | 
| 172 | 
            +
                @buf.get_int                # eat message size
         | 
| 173 | 
            +
                doc = OrderedHash.new
         | 
| 174 | 
            +
                while @buf.more?
         | 
| 175 | 
            +
                  type = @buf.get
         | 
| 176 | 
            +
                  case type
         | 
| 177 | 
            +
                  when STRING, CODE
         | 
| 178 | 
            +
                    key = deserialize_cstr(@buf)
         | 
| 179 | 
            +
                    doc[key] = deserialize_string_data(@buf)
         | 
| 180 | 
            +
                  when SYMBOL
         | 
| 181 | 
            +
                    key = deserialize_cstr(@buf)
         | 
| 182 | 
            +
                    doc[key] = deserialize_string_data(@buf).intern
         | 
| 183 | 
            +
                  when NUMBER
         | 
| 184 | 
            +
                    key = deserialize_cstr(@buf)
         | 
| 185 | 
            +
                    doc[key] = deserialize_number_data(@buf)
         | 
| 186 | 
            +
                  when NUMBER_INT
         | 
| 187 | 
            +
                    key = deserialize_cstr(@buf)
         | 
| 188 | 
            +
                    doc[key] = deserialize_number_int_data(@buf)
         | 
| 189 | 
            +
                  when NUMBER_LONG
         | 
| 190 | 
            +
                    key = deserialize_cstr(@buf)
         | 
| 191 | 
            +
                    doc[key] = deserialize_number_long_data(@buf)
         | 
| 192 | 
            +
                  when OID
         | 
| 193 | 
            +
                    key = deserialize_cstr(@buf)
         | 
| 194 | 
            +
                    doc[key] = deserialize_oid_data(@buf)
         | 
| 195 | 
            +
                  when ARRAY
         | 
| 196 | 
            +
                    key = deserialize_cstr(@buf)
         | 
| 197 | 
            +
                    doc[key] = deserialize_array_data(@buf)
         | 
| 198 | 
            +
                  when REGEX
         | 
| 199 | 
            +
                    key = deserialize_cstr(@buf)
         | 
| 200 | 
            +
                    doc[key] = deserialize_regex_data(@buf)
         | 
| 201 | 
            +
                  when OBJECT
         | 
| 202 | 
            +
                    key = deserialize_cstr(@buf)
         | 
| 203 | 
            +
                    doc[key] = deserialize_object_data(@buf)
         | 
| 204 | 
            +
                  when BOOLEAN
         | 
| 205 | 
            +
                    key = deserialize_cstr(@buf)
         | 
| 206 | 
            +
                    doc[key] = deserialize_boolean_data(@buf)
         | 
| 207 | 
            +
                  when DATE
         | 
| 208 | 
            +
                    key = deserialize_cstr(@buf)
         | 
| 209 | 
            +
                    doc[key] = deserialize_date_data(@buf)
         | 
| 210 | 
            +
                  when NULL
         | 
| 211 | 
            +
                    key = deserialize_cstr(@buf)
         | 
| 212 | 
            +
                    doc[key] = nil
         | 
| 213 | 
            +
                  when UNDEFINED
         | 
| 214 | 
            +
                    key = deserialize_cstr(@buf)
         | 
| 215 | 
            +
                    doc[key] = nil
         | 
| 216 | 
            +
                  when REF
         | 
| 217 | 
            +
                    key = deserialize_cstr(@buf)
         | 
| 218 | 
            +
                    doc[key] = deserialize_dbref_data(@buf)
         | 
| 219 | 
            +
                  when BINARY
         | 
| 220 | 
            +
                    key = deserialize_cstr(@buf)
         | 
| 221 | 
            +
                    doc[key] = deserialize_binary_data(@buf)
         | 
| 222 | 
            +
                  when CODE_W_SCOPE
         | 
| 223 | 
            +
                    key = deserialize_cstr(@buf)
         | 
| 224 | 
            +
                    doc[key] = deserialize_code_w_scope_data(@buf)
         | 
| 225 | 
            +
                  when TIMESTAMP
         | 
| 226 | 
            +
                    key = deserialize_cstr(@buf)
         | 
| 227 | 
            +
                    doc[key] = [deserialize_number_int_data(@buf),
         | 
| 228 | 
            +
                                deserialize_number_int_data(@buf)]
         | 
| 229 | 
            +
                  when EOO
         | 
| 230 | 
            +
                    break
         | 
| 174 231 | 
             
                  else
         | 
| 175 | 
            -
                     | 
| 176 | 
            -
                  end
         | 
| 177 | 
            -
                  @buf.rewind
         | 
| 178 | 
            -
                  @buf.get_int                # eat message size
         | 
| 179 | 
            -
                  doc = OrderedHash.new
         | 
| 180 | 
            -
                  while @buf.more?
         | 
| 181 | 
            -
                    type = @buf.get
         | 
| 182 | 
            -
                    case type
         | 
| 183 | 
            -
                    when STRING, CODE
         | 
| 184 | 
            -
                      key = deserialize_cstr(@buf)
         | 
| 185 | 
            -
                      doc[key] = deserialize_string_data(@buf)
         | 
| 186 | 
            -
                    when SYMBOL
         | 
| 187 | 
            -
                      key = deserialize_cstr(@buf)
         | 
| 188 | 
            -
                      doc[key] = deserialize_string_data(@buf).intern
         | 
| 189 | 
            -
                    when NUMBER
         | 
| 190 | 
            -
                      key = deserialize_cstr(@buf)
         | 
| 191 | 
            -
                      doc[key] = deserialize_number_data(@buf)
         | 
| 192 | 
            -
                    when NUMBER_INT
         | 
| 193 | 
            -
                      key = deserialize_cstr(@buf)
         | 
| 194 | 
            -
                      doc[key] = deserialize_number_int_data(@buf)
         | 
| 195 | 
            -
                    when NUMBER_LONG
         | 
| 196 | 
            -
                      key = deserialize_cstr(@buf)
         | 
| 197 | 
            -
                      doc[key] = deserialize_number_long_data(@buf)
         | 
| 198 | 
            -
                    when OID
         | 
| 199 | 
            -
                      key = deserialize_cstr(@buf)
         | 
| 200 | 
            -
                      doc[key] = deserialize_oid_data(@buf)
         | 
| 201 | 
            -
                    when ARRAY
         | 
| 202 | 
            -
                      key = deserialize_cstr(@buf)
         | 
| 203 | 
            -
                      doc[key] = deserialize_array_data(@buf)
         | 
| 204 | 
            -
                    when REGEX
         | 
| 205 | 
            -
                      key = deserialize_cstr(@buf)
         | 
| 206 | 
            -
                      doc[key] = deserialize_regex_data(@buf)
         | 
| 207 | 
            -
                    when OBJECT
         | 
| 208 | 
            -
                      key = deserialize_cstr(@buf)
         | 
| 209 | 
            -
                      doc[key] = deserialize_object_data(@buf)
         | 
| 210 | 
            -
                    when BOOLEAN
         | 
| 211 | 
            -
                      key = deserialize_cstr(@buf)
         | 
| 212 | 
            -
                      doc[key] = deserialize_boolean_data(@buf)
         | 
| 213 | 
            -
                    when DATE
         | 
| 214 | 
            -
                      key = deserialize_cstr(@buf)
         | 
| 215 | 
            -
                      doc[key] = deserialize_date_data(@buf)
         | 
| 216 | 
            -
                    when NULL
         | 
| 217 | 
            -
                      key = deserialize_cstr(@buf)
         | 
| 218 | 
            -
                      doc[key] = nil
         | 
| 219 | 
            -
                    when UNDEFINED
         | 
| 220 | 
            -
                      key = deserialize_cstr(@buf)
         | 
| 221 | 
            -
                      doc[key] = nil
         | 
| 222 | 
            -
                    when REF
         | 
| 223 | 
            -
                      key = deserialize_cstr(@buf)
         | 
| 224 | 
            -
                      doc[key] = deserialize_dbref_data(@buf)
         | 
| 225 | 
            -
                    when BINARY
         | 
| 226 | 
            -
                      key = deserialize_cstr(@buf)
         | 
| 227 | 
            -
                      doc[key] = deserialize_binary_data(@buf)
         | 
| 228 | 
            -
                    when CODE_W_SCOPE
         | 
| 229 | 
            -
                      key = deserialize_cstr(@buf)
         | 
| 230 | 
            -
                      doc[key] = deserialize_code_w_scope_data(@buf)
         | 
| 231 | 
            -
                    when TIMESTAMP
         | 
| 232 | 
            -
                      key = deserialize_cstr(@buf)
         | 
| 233 | 
            -
                      doc[key] = [deserialize_number_int_data(@buf),
         | 
| 234 | 
            -
                                  deserialize_number_int_data(@buf)]
         | 
| 235 | 
            -
                    when EOO
         | 
| 236 | 
            -
                      break
         | 
| 237 | 
            -
                    else
         | 
| 238 | 
            -
                      raise "Unknown type #{type}, key = #{key}"
         | 
| 239 | 
            -
                    end
         | 
| 232 | 
            +
                    raise "Unknown type #{type}, key = #{key}"
         | 
| 240 233 | 
             
                  end
         | 
| 241 | 
            -
                  @buf.rewind
         | 
| 242 | 
            -
                  doc
         | 
| 243 234 | 
             
                end
         | 
| 235 | 
            +
                @buf.rewind
         | 
| 236 | 
            +
                doc
         | 
| 244 237 | 
             
              end
         | 
| 245 238 |  | 
| 246 239 | 
             
              # For debugging.
         |