mongodoc 0.0.0 → 0.1.0
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 +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
|
|