mongodoc 0.0.0 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +44 -3
- data/VERSION +1 -1
- data/features/step_definitions/collection_steps.rb +1 -1
- data/features/step_definitions/criteria_steps.rb +79 -0
- data/features/step_definitions/document_steps.rb +0 -1
- data/features/step_definitions/documents.rb +19 -0
- data/features/step_definitions/json_steps.rb +4 -4
- data/features/step_definitions/object_steps.rb +11 -4
- data/features/step_definitions/objects.rb +24 -0
- data/features/support/support.rb +0 -2
- data/features/using_criteria.feature +146 -0
- data/lib/mongodoc/attributes.rb +69 -71
- data/lib/mongodoc/collection.rb +45 -0
- data/lib/mongodoc/connection.rb +2 -2
- data/lib/mongodoc/criteria.rb +485 -0
- data/lib/mongodoc/cursor.rb +24 -0
- data/lib/mongodoc/document.rb +138 -0
- data/lib/mongodoc/parent_proxy.rb +24 -26
- data/lib/mongodoc/proxy.rb +55 -57
- data/lib/mongodoc.rb +5 -1
- data/mongodoc.gemspec +22 -14
- data/spec/attributes_spec.rb +14 -14
- data/spec/bson_spec.rb +23 -143
- data/spec/collection_spec.rb +180 -0
- data/spec/connection_spec.rb +11 -1
- data/spec/criteria_spec.rb +846 -0
- data/spec/cursor_spec.rb +81 -0
- data/spec/{base_ext.rb → document_ext.rb} +1 -1
- data/spec/document_spec.rb +678 -0
- data/spec/parent_proxy_spec.rb +4 -4
- data/spec/spec_helper.rb +1 -3
- metadata +20 -12
- data/lib/mongodoc/base.rb +0 -163
- data/lib/mongodoc/value_equals.rb +0 -8
- data/spec/base_spec.rb +0 -273
- data/spec/test_classes.rb +0 -19
- data/spec/test_documents.rb +0 -35
data/README.rdoc
CHANGED
@@ -1,16 +1,57 @@
|
|
1
1
|
= mongodoc
|
2
2
|
|
3
|
-
|
3
|
+
Version: 0.1 12/7/09
|
4
|
+
|
5
|
+
== Introduction
|
6
|
+
|
7
|
+
mongodoc is yet another ODM for MongoDB in Ruby.
|
8
|
+
|
9
|
+
Here is an example:
|
10
|
+
|
11
|
+
require 'mongodoc'
|
12
|
+
|
13
|
+
class Address < MongoDoc::Document
|
14
|
+
key :street
|
15
|
+
key :city
|
16
|
+
key :state
|
17
|
+
key :zip_code
|
18
|
+
end
|
19
|
+
|
20
|
+
class Contact < MongoDoc::Document
|
21
|
+
key :name
|
22
|
+
key :interests
|
23
|
+
has_many :addresses
|
24
|
+
end
|
25
|
+
|
26
|
+
MongoDoc.connect
|
27
|
+
contact = Contact.new(:name => 'Joe Strummer', :interests => ['music', 'art', 'acting'])
|
28
|
+
contact.addresses << Address.new(:street => '1 Main Street', :city => 'Anywhere', :state => 'ID', :zip_code => '56789')
|
29
|
+
contact.save
|
30
|
+
|
31
|
+
Contact.criteria.in({'interests' => ['ruby', 'art']}).first
|
32
|
+
Contact.criteria.where('addresses.state' => 'ID').first
|
33
|
+
|
34
|
+
== Installation
|
35
|
+
|
36
|
+
== Credits
|
37
|
+
|
38
|
+
Les Hill, leshill on github
|
39
|
+
|
40
|
+
mongodoc
|
41
|
+
|
42
|
+
=== Thanks
|
43
|
+
|
44
|
+
Thanks to Sandro and Durran for some great conversations and some lovely code.
|
4
45
|
|
5
46
|
== Note on Patches/Pull Requests
|
6
|
-
|
47
|
+
|
7
48
|
* Fork the project.
|
8
49
|
* Make your feature addition or bug fix.
|
9
50
|
* Add tests for it. This is important so I don't break it in a
|
10
51
|
future version unintentionally.
|
11
52
|
* Commit, do not mess with rakefile, version, or history.
|
12
53
|
(if you want to have your own version, that is fine but
|
13
|
-
|
54
|
+
bump version in a commit by itself I can ignore when I pull)
|
14
55
|
* Send me a pull request. Bonus points for topic branches.
|
15
56
|
|
16
57
|
== Copyright
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.1.0
|
@@ -0,0 +1,79 @@
|
|
1
|
+
def query(klass_name = nil)
|
2
|
+
return @query if @query or klass_name.nil?
|
3
|
+
klass = klass_name.singularize.camelize.constantize
|
4
|
+
@query = klass.criteria
|
5
|
+
end
|
6
|
+
|
7
|
+
# When /^I query (.*) with all "([^\"]*)"$/ do |doc, selections_text|
|
8
|
+
# selections = eval(selections_text)
|
9
|
+
# query(doc).all(selections)
|
10
|
+
# end
|
11
|
+
|
12
|
+
When /^I query (.*) to select fields? "([^\"]*)"$/ do |doc, fields|
|
13
|
+
fields = fields.split
|
14
|
+
query(doc).select(*fields)
|
15
|
+
end
|
16
|
+
|
17
|
+
When /^I query (.*) where "([^\"]*)"$/ do |doc, where_text|
|
18
|
+
where = eval(where_text)
|
19
|
+
query(doc).where(where)
|
20
|
+
end
|
21
|
+
|
22
|
+
When /^I query (.*) that excludes "([^\"]*)"$/ do |doc, exclude_text|
|
23
|
+
exclude = eval(exclude_text)
|
24
|
+
query(doc).excludes(exclude)
|
25
|
+
end
|
26
|
+
|
27
|
+
When /^I set the query on (.*) to (limit|skip) (.*)$/ do |doc, op, count|
|
28
|
+
query(doc).send(op, count.to_i)
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
When /^I query (.*) with (every|not in|in) "([^\"]*)"$/ do |doc, op, hash_text|
|
33
|
+
hash = eval(hash_text)
|
34
|
+
query(doc).send(op.gsub(' ', '_'), hash)
|
35
|
+
end
|
36
|
+
|
37
|
+
When /^I query (.*) with '(.*)' id$/ do |doc, name|
|
38
|
+
object = instance_variable_get("@#{name}")
|
39
|
+
query(doc).id(object.id)
|
40
|
+
end
|
41
|
+
|
42
|
+
When /^I set the query extras limit on (.*) to (.*)$/ do |doc, count|
|
43
|
+
query(doc).limit(count.to_i)
|
44
|
+
end
|
45
|
+
|
46
|
+
When /^I order the (.*) query by "([^\"]*)"$/ do |doc, order_text|
|
47
|
+
order = eval(order_text)
|
48
|
+
query(doc).order_by(order)
|
49
|
+
end
|
50
|
+
|
51
|
+
Then /^the (first|last) query result is equal to the document '(.*)'$/ do |position, name|
|
52
|
+
object = instance_variable_get("@#{name}")
|
53
|
+
query.send(position).should == object
|
54
|
+
end
|
55
|
+
|
56
|
+
Then /^one of the query results is the document '(.*)'$/ do |name|
|
57
|
+
object = instance_variable_get("@#{name}")
|
58
|
+
query.any? {|doc| doc == object}
|
59
|
+
end
|
60
|
+
|
61
|
+
Then /^the aggregate query result with "(.*)" == "(.*)" has a count of (.*)$/ do |key, value, count|
|
62
|
+
result = query.aggregate
|
63
|
+
result.find {|r| r.has_key?(key) and r[key] == value }['count'].should == count.to_i
|
64
|
+
end
|
65
|
+
|
66
|
+
Then /^the query result has (.*) documents*$/ do |count|
|
67
|
+
query.count.should == count.to_i
|
68
|
+
end
|
69
|
+
|
70
|
+
Then /^the size of the query result is (.*)$/ do |count|
|
71
|
+
query.to_a.size.should == count.to_i
|
72
|
+
end
|
73
|
+
|
74
|
+
Then /^the group query result with "([^\"]*)" == "([^\"]*)" has the document '(.*)'$/ do |key, value, name|
|
75
|
+
object = instance_variable_get("@#{name}")
|
76
|
+
result = query.group
|
77
|
+
result.find {|r| r.has_key?(key) and r[key] == value }['group'].should include(object)
|
78
|
+
end
|
79
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Address < MongoDoc::Document
|
2
|
+
key :street
|
3
|
+
key :city
|
4
|
+
key :state
|
5
|
+
key :zip_code
|
6
|
+
end
|
7
|
+
|
8
|
+
class Place < MongoDoc::Document
|
9
|
+
key :name
|
10
|
+
key :type
|
11
|
+
has_one :address
|
12
|
+
end
|
13
|
+
|
14
|
+
class Contact < MongoDoc::Document
|
15
|
+
key :name
|
16
|
+
key :type
|
17
|
+
key :interests
|
18
|
+
has_many :addresses
|
19
|
+
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
When /^I save the json '(\{.*\})'$/ do |json_text|
|
2
|
-
|
3
|
-
@last_save = @collection.save(
|
2
|
+
json = JSON.parse(json_text)
|
3
|
+
@last_save = @collection.save(json)
|
4
4
|
end
|
5
5
|
|
6
6
|
Then /^the json '(\{.*\})' roundtrips$/ do |json_text|
|
7
|
-
|
8
|
-
|
7
|
+
json = JSON.parse(json_text)
|
8
|
+
@collection.find_one(@last_save).should be_mongo_eql(json, false)
|
9
9
|
end
|
@@ -22,22 +22,29 @@ Given /^a hash named '(.*)':$/ do |name, table|
|
|
22
22
|
@all << @last
|
23
23
|
end
|
24
24
|
instance_variable_set("@#{name}", @last)
|
25
|
-
|
26
25
|
end
|
27
26
|
|
27
|
+
Given /^'(.*)' has (.*), an array of:$/ do |name, attribute, table|
|
28
|
+
object = instance_variable_get("@#{name}")
|
29
|
+
object.send(attribute + "=", [])
|
30
|
+
table.hashes.each do |hash|
|
31
|
+
hash.each {|key, value| object.send(attribute) << value}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
28
36
|
When /^I save the object '(.*)'$/ do |name|
|
29
37
|
object = instance_variable_get("@#{name}")
|
30
|
-
@last_save = @collection.save(object
|
38
|
+
@last_save = @collection.save(object)
|
31
39
|
end
|
32
40
|
|
33
41
|
Then /^the object '(.*)' roundtrips$/ do |name|
|
34
42
|
object = instance_variable_get("@#{name}")
|
35
43
|
object.instance_variable_set("@_id", @last_save)
|
36
|
-
|
44
|
+
@collection.find_one(@last_save).should == object
|
37
45
|
end
|
38
46
|
|
39
47
|
Then /^the attribute '(.*)' of '(.*)' is '(.*)'$/ do |attr, var, value|
|
40
48
|
object = instance_variable_get("@#{var}")
|
41
49
|
object.send(attr).to_s.should == value
|
42
50
|
end
|
43
|
-
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ValueEquals
|
2
|
+
def ==(other)
|
3
|
+
return false unless instance_variables.size == other.instance_variables.size
|
4
|
+
instance_variables.all? {|var| self.instance_variable_get(var) == other.instance_variable_get(var)}
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class Movie
|
9
|
+
include ValueEquals
|
10
|
+
|
11
|
+
attr_accessor :title, :director, :writers
|
12
|
+
end
|
13
|
+
|
14
|
+
class Director
|
15
|
+
include ValueEquals
|
16
|
+
|
17
|
+
attr_accessor :name, :awards
|
18
|
+
end
|
19
|
+
|
20
|
+
class AcademyAward
|
21
|
+
include ValueEquals
|
22
|
+
|
23
|
+
attr_accessor :year, :category
|
24
|
+
end
|
data/features/support/support.rb
CHANGED
@@ -3,7 +3,5 @@ require 'cucumber'
|
|
3
3
|
require 'spec/expectations'
|
4
4
|
require 'spec/bson_matchers'
|
5
5
|
require 'mongodoc'
|
6
|
-
require File.join(File.dirname(__FILE__), '..', '..', 'spec', 'test_classes')
|
7
|
-
require File.join(File.dirname(__FILE__), '..', '..', 'spec', 'test_documents')
|
8
6
|
|
9
7
|
World(BsonMatchers)
|
@@ -0,0 +1,146 @@
|
|
1
|
+
Feature: MongoDoc::Base
|
2
|
+
|
3
|
+
Background:
|
4
|
+
Given a valid connection to the 'test' database
|
5
|
+
And an empty Contact document collection
|
6
|
+
And a Contact document named 'hashrocket' :
|
7
|
+
| Name | Type |
|
8
|
+
| Hashrocket | company |
|
9
|
+
And 'hashrocket' has interests, an array of:
|
10
|
+
| Interest |
|
11
|
+
| ruby |
|
12
|
+
| rails |
|
13
|
+
| employment |
|
14
|
+
| contract work |
|
15
|
+
| restaurants |
|
16
|
+
| hotels |
|
17
|
+
| flights |
|
18
|
+
| car rentals |
|
19
|
+
And 'hashrocket' has many addresses :
|
20
|
+
| Street | City | State | Zip Code |
|
21
|
+
| 320 First Street North | Jacksonville Beach | FL | 32250 |
|
22
|
+
| 1 Lake Michigan Street | Chicago | IL | 60611 |
|
23
|
+
| 1 Main Street | Santiago | Chile | |
|
24
|
+
And I save the document 'hashrocket'
|
25
|
+
And a Contact document named 'rocketeer' :
|
26
|
+
| Name |
|
27
|
+
| Rocketeer Mike |
|
28
|
+
And 'rocketeer' has interests, an array of:
|
29
|
+
| Interest |
|
30
|
+
| ruby |
|
31
|
+
| rails |
|
32
|
+
| restaurants |
|
33
|
+
| employment |
|
34
|
+
And 'rocketeer' has many addresses :
|
35
|
+
| Street | City | State | Zip Code |
|
36
|
+
| 1 Main Street | Atlantic Beach | FL | 32233 |
|
37
|
+
And I save the document 'rocketeer'
|
38
|
+
And a Contact document named 'contractor' :
|
39
|
+
| Name |
|
40
|
+
| Contractor Joe |
|
41
|
+
And 'contractor' has interests, an array of:
|
42
|
+
| Interest |
|
43
|
+
| ruby |
|
44
|
+
| rails |
|
45
|
+
| contract work |
|
46
|
+
| flights |
|
47
|
+
| car rentals |
|
48
|
+
| hotels |
|
49
|
+
| restaurants |
|
50
|
+
And 'contractor' has many addresses :
|
51
|
+
| Street | City | State | Zip Code |
|
52
|
+
| 1 Main St. | Jacksonville | FL | 32218 |
|
53
|
+
And I save the document 'contractor'
|
54
|
+
And an empty Place document collection
|
55
|
+
And a Place document named 'one_ocean' :
|
56
|
+
| Name | Type |
|
57
|
+
| One Ocean | hotel |
|
58
|
+
And 'one_ocean' has one Address as address :
|
59
|
+
| Street | City | State | Zip Code |
|
60
|
+
| 1 Ocean Street | Atlantic Beach | FL | 32233 |
|
61
|
+
And I save the document 'one_ocean'
|
62
|
+
And a Place document named 'sea_horse' :
|
63
|
+
| Name | Type |
|
64
|
+
| Sea Horse | hotel |
|
65
|
+
And 'sea_horse' has one Address as address :
|
66
|
+
| Street | City | State | Zip Code |
|
67
|
+
| 1401 Atlantic Blvd | Neptune Beach | FL | 32266 |
|
68
|
+
And I save the document 'sea_horse'
|
69
|
+
And a Place document named 'jax' :
|
70
|
+
| Name | Type |
|
71
|
+
| Jacksonville International Airport | airport |
|
72
|
+
And 'jax' has one Address as address :
|
73
|
+
| Street | City | State | Zip Code |
|
74
|
+
| | Jacksonville | FL | 32218 |
|
75
|
+
And I save the document 'jax'
|
76
|
+
|
77
|
+
Scenario: Counting results
|
78
|
+
When I query contacts with every "{'interests' => ['ruby', 'rails', 'employment']}"
|
79
|
+
Then the query result has 2 documents
|
80
|
+
|
81
|
+
Scenario: Finding contacts with interests in ruby and rails
|
82
|
+
When I query contacts with every "{'interests' => ['ruby', 'rails', 'employment']}"
|
83
|
+
Then the query result has 2 documents
|
84
|
+
And one of the query results is the document 'rocketeer'
|
85
|
+
|
86
|
+
Scenario: Finding contacts with interests in restaurants and hotels
|
87
|
+
When I query contacts with every "{'interests' => ['ruby', 'rails', 'employment']}"
|
88
|
+
Then the query result has 2 documents
|
89
|
+
And one of the query results is the document 'contractor'
|
90
|
+
|
91
|
+
Scenario: Aggregating Places
|
92
|
+
When I query places to select field "type"
|
93
|
+
And I query places where "{'address.state' => 'FL'}"
|
94
|
+
Then the aggregate query result with "type" == "hotel" has a count of 2
|
95
|
+
|
96
|
+
Scenario: Excluding places in Neptune Beach
|
97
|
+
When I query places to select field "type"
|
98
|
+
And I query places that excludes "{'address.city' => 'Neptune Beach'}"
|
99
|
+
Then the aggregate query result with "type" == "hotel" has a count of 1
|
100
|
+
|
101
|
+
Scenario: Using extras to limit results
|
102
|
+
When I query contacts with every "{'interests' => ['ruby', 'rails', 'employment']}"
|
103
|
+
And I set the query extras limit on contacts to 1
|
104
|
+
Then the size of the query result is 1
|
105
|
+
|
106
|
+
Scenario: Finding the first result
|
107
|
+
When I query contacts with every "{'interests' => ['ruby', 'rails', 'employment']}"
|
108
|
+
Then the first query result is equal to the document 'hashrocket'
|
109
|
+
|
110
|
+
Scenario: Grouping places by type
|
111
|
+
When I query places to select field "type"
|
112
|
+
And I query places where "{'type' => 'hotel'}"
|
113
|
+
Then the group query result with "type" == "hotel" has the document 'one_ocean'
|
114
|
+
|
115
|
+
Scenario: Selecting contacts with in operator
|
116
|
+
When I query contacts with in "{'interests' => ['ruby', 'rails', 'employment']}"
|
117
|
+
Then the query result has 3 documents
|
118
|
+
|
119
|
+
Scenario: Selecting a contact with the id operator
|
120
|
+
When I query contacts with 'hashrocket' id
|
121
|
+
Then the query result has 1 documents
|
122
|
+
And the first query result is equal to the document 'hashrocket'
|
123
|
+
|
124
|
+
Scenario: Finding the last result
|
125
|
+
When I query contacts with every "{'interests' => ['ruby', 'rails', 'employment']}"
|
126
|
+
Then the last query result is equal to the document 'rocketeer'
|
127
|
+
|
128
|
+
Scenario: Using limit on results
|
129
|
+
When I query contacts with every "{'interests' => ['ruby', 'rails', 'employment']}"
|
130
|
+
And I set the query on contacts to limit 1
|
131
|
+
Then the size of the query result is 1
|
132
|
+
|
133
|
+
Scenario: Selecting contacts with not in operator
|
134
|
+
When I query contacts with not in "{'interests' => ['contract work', 'employment']}"
|
135
|
+
Then the query result has 0 documents
|
136
|
+
|
137
|
+
Scenario: Ordering contacts
|
138
|
+
When I query contacts with in "{'interests' => ['ruby', 'rails']}"
|
139
|
+
And I order the contacts query by "[[:name, :asc]]"
|
140
|
+
Then the first query result is equal to the document 'contractor'
|
141
|
+
Then the last query result is equal to the document 'rocketeer'
|
142
|
+
|
143
|
+
Scenario: Using skip on results
|
144
|
+
When I query contacts with every "{'interests' => ['ruby', 'rails']}"
|
145
|
+
And I set the query on contacts to skip 1
|
146
|
+
Then the size of the query result is 2
|
data/lib/mongodoc/attributes.rb
CHANGED
@@ -2,96 +2,94 @@ require 'mongodoc/proxy'
|
|
2
2
|
require 'mongodoc/parent_proxy'
|
3
3
|
|
4
4
|
module MongoDoc
|
5
|
-
module
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
5
|
+
module Attributes
|
6
|
+
def self.extended(klass)
|
7
|
+
klass.class_inheritable_array :_keys
|
8
|
+
klass._keys = []
|
9
|
+
klass.class_inheritable_array :_associations
|
10
|
+
klass._associations = []
|
11
|
+
|
12
|
+
klass.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
13
|
+
attr_accessor :_parent
|
14
|
+
|
15
|
+
def _root
|
16
|
+
@_root
|
17
|
+
end
|
19
18
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
19
|
+
def _root=(root)
|
20
|
+
@_root = root
|
21
|
+
_associations.each do|a|
|
22
|
+
association = send(a)
|
23
|
+
association._root = root if association
|
26
24
|
end
|
25
|
+
end
|
27
26
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
27
|
+
def path_to_root(prev)
|
28
|
+
return prev unless _parent
|
29
|
+
_parent.path_to_root(prev)
|
30
|
+
end
|
31
|
+
RUBY
|
32
|
+
end
|
34
33
|
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
def _attributes
|
35
|
+
_keys + _associations
|
36
|
+
end
|
38
37
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
38
|
+
def key(*args)
|
39
|
+
args.each do |name|
|
40
|
+
_keys << name unless _keys.include?(name)
|
41
|
+
attr_accessor name
|
44
42
|
end
|
43
|
+
end
|
45
44
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
define_method("#{name}=") do |value|
|
52
|
-
if value
|
53
|
-
raise NotADocumentError unless Document === value
|
54
|
-
value._parent = ParentProxy.new(self, name)
|
55
|
-
value._root = _root || self
|
56
|
-
end
|
57
|
-
instance_variable_set("@#{name}", value)
|
58
|
-
end
|
45
|
+
def has_one(*args)
|
46
|
+
args.each do |name|
|
47
|
+
_associations << name unless _associations.include?(name)
|
48
|
+
attr_reader name
|
59
49
|
|
60
|
-
|
50
|
+
define_method("#{name}=") do |value|
|
51
|
+
if value
|
52
|
+
raise NotADocumentError unless Document === value
|
53
|
+
value._parent = ParentProxy.new(self, name)
|
54
|
+
value._root = _root || self
|
55
|
+
end
|
56
|
+
instance_variable_set("@#{name}", value)
|
61
57
|
end
|
58
|
+
|
59
|
+
validates_associated name
|
62
60
|
end
|
61
|
+
end
|
63
62
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
63
|
+
def has_many(*args)
|
64
|
+
options = args.extract_options!
|
65
|
+
collection_class = if class_name = options.delete(:class_name)
|
66
|
+
type_name_with_module(class_name).constantize
|
67
|
+
end
|
69
68
|
|
70
|
-
|
71
|
-
|
69
|
+
args.each do |name|
|
70
|
+
_associations << name unless _associations.include?(name)
|
72
71
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
end
|
79
|
-
association
|
72
|
+
define_method("#{name}") do
|
73
|
+
association = instance_variable_get("@#{name}")
|
74
|
+
unless association
|
75
|
+
association = Proxy.new(:root => _root || self, :parent => self, :assoc_name => name, :collection_class => collection_class || self.class.type_name_with_module(name.to_s.classify).constantize)
|
76
|
+
instance_variable_set("@#{name}", association)
|
80
77
|
end
|
78
|
+
association
|
79
|
+
end
|
81
80
|
|
82
|
-
|
81
|
+
validates_associated name
|
83
82
|
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
end
|
83
|
+
define_method("#{name}=") do |array|
|
84
|
+
proxy = send("#{name}")
|
85
|
+
proxy.clear
|
86
|
+
proxy << array
|
89
87
|
end
|
90
88
|
end
|
89
|
+
end
|
91
90
|
|
92
|
-
|
93
|
-
|
94
|
-
end
|
91
|
+
def type_name_with_module(type_name)
|
92
|
+
(/^::/ =~ type_name) ? type_name : "#{parents}::#{type_name}"
|
95
93
|
end
|
96
94
|
end
|
97
95
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'mongodoc/cursor'
|
2
|
+
|
3
|
+
module MongoDoc
|
4
|
+
class Collection
|
5
|
+
attr_accessor :_collection
|
6
|
+
delegate :[], :clear, :count, :create_index, :db, :drop, :drop_index, :drop_indexes, :group, :hint, :index_information, :name, :options, :remove, :rename, :size, :to => :_collection
|
7
|
+
|
8
|
+
def initialize(name)
|
9
|
+
self._collection = self.class.mongo_collection(name)
|
10
|
+
end
|
11
|
+
|
12
|
+
def find(query = {}, options = {})
|
13
|
+
cursor = MongoDoc::Cursor.new(_collection.find(query, options))
|
14
|
+
if block_given?
|
15
|
+
yield cursor
|
16
|
+
cursor.close
|
17
|
+
else
|
18
|
+
cursor
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def find_one(spec_or_object_id = nil, options = {})
|
23
|
+
MongoDoc::BSON.decode(_collection.find_one(spec_or_object_id, options))
|
24
|
+
end
|
25
|
+
|
26
|
+
def insert(doc_or_docs, options = {})
|
27
|
+
_collection.insert(doc_or_docs.to_bson, options)
|
28
|
+
end
|
29
|
+
alias :<< :insert
|
30
|
+
|
31
|
+
def save(doc, options = {})
|
32
|
+
_collection.save(doc.to_bson, options)
|
33
|
+
end
|
34
|
+
|
35
|
+
def update(spec, doc, options = {})
|
36
|
+
_collection.update(spec, doc.to_bson, options)
|
37
|
+
result = MongoDoc.database.db_command({'getlasterror' => 1})
|
38
|
+
(result and result.has_key?('updatedExisting')) ? result['updatedExisting'] : false
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.mongo_collection(name)
|
42
|
+
MongoDoc.database.collection(name)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/mongodoc/connection.rb
CHANGED
@@ -3,13 +3,13 @@ module MongoDoc
|
|
3
3
|
if name
|
4
4
|
@@database = connection.db(name)
|
5
5
|
else
|
6
|
-
raise NoDatabaseError unless defined? @@database
|
6
|
+
raise NoDatabaseError unless defined? @@database and @@database
|
7
7
|
@@database
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
11
|
def self.connection
|
12
|
-
raise NoConnectionError unless defined? @@connection
|
12
|
+
raise NoConnectionError unless defined? @@connection and @@connection
|
13
13
|
@@connection
|
14
14
|
end
|
15
15
|
|