mongodoc 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +143 -0
- data/Rakefile +35 -3
- data/VERSION +1 -1
- data/examples/simple_document.rb +35 -0
- data/examples/simple_object.rb +32 -0
- data/features/finders.feature +72 -0
- data/features/mongodoc_base.feature +12 -2
- data/features/named_scopes.feature +66 -0
- data/features/new_record.feature +36 -0
- data/features/partial_updates.feature +105 -0
- data/features/step_definitions/criteria_steps.rb +4 -41
- data/features/step_definitions/document_steps.rb +56 -5
- data/features/step_definitions/documents.rb +14 -3
- data/features/step_definitions/finder_steps.rb +15 -0
- data/features/step_definitions/named_scope_steps.rb +18 -0
- data/features/step_definitions/partial_update_steps.rb +32 -0
- data/features/step_definitions/query_steps.rb +51 -0
- data/features/using_criteria.feature +5 -1
- data/lib/mongodoc/attributes.rb +76 -63
- data/lib/mongodoc/collection.rb +9 -9
- data/lib/mongodoc/criteria.rb +152 -161
- data/lib/mongodoc/cursor.rb +7 -5
- data/lib/mongodoc/document.rb +95 -31
- data/lib/mongodoc/finders.rb +29 -0
- data/lib/mongodoc/named_scope.rb +68 -0
- data/lib/mongodoc/parent_proxy.rb +15 -6
- data/lib/mongodoc/proxy.rb +22 -13
- data/lib/mongodoc.rb +3 -3
- data/mongodoc.gemspec +42 -14
- data/perf/mongodoc_runner.rb +90 -0
- data/perf/ruby_driver_runner.rb +64 -0
- data/spec/attributes_spec.rb +46 -12
- data/spec/collection_spec.rb +23 -23
- data/spec/criteria_spec.rb +124 -187
- data/spec/cursor_spec.rb +21 -17
- data/spec/document_ext.rb +2 -2
- data/spec/document_spec.rb +187 -218
- data/spec/embedded_save_spec.rb +104 -0
- data/spec/finders_spec.rb +81 -0
- data/spec/hash_matchers.rb +27 -0
- data/spec/named_scope_spec.rb +82 -0
- data/spec/new_record_spec.rb +216 -0
- data/spec/parent_proxy_spec.rb +8 -6
- data/spec/proxy_spec.rb +80 -0
- data/spec/spec_helper.rb +2 -0
- metadata +35 -7
- data/README.rdoc +0 -75
data/README.textile
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
h1. MongoDoc
|
2
|
+
|
3
|
+
Version: 0.2 1/18/10
|
4
|
+
|
5
|
+
h2. Introduction
|
6
|
+
|
7
|
+
MongoDoc is simple and easy to use ActiveRecord-like object mapper for "mongoDB":http://www.mongodb.org in Ruby.
|
8
|
+
|
9
|
+
MongoDoc is _also_ an extension of the "Mongo Ruby Driver":http://github.com/mongodb/mongo-ruby-driver making it a snap to get Ruby in and out of mongoDB.
|
10
|
+
|
11
|
+
MongoDoc is *not* ActiveRecord for mongoDB. We do not have callbacks, nor do we have dynamic finders. We do have associations, named scopes, and other features.
|
12
|
+
|
13
|
+
MongoDoc *is* simple, easy-to-use, and fast. And it works with Rails (2.3.x at the moment, 3 soonish?).
|
14
|
+
|
15
|
+
MongoDoc is designed to work with document data, if you are looking to map relational data in mongoDB, you will have to look elsewhere.
|
16
|
+
|
17
|
+
h2. Ruby objects in mongoDB
|
18
|
+
|
19
|
+
Lets just get right into it and save some Ruby objects in mongoDB!
|
20
|
+
|
21
|
+
bc.. class Contact
|
22
|
+
attr_accessor :name, :addresses, :interests
|
23
|
+
end
|
24
|
+
|
25
|
+
class Address
|
26
|
+
attr_accessor :street, :city, :state, :zip, :phone_number
|
27
|
+
end
|
28
|
+
|
29
|
+
p. With MongoDoc, instead of saving JSON[1], we can save an object directly:
|
30
|
+
|
31
|
+
bc.. contact = Contact.new
|
32
|
+
contact.name = 'Hashrocket'
|
33
|
+
contact.interests = ['ruby', 'rails', 'agile']
|
34
|
+
|
35
|
+
address = Address.new
|
36
|
+
address.street = '320 First Street North, #712'
|
37
|
+
address.city = 'Jacksonville Beach'
|
38
|
+
address.state = 'FL'
|
39
|
+
address.zip = '32250'
|
40
|
+
address.phone_number = '877 885 8846'
|
41
|
+
contact.addresses = [address]
|
42
|
+
|
43
|
+
collection.save(contact)
|
44
|
+
|
45
|
+
p. We can query using the powerful mongoDB query syntax, and have it return Ruby objects:
|
46
|
+
|
47
|
+
bc.. results = collection.find('addresses.state' => 'FL')
|
48
|
+
hashrocket = results.to_a.find {|contact| contact.name == 'Hashrocket'}
|
49
|
+
puts hashrocket.addresses.first.phone_number
|
50
|
+
|
51
|
+
p. Take a look in the examples directory for more code.
|
52
|
+
|
53
|
+
h2. Mapping Documents
|
54
|
+
|
55
|
+
MongoDoc provides ActiveRecord-like persistence, associations, named scopes, and validations (from "Validatable":http://github.com/durran/validatable) as well as a mongoDB query language (from "Mongoid":http://mongoid.org/home). MongoDoc also plays nicely with Rails.
|
56
|
+
|
57
|
+
@MongoDoc::Document@ provides all these features as a mixin. A @MongoDoc::Document@ can either be a top-level mongoDB document, or an embedded document contained within a top-level document. Top-level documents are stored in collections named after their class: @Contact@ objects are stored in the 'contacts' collection (much like ActiveRecord).
|
58
|
+
|
59
|
+
Lets define a @Contact@ document with an @Address@ embedded document:
|
60
|
+
|
61
|
+
bc.. class Address
|
62
|
+
include MongoDoc::Document
|
63
|
+
|
64
|
+
key :street
|
65
|
+
key :city
|
66
|
+
key :state
|
67
|
+
key :zip_code
|
68
|
+
key :phone_number
|
69
|
+
end
|
70
|
+
|
71
|
+
class Contact
|
72
|
+
include MongoDoc::Document
|
73
|
+
|
74
|
+
key :name
|
75
|
+
key :interests
|
76
|
+
has_many :addresses
|
77
|
+
|
78
|
+
named_scope :in_state, lambda {|state| {:where => {'addresses.state' => state}}}
|
79
|
+
end
|
80
|
+
|
81
|
+
p. Since a mongoDB document has no fixed schema, we define the composition of a document directly in our classes. Please note we do not specify types! We can also specify @has_one@ or @has_many@ associations.
|
82
|
+
|
83
|
+
Building and saving a document is easy:
|
84
|
+
|
85
|
+
bc.. contact = Contact.new(:name => 'Hashrocket', :interests => ['ruby', 'rails', 'agile'])
|
86
|
+
contact.addresses << Address.new(:street => '320 1st Street North, #712',
|
87
|
+
:city => 'Jacksonville Beach',
|
88
|
+
:state => 'FL',
|
89
|
+
:zip_code => '32250',
|
90
|
+
:phone_number => '877 885 8846')
|
91
|
+
contact.save
|
92
|
+
|
93
|
+
p. Now that we have some data, we can query using our named scope:
|
94
|
+
|
95
|
+
bc. hashrocket = Contact.in_state('FL').find {|contact| contact.name == 'Hashrocket'}
|
96
|
+
|
97
|
+
p. And we can even perform partial updates:
|
98
|
+
|
99
|
+
bc. hashrocket.addresses.first.update_attributes(:street => '320 First Street North, #712')
|
100
|
+
|
101
|
+
h2. Installation
|
102
|
+
|
103
|
+
bc. sudo gem install mongodoc
|
104
|
+
|
105
|
+
h2. Configuration
|
106
|
+
|
107
|
+
Configure your database connection in ./mongodb.yml, you do not need one if you are running on localhost with the default port
|
108
|
+
|
109
|
+
bc. name: test
|
110
|
+
host: localhost
|
111
|
+
port: 27017
|
112
|
+
options:
|
113
|
+
auto_reconnect: true
|
114
|
+
|
115
|
+
You can change the location of the configuration file:
|
116
|
+
|
117
|
+
bc. MongoDoc.config_path = './config/mongodb.yml'
|
118
|
+
|
119
|
+
h2. Credits
|
120
|
+
|
121
|
+
Les Hill, leshill on github
|
122
|
+
|
123
|
+
h3. Thanks
|
124
|
+
|
125
|
+
Thanks to Sandro and Durran for some great conversations and some lovely code.
|
126
|
+
|
127
|
+
h2. Note on Patches/Pull Requests
|
128
|
+
|
129
|
+
* Fork the project.
|
130
|
+
* Make your feature addition or bug fix.
|
131
|
+
* Add tests for it. This is important so I don't break it in a
|
132
|
+
future version unintentionally.
|
133
|
+
* Commit, do not mess with rakefile, version, or history.
|
134
|
+
(if you want to have your own version, that is fine but
|
135
|
+
bump version in a commit by itself I can ignore when I pull)
|
136
|
+
* Send me a pull request. Bonus points for topic branches.
|
137
|
+
|
138
|
+
h2. Copyright
|
139
|
+
|
140
|
+
Copyright (c) 2009 Les Hill. See LICENSE for details.
|
141
|
+
|
142
|
+
fn1. The Ruby driver exposes an API that understands JSON.
|
143
|
+
|
data/Rakefile
CHANGED
@@ -10,9 +10,9 @@ begin
|
|
10
10
|
gem.email = "leshill@gmail.com"
|
11
11
|
gem.homepage = "http://github.com/leshill/mongodoc"
|
12
12
|
gem.authors = ["Les Hill"]
|
13
|
-
gem.add_dependency "mongo", "= 0.18.
|
14
|
-
gem.add_dependency "mongo_ext", "= 0.18.
|
15
|
-
gem.add_dependency "durran-validatable", "= 1.8.
|
13
|
+
gem.add_dependency "mongo", "= 0.18.2"
|
14
|
+
gem.add_dependency "mongo_ext", "= 0.18.2"
|
15
|
+
gem.add_dependency "durran-validatable", "= 1.8.4"
|
16
16
|
gem.add_dependency "leshill-will_paginate", "= 2.3.11"
|
17
17
|
gem.add_development_dependency "rspec", "= 1.2.9"
|
18
18
|
gem.add_development_dependency "cucumber", "= 0.4.4"
|
@@ -80,3 +80,35 @@ namespace :mongo do
|
|
80
80
|
puts "\n"
|
81
81
|
end
|
82
82
|
end
|
83
|
+
|
84
|
+
namespace :bench do
|
85
|
+
desc 'Run benchmark for MongoDoc'
|
86
|
+
task 'mongodoc' do
|
87
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
88
|
+
require 'perf/mongodoc_runner'
|
89
|
+
MongoDocRunner.benchmark
|
90
|
+
end
|
91
|
+
|
92
|
+
desc 'Run profiler for driver'
|
93
|
+
task 'driver' do
|
94
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
95
|
+
require 'perf/ruby_driver_runner'
|
96
|
+
RubyDriverRunner.benchmark
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
namespace :prof do
|
101
|
+
desc 'Run profiler for MongoDoc'
|
102
|
+
task 'mongodoc' do
|
103
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
104
|
+
require 'perf/mongodoc_runner'
|
105
|
+
MongoDocRunner.profile
|
106
|
+
end
|
107
|
+
|
108
|
+
desc 'Run profiler for driver'
|
109
|
+
task 'driver' do
|
110
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), 'lib'))
|
111
|
+
require 'perf/ruby_driver_runner'
|
112
|
+
RubyDriverRunner.profile
|
113
|
+
end
|
114
|
+
end
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'mongodoc'
|
2
|
+
|
3
|
+
class Address
|
4
|
+
include MongoDoc::Document
|
5
|
+
|
6
|
+
key :street
|
7
|
+
key :city
|
8
|
+
key :state
|
9
|
+
key :zip_code
|
10
|
+
key :phone_number
|
11
|
+
end
|
12
|
+
|
13
|
+
class Contact
|
14
|
+
include MongoDoc::Document
|
15
|
+
|
16
|
+
key :name
|
17
|
+
key :interests
|
18
|
+
has_many :addresses
|
19
|
+
|
20
|
+
named_scope :in_state, lambda {|state| {:where => {'addresses.state' => state}}}
|
21
|
+
end
|
22
|
+
|
23
|
+
MongoDoc.connect_to_database 'test'
|
24
|
+
Contact.collection.drop
|
25
|
+
|
26
|
+
contact = Contact.new(:name => 'Hashrocket', :interests => ['ruby', 'rails', 'agile'])
|
27
|
+
contact.addresses << Address.new(:street => '320 1st Street North, #712', :city => 'Jacksonville Beach', :state => 'FL', :zip_code => '32250', :phone_number => '877 885 8846')
|
28
|
+
contact.save
|
29
|
+
|
30
|
+
hashrocket = Contact.in_state('FL').find {|contact| contact.name == 'Hashrocket'}
|
31
|
+
|
32
|
+
hashrocket_address = hashrocket.addresses.first
|
33
|
+
hashrocket_address.update_attributes(:street => '320 First Street North, #712')
|
34
|
+
|
35
|
+
puts Contact.find_one(:where => {:name => 'Hashrocket'}).addresses.first.street
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'mongodoc'
|
2
|
+
|
3
|
+
class Contact
|
4
|
+
attr_accessor :name, :addresses, :interests
|
5
|
+
end
|
6
|
+
|
7
|
+
class Address
|
8
|
+
attr_accessor :street, :city, :state, :zip, :phone_number
|
9
|
+
end
|
10
|
+
|
11
|
+
MongoDoc.connect_to_database 'test'
|
12
|
+
|
13
|
+
collection = MongoDoc::Collection.new('contacts')
|
14
|
+
collection.drop
|
15
|
+
|
16
|
+
contact = Contact.new
|
17
|
+
contact.name = 'Hashrocket'
|
18
|
+
contact.interests = ['ruby', 'rails', 'agile']
|
19
|
+
|
20
|
+
address = Address.new
|
21
|
+
address.street = '320 First Street North, #712'
|
22
|
+
address.city = 'Jacksonville Beach'
|
23
|
+
address.state = 'FL'
|
24
|
+
address.zip = '32250'
|
25
|
+
address.phone_number = '877 885 8846'
|
26
|
+
contact.addresses = [address]
|
27
|
+
|
28
|
+
collection.save(contact)
|
29
|
+
|
30
|
+
results = collection.find('addresses.state' => 'FL')
|
31
|
+
hashrocket = results.to_a.find {|contact| contact.name == 'Hashrocket'}
|
32
|
+
puts hashrocket.addresses.first.phone_number
|
@@ -0,0 +1,72 @@
|
|
1
|
+
Feature: Finders
|
2
|
+
|
3
|
+
Background:
|
4
|
+
Given an empty Contact document collection
|
5
|
+
And a Contact document named 'hashrocket' :
|
6
|
+
| Name | Type |
|
7
|
+
| Hashrocket | company |
|
8
|
+
And 'hashrocket' has interests, an array of:
|
9
|
+
| Interest |
|
10
|
+
| ruby |
|
11
|
+
| rails |
|
12
|
+
| employment |
|
13
|
+
| contract work |
|
14
|
+
| restaurants |
|
15
|
+
| hotels |
|
16
|
+
| flights |
|
17
|
+
| car rentals |
|
18
|
+
And 'hashrocket' has many addresses :
|
19
|
+
| Street | City | State | Zip Code |
|
20
|
+
| 320 First Street North | Jacksonville Beach | FL | 32250 |
|
21
|
+
| 1 Lake Michigan Street | Chicago | IL | 60611 |
|
22
|
+
| 1 Main Street | Santiago | Chile | |
|
23
|
+
And I save the document 'hashrocket'
|
24
|
+
And a Contact document named 'rocketeer' :
|
25
|
+
| Name |
|
26
|
+
| Rocketeer Mike |
|
27
|
+
And 'rocketeer' has interests, an array of:
|
28
|
+
| Interest |
|
29
|
+
| ruby |
|
30
|
+
| rails |
|
31
|
+
| restaurants |
|
32
|
+
| employment |
|
33
|
+
And 'rocketeer' has many addresses :
|
34
|
+
| Street | City | State | Zip Code |
|
35
|
+
| 1 Main Street | Atlantic Beach | FL | 32233 |
|
36
|
+
And I save the document 'rocketeer'
|
37
|
+
And a Contact document named 'contractor' :
|
38
|
+
| Name |
|
39
|
+
| Contractor Joe |
|
40
|
+
And 'contractor' has interests, an array of:
|
41
|
+
| Interest |
|
42
|
+
| ruby |
|
43
|
+
| rails |
|
44
|
+
| contract work |
|
45
|
+
| flights |
|
46
|
+
| car rentals |
|
47
|
+
| hotels |
|
48
|
+
| restaurants |
|
49
|
+
And 'contractor' has many addresses :
|
50
|
+
| Street | City | State | Zip Code |
|
51
|
+
| 1 Main St. | Jacksonville | FL | 32218 |
|
52
|
+
And I save the document 'contractor'
|
53
|
+
|
54
|
+
Scenario: All
|
55
|
+
When I query contacts with all
|
56
|
+
Then the query result has 3 documents
|
57
|
+
|
58
|
+
Scenario: Count
|
59
|
+
When I query contacts with count
|
60
|
+
Then the query result was 3 documents
|
61
|
+
|
62
|
+
Scenario: First
|
63
|
+
When I query contacts with first
|
64
|
+
Then the query result is the document 'hashrocket'
|
65
|
+
|
66
|
+
Scenario: Last
|
67
|
+
When I query contacts with last
|
68
|
+
Then the query result is the document 'contractor'
|
69
|
+
|
70
|
+
Scenario: Find One
|
71
|
+
When I query contacts to find_one with the id of the 'contractor' document
|
72
|
+
Then the query result is the document 'contractor'
|
@@ -74,7 +74,7 @@ Feature: MongoDoc::Base
|
|
74
74
|
And the Contact collection should have 1 document
|
75
75
|
And the document 'hashrocket' roundtrips
|
76
76
|
|
77
|
-
Scenario:
|
77
|
+
Scenario: Update attributes from a has_many child document
|
78
78
|
Given an empty Contact document collection
|
79
79
|
And a Contact document named 'hashrocket' :
|
80
80
|
| Name |
|
@@ -89,7 +89,8 @@ Feature: MongoDoc::Base
|
|
89
89
|
| Street |
|
90
90
|
| 1a Calle |
|
91
91
|
When I update the document 'chile' with the hash named 'street'
|
92
|
-
Then the last return value is
|
92
|
+
Then the last return value is true
|
93
|
+
And the document 'hashrocket' roundtrips
|
93
94
|
|
94
95
|
Scenario: update attributes from a has_one child document
|
95
96
|
Given an empty Place document collection
|
@@ -107,3 +108,12 @@ Feature: MongoDoc::Base
|
|
107
108
|
When I update the document 'address' with the hash named 'street'
|
108
109
|
Then the Place collection should have 1 document
|
109
110
|
And the document 'hashrocket' roundtrips
|
111
|
+
|
112
|
+
Scenario: Finder
|
113
|
+
Given an empty Contact document collection
|
114
|
+
And a Contact document named 'hashrocket' :
|
115
|
+
| Name | Type |
|
116
|
+
| Hashrocket | company |
|
117
|
+
And I save the last document
|
118
|
+
When I query contacts with find {:where => {'type' => 'company'}}
|
119
|
+
Then the size of the last return value is 1
|
@@ -0,0 +1,66 @@
|
|
1
|
+
Feature: Named Scopes
|
2
|
+
|
3
|
+
Background:
|
4
|
+
Given an empty Contact document collection
|
5
|
+
And a Contact document named 'hashrocket' :
|
6
|
+
| Name | Type |
|
7
|
+
| Hashrocket | company |
|
8
|
+
And 'hashrocket' has interests, an array of:
|
9
|
+
| Interest |
|
10
|
+
| ruby |
|
11
|
+
| rails |
|
12
|
+
| employment |
|
13
|
+
| contract work |
|
14
|
+
| restaurants |
|
15
|
+
| hotels |
|
16
|
+
| flights |
|
17
|
+
| car rentals |
|
18
|
+
And 'hashrocket' has many addresses :
|
19
|
+
| Street | City | State | Zip Code |
|
20
|
+
| 320 First Street North | Jacksonville Beach | FL | 32250 |
|
21
|
+
| 1 Lake Michigan Street | Chicago | IL | 60611 |
|
22
|
+
| 1 Main Street | Santiago | Chile | |
|
23
|
+
And I save the document 'hashrocket'
|
24
|
+
And a Contact document named 'rocketeer' :
|
25
|
+
| Name |
|
26
|
+
| Rocketeer Mike |
|
27
|
+
And 'rocketeer' has interests, an array of:
|
28
|
+
| Interest |
|
29
|
+
| ruby |
|
30
|
+
| rails |
|
31
|
+
| restaurants |
|
32
|
+
| employment |
|
33
|
+
And 'rocketeer' has many addresses :
|
34
|
+
| Street | City | State | Zip Code |
|
35
|
+
| 1 Main Street | Atlantic Beach | FL | 32233 |
|
36
|
+
And I save the document 'rocketeer'
|
37
|
+
And a Contact document named 'contractor' :
|
38
|
+
| Name |
|
39
|
+
| Contractor Joe |
|
40
|
+
And 'contractor' has interests, an array of:
|
41
|
+
| Interest |
|
42
|
+
| ruby |
|
43
|
+
| rails |
|
44
|
+
| contract work |
|
45
|
+
| flights |
|
46
|
+
| car rentals |
|
47
|
+
| hotels |
|
48
|
+
| restaurants |
|
49
|
+
And 'contractor' has many addresses :
|
50
|
+
| Street | City | State | Zip Code |
|
51
|
+
| 1 Main St. | Jacksonville | FL | 32218 |
|
52
|
+
And I save the document 'contractor'
|
53
|
+
|
54
|
+
Scenario: Simple named scope
|
55
|
+
When I query contacts with scope 'rubyists'
|
56
|
+
Then the query result has 3 documents
|
57
|
+
|
58
|
+
Scenario: Simple chained scope
|
59
|
+
When I query contacts with scopes 'rubyists, contract_work'
|
60
|
+
Then the query result has 2 documents
|
61
|
+
And one of the query results is the document 'contractor'
|
62
|
+
|
63
|
+
Scenario: Named scope with lambda
|
64
|
+
When I query contacts with lambda scope 'in_state' with parameters 'IL'
|
65
|
+
Then the query result has 1 documents
|
66
|
+
And one of the query results is the document 'hashrocket'
|
@@ -0,0 +1,36 @@
|
|
1
|
+
Feature: New record
|
2
|
+
|
3
|
+
Scenario: saving a has_many children document
|
4
|
+
Given an empty Contact document collection
|
5
|
+
And a Contact document named 'hashrocket' :
|
6
|
+
| Name |
|
7
|
+
| Hashrocket |
|
8
|
+
And 'hashrocket' has many addresses :
|
9
|
+
| Street | City | State | Zip Code |
|
10
|
+
| 320 First Street North | Jacksonville Beach | FL | 32250 |
|
11
|
+
| 1 Main Street | Santiago | Chile | |
|
12
|
+
When I save the document 'hashrocket'
|
13
|
+
Then the first address of 'hashrocket' is not a new record
|
14
|
+
|
15
|
+
Scenario: saving a has_one child document
|
16
|
+
Given an empty Place document collection
|
17
|
+
And a Place document named 'hashrocket' :
|
18
|
+
| Name |
|
19
|
+
| Hashrocket |
|
20
|
+
And 'hashrocket' has one Address as address :
|
21
|
+
| Street | City | State | Zip Code |
|
22
|
+
| 320 First Street North | Jacksonville Beach | FL | 32250 |
|
23
|
+
When I save the document 'hashrocket'
|
24
|
+
Then the address of 'hashrocket' is not a new record
|
25
|
+
|
26
|
+
Scenario: id is roundtripped when saving a has_one child document
|
27
|
+
Given an empty Place document collection
|
28
|
+
And a Place document named 'hashrocket' :
|
29
|
+
| Name |
|
30
|
+
| Hashrocket |
|
31
|
+
And 'hashrocket' has one Address as address :
|
32
|
+
| Street | City | State | Zip Code |
|
33
|
+
| 320 First Street North | Jacksonville Beach | FL | 32250 |
|
34
|
+
When I save the document 'hashrocket'
|
35
|
+
Then the address of 'hashrocket' roundtrips
|
36
|
+
|
@@ -0,0 +1,105 @@
|
|
1
|
+
Feature: Partial Updates
|
2
|
+
|
3
|
+
Background:
|
4
|
+
Given an empty Contact document collection
|
5
|
+
And a Contact document named 'hashrocket' :
|
6
|
+
| Name | Type | Note |
|
7
|
+
| Hashrocket | company | Premier Rails development shop! |
|
8
|
+
And 'hashrocket' has interests, an array of:
|
9
|
+
| Interest |
|
10
|
+
| ruby |
|
11
|
+
| rails |
|
12
|
+
| employment |
|
13
|
+
| contract work |
|
14
|
+
| restaurants |
|
15
|
+
| hotels |
|
16
|
+
| flights |
|
17
|
+
| car rentals |
|
18
|
+
And 'hashrocket' has many addresses :
|
19
|
+
| Street | City | State | Zip Code |
|
20
|
+
| 320 First Street North | Jacksonville Beach | FL | 32250 |
|
21
|
+
| 1 Lake Michigan Street | Chicago | IL | 60611 |
|
22
|
+
| 1 Main Street | Santiago | Chile | |
|
23
|
+
And I save the document 'hashrocket'
|
24
|
+
And a Contact document named 'rocketeer' :
|
25
|
+
| Name | Note |
|
26
|
+
| Rocketeer Mike | Fantastic developer |
|
27
|
+
And 'rocketeer' has interests, an array of:
|
28
|
+
| Interest |
|
29
|
+
| ruby |
|
30
|
+
| rails |
|
31
|
+
| restaurants |
|
32
|
+
| employment |
|
33
|
+
And 'rocketeer' has many addresses :
|
34
|
+
| Street | City | State | Zip Code |
|
35
|
+
| 1 Main Street | Atlantic Beach | FL | 32233 |
|
36
|
+
And I save the document 'rocketeer'
|
37
|
+
And a Contact document named 'contractor' :
|
38
|
+
| Name | Note |
|
39
|
+
| Contractor Joe | Knows MongoDB |
|
40
|
+
And 'contractor' has interests, an array of:
|
41
|
+
| Interest |
|
42
|
+
| ruby |
|
43
|
+
| rails |
|
44
|
+
| contract work |
|
45
|
+
| flights |
|
46
|
+
| car rentals |
|
47
|
+
| hotels |
|
48
|
+
| restaurants |
|
49
|
+
And 'contractor' has many addresses :
|
50
|
+
| Street | City | State | Zip Code |
|
51
|
+
| 1 Main St. | Jacksonville | FL | 32218 |
|
52
|
+
And I save the document 'contractor'
|
53
|
+
And an empty Place document collection
|
54
|
+
And a Place document named 'hashrocket_hq' :
|
55
|
+
| Name | Type |
|
56
|
+
| Hashrocket | company |
|
57
|
+
And 'hashrocket_hq' has one Address as address (identified by 'hq_address'):
|
58
|
+
| Street | City | State | Zip Code |
|
59
|
+
| 1 Main St. | Jacksonville | FL | 32218 |
|
60
|
+
And I save the document 'hashrocket_hq'
|
61
|
+
|
62
|
+
Scenario: Naive Update
|
63
|
+
When I update the 'note' for 'contractor' to 'Knows MongoDB and MongoDoc'
|
64
|
+
Then the document 'contractor' roundtrips
|
65
|
+
|
66
|
+
Scenario: Naive Update on a has one
|
67
|
+
When I update the 'street' for 'hq_address' to '320 1st Street North'
|
68
|
+
Then the document 'hashrocket_hq' roundtrips
|
69
|
+
|
70
|
+
Scenario: Naive Update on a has many
|
71
|
+
When 'hq_address' is the first address of 'hashrocket'
|
72
|
+
And I update the 'street' for 'hq_address' to '320 1st Street North'
|
73
|
+
Then the document 'hashrocket' roundtrips
|
74
|
+
|
75
|
+
Scenario: Strict Update
|
76
|
+
When I strict update the 'note' for 'contractor' to 'Knows MongoDB and MongoDoc'
|
77
|
+
Then the document 'contractor' roundtrips
|
78
|
+
|
79
|
+
Scenario: Strict Update on a has one
|
80
|
+
When I strict update the 'street' for 'hq_address' to '320 1st Street North'
|
81
|
+
Then the document 'hashrocket_hq' roundtrips
|
82
|
+
|
83
|
+
Scenario: Strict Update on a has many
|
84
|
+
When 'hq_address' is the first address of 'hashrocket'
|
85
|
+
And I strict update the 'street' for 'hq_address' to '320 1st Street North'
|
86
|
+
Then the document 'hashrocket' roundtrips
|
87
|
+
|
88
|
+
Scenario: Failing Strict Update on a has one
|
89
|
+
When someone else changes the Address 'address' of 'hashrocket_hq' to
|
90
|
+
| Street | City | State | Zip Code |
|
91
|
+
| 1 Ocean Blvd. | Jacksonville | FL | 32218 |
|
92
|
+
And I strict update the 'street' for 'hq_address' to '320 1st Street North'
|
93
|
+
Then the last return value is false
|
94
|
+
And the document 'hashrocket_hq' does not roundtrip
|
95
|
+
|
96
|
+
Scenario: Failing Strict Update on a has many
|
97
|
+
When 'hq_address' is the first address of 'hashrocket'
|
98
|
+
And someone else changes the addresses of 'hashrocket':
|
99
|
+
| Street | City | State | Zip Code |
|
100
|
+
| 320 1st N, #712 | Jacksonville Beach | FL | 32250 |
|
101
|
+
| 1001 Mulligan Street | Chicago | IL | 60611 |
|
102
|
+
| 345 Avenida Grande | Santiago | Chile | |
|
103
|
+
And I strict update the 'street' for 'hq_address' to '320 1st Street North'
|
104
|
+
Then the last return value is false
|
105
|
+
And the document 'hashrocket' does not roundtrip
|
@@ -1,17 +1,10 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
klass = klass_name.singularize.camelize.constantize
|
4
|
-
@query = klass.criteria
|
1
|
+
When /^I query (.*) with 'all'$/ do |doc|
|
2
|
+
query(doc).all
|
5
3
|
end
|
6
4
|
|
7
|
-
# When /^I query (.*) with all "([^\"]*)"$/ do |doc, selections_text|
|
8
|
-
# selections = eval(selections_text)
|
9
|
-
# query(doc).all(selections)
|
10
|
-
# end
|
11
|
-
|
12
5
|
When /^I query (.*) to select fields? "([^\"]*)"$/ do |doc, fields|
|
13
6
|
fields = fields.split
|
14
|
-
query(doc).
|
7
|
+
query(doc).only(*fields)
|
15
8
|
end
|
16
9
|
|
17
10
|
When /^I query (.*) where "([^\"]*)"$/ do |doc, where_text|
|
@@ -34,7 +27,7 @@ When /^I query (.*) with (every|not in|in) "([^\"]*)"$/ do |doc, op, hash_text|
|
|
34
27
|
query(doc).send(op.gsub(' ', '_'), hash)
|
35
28
|
end
|
36
29
|
|
37
|
-
When /^I query (.*) with '(.*)' id$/ do |doc, name|
|
30
|
+
When /^I query (.*) with the '(.*)' id$/ do |doc, name|
|
38
31
|
object = instance_variable_get("@#{name}")
|
39
32
|
query(doc).id(object.id)
|
40
33
|
end
|
@@ -47,33 +40,3 @@ When /^I order the (.*) query by "([^\"]*)"$/ do |doc, order_text|
|
|
47
40
|
order = eval(order_text)
|
48
41
|
query(doc).order_by(order)
|
49
42
|
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
|
-
|