mongodoc 0.1.2 → 0.2.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.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
|
-
|