Skip to content

Mongoid update #98

Closed
wants to merge 29 commits into from
Select commit
+696 −205
View
1 Gemfile
@@ -16,4 +16,5 @@ group :development do
gem 'em-hiredis'
gem 'mongo'
gem 'amqp'
+ gem 'mongoid'
end
View
256 lib/em-synchrony/em-mongo.rb
@@ -24,6 +24,40 @@ def authenticate(username, password)
raise AuthenticationError, auth_result2["errmsg"]
end
end
+
+ %w(collections create_collection drop_collection drop_index index_information get_last_error error? add_user).each do |name|
+ class_eval <<-EOS, __FILE__, __LINE__
+ alias :a#{name} :#{name}
+ def #{name}(*args)
+ EM::Synchrony.sync a#{name}(*args)
+ end
+ EOS
+ end
+
+ # this method is reimplemented because it requires Cursor#next_document to be async, which is no longer is.
+ def command(selector, opts={})
+ check_response = opts.fetch(:check_response, true)
+ raise MongoArgumentError, "command must be given a selector" unless selector.is_a?(Hash) && !selector.empty?
+
+ response = RequestResponse.new
+ cmd_resp = Cursor.new(self.collection(SYSTEM_COMMAND_COLLECTION), :limit => -1, :selector => selector).anext_document
+
+ cmd_resp.callback do |doc|
+ if doc.nil?
+ response.fail([OperationFailure, "Database command '#{selector.keys.first}' failed: returned null."])
+ elsif (check_response && !EM::Mongo::Support.ok?(doc))
+ response.fail([OperationFailure, "Database command '#{selector.keys.first}' failed: #{doc.inspect}"])
+ else
+ response.succeed(doc)
+ end
+ end
+
+ cmd_resp.errback do |err|
+ response.fail([OperationFailure, "Database command '#{selector.keys.first}' failed: #{err[1]}"])
+ end
+
+ response
+ end
end
class Connection
@@ -32,101 +66,169 @@ def initialize(host = DEFAULT_IP, port = DEFAULT_PORT, timeout = nil, opts = {})
@em_connection = EMConnection.connect(host, port, timeout, opts)
@db = {}
-
# establish connection before returning
- EM.next_tick { f.resume }
+ @em_connection.callback { f.resume }
+ @em_connection.errback { @on_close.call; f.resume }
+
Fiber.yield
end
+
end
class Collection
- #
- # The upcoming versions of EM-Mongo change Collection#find's interface: it
- # now returns a deferrable cursor YAY. This breaks compatibility with past
- # versions BOO. We'll just choose based on the presence/absence of
- # EM::Mongo::Cursor YAY
- #
-
- #
- # em-mongo version > 0.3.6
- #
- if defined?(EM::Mongo::Cursor)
-
- # afind is the old (async) find
- # afind_one is rewritten to call afind
- # find is sync, using a callback on the cursor
- # find_one is sync, by calling find and taking the first element.
- # first is sync, an alias for find_one
-
- alias :afind :find
- def find(*args)
- f = Fiber.current
- cursor = afind(*args)
- cursor.to_a.callback{ |res| f.resume(res) }
- Fiber.yield
- end
-
- # need to rewrite afind_one manually, as it calls 'find' (reasonably
- # expecting it to be what is now known as 'afind')
-
- def afind_one(spec_or_object_id=nil, opts={})
- spec = case spec_or_object_id
- when nil
- {}
- when BSON::ObjectId
- {:_id => spec_or_object_id}
- when Hash
- spec_or_object_id
- else
- raise TypeError, "spec_or_object_id must be an instance of ObjectId or Hash, or nil"
- end
- afind(spec, opts.merge(:limit => -1)).next_document
- end
- alias :afirst :afind_one
+ # changed to make the cursor sync for versions greater than 0.3.6
+ # this means find and afind are the same
+
+ # afind_one is rewritten to call anext_document on the cursor returned from find
+ # find_one is sync in the original form, unchanged because cursor is changed
+ # first is sync, an alias for find_one
+
+ alias :afind :find
+
+ # need to rewrite afind_one manually, as it calls next_document on
+ # the cursor
+
+ def afind_one(spec_or_object_id=nil, opts={})
+ spec = case spec_or_object_id
+ when nil
+ {}
+ when BSON::ObjectId
+ {:_id => spec_or_object_id}
+ when Hash
+ spec_or_object_id
+ else
+ raise TypeError, "spec_or_object_id must be an instance of ObjectId or Hash, or nil"
+ end
+ find(spec, opts.merge(:limit => -1)).anext_document
+ end
+ alias :afirst :afind_one
+ # alias :first :find_one
+
+ %w(safe_insert safe_update find_and_modify map_reduce distinct group ).each do |name|
+ class_eval <<-EOS, __FILE__, __LINE__
+ alias :a#{name} :#{name}
+ def #{name}(*args)
+ EM::Synchrony.sync a#{name}(*args)
+ end
+ EOS
+ end
- def find_one(selector={}, opts={})
- opts[:limit] = 1
- find(selector, opts).first
- end
- alias :first :find_one
+ alias :mapreduce :map_reduce
+ alias :amapreduce :amap_reduce
- #
- # em-mongo version <= 0.3.6
- #
- else
+ def save(doc, opts={})
+ safe_save(doc, opts.merge(:safe => false))
+ end
- alias :afind :find
- def find(selector={}, opts={})
+ def update(selector, document, opts={})
+ # Initial byte is 0.
+ safe_update(selector, document, opts.merge(:safe => false))
+ end
+
+ def insert(doc_or_docs)
+ safe_insert(doc_or_docs, :safe => false)
+ end
- f = Fiber.current
- cb = proc { |res| f.resume(res) }
+ end
- skip = opts.delete(:skip) || 0
- limit = opts.delete(:limit) || 0
- order = opts.delete(:order)
+ class Cursor
- @connection.find(@name, skip, limit, order, selector, nil, &cb)
- Fiber.yield
+ %w( next_document has_next? explain count ).each do |name|
+ class_eval <<-EOS, __FILE__, __LINE__
+ alias :a#{name} :#{name}
+ def #{name}(*args)
+ EM::Synchrony.sync a#{name}(*args)
+ end
+ EOS
+ end
+
+ # the following methods are hand tweaked because they are the primary interface
+ # to the rest of the functionality. The key things to understand:
+ #
+ # - #next_document - has an async refresh call internally (what actually runs the query for
+ # this cursor). Most users will not use this method directly, but it should work
+ # sychronously. Used internally by Cursor#explain
+ # - #each - only calls next_document, and is only called by Cursor#defer_as_a internally. It is
+ # THE primary method to interact with the cursor so... MUST be sync.
+ # - #defer_as_a - the fly in the ointment. It is meant to return a deferrable (hence the name). It
+ # is used by Database#collection_names and Database#index_information internally. Can be used
+ # by developers. So LEAVE IT ASYNC. That requires an async #each impl, which makes me
+ # itch. No better idea yet.
+ # - #explain - rewriting to make it fully synchronous - just was easier for now. Mongoid does
+ # reference it as a delegated operation to the driver.
+
+ # alias :anext_document :next_document
+ alias :anext :anext_document
+ alias :next :next_document
+
+ def each(&blk)
+ raise "A callback block is required for #each" unless blk
+
+ # using this bizarre looping construct because next_document doesn't
+ # return nil, at least not with the current EM:Synchrony.sync impl.
+ # so, we do this. I could do a break inside, I guess, but meh. This works.
+ doc = next_document
+ while !doc.nil? && !(doc.kind_of?(Array) && doc.none?)
+ blk.call(doc)
+ doc = next_document
end
-
- # need to rewrite afirst manually, as it calls 'find' (reasonably
- # expecting it to be what is now known as 'afind')
-
- def afirst(selector={}, opts={}, &blk)
- opts[:limit] = 1
- afind(selector, opts) do |res|
- yield res.first
+ close
+ end
+
+ def aeach(&blk)
+ raise "A callback block is required for #each" unless blk
+ EM.next_tick do
+ next_doc_resp = anext_document
+ next_doc_resp.callback do |doc|
+ blk.call(doc)
+ doc.nil? ? close : self.aeach(&blk)
+ end
+ next_doc_resp.errback do |err|
+ if blk.arity > 1
+ blk.call(:error, err)
+ else
+ blk.call(:error)
+ end
end
end
-
- def first(selector={}, opts={})
- opts[:limit] = 1
- find(selector, opts).first
+ end
+
+ def to_a
+ items = []
+ self.each do |doc|
+ if (doc != :error)
+ items << doc
+ end
end
+ items
+ end
+
+ def defer_as_a
+ response = RequestResponse.new
+ items = []
+ self.aeach do |doc,err|
+ if doc == :error
+ response.fail(err)
+ elsif doc
+ items << doc
+ else
+ response.succeed(items)
+ end
+ end
+ response
+ end
+ alias :ato_a :defer_as_a
+
+ def explain
+ response = RequestResponse.new
+ c = Cursor.new(@collection, query_options_hash.merge(:limit => -@limit.abs, :explain => true))
+ explanation = c.next_document
+ c.close
+ explanation
end
- end
-
+ end # end Cursor
+
end
end
View
6 lib/em-synchrony/mongoid.rb
@@ -1,4 +1,8 @@
-require "em-synchrony/mongo"
+require "em-mongo"
+require "em-synchrony/em-mongo"
+require "em-synchrony/mongoid/database"
+require "em-synchrony/mongoid/cursor"
+require "em-synchrony/mongoid/extras"
# disable mongoid connection initializer
if defined? Rails
View
15 lib/em-synchrony/mongoid/cursor.rb
@@ -0,0 +1,15 @@
+module Mongoid
+ class Cursor
+
+ # em-mongo returns nil to indicate the end of the result set.
+ # this makes no sense to me, but keeping em-mongo semantics the same
+ # and adding a nil check here to protect downstream code used to
+ # mongo-ruby-driver semantics
+ def each
+ cursor.each do |document,error|
+ raise OperationFailure.new(error, -1, {}) if document == :error
+ yield Mongoid::Factory.from_db(klass, document) unless document.nil?
+ end
+ end
+ end
+end
View
104 lib/em-synchrony/mongoid/database.rb
@@ -0,0 +1,104 @@
+# encoding: utf-8
+module Mongoid #:nodoc:
+ module Config #:nodoc:
+
+ # This class handles the configuration and initialization of a mongodb
+ # database using em-mongo
+ class Database < Hash
+
+ # keys to remove from self to not pass through to Mongo::Connection
+ ASYNC_PRIVATE_OPTIONS = %w(uri database username password logger pool_timeout pool_size)
+
+ # Takes the supplied options in the hash and created a URI from them to
+ # pass to the Mongo connection object.
+ #
+ # @example Build the URI.
+ # db.build_uri
+ #
+ # @param [ Hash ] options The options to build with.
+ #
+ # @return [ String ] A mongo compliant URI string.
+ #
+ # @since 2.0.0.rc.1
+ def build_uri(options = {})
+ raise "URI not supported in async mode"
+ end
+
+ # Create the mongo master connection from either the supplied URI
+ # or a generated one, while setting pool size and logging.
+ #
+ # @example Create the connection.
+ # db.connection
+ #
+ # @return [ Mongo::Connection ] The mongo connection.
+ #
+ # @since 2.0.0.rc.1
+ def master
+ EventMachine::Synchrony::ConnectionPool.new(size: (self["pool_size"] || 1) ) do
+ conn = EM::Mongo::Connection.new(self["host"], (self["port"] || 27017), self["timeout"] || 1, optional)
+ if authenticating?
+ db = conn.db(self["database"])
+ db.authenticate(username, password)
+ end
+ conn
+ end
+ end
+
+ # Create the mongo slave connections from either the supplied URI
+ # or a generated one, while setting pool size and logging.
+ #
+ # @example Create the connection.
+ # db.connection
+ #
+ # @return [ Array<Mongo::Connection> ] The mongo slave connections.
+ #
+ # @since 2.0.0.rc.1
+ def slaves
+ (self["slaves"] || []).map do |options|
+ EventMachine::Synchrony::ConnectionPool.new(size: self["pool_size"] || 1) do
+ conn = EM::Mongo::Connection.new(self["host"], (self["port"] || 27017), self["timeout"] || 0, optional(true))
+ if authenticating?
+ db = conn.db(self["database"])
+ db.authenticate(username, password)
+ end
+ conn
+ end
+ end
+ end
+
+ # Get the name of the database, from either the URI supplied or the
+ # database value in the options.
+ #
+ # @example Get the database name.
+ # db.name
+ #
+ # @return [ String ] The database name.
+ #
+ # @since 2.0.0.rc.1
+ def name
+ database
+ end
+
+ # Get the options used in creating the database connection.
+ #
+ # @example Get the options.
+ # db.options
+ #
+ # @param [ true, false ] slave Are the options for a slave db?
+ #
+ # @return [ Hash ] The hash of configuration options.
+ #
+ # @since 2.0.0.rc.1
+ def optional(slave = false)
+ ({
+ # pool size is now used in the main connection setup
+ # :pool_size => pool_size,
+ :logger => logger? ? Mongoid::Logger.new : nil,
+ :slave_ok => slave
+ }).merge(self).reject { |k,v| ASYNC_PRIVATE_OPTIONS.include? k }.
+ inject({}) { |memo, (k, v)| memo[k.to_sym] = v; memo} # mongo likes symbols
+ end
+
+ end
+ end
+end
View
26 lib/em-synchrony/mongoid/extras.rb
@@ -0,0 +1,26 @@
+module EM::Mongo
+ class Database
+ alias :driver_create_collection :create_collection
+ def create_collection(name, options=nil)
+ driver_create_collection(name)
+ end
+ end
+
+ class Collection
+ alias :driver_insert :insert
+ def insert(doc_or_docs,opts=nil)
+ driver_insert(doc_or_docs)
+ end
+
+ # Mongoid expects :_id to be "_id" (String, not a Symbol) for
+ # serialization. it looks like the mongo-ruby-driver accepts either...
+ # and it doesn't force it to be a Symbol like em-mongo does. Probably
+ # should make the real em-mongo match this. Until then... flip
+ # sym to string by standardizing on string...
+ def sanitize_id!(doc)
+ doc["_id"] = has_id?(doc) || BSON::ObjectId.new
+ doc.delete(:_id)
+ doc
+ end
+ end
+end
View
181 spec/em-mongo_spec.rb
@@ -23,8 +23,8 @@
obj.should be_a(BSON::ObjectId)
obj = collection.find
- obj.size.should == 1
- obj.first['hello'].should == 'world'
+ obj.count.should == 1
+ obj.next['hello'].should == 'world'
EventMachine.stop
end
@@ -39,10 +39,10 @@
obj = collection.insert('hello2' => 'world2')
obj = collection.find({})
- obj.size.should == 2
+ obj.count.should == 2
obj2 = collection.find({}, {:limit => 1})
- obj2.size.should == 1
+ obj2.count(true).should == 1
obj3 = collection.first
obj3.is_a?(Hash).should be_true
@@ -60,12 +60,12 @@
collection.insert(:name => 'three', :position => 2)
collection.insert(:name => 'two', :position => 1)
- res = collection.find({}, {:order => 'position'})
+ res = collection.find({}, {:order => 'position'}).to_a
res[0]["name"].should == 'one'
res[1]["name"].should == 'two'
res[2]["name"].should == 'three'
- res1 = collection.find({}, {:order => [:position, :desc]})
+ res1 = collection.find({}, {:order => [:position, :desc]}).to_a
res1[0]["name"].should == 'three'
res1[1]["name"].should == 'two'
res1[2]["name"].should == 'one'
@@ -76,144 +76,71 @@
end
- #
- # em-mongo version > 0.3.6
- #
- if defined?(EM::Mongo::Cursor)
- describe '*A*synchronously (afind & afirst) [Mongo > 0.3.6, using cursor]' do
- it "should insert a record into db" do
- EventMachine.synchrony do
- collection = EM::Mongo::Connection.new.db('db').collection('test')
- collection.remove({}) # nuke all keys in collection
-
- obj = collection.insert('hello' => 'world')
- obj.should be_a(BSON::ObjectId)
-
- cursor = collection.afind
- cursor.should be_a(EM::Mongo::Cursor)
- cursor.to_a.callback do |obj|
- obj.size.should == 1
- obj.first['hello'].should == 'world'
- EM.next_tick{ EventMachine.stop }
- end
- end
- end
+ describe '*A*synchronously (afind & afirst) [Mongo > 0.3.6, using cursor]' do
+ it "should insert a record into db" do
+ EventMachine.synchrony do
+ collection = EM::Mongo::Connection.new.db('db').collection('test')
+ collection.remove({}) # nuke all keys in collection
+
+ obj = collection.insert('hello' => 'world')
+ obj.should be_a(BSON::ObjectId)
- it "should insert a record into db and be able to find it" do
- EventMachine.synchrony do
- collection = EM::Mongo::Connection.new.db('db').collection('test')
- collection.remove({}) # nuke all keys in collection
-
- obj = collection.insert('hello' => 'world')
- obj = collection.insert('hello2' => 'world2')
-
- collection.afind({}).to_a.callback do |obj|
- obj.size.should == 2
- end
- collection.afind({}, {:limit => 1}).to_a.callback do |obj2|
- obj2.size.should == 1
- end
- collection.afirst.callback do |obj3|
- obj3.is_a?(Hash).should be_true
- obj3['hello'].should == 'world'
- EM.next_tick{ EventMachine.stop }
- end
+ cursor = collection.afind
+ cursor.should be_a(EM::Mongo::Cursor)
+ cursor.ato_a.callback do |obj|
+ obj.size.should == 1
+ obj.first['hello'].should == 'world'
+ EM.next_tick{ EventMachine.stop }
end
end
+ end
- it "should be able to order results" do
- EventMachine.synchrony do
- collection = EM::Mongo::Connection.new.db('db').collection('test')
- collection.remove({}) # nuke all keys in collection
-
- collection.insert(:name => 'one', :position => 0)
- collection.insert(:name => 'three', :position => 2)
- collection.insert(:name => 'two', :position => 1)
-
- collection.afind({}, {:order => 'position'}).to_a.callback do |res|
- res[0]["name"].should == 'one'
- res[1]["name"].should == 'two'
- res[2]["name"].should == 'three'
- end
+ it "should insert a record into db and be able to find it" do
+ EventMachine.synchrony do
+ collection = EM::Mongo::Connection.new.db('db').collection('test')
+ collection.remove({}) # nuke all keys in collection
- collection.afind({}, {:order => [:position, :desc]}).to_a.callback do |res1|
- res1[0]["name"].should == 'three'
- res1[1]["name"].should == 'two'
- res1[2]["name"].should == 'one'
- EM.next_tick{ EventMachine.stop }
- end
+ obj = collection.insert('hello' => 'world')
+ obj = collection.insert('hello2' => 'world2')
+ collection.afind({}).ato_a.callback do |obj|
+ obj.size.should == 2
end
- end
- end
-
- else
- describe '*A*synchronously (afind & afirst) [Mongo <= 0.3.6, using blocks]' do
- it "should insert a record into db" do
- EventMachine.synchrony do
- collection = EM::Mongo::Connection.new.db('db').collection('test')
- collection.remove({}) # nuke all keys in collection
-
- obj = collection.insert('hello' => 'world')
- obj.should be_a(BSON::ObjectId)
-
- ret_val = collection.afind do |obj|
- obj.size.should == 1
- obj.first['hello'].should == 'world'
- EM.next_tick{ EventMachine.stop }
- end
- ret_val.should be_a(Integer)
+ collection.afind({}, {:limit => 1}).ato_a.callback do |obj2|
+ obj2.size.should == 1
end
- end
-
- it "should insert a record into db and be able to find it" do
- EventMachine.synchrony do
- collection = EM::Mongo::Connection.new.db('db').collection('test')
- collection.remove({}) # nuke all keys in collection
-
- obj = collection.insert('hello' => 'world')
- obj = collection.insert('hello2' => 'world2')
-
- collection.afind({}) do |obj|
- obj.size.should == 2
- end
- collection.afind({}, {:limit => 1}) do |obj2|
- obj2.size.should == 1
- end
- collection.afirst do |obj3|
- obj3.is_a?(Hash).should be_true
- obj3['hello'].should == 'world'
- EM.next_tick{ EventMachine.stop }
- end
+ collection.afirst.callback do |obj3|
+ obj3.is_a?(Hash).should be_true
+ obj3['hello'].should == 'world'
+ EM.next_tick{ EventMachine.stop }
end
end
+ end
- it "should be able to order results" do
- EventMachine.synchrony do
- collection = EM::Mongo::Connection.new.db('db').collection('test')
- collection.remove({}) # nuke all keys in collection
-
- collection.insert(:name => 'one', :position => 0)
- collection.insert(:name => 'three', :position => 2)
- collection.insert(:name => 'two', :position => 1)
+ it "should be able to order results" do
+ EventMachine.synchrony do
+ collection = EM::Mongo::Connection.new.db('db').collection('test')
+ collection.remove({}) # nuke all keys in collection
- collection.afind({}, {:order => 'position'}) do |res|
- res[0]["name"].should == 'one'
- res[1]["name"].should == 'two'
- res[2]["name"].should == 'three'
- end
+ collection.insert(:name => 'one', :position => 0)
+ collection.insert(:name => 'three', :position => 2)
+ collection.insert(:name => 'two', :position => 1)
- collection.afind({}, {:order => [:position, :desc]}) do |res1|
- res1[0]["name"].should == 'three'
- res1[1]["name"].should == 'two'
- res1[2]["name"].should == 'one'
- EM.next_tick{ EventMachine.stop }
- end
+ collection.afind({}, {:order => 'position'}).ato_a.callback do |res|
+ res[0]["name"].should == 'one'
+ res[1]["name"].should == 'two'
+ res[2]["name"].should == 'three'
+ end
+ collection.afind({}, {:order => [:position, :desc]}).ato_a.callback do |res1|
+ res1[0]["name"].should == 'three'
+ res1[1]["name"].should == 'two'
+ res1[2]["name"].should == 'one'
+ EM.next_tick{ EventMachine.stop }
end
+
end
end
-
end
it "should update records in db" do
View
1 spec/helper/all.rb
@@ -8,6 +8,7 @@
require 'lib/em-synchrony/em-remcached'
require 'lib/em-synchrony/em-memcache'
require 'lib/em-synchrony/em-mongo'
+require 'lib/em-synchrony/mongoid'
require 'lib/em-synchrony/em-redis'
require 'lib/em-synchrony/em-hiredis'
require 'lib/em-synchrony/amqp'
View
13 spec/mongoid/app/models/comment.rb
@@ -0,0 +1,13 @@
+class Comment
+ include Mongoid::Document
+ include Mongoid::Timestamps
+
+ field :title
+ field :body
+ field :author_name
+
+ embedded_in :posts
+
+ validates_presence_of :title
+
+end
View
10 spec/mongoid/app/models/link.rb
@@ -0,0 +1,10 @@
+class Link
+ include Mongoid::Document
+ include Mongoid::Timestamps
+
+ field :url
+ field :score, type: Integer, default: 0
+
+ belongs_to :post
+
+end
View
22 spec/mongoid/app/models/post.rb
@@ -0,0 +1,22 @@
+class Post
+ include Mongoid::Document
+
+ field :title
+ field :body
+ field :extra_field
+ field :secure_field
+ field :simple_counter, type: Integer
+
+ embeds_many :comments
+
+ has_many :links
+
+ attr_protected :extra_field, :as => [:default, :parser]
+ attr_protected :simple_counter, :as => :parser
+ attr_protected :secure_field
+
+ index :title, :unique => true
+
+ validates_presence_of :title
+
+end
View
7 spec/mongoid/app/models/simple_document.rb
@@ -0,0 +1,7 @@
+class SimpleDocument
+ include Mongoid::Document
+
+ field :name
+ field :counter, :type=>Integer
+
+end
View
32 spec/mongoid/connection_spec.rb
@@ -0,0 +1,32 @@
+require "spec/mongoid/mongoid_helper"
+
+describe Mongoid do
+
+ describe "pooling" do
+ it "should use the pool arguments" do
+ EM.synchrony do
+ Mongoid.from_hash({"host"=> "localhost", "database"=> "test", "pool_size"=>5})
+ sd = SimpleDocument.first
+ sd = SimpleDocument.first
+ sd = SimpleDocument.first
+ sd = SimpleDocument.first
+ sd = SimpleDocument.first
+ EventMachine.stop
+ end
+
+ end
+ end
+
+ describe "a plain document" do
+ it "should save" do
+ EM.synchrony do
+ Mongoid.from_hash({"host"=> "localhost", "database"=> "test"})
+ sd = SimpleDocument.new(name: "test1", counter: 1)
+ sd.save.should == true
+
+ EventMachine.stop
+ end
+ end
+ end
+
+end
View
114 spec/mongoid/functional/finders_spec.rb
@@ -0,0 +1,114 @@
+require "spec/mongoid/mongoid_helper"
+
+describe "Mongoid Finders" do
+
+ it "should find one" do
+
+ post = nil
+
+ EM.synchrony do
+ begin
+ Mongoid.from_hash({"host"=> "localhost", "port"=>27017, "database"=> "test", "pool_size"=>10})
+ Post.create(title: "Sample Post Alpha!", body: "rocking the rocker rocket. (yes, I'm tired...)")
+ Post.create(title: "Sample Post Beta!", body: "rocking the rocker rocket. (yes, I'm tired...)")
+ Post.create(title: "Sample Post Gamma!", body: "rocking the rocker rocket. (yes, I'm tired...)")
+ post = Post.first
+ rescue Exception,Error
+ puts $!
+ end
+ EM.stop
+ end
+
+ post.should_not be_nil
+ post.title.should eq("Sample Post Alpha!")
+
+ end
+
+ it "should find a simple document" do
+
+ doc = nil
+
+ EM.synchrony do
+ begin
+ Mongoid.from_hash({"host"=> "localhost", "port"=>27017, "database"=> "test", "pool_size"=>10})
+ SimpleDocument.delete_all
+ SimpleDocument.create(name: "Simple Doc Alpha!")
+ doc = SimpleDocument.first
+ rescue Exception,Error
+ puts $!
+ end
+ EM.stop
+ end
+
+ doc.should_not be_nil
+ doc.name.should eq("Simple Doc Alpha!")
+
+ end
+
+ it "should traverse a relation" do
+ link = nil
+
+ puts "traverse"
+
+ EM.synchrony do
+ begin
+ Mongoid.from_hash({"host"=> "localhost", "port"=>27017, "database"=> "test", "pool_size"=>10})
+ puts "creating post"
+ p = Post.create(title: "Link Testing Post")
+ puts "got p - #{p.new_record?}"
+ puts "creating link 1"
+ l1 = Link.new(url: "http://apple.com", score: 10)
+ l1.post = p
+ puts "saving #{l1.save}"
+ l1.reload
+ puts "creating link 2"
+ l2 = Link.new(url: "http://windowsphone.com", score: 9)
+ l2.post = p
+ puts "saving #{l2.save}"
+ puts "creating link 3"
+ l3 = Link.new(url: "http://android.com", score: 8)
+ l3.post = p
+ puts "saving #{l3.save}"
+
+ link = p.links.order_by([:score, :desc]).first
+ rescue Exception,Error
+ puts $!
+ end
+ EM.stop
+ end
+
+ link.should_not be_nil
+ link.url.should eq("http://apple.com")
+ link.score.should eq(10)
+
+ end
+
+
+ it "should find a Link" do
+
+ puts "fourth test - should find a link"
+
+ link = nil
+
+ EM.synchrony do
+ puts "synchro started"
+ begin
+ Mongoid.from_hash({"host"=> "localhost", "port"=>27017, "database"=> "test", "pool_size"=>10})
+ puts "connected!"
+ Link.delete_all
+ puts "boom"
+ Link.create(url: "http://fatmixx.com")
+ link = Link.first
+ rescue Exception,Error
+ puts $!
+ end
+ EM.stop
+ end
+
+ link.should_not be_nil
+ link.url.should eq("http://fatmixx.com")
+
+ end
+
+
+end
View
87 spec/mongoid/functional/persistence_spec.rb
@@ -0,0 +1,87 @@
+require "spec/mongoid/mongoid_helper"
+require 'em-synchrony/mongoid'
+
+describe Mongoid::Persistence do
+
+ before(:all) do
+ EM.synchrony do
+ Mongoid.from_hash({"host"=> "localhost", "database"=> "test", "pool_size"=>5})
+ [ Post, Link ].each(&:delete_all)
+ Mongoid.persist_in_safe_mode = true
+ Mongoid.parameterize_keys = false
+ EventMachine.stop
+ end
+ end
+
+ describe ".create" do
+ context "when providing attributes" do
+ it "saves and returns the document" do
+ post = nil
+ EM.synchrony do
+ Mongoid.from_hash({"host"=> "localhost", "database"=> "test", "pool_size"=>5})
+ post = Post.create(:title => "Sensei", :body => "Testing 1 2 3")
+ EventMachine.stop
+ end
+
+ post.should be_persisted
+ post.should be_a_kind_of(Post)
+ end
+ end
+
+ it "sets attributes, persists the document" do
+ post = nil
+ EM.synchrony do
+ Mongoid.from_hash({"host"=> "localhost", "database"=> "test", "pool_size"=>5})
+ post = Post.create do |poster|
+ poster.title = "Yahooooo"
+ end
+ EventMachine.stop
+ end
+
+ post.title.should eq("Yahooooo")
+ post.should be_persisted
+ end
+
+ context "when passing in a block" do
+
+ it "sets attributes, persists the document" do
+ post = nil
+ EM.synchrony do
+ Mongoid.from_hash({"host"=> "localhost", "database"=> "test", "pool_size"=>5})
+ post = Post.create do |poster|
+ poster.title = "Yahooooo"
+ end
+ EventMachine.stop
+ end
+
+ post.title.should eq("Yahooooo")
+ post.should be_persisted
+ end
+ end
+
+ context "when mass assignment role is indicated" do
+
+ context "when attributes assigned from default role" do
+
+ it "validates the roles for mass assignment" do
+ dpost = nil
+ EM.synchrony do
+ Mongoid.from_hash({"host"=> "localhost", "database"=> "test", "pool_size"=>5})
+ dpost = Post.create(
+ :title => "Some Title",
+ :body => "Some text",
+ :extra_field => "something else",
+ :secure_field => "secure!"
+ )
+ EventMachine.stop
+ end
+
+ dpost.title.should eq("Some Title")
+ dpost.extra_field.should be_nil
+ dpost.secure_field.should be_nil
+ end
+ end
+ end
+
+ end
+end
View
26 spec/mongoid/mongoid_helper.rb
@@ -0,0 +1,26 @@
+require 'rubygems'
+require 'rspec'
+require 'pp'
+
+require 'spec/helper/tolerance_matcher'
+
+require 'mongoid'
+require 'lib/em-synchrony'
+require 'lib/em-synchrony/mongoid'
+
+def now(); Time.now.to_f; end
+
+RSpec.configure do |config|
+ config.include(Sander6::CustomMatchers)
+end
+
+
+MODELS = File.join(File.dirname(__FILE__), "app/models")
+$LOAD_PATH.unshift(MODELS)
+
+Dir[ File.join(MODELS, "*.rb") ].sort.each do |file|
+ name = File.basename(file, ".rb")
+ autoload name.camelize.to_sym, name
+end
+
+
Something went wrong with that request. Please try again.