mongo_doc_rails2 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|