Level 1 support:

- Added PK factory support to the db class. Documented it in the README.

- Mongo#db now takes an options hash (right now, for :strict and :pk) and
  passes it in to the DB constructor.
This commit is contained in:
Jim Menard 2009-01-16 14:41:53 -05:00
parent 94f5214e45
commit e0162aebb9
4 changed files with 114 additions and 8 deletions

View File

@ -63,7 +63,58 @@ read from Mongo will have their character encodings set to UTF-8.
When used with Ruby 1.8, the bytes in each string are written to and read from
Mongo as-is. If the string is ASCII all is well, because ASCII is a subset of
UTF-8. If the string is not ASCII then it may not be a well-formed UTF-8 string.
UTF-8. If the string is not ASCII then it may not be a well-formed UTF-8
string.
== The DB class
=== Primary Key Factories
A basic Mongo driver is not responsible for creating primary keys or knowing
how to interpret them. You can tell the Ruby Mongo driver how to create
primary keys by passing in the :pk option to the Mongo#db method.
include XGen::Mongo::Driver
db = Mongo.new.db('dbname', :pk => MyPKFactory)
A primary key factory object must respond to :create_pk, which should take a
hash and return a hash which merges the original hash with any primary key
fields the factory wishes to inject. NOTE: if the object already has a primary
key, the factory should not inject a new key; this means that the object is
being used in a repsert but it already exists. The idea here is that when ever
a record is inserted, the :pk object's +create_pk+ method will be called and
the new hash returned will be inserted.
Here is a sample primary key factory, taken from the tests:
class TestPKFactory
def create_pk(row)
row['_id'] ||= XGen::Mongo::Driver::ObjectID.new
row
end
end
A database's PK factory object may not be changed. Right now, it may only be
specified when you obtain the db object.
=== Strict mode
Each database has an optional strict mode. If strict mode is on, then asking
for a collection that does not exist will raise an error, as will asking to
create a collection that already exists. Note that both these operations are
completely harmless; strict mode is a programmer convenience only.
To turn on strict mode, either pass in :strict => true when obtaining a DB
object or call the :strict= method:
db = XGen::Mongo::Driver::Mongo.new.db('dbname', :strict => true)
# I'm feeling lax
db.strict = false
# No, I'm not!
db.strict = true
The method DB#strict? returns the current value of that flag.
= Testing

View File

@ -59,17 +59,40 @@ module XGen
# The database's socket. For internal (and Cursor) use only.
attr_reader :socket
# A primary key factory object (or +nil+). See the README.doc file or
# DB#new for details.
attr_reader :pk_factory
# db_name :: The database name
#
# nodes :: An array of [host, port] pairs.
#
# options :: A hash of options.
#
# Options:
#
# :strict :: If true, collections must exist to be accessed and must
# not exist to be created. See #collection and
# #create_collection.
#
# :pk :: A primary key factory object that must respond to :create_pk,
# which should take a hash and return a hash which merges the
# original hash with any primary key fields the factory wishes
# to inject. (NOTE: if the object already has a primary key,
# the factory should not inject a new key; this means that the
# object is being used in a repsert but it already exists.) The
# idea here is that when ever a record is inserted, the :pk
# object's +create_pk+ method will be called and the new hash
# returned will be inserted.
#
# When a DB object first connects, it tries the first node. If that
# fails, it keeps trying to connect to the remaining nodes until it
# sucessfully connects.
def initialize(db_name, nodes)
def initialize(db_name, nodes, options={})
raise "Invalid DB name" if !db_name || (db_name && db_name.length > 0 && db_name.include?("."))
@name, @nodes = db_name, nodes
@strict = false
@strict = options[:strict]
@pk_factory = options[:pk]
@semaphore = Object.new
@semaphore.extend Mutex_m
connect_to_first_available_host
@ -252,8 +275,8 @@ module XGen
# applying +obj+ as an update. If no match, inserts (???). Normally
# called by Collection#repsert.
def repsert_in_db(collection_name, selector, obj)
# TODO if PKInjector, inject
@semaphore.synchronize {
obj = @pk_factory.create_pk(obj) if @pk_factory
send_to_db(UpdateMessage.new(@name, collection_name, selector, obj, true))
obj
}
@ -325,7 +348,10 @@ module XGen
# Collection#insert.
def insert_into_db(collection_name, objects)
@semaphore.synchronize {
objects.each { |o| send_to_db(InsertMessage.new(@name, collection_name, o)) }
objects.each { |o|
o = @pk_factory.create_pk(o) if @pk_factory
send_to_db(InsertMessage.new(@name, collection_name, o))
}
}
end

View File

@ -52,9 +52,10 @@ module XGen
end
end
# Return the XGen::Mongo::Driver::DB named +db_name+.
def db(db_name)
XGen::Mongo::Driver::DB.new(db_name, @nodes)
# Return the XGen::Mongo::Driver::DB named +db_name+. See DB#new for
# +options+.
def db(db_name, options={})
XGen::Mongo::Driver::DB.new(db_name, @nodes, options)
end
# Returns a hash containing database names as keys and disk space for

View File

@ -2,6 +2,13 @@ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
require 'mongo'
require 'test/unit'
class TestPKFactory
def create_pk(row)
row['_id'] ||= XGen::Mongo::Driver::ObjectID.new
row
end
end
# NOTE: assumes Mongo is running
class DBTest < Test::Unit::TestCase
@ -48,4 +55,25 @@ class DBTest < Test::Unit::TestCase
assert @db.connected?
end
def test_pk_factory
db = Mongo.new(@host, @port).db('ruby-mongo-test', :pk => TestPKFactory.new)
coll = db.collection('test')
coll.clear
coll.insert('name' => 'Fred')
row = coll.find({'name' => 'Fred'}, :limit => 1).next_object
assert_not_nil row
assert_equal 'Fred', row['name']
assert_kind_of XGen::Mongo::Driver::ObjectID, row['_id']
oid = XGen::Mongo::Driver::ObjectID.new
coll.insert('_id' => oid, 'name' => 'Barney')
row = coll.find({'name' => 'Barney'}, :limit => 1).next_object
assert_not_nil row
assert_equal 'Barney', row['name']
assert_equal oid, row['_id']
coll.clear
end
end