em-mongo 0.2.14 → 0.3.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/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.3.1
@@ -25,10 +25,10 @@ module EM::Mongo
25
25
  end
26
26
  end
27
27
 
28
- def insert(obj)
29
- obj['_id'] ||= BSON::ObjectId.new
30
- @connection.insert(@name, obj)
31
- obj
28
+ def insert(doc)
29
+ sanitize_id!(doc)
30
+ @connection.insert(@name, doc)
31
+ doc[:_id] # mongo-ruby-driver returns ID
32
32
  end
33
33
 
34
34
  def update(selector, updater, opts={})
@@ -36,10 +36,37 @@ module EM::Mongo
36
36
  true
37
37
  end
38
38
 
39
+ # XXX Missing tests
40
+ def save(doc, opts={})
41
+ id = has_id?(doc)
42
+ sanitize_id!(doc)
43
+ if id
44
+ update({:_id => id}, doc, :upsert => true)
45
+ id
46
+ else
47
+ insert(doc)
48
+ end
49
+ end
50
+
39
51
  def remove(obj = {})
40
52
  @connection.delete(@name, obj)
41
53
  true
42
54
  end
43
55
 
56
+ private
57
+
58
+ def has_id?(doc)
59
+ # mongo-ruby-driver seems to take :_id over '_id' for some reason
60
+ id = doc[:_id] || doc['_id']
61
+ return id if id
62
+ nil
63
+ end
64
+
65
+ def sanitize_id!(doc)
66
+ doc[:_id] = has_id?(doc) || BSON::ObjectId.new
67
+ doc.delete('_id')
68
+ doc
69
+ end
70
+
44
71
  end
45
72
  end
@@ -239,25 +239,10 @@ module EM::Mongo
239
239
 
240
240
  end
241
241
 
242
- # Make EM::Mongo look like mongo-ruby-driver
243
242
  module EM::Mongo
244
- class Database
245
- def initialize(name = DEFAULT_DB, connection = nil)
246
- @db_name = name
247
- @em_connection = connection || EM::Mongo::Connection.new
248
- @collection = nil
249
- end
250
-
251
- def collection(name = DEFAULT_NS)
252
- @collection = EM::Mongo::Collection.new(@db_name, name, @em_connection)
253
- end
254
-
255
- def close
256
- @em_connection.close
257
- end
258
- end
259
243
 
260
244
  class Connection
245
+
261
246
  def initialize(host = DEFAULT_IP, port = DEFAULT_PORT, timeout = nil, opts = {})
262
247
  @em_connection = EMConnection.connect(host, port, timeout, opts)
263
248
  @db = {}
@@ -0,0 +1,89 @@
1
+ # encoding: UTF-8
2
+
3
+ # --
4
+ # Copyright (C) 2008-2010 10gen Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ # ++
18
+ module Mongo #:nodoc:
19
+
20
+ # Utility module to include when needing to convert certain types of
21
+ # objects to mongo-friendly parameters.
22
+ module Conversions
23
+
24
+ ASCENDING_CONVERSION = ["ascending", "asc", "1"]
25
+ DESCENDING_CONVERSION = ["descending", "desc", "-1"]
26
+
27
+ # Converts the supplied +Array+ to a +Hash+ to pass to mongo as
28
+ # sorting parameters. The returned +Hash+ will vary depending
29
+ # on whether the passed +Array+ is one or two dimensional.
30
+ #
31
+ # Example:
32
+ #
33
+ # <tt>array_as_sort_parameters([["field1", :asc], ["field2", :desc]])</tt> =>
34
+ # <tt>{ "field1" => 1, "field2" => -1}</tt>
35
+ def array_as_sort_parameters(value)
36
+ order_by = BSON::OrderedHash.new
37
+ if value.first.is_a? Array
38
+ value.each do |param|
39
+ if (param.class.name == "String")
40
+ order_by[param] = 1
41
+ else
42
+ order_by[param[0]] = sort_value(param[1]) unless param[1].nil?
43
+ end
44
+ end
45
+ elsif !value.empty?
46
+ if order_by.size == 1
47
+ order_by[value.first] = 1
48
+ else
49
+ order_by[value.first] = sort_value(value[1])
50
+ end
51
+ end
52
+ order_by
53
+ end
54
+
55
+ # Converts the supplied +String+ or +Symbol+ to a +Hash+ to pass to mongo as
56
+ # a sorting parameter with ascending order. If the +String+
57
+ # is empty then an empty +Hash+ will be returned.
58
+ #
59
+ # Example:
60
+ #
61
+ # *DEPRECATED
62
+ #
63
+ # <tt>string_as_sort_parameters("field")</tt> => <tt>{ "field" => 1 }</tt>
64
+ # <tt>string_as_sort_parameters("")</tt> => <tt>{}</tt>
65
+ def string_as_sort_parameters(value)
66
+ return {} if (str = value.to_s).empty?
67
+ { str => 1 }
68
+ end
69
+
70
+ # Converts the +String+, +Symbol+, or +Integer+ to the
71
+ # corresponding sort value in MongoDB.
72
+ #
73
+ # Valid conversions (case-insensitive):
74
+ #
75
+ # <tt>ascending, asc, :ascending, :asc, 1</tt> => <tt>1</tt>
76
+ # <tt>descending, desc, :descending, :desc, -1</tt> => <tt>-1</tt>
77
+ #
78
+ # If the value is invalid then an error will be raised.
79
+ def sort_value(value)
80
+ val = value.to_s.downcase
81
+ return 1 if ASCENDING_CONVERSION.include?(val)
82
+ return -1 if DESCENDING_CONVERSION.include?(val)
83
+ raise InvalidSortValueError.new(
84
+ "#{self} was supplied as a sort direction when acceptable values are: " +
85
+ "Mongo::ASCENDING, 'ascending', 'asc', :ascending, :asc, 1, Mongo::DESCENDING, " +
86
+ "'descending', 'desc', :descending, :desc, -1.")
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,59 @@
1
+ module EM::Mongo
2
+ class Database
3
+
4
+ SYSTEM_NAMESPACE_COLLECTION = "system.namespaces"
5
+ SYSTEM_INDEX_COLLECTION = "system.indexes"
6
+ SYSTEM_PROFILE_COLLECTION = "system.profile"
7
+ SYSTEM_USER_COLLECTION = "system.users"
8
+ SYSTEM_JS_COLLECTION = "system.js"
9
+ SYSTEM_COMMAND_COLLECTION = "$cmd"
10
+
11
+ def initialize(name = DEFAULT_DB, connection = nil)
12
+ @db_name = name
13
+ @em_connection = connection || EM::Mongo::Connection.new
14
+ @collection = nil
15
+ @collections = {}
16
+ end
17
+
18
+ def collection(name = EM::Mongo::DEFAULT_NS)
19
+ @collections[@db_name] ||= EM::Mongo::Collection.new(@db_name, name, @em_connection)
20
+ end
21
+
22
+ def connection
23
+ @em_connection
24
+ end
25
+
26
+ def close
27
+ @em_connection.close
28
+ end
29
+
30
+ def authenticate(username, password)
31
+ self.collection(SYSTEM_COMMAND_COLLECTION).first({'getnonce' => 1}) do |res|
32
+ yield false if not res or not res['nonce']
33
+
34
+ auth = BSON::OrderedHash.new
35
+ auth['authenticate'] = 1
36
+ auth['user'] = username
37
+ auth['nonce'] = res['nonce']
38
+ auth['key'] = Mongo::Support.auth_key(username, password, res['nonce'])
39
+
40
+ self.collection(SYSTEM_COMMAND_COLLECTION).first(auth) do |res|
41
+ if Mongo::Support.ok?(res)
42
+ yield true
43
+ else
44
+ yield res
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ def add_user(username, password, &blk)
51
+ self.collection(SYSTEM_USER_COLLECTION).first({:user => username}) do |res|
52
+ user = res || {:user => username}
53
+ user['pwd'] = Mongo::Support.hash_password(username, password)
54
+ yield self.collection(SYSTEM_USER_COLLECTION).save(user)
55
+ end
56
+ end
57
+
58
+ end
59
+ end
@@ -0,0 +1,82 @@
1
+ # encoding: UTF-8
2
+
3
+ # --
4
+ # Copyright (C) 2008-2010 10gen Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ # ++
18
+
19
+ require 'digest/md5'
20
+
21
+ module Mongo
22
+ module Support
23
+ include Mongo::Conversions
24
+ extend self
25
+
26
+ # Generate an MD5 for authentication.
27
+ #
28
+ # @param [String] username
29
+ # @param [String] password
30
+ # @param [String] nonce
31
+ #
32
+ # @return [String] a key for db authentication.
33
+ def auth_key(username, password, nonce)
34
+ Digest::MD5.hexdigest("#{nonce}#{username}#{hash_password(username, password)}")
35
+ end
36
+
37
+ # Return a hashed password for auth.
38
+ #
39
+ # @param [String] username
40
+ # @param [String] plaintext
41
+ #
42
+ # @return [String]
43
+ def hash_password(username, plaintext)
44
+ Digest::MD5.hexdigest("#{username}:mongo:#{plaintext}")
45
+ end
46
+
47
+
48
+ def validate_db_name(db_name)
49
+ unless [String, Symbol].include?(db_name.class)
50
+ raise TypeError, "db_name must be a string or symbol"
51
+ end
52
+
53
+ [" ", ".", "$", "/", "\\"].each do |invalid_char|
54
+ if db_name.include? invalid_char
55
+ raise Mongo::InvalidNSName, "database names cannot contain the character '#{invalid_char}'"
56
+ end
57
+ end
58
+ raise Mongo::InvalidNSName, "database name cannot be the empty string" if db_name.empty?
59
+ db_name
60
+ end
61
+
62
+ def format_order_clause(order)
63
+ case order
64
+ when String, Symbol then string_as_sort_parameters(order)
65
+ when Array then array_as_sort_parameters(order)
66
+ else
67
+ raise InvalidSortValueError, "Illegal sort clause, '#{order.class.name}'; must be of the form " +
68
+ "[['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]"
69
+ end
70
+ end
71
+
72
+ # Determine if a database command has succeeded by
73
+ # checking the document response.
74
+ #
75
+ # @param [Hash] doc
76
+ #
77
+ # @return [Boolean] true if the 'ok' key is either 1 or *true*.
78
+ def ok?(doc)
79
+ doc['ok'] == 1.0 || doc['ok'] == true
80
+ end
81
+ end
82
+ end
data/lib/em-mongo.rb CHANGED
@@ -1,21 +1,32 @@
1
-
2
- require "eventmachine"
3
- begin; require "bson_ext"; rescue LoadError; require "bson"; end
1
+ begin
2
+ require "rubygems"
3
+ require "bundler"
4
+ Bundler.setup(:default)
5
+ rescue LoadError
6
+ ensure
7
+ require "eventmachine"
8
+ begin
9
+ require "bson_ext"
10
+ rescue LoadError
11
+ require "bson"
12
+ end
13
+ end
4
14
 
5
15
  module EM::Mongo
6
16
 
7
17
  module Version
8
- MAJOR = 0
9
- MINOR = 2
10
- TINY = 10
11
- STRING = [MAJOR, MINOR, TINY].join('.')
18
+ STRING = File.read(File.dirname(__FILE__) + '/../VERSION')
19
+ MAJOR, MINOR, TINY = STRING.split('.')
12
20
  end
13
-
21
+
14
22
  NAME = 'em-mongo'
15
23
  LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
16
24
  PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
17
25
  end
18
26
 
27
+ require File.join(EM::Mongo::LIBPATH, "em-mongo/conversions")
28
+ require File.join(EM::Mongo::LIBPATH, "em-mongo/support")
29
+ require File.join(EM::Mongo::LIBPATH, "em-mongo/database")
19
30
  require File.join(EM::Mongo::LIBPATH, "em-mongo/connection")
20
31
  require File.join(EM::Mongo::LIBPATH, "em-mongo/collection")
21
32
 
@@ -4,69 +4,79 @@ describe EMMongo::Collection do
4
4
  include EM::Spec
5
5
 
6
6
  it 'should insert an object' do
7
- @conn, @coll = connection_and_collection
7
+ @conn, @coll = connection_and_collection
8
8
 
9
- obj = @coll .insert('hello' => 'world')
10
- obj.keys.should include '_id'
11
- obj['_id'].should be_a_kind_of(BSON::ObjectId)
12
- done
9
+ doc = {'hello' => 'world'}
10
+ id = @coll.insert(doc)
11
+ id.should be_a_kind_of(BSON::ObjectId)
12
+ doc[:_id].should be_a_kind_of(BSON::ObjectId)
13
+ done
13
14
  end
14
15
 
15
16
  it 'should insert an object with a custom _id' do
16
- @conn, @coll = connection_and_collection
17
+ @conn, @coll = connection_and_collection
17
18
 
18
- obj = @coll .insert('_id' => 1234, 'hello' => 'world')
19
- obj.keys.should include '_id'
20
- obj['_id'].should == 1234
21
- r = @coll.find({"hello" => "world"},{}) do |res|
22
- res.size.should >= 1
23
- res[0]['_id'].should == 1234
24
- done
25
- end
19
+ id = @coll.insert(:_id => 1234, 'hello' => 'world')
20
+ id.should == 1234
21
+ @coll.first({'hello' => 'world'}) do |res|
22
+ res['_id'].should == 1234
23
+ done
24
+ end
26
25
  end
27
26
 
28
27
  it 'should find an object by attribute' do
29
- @conn, @coll = connection_and_collection
28
+ @conn, @coll = connection_and_collection
30
29
 
31
- @coll.insert("hello" => 'world')
32
- r = @coll.find({"hello" => "world"},{}) do |res|
33
- res.size.should >= 1
34
- res[0]["hello"].should == "world"
35
- done
36
- end
30
+ @coll.insert("hello" => 'world')
31
+ @coll.find({"hello" => "world"},{}) do |res|
32
+ res.size.should >= 1
33
+ res[0]["hello"].should == "world"
34
+ done
35
+ end
36
+ end
37
+
38
+ it 'should take strings or symbols for hashes' do
39
+ @conn, @coll = connection_and_collection
40
+
41
+ obj = @coll.insert({:_id => 1234, 'foo' => 'bar', :hello => 'world'})
42
+ @coll.first({:_id => 1234},{}) do |res|
43
+ res['hello'].should == 'world'
44
+ res['foo'].should == 'bar'
45
+ done
46
+ end
37
47
  end
38
48
 
39
49
  it 'should find an object by symbol' do
40
- @conn, @coll = connection_and_collection
50
+ @conn, @coll = connection_and_collection
41
51
 
42
- @coll.insert('hello' => 'world')
43
- r = @coll.find({:hello => "world"},{}) do |res|
44
- res.size.should >= 1
45
- res[0]["hello"].should == "world"
46
- done
47
- end
52
+ @coll.insert('hello' => 'world')
53
+ @coll.find({:hello => "world"},{}) do |res|
54
+ res.size.should >= 1
55
+ res[0]["hello"].should == "world"
56
+ done
57
+ end
48
58
  end
49
59
 
50
60
  it 'should find an object by id' do
51
- @conn, @coll = connection_and_collection
61
+ @conn, @coll = connection_and_collection
52
62
 
53
- obj = @coll.insert('hello' => 'world')
54
- @coll.find({'_id' => obj['_id']},{}) do |res|
55
- res.size.should >= 1
56
- res[0]['hello'].should == "world"
57
- done
58
- end
63
+ id = @coll.insert('hello' => 'world')
64
+ @coll.find({:_id => id},{}) do |res|
65
+ res.size.should >= 1
66
+ res[0]['hello'].should == "world"
67
+ done
68
+ end
59
69
  end
60
70
 
61
71
  it 'should find all objects' do
62
- @conn, @coll = connection_and_collection
72
+ @conn, @coll = connection_and_collection
63
73
 
64
- @coll.insert('one' => 'one')
65
- @coll.insert('two' => 'two')
66
- @coll.find do |res|
67
- res.size.should >= 2
68
- done
69
- end
74
+ @coll.insert('one' => 'one')
75
+ @coll.insert('two' => 'two')
76
+ @coll.find do |res|
77
+ res.size.should >= 2
78
+ done
79
+ end
70
80
  end
71
81
 
72
82
  it 'should find large sets of objects' do
@@ -85,9 +95,9 @@ describe EMMongo::Collection do
85
95
  it 'should update an object' do
86
96
  @conn, @coll = connection_and_collection
87
97
 
88
- obj = @coll.insert('hello' => 'world')
98
+ id = @coll.insert('hello' => 'world')
89
99
  @coll.update({'hello' => 'world'}, {'hello' => 'newworld'})
90
- @coll.find({'_id' => obj['_id']},{}) do |res|
100
+ @coll.find({:_id => id},{}) do |res|
91
101
  res[0]['hello'].should == 'newworld'
92
102
  done
93
103
  end
@@ -96,9 +106,9 @@ describe EMMongo::Collection do
96
106
  it 'should update an object wxith $inc' do
97
107
  @conn, @coll = connection_and_collection
98
108
 
99
- obj = @coll.insert('hello' => 'world')
109
+ id = @coll.insert('hello' => 'world')
100
110
  @coll.update({'hello' => 'world'}, {'$inc' => {'count' => 1}})
101
- @coll.find({'_id' => obj['_id']},{}) do |res|
111
+ @coll.find({:_id => id},{}) do |res|
102
112
  res.first['hello'].should == 'world'
103
113
  res.first['count'].should == 1
104
114
  done
@@ -108,8 +118,8 @@ describe EMMongo::Collection do
108
118
  it 'should remove an object' do
109
119
  @conn, @coll = connection_and_collection
110
120
 
111
- obj = @coll.insert('hello' => 'world')
112
- @coll.remove('_id' => obj['_id'])
121
+ id = @coll.insert('hello' => 'world')
122
+ @coll.remove(:_id => id)
113
123
  @coll.find({'hello' => "world"}) do |res|
114
124
  res.size.should == 0
115
125
  done
@@ -153,9 +163,10 @@ describe EMMongo::Collection do
153
163
  'regex' => /abc$/ix
154
164
  }
155
165
  retobj = @coll.insert(obj)
156
- @coll.find({'_id' => obj['_id']}) do |ret|
166
+ @coll.find({:_id => obj[:_id]}) do |ret|
157
167
  ret.size.should == 1
158
168
  ret[0].each_key do |key|
169
+ next if key == '_id'
159
170
  ret[0][key].should == obj[key]
160
171
  end
161
172
  done
@@ -209,7 +220,7 @@ describe EMMongo::Collection do
209
220
  it 'should handle multiple pending queries' do
210
221
  @conn, @coll = connection_and_collection
211
222
 
212
- id = @coll.insert("foo" => "bar")['_id']
223
+ id = @coll.insert("foo" => "bar")
213
224
  received = 0
214
225
 
215
226
  10.times do |n|
@@ -0,0 +1,27 @@
1
+ require File.expand_path('spec_helper', File.dirname(__FILE__) + '/../')
2
+
3
+ describe EMMongo::Database do
4
+ include EM::Spec
5
+
6
+ it 'should add a user' do
7
+ @conn = EM::Mongo::Connection.new
8
+ @db = @conn.db
9
+ @db.add_user('test', 'test') do |res|
10
+ res.should_not == nil
11
+ res.should be_a_kind_of(BSON::ObjectId)
12
+ done
13
+ end
14
+ end
15
+
16
+ # This test requires the above test.
17
+ it 'should authenticate a user' do
18
+ @conn = EM::Mongo::Connection.new
19
+ @db = @conn.db
20
+ @db.authenticate('test', 'test') do |res|
21
+ res.should == true
22
+ done
23
+ end
24
+ end
25
+
26
+
27
+ end
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 2
8
- - 14
9
- version: 0.2.14
7
+ - 3
8
+ - 1
9
+ version: 0.3.1
10
10
  platform: ruby
11
11
  authors:
12
12
  - bcg
@@ -47,7 +47,22 @@ dependencies:
47
47
  version: 0.20.1
48
48
  type: :runtime
49
49
  version_requirements: *id002
50
- description: EventMachine drive for MongoDB.
50
+ - !ruby/object:Gem::Dependency
51
+ name: bson_ext
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ segments:
59
+ - 0
60
+ - 20
61
+ - 1
62
+ version: 0.20.1
63
+ type: :runtime
64
+ version_requirements: *id003
65
+ description: EventMachine driver for MongoDB.
51
66
  email: brenden.grace@gmail.com
52
67
  executables: []
53
68
 
@@ -56,11 +71,16 @@ extensions: []
56
71
  extra_rdoc_files: []
57
72
 
58
73
  files:
74
+ - VERSION
59
75
  - lib/em-mongo/collection.rb
60
76
  - lib/em-mongo/connection.rb
77
+ - lib/em-mongo/conversions.rb
78
+ - lib/em-mongo/database.rb
79
+ - lib/em-mongo/support.rb
61
80
  - lib/em-mongo.rb
62
81
  - spec/integration/collection_spec.rb
63
82
  - spec/integration/connection_spec.rb
83
+ - spec/integration/database_spec.rb
64
84
  has_rdoc: true
65
85
  homepage:
66
86
  licenses: []
@@ -96,3 +116,4 @@ summary: EventMachine driver for MongoDB.
96
116
  test_files:
97
117
  - spec/integration/collection_spec.rb
98
118
  - spec/integration/connection_spec.rb
119
+ - spec/integration/database_spec.rb