mongo_doc_rails2 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +8 -0
- data/HISTORY.md +11 -0
- data/LICENSE +20 -0
- data/README.textile +185 -0
- data/Rakefile +188 -0
- data/TODO +40 -0
- data/VERSION +1 -0
- data/data/.gitignore +2 -0
- data/examples/simple_document.rb +46 -0
- data/examples/simple_object.rb +34 -0
- data/features/collections.feature +9 -0
- data/features/embed_hash.feature +16 -0
- data/features/finders.feature +76 -0
- data/features/indexes.feature +28 -0
- data/features/mongodb.yml +7 -0
- data/features/mongodoc_base.feature +128 -0
- data/features/new_record.feature +36 -0
- data/features/partial_updates.feature +95 -0
- data/features/removing_documents.feature +68 -0
- data/features/saving_an_object.feature +15 -0
- data/features/scopes.feature +66 -0
- data/features/step_definitions/collection_steps.rb +17 -0
- data/features/step_definitions/document_steps.rb +149 -0
- data/features/step_definitions/documents.rb +40 -0
- data/features/step_definitions/embed_hash_steps.rb +6 -0
- data/features/step_definitions/finder_steps.rb +15 -0
- data/features/step_definitions/index_steps.rb +10 -0
- data/features/step_definitions/json_steps.rb +9 -0
- data/features/step_definitions/object_steps.rb +50 -0
- data/features/step_definitions/objects.rb +24 -0
- data/features/step_definitions/partial_update_steps.rb +31 -0
- data/features/step_definitions/query_steps.rb +66 -0
- data/features/step_definitions/removing_documents_steps.rb +14 -0
- data/features/step_definitions/scope_steps.rb +18 -0
- data/features/step_definitions/string_casting_steps.rb +29 -0
- data/features/step_definitions/util_steps.rb +7 -0
- data/features/string_casting.feature +10 -0
- data/features/support/support.rb +10 -0
- data/features/using_criteria.feature +142 -0
- data/lib/mongo_doc.rb +12 -0
- data/lib/mongo_doc/associations.rb +109 -0
- data/lib/mongo_doc/associations/collection_proxy.rb +121 -0
- data/lib/mongo_doc/associations/document_proxy.rb +65 -0
- data/lib/mongo_doc/associations/hash_proxy.rb +102 -0
- data/lib/mongo_doc/associations/proxy_base.rb +48 -0
- data/lib/mongo_doc/attributes.rb +84 -0
- data/lib/mongo_doc/bson.rb +31 -0
- data/lib/mongo_doc/collection.rb +82 -0
- data/lib/mongo_doc/connection.rb +88 -0
- data/lib/mongo_doc/contexts.rb +31 -0
- data/lib/mongo_doc/contexts/ids.rb +41 -0
- data/lib/mongo_doc/contexts/mongo.rb +272 -0
- data/lib/mongo_doc/criteria.rb +70 -0
- data/lib/mongo_doc/cursor.rb +32 -0
- data/lib/mongo_doc/document.rb +205 -0
- data/lib/mongo_doc/ext.rb +16 -0
- data/lib/mongo_doc/ext/array.rb +5 -0
- data/lib/mongo_doc/ext/binary.rb +7 -0
- data/lib/mongo_doc/ext/boolean_class.rb +17 -0
- data/lib/mongo_doc/ext/date.rb +19 -0
- data/lib/mongo_doc/ext/date_time.rb +17 -0
- data/lib/mongo_doc/ext/dbref.rb +7 -0
- data/lib/mongo_doc/ext/hash.rb +7 -0
- data/lib/mongo_doc/ext/min_max_keys.rb +13 -0
- data/lib/mongo_doc/ext/nil_class.rb +5 -0
- data/lib/mongo_doc/ext/numeric.rb +17 -0
- data/lib/mongo_doc/ext/object.rb +19 -0
- data/lib/mongo_doc/ext/object_id.rb +7 -0
- data/lib/mongo_doc/ext/regexp.rb +5 -0
- data/lib/mongo_doc/ext/string.rb +5 -0
- data/lib/mongo_doc/ext/symbol.rb +5 -0
- data/lib/mongo_doc/ext/time.rb +9 -0
- data/lib/mongo_doc/finders.rb +38 -0
- data/lib/mongo_doc/index.rb +46 -0
- data/lib/mongo_doc/matchers.rb +35 -0
- data/lib/mongo_doc/root.rb +26 -0
- data/lib/mongo_doc/scope.rb +64 -0
- data/lib/mongo_doc/validations.rb +12 -0
- data/lib/mongo_doc/validations/macros.rb +11 -0
- data/lib/mongo_doc/validations/validates_embedded.rb +13 -0
- data/lib/mongoid/contexts/enumerable.rb +151 -0
- data/lib/mongoid/contexts/paging.rb +42 -0
- data/lib/mongoid/criteria.rb +239 -0
- data/lib/mongoid/criterion/complex.rb +21 -0
- data/lib/mongoid/criterion/exclusion.rb +65 -0
- data/lib/mongoid/criterion/inclusion.rb +93 -0
- data/lib/mongoid/criterion/optional.rb +136 -0
- data/lib/mongoid/extensions/hash/criteria_helpers.rb +20 -0
- data/lib/mongoid/extensions/symbol/inflections.rb +36 -0
- data/lib/mongoid/matchers/all.rb +11 -0
- data/lib/mongoid/matchers/default.rb +26 -0
- data/lib/mongoid/matchers/exists.rb +13 -0
- data/lib/mongoid/matchers/gt.rb +11 -0
- data/lib/mongoid/matchers/gte.rb +11 -0
- data/lib/mongoid/matchers/in.rb +11 -0
- data/lib/mongoid/matchers/lt.rb +11 -0
- data/lib/mongoid/matchers/lte.rb +11 -0
- data/lib/mongoid/matchers/ne.rb +11 -0
- data/lib/mongoid/matchers/nin.rb +11 -0
- data/lib/mongoid/matchers/size.rb +11 -0
- data/mongo_doc_rails2.gemspec +237 -0
- data/mongod.example.yml +2 -0
- data/mongodb.example.yml +14 -0
- data/perf/mongo_doc_object.rb +83 -0
- data/perf/mongo_document.rb +84 -0
- data/perf/ruby_driver.rb +49 -0
- data/script/console +8 -0
- data/spec/array_including_argument_matcher.rb +62 -0
- data/spec/associations/collection_proxy_spec.rb +233 -0
- data/spec/associations/document_proxy_spec.rb +45 -0
- data/spec/associations/hash_proxy_spec.rb +181 -0
- data/spec/associations/proxy_base_spec.rb +92 -0
- data/spec/associations_spec.rb +218 -0
- data/spec/attributes_accessor_spec.rb +33 -0
- data/spec/attributes_spec.rb +145 -0
- data/spec/bson_matchers.rb +54 -0
- data/spec/bson_spec.rb +196 -0
- data/spec/collection_spec.rb +169 -0
- data/spec/connection_spec.rb +147 -0
- data/spec/contexts/ids_spec.rb +49 -0
- data/spec/contexts/mongo_spec.rb +235 -0
- data/spec/contexts_spec.rb +56 -0
- data/spec/criteria_spec.rb +69 -0
- data/spec/cursor_spec.rb +91 -0
- data/spec/document_ext.rb +9 -0
- data/spec/document_spec.rb +553 -0
- data/spec/embedded_save_spec.rb +73 -0
- data/spec/ext_spec.rb +89 -0
- data/spec/finders_spec.rb +61 -0
- data/spec/hash_matchers.rb +27 -0
- data/spec/index_spec.rb +79 -0
- data/spec/matchers_spec.rb +342 -0
- data/spec/mongodb.yml +6 -0
- data/spec/mongodb_pairs.yml +8 -0
- data/spec/new_record_spec.rb +128 -0
- data/spec/root_spec.rb +41 -0
- data/spec/scope_spec.rb +79 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/validations_spec.rb +30 -0
- metadata +346 -0
|
@@ -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
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
When /^I update the '(.+)' for '(.+)' to '(.+)'$/ do |attr, doc_name, value|
|
|
2
|
+
doc = instance_variable_get("@#{doc_name}")
|
|
3
|
+
attrs = {attr => value}
|
|
4
|
+
@last_return = doc.update_attributes(attrs)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
When /^someone else changes the (.+?) '(.+)' of '(.+)' to$/ do |assoc_klass, assoc_name, name, table|
|
|
8
|
+
orig = instance_variable_get("@#{name}")
|
|
9
|
+
doc = orig.class.find_one(orig._id)
|
|
10
|
+
obj = assoc_klass.constantize.new
|
|
11
|
+
table.hashes.each do |hash|
|
|
12
|
+
hash.each do |key, value|
|
|
13
|
+
obj.send("#{key.underscore.gsub(' ', '_')}=", value)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
doc.send("#{assoc_name.underscore.gsub(' ', '_')}=", obj)
|
|
17
|
+
doc.save
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
When /^someone else changes the (.+) of '(.+)':$/ do |assoc_name, name, table|
|
|
21
|
+
orig = instance_variable_get("@#{name}")
|
|
22
|
+
doc = orig.class.find_one(orig._id)
|
|
23
|
+
doc.send(assoc_name).clear
|
|
24
|
+
table.hashes.each do |hash|
|
|
25
|
+
doc.send(assoc_name) << hash.inject({}) do |attrs, (attr, value)|
|
|
26
|
+
attrs["#{attr.underscore.gsub(' ', '_')}"] = value
|
|
27
|
+
attrs
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
doc.save
|
|
31
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
def klass(klass_name = nil)
|
|
2
|
+
@klass ||= klass_name.singularize.camelize.constantize
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
def query(klass_name = nil)
|
|
6
|
+
@query ||= klass(klass_name).criteria
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
When "I also want a $number query with criteria $criteria" do |number, criteria|
|
|
10
|
+
instance_variable_set("@#{number}", eval("query.#{criteria}"))
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
Then /^the query result is equal to the document '(.*)'$/ do |name|
|
|
14
|
+
doc = instance_variable_get("@#{name}")
|
|
15
|
+
query.should == doc
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
Then /^one of the query results is the document '(.*)'$/ do |name|
|
|
19
|
+
doc = instance_variable_get("@#{name}")
|
|
20
|
+
query.any? {|d| d == doc}.should be_true
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
Then /^the query result with "(.*)" == "(.*)" has a count of (.*)$/ do |key, value, count|
|
|
24
|
+
query.find {|r| r.has_key?(key) and r[key] == value }['count'].should == count.to_i
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
Then /^the query result with "([^\"]*)" == "([^\"]*)" has the document '(.*)'$/ do |key, value, name|
|
|
28
|
+
doc = instance_variable_get("@#{name}")
|
|
29
|
+
query.find {|r| r.has_key?(key) and r[key] == value }['group'].should include(doc)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
Then /^the query result has (.*) documents*$/ do |count|
|
|
33
|
+
if query.respond_to?(:size)
|
|
34
|
+
query.size.should == count.to_i
|
|
35
|
+
else
|
|
36
|
+
query.count.should == count.to_i
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
Then /^the (first|last) query result is the document '(.*)'$/ do |position, name|
|
|
41
|
+
doc = instance_variable_get("@#{name}")
|
|
42
|
+
query.entries.send(position).should == doc
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
Then /^the size of the query result is (.*)$/ do |count|
|
|
46
|
+
query.to_a.size.should == count.to_i
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
Then /^the query result is the document '(.*)'$/ do |name|
|
|
50
|
+
object = instance_variable_get("@#{name}")
|
|
51
|
+
if query.kind_of?(Array)
|
|
52
|
+
query.size.should == 1
|
|
53
|
+
query.first.should == object
|
|
54
|
+
else
|
|
55
|
+
query.should == object
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
Then /^the query (is|is not) (empty|blank)$/ do |is, empty|
|
|
60
|
+
query.send("#{empty}?").should == (is == 'is')
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
Then /^the (.+) query (is|is not) (empty|blank)$/ do |number, is, empty|
|
|
64
|
+
instance_variable_get("@#{number}").send("#{empty}?").should == (is == 'is')
|
|
65
|
+
end
|
|
66
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
When /^I remove '(.+)'$/ do |doc_name|
|
|
2
|
+
doc = instance_variable_get("@#{doc_name}")
|
|
3
|
+
doc.remove
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
Then /^the document '(.+)' is not found$/ do |doc_name|
|
|
7
|
+
doc = instance_variable_get("@#{doc_name}")
|
|
8
|
+
doc.class.find_one(doc.id)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
Then /^an exception is raised if I remove '(.+)'$/ do |doc_name|
|
|
12
|
+
doc = instance_variable_get("@#{doc_name}")
|
|
13
|
+
lambda { doc.remove }.should raise_error
|
|
14
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
def scope_query=(scope)
|
|
2
|
+
@query = scope
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
When /^I query (.*) with scope '(.*)'$/ do |doc, scope|
|
|
6
|
+
self.scope_query = klass(doc).send(scope)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
When /^I query (.*) with scopes '(.*)'$/ do |doc, scopes|
|
|
10
|
+
self.scope_query = scopes.split(',').inject(klass(doc)) do |result, scope|
|
|
11
|
+
result.send(scope.strip)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
When /^I query (.*) with lambda scope '(.*)' with parameters '(.*)'$/ do |doc, scope, params_text|
|
|
16
|
+
params = params_text.split(',').map(&:strip)
|
|
17
|
+
self.scope_query = klass(doc).send(scope, *params)
|
|
18
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
def last
|
|
2
|
+
@last
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
def last=(value)
|
|
6
|
+
@last = value
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def all
|
|
10
|
+
@all ||= []
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def all=(value)
|
|
14
|
+
@all = value
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Given /^a class (.+)$/ do |type_name|
|
|
18
|
+
type_name.constantize.should be_kind_of(Class)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
Given /^I create an (.+) '(.+)' with:$/ do |klass_name, object_name, table|
|
|
22
|
+
Given "an #{klass_name} document named '#{object_name}' :", table
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
Then /^the object '(.+)' has an attribute '(.+)' of type (.*)$/ do |object_name, attr_name, type_name|
|
|
26
|
+
object = instance_variable_get("@#{object_name}")
|
|
27
|
+
type_name.constantize.should === object.send(attr_name)
|
|
28
|
+
end
|
|
29
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
Feature: String casting
|
|
2
|
+
|
|
3
|
+
Background:
|
|
4
|
+
Given a class Event
|
|
5
|
+
|
|
6
|
+
Scenario: Creating a new document
|
|
7
|
+
When I create an Event 'event' with:
|
|
8
|
+
| Name | Venue | Date |
|
|
9
|
+
| NoSQL Live | John Hancock Conference Center | 2010-03-11 |
|
|
10
|
+
Then the object 'event' has an attribute 'date' of type Date
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', '..', 'lib'))
|
|
2
|
+
require 'cucumber'
|
|
3
|
+
require 'spec/expectations'
|
|
4
|
+
require 'spec/bson_matchers'
|
|
5
|
+
require 'mongo_doc'
|
|
6
|
+
|
|
7
|
+
MongoDoc::Connection.env = 'cucumber'
|
|
8
|
+
MongoDoc::Connection.config_path = './features/mongodb.yml'
|
|
9
|
+
|
|
10
|
+
World(BsonMatchers)
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
Feature: MongoDoc::Base
|
|
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
|
+
And an empty Place document collection
|
|
54
|
+
And a Place document named 'one_ocean' :
|
|
55
|
+
| Name | Type |
|
|
56
|
+
| One Ocean | hotel |
|
|
57
|
+
And 'one_ocean' has one Address as address :
|
|
58
|
+
| Street | City | State | Zip Code |
|
|
59
|
+
| 1 Ocean Street | Atlantic Beach | FL | 32233 |
|
|
60
|
+
And I save the document 'one_ocean'
|
|
61
|
+
And a Place document named 'sea_horse' :
|
|
62
|
+
| Name | Type |
|
|
63
|
+
| Sea Horse | hotel |
|
|
64
|
+
And 'sea_horse' has one Address as address :
|
|
65
|
+
| Street | City | State | Zip Code |
|
|
66
|
+
| 1401 Atlantic Blvd | Neptune Beach | FL | 32266 |
|
|
67
|
+
And I save the document 'sea_horse'
|
|
68
|
+
And a Place document named 'jax' :
|
|
69
|
+
| Name | Type |
|
|
70
|
+
| Jacksonville International Airport | airport |
|
|
71
|
+
And 'jax' has one Address as address :
|
|
72
|
+
| Street | City | State | Zip Code |
|
|
73
|
+
| | Jacksonville | FL | 32218 |
|
|
74
|
+
And I save the document 'jax'
|
|
75
|
+
|
|
76
|
+
Scenario: Counting results
|
|
77
|
+
When I query contacts with criteria all('interests' => ['ruby', 'rails', 'employment'])
|
|
78
|
+
Then the query result has 2 documents
|
|
79
|
+
|
|
80
|
+
Scenario: Finding contacts with interests in ruby and rails
|
|
81
|
+
When I query contacts with criteria all('interests' => ['ruby', 'rails', 'employment'])
|
|
82
|
+
Then one of the query results is the document 'rocketeer'
|
|
83
|
+
|
|
84
|
+
Scenario: Finding contacts with interests in restaurants or hotels
|
|
85
|
+
When I query contacts with criteria in('interests' => ['restaurants', 'hotels'])
|
|
86
|
+
Then one of the query results is the document 'contractor'
|
|
87
|
+
|
|
88
|
+
Scenario: Aggregating Places
|
|
89
|
+
When I query places with criteria only('type').where('address.state' => 'FL').aggregate
|
|
90
|
+
Then the query result with "type" == "hotel" has a count of 2
|
|
91
|
+
|
|
92
|
+
Scenario: Excluding places in Neptune Beach
|
|
93
|
+
When I query places with criteria only('type').where('address.city' => 'Neptune Beach').aggregate
|
|
94
|
+
Then the query result with "type" == "hotel" has a count of 1
|
|
95
|
+
|
|
96
|
+
Scenario: Using extras to limit results
|
|
97
|
+
When I query contacts with criteria all('interests' => ['ruby', 'rails', 'employment']).limit(1)
|
|
98
|
+
Then the size of the query result is 1
|
|
99
|
+
|
|
100
|
+
Scenario: Finding the first result
|
|
101
|
+
When I query contacts with criteria all('interests' => ['ruby', 'rails', 'employment']).first
|
|
102
|
+
Then the query result is equal to the document 'hashrocket'
|
|
103
|
+
|
|
104
|
+
Scenario: Grouping places by type
|
|
105
|
+
When I query places with criteria only('type').where('type' => 'hotel').group
|
|
106
|
+
Then the query result with "type" == "hotel" has the document 'one_ocean'
|
|
107
|
+
|
|
108
|
+
Scenario: Selecting contacts with in operator
|
|
109
|
+
When I query contacts with criteria in('interests' => ['ruby', 'rails', 'employment'])
|
|
110
|
+
Then the query result has 3 documents
|
|
111
|
+
|
|
112
|
+
Scenario: Selecting a contact with the id operator
|
|
113
|
+
When I query contacts with the 'hashrocket' id
|
|
114
|
+
Then the query result has 1 documents
|
|
115
|
+
And the query result is the document 'hashrocket'
|
|
116
|
+
|
|
117
|
+
Scenario: Selecting contacts with not in operator
|
|
118
|
+
When I query contacts with criteria not_in('interests' => ['contract work', 'employment'])
|
|
119
|
+
Then the query result has 0 documents
|
|
120
|
+
|
|
121
|
+
Scenario: Ordering contacts
|
|
122
|
+
When I query contacts with criteria in('interests' => ['ruby', 'rails']).order_by([[:name, :asc]]).entries
|
|
123
|
+
Then the first query result is the document 'contractor'
|
|
124
|
+
And the last query result is the document 'rocketeer'
|
|
125
|
+
|
|
126
|
+
Scenario: Using skip on results
|
|
127
|
+
When I query contacts with criteria all('interests' => ['ruby', 'rails']).skip(1)
|
|
128
|
+
Then the size of the query result is 2
|
|
129
|
+
|
|
130
|
+
Scenario: Is a criteria blank?
|
|
131
|
+
When I query contacts with criteria all('interests' => ['wii'])
|
|
132
|
+
Then the query is blank
|
|
133
|
+
|
|
134
|
+
Scenario: Is a criteria empty?
|
|
135
|
+
When I query contacts with criteria all('interests' => ['ruby'])
|
|
136
|
+
Then the query is not empty
|
|
137
|
+
|
|
138
|
+
Scenario: Criteria return a new criteria
|
|
139
|
+
When I query contacts with criteria all('interests' => ['ruby'])
|
|
140
|
+
And I also want a second query with criteria where('addresses.state' => 'HI')
|
|
141
|
+
Then the query is not empty
|
|
142
|
+
And the second query is empty
|
data/lib/mongo_doc.rb
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
require 'mongo_doc/associations/proxy_base'
|
|
2
|
+
require 'mongo_doc/associations/collection_proxy'
|
|
3
|
+
require 'mongo_doc/associations/document_proxy'
|
|
4
|
+
require 'mongo_doc/associations/hash_proxy'
|
|
5
|
+
|
|
6
|
+
module MongoDoc
|
|
7
|
+
module Associations
|
|
8
|
+
|
|
9
|
+
def embed(*args)
|
|
10
|
+
options = args.extract_options!
|
|
11
|
+
assoc_class = if class_name = options.delete(:class_name)
|
|
12
|
+
self.class_from_name(class_name)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
args.each do |name|
|
|
16
|
+
_associations << name unless _associations.include?(name)
|
|
17
|
+
|
|
18
|
+
attr_reader name
|
|
19
|
+
|
|
20
|
+
define_method("#{name}=") do |value|
|
|
21
|
+
association = instance_variable_get("@#{name}")
|
|
22
|
+
unless association
|
|
23
|
+
association = Associations::DocumentProxy.new(:path => _selector_path,
|
|
24
|
+
:root => _root || self,
|
|
25
|
+
:assoc_name => name,
|
|
26
|
+
:assoc_class => assoc_class || self.class.class_from_name(name))
|
|
27
|
+
instance_variable_set("@#{name}", association)
|
|
28
|
+
end
|
|
29
|
+
association.document = value
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
validates_embedded name, :if => Proc.new { !send(name).nil? }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
alias has_one embed
|
|
36
|
+
|
|
37
|
+
def embed_many(*args)
|
|
38
|
+
options = args.extract_options!
|
|
39
|
+
assoc_class = if class_name = options.delete(:class_name)
|
|
40
|
+
self.class_from_name(class_name)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
args.each do |name|
|
|
44
|
+
_associations << name unless _associations.include?(name)
|
|
45
|
+
|
|
46
|
+
define_method("#{name}") do
|
|
47
|
+
association = instance_variable_get("@#{name}")
|
|
48
|
+
unless association
|
|
49
|
+
association = Associations::CollectionProxy.new(:path => _selector_path,
|
|
50
|
+
:root => _root || self,
|
|
51
|
+
:assoc_name => name,
|
|
52
|
+
:assoc_class => assoc_class || self.class.class_from_name(name))
|
|
53
|
+
instance_variable_set("@#{name}", association)
|
|
54
|
+
end
|
|
55
|
+
association
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
validates_embedded name
|
|
59
|
+
|
|
60
|
+
define_method("#{name}=") do |arrayish|
|
|
61
|
+
proxy = send("#{name}")
|
|
62
|
+
proxy.clear
|
|
63
|
+
Array.wrap(arrayish).each do|item|
|
|
64
|
+
proxy << item
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
alias has_many embed_many
|
|
70
|
+
|
|
71
|
+
def embed_hash(*args)
|
|
72
|
+
options = args.extract_options!
|
|
73
|
+
assoc_class = if class_name = options.delete(:class_name)
|
|
74
|
+
self.class_from_name(class_name)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
args.each do |name|
|
|
78
|
+
_associations << name unless _associations.include?(name)
|
|
79
|
+
|
|
80
|
+
define_method("#{name}") do
|
|
81
|
+
association = instance_variable_get("@#{name}")
|
|
82
|
+
unless association
|
|
83
|
+
association = Associations::HashProxy.new(:path => _selector_path,
|
|
84
|
+
:root => _root || self,
|
|
85
|
+
:assoc_name => name,
|
|
86
|
+
:assoc_class => assoc_class || self.class.class_from_name(name))
|
|
87
|
+
instance_variable_set("@#{name}", association)
|
|
88
|
+
end
|
|
89
|
+
association
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
validates_embedded name
|
|
93
|
+
|
|
94
|
+
define_method("#{name}=") do |hash|
|
|
95
|
+
send("#{name}").replace(hash)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
alias has_hash embed_hash
|
|
100
|
+
|
|
101
|
+
def class_from_name(name)
|
|
102
|
+
type_name_with_module(name.to_s.classify).constantize rescue nil
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def type_name_with_module(type_name)
|
|
106
|
+
(/^::/ =~ type_name) ? type_name : "#{parent}::#{type_name}"
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|