mongodoc 0.2.2 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +21 -0
- data/TODO +6 -1
- data/VERSION +1 -1
- data/features/finders.feature +1 -1
- data/features/mongodoc_base.feature +11 -2
- data/features/{named_scopes.feature → scopes.feature} +0 -0
- data/features/step_definitions/document_steps.rb +15 -4
- data/features/step_definitions/documents.rb +3 -3
- data/features/step_definitions/query_steps.rb +17 -14
- data/features/step_definitions/{named_scope_steps.rb → scope_steps.rb} +0 -0
- data/features/using_criteria.feature +22 -43
- data/lib/mongodoc.rb +1 -1
- data/lib/mongodoc/associations/collection_proxy.rb +3 -1
- data/lib/mongodoc/associations/document_proxy.rb +4 -1
- data/lib/mongodoc/associations/hash_proxy.rb +3 -1
- data/lib/mongodoc/associations/proxy_base.rb +6 -4
- data/lib/mongodoc/attributes.rb +6 -6
- data/lib/mongodoc/contexts.rb +24 -0
- data/lib/mongodoc/contexts/enumerable.rb +132 -0
- data/lib/mongodoc/contexts/mongo.rb +215 -0
- data/lib/mongodoc/criteria.rb +36 -479
- data/lib/mongodoc/document.rb +3 -2
- data/lib/mongodoc/finders.rb +31 -11
- data/lib/mongodoc/matchers.rb +35 -0
- data/lib/mongodoc/scope.rb +64 -0
- data/lib/mongoid/contexts/paging.rb +42 -0
- data/lib/mongoid/criteria.rb +264 -0
- data/lib/mongoid/criterion/complex.rb +21 -0
- data/lib/mongoid/criterion/exclusion.rb +65 -0
- data/lib/mongoid/criterion/inclusion.rb +92 -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/mongodoc.gemspec +39 -9
- data/spec/attributes_spec.rb +16 -2
- data/spec/contexts/enumerable_spec.rb +335 -0
- data/spec/contexts/mongo_spec.rb +148 -0
- data/spec/contexts_spec.rb +28 -0
- data/spec/criteria_spec.rb +15 -766
- data/spec/finders_spec.rb +28 -36
- data/spec/matchers_spec.rb +342 -0
- data/spec/scope_spec.rb +79 -0
- metadata +40 -10
- data/features/step_definitions/criteria_steps.rb +0 -42
- data/lib/mongodoc/named_scope.rb +0 -68
- data/spec/named_scope_spec.rb +0 -82
data/Rakefile
CHANGED
@@ -81,6 +81,27 @@ namespace :mongo do
|
|
81
81
|
end
|
82
82
|
end
|
83
83
|
|
84
|
+
namespace :mongoid do
|
85
|
+
desc 'Sync criteria from Mongoid'
|
86
|
+
task :sync do
|
87
|
+
require 'pathname'
|
88
|
+
|
89
|
+
src_dir = Pathname.new('../durran-mongoid/lib/mongoid')
|
90
|
+
dest_dir = Pathname.new('lib/mongoid')
|
91
|
+
dest_dir.mkpath
|
92
|
+
%w(criteria.rb contexts/paging.rb criterion extensions/symbol/inflections.rb extensions/hash/criteria_helpers.rb matchers).each do |f|
|
93
|
+
src = src_dir + f
|
94
|
+
if src.directory?
|
95
|
+
FileUtils.cp_r(src, dest_dir)
|
96
|
+
else
|
97
|
+
dest = dest_dir + f
|
98
|
+
dest.dirname.mkpath
|
99
|
+
FileUtils.cp(src, dest)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
84
105
|
namespace :bench do
|
85
106
|
desc 'Run benchmark for MongoDoc'
|
86
107
|
task 'mongodoc' do
|
data/TODO
CHANGED
@@ -1,10 +1,15 @@
|
|
1
|
-
As of 2010-02-
|
1
|
+
As of 2010-02-23
|
2
2
|
|
3
3
|
Associations
|
4
4
|
------------
|
5
5
|
|
6
6
|
Pull associations out of attributes and refactor
|
7
7
|
|
8
|
+
Criteria
|
9
|
+
--------
|
10
|
+
|
11
|
+
Make critieria module, include into Document, CriteriaProxy, Association?
|
12
|
+
|
8
13
|
Documentation
|
9
14
|
-------------
|
10
15
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.4
|
data/features/finders.feature
CHANGED
@@ -109,11 +109,20 @@ Feature: MongoDoc::Base
|
|
109
109
|
Then the Place collection should have 1 document
|
110
110
|
And the document 'hashrocket' roundtrips
|
111
111
|
|
112
|
-
Scenario:
|
112
|
+
Scenario: Class criteria
|
113
113
|
Given an empty Contact document collection
|
114
114
|
And a Contact document named 'hashrocket' :
|
115
115
|
| Name | Type |
|
116
116
|
| Hashrocket | company |
|
117
117
|
And I save the last document
|
118
|
-
When I query contacts with
|
118
|
+
When I query contacts with criteria where('type' => 'company')
|
119
|
+
Then the size of the last return value is 1
|
120
|
+
|
121
|
+
Scenario: Finder
|
122
|
+
Given an empty Contact document collection
|
123
|
+
And a Contact document named 'hashrocket' :
|
124
|
+
| Name | Type |
|
125
|
+
| Hashrocket | company |
|
126
|
+
And I save the document 'hashrocket'
|
127
|
+
When I find a contact using the id of 'hashrocket'
|
119
128
|
Then the size of the last return value is 1
|
File without changes
|
@@ -42,7 +42,7 @@ end
|
|
42
42
|
|
43
43
|
Given /^I set the id on the document '(.*)' to (.*)$/ do |doc_name, value|
|
44
44
|
doc = instance_variable_get("@#{doc_name}")
|
45
|
-
doc._id = Mongo::ObjectID.
|
45
|
+
doc._id = Mongo::ObjectID.from_string("%024x" % value.to_i(16))
|
46
46
|
end
|
47
47
|
|
48
48
|
Given /^'(.+)' has one (.+?) as (.+?) \(identified by '(.+)'\):$/ do |doc_name, class_name, assoc_name, var_name, table|
|
@@ -79,10 +79,21 @@ When /^I update the document '(.*)' with the hash named '(.*)'$/ do |doc_name, h
|
|
79
79
|
@last_return = doc.update_attributes(attrs)
|
80
80
|
end
|
81
81
|
|
82
|
-
When /^I query (.*) with
|
82
|
+
When /^I query (.*) with criteria (.*)$/ do |doc, criteria_text|
|
83
|
+
klass = doc.singularize.camelize
|
84
|
+
@query = @last_return = eval("#{klass}.criteria.#{criteria_text}")
|
85
|
+
end
|
86
|
+
|
87
|
+
When /^I query (.*) with the '(.*)' id$/ do |doc, name|
|
83
88
|
klass = doc.singularize.camelize.constantize
|
84
|
-
|
85
|
-
@last_return = klass.
|
89
|
+
doc = instance_variable_get("@#{name}")
|
90
|
+
@query = @last_return = klass.criteria.id(doc.id).entries
|
91
|
+
end
|
92
|
+
|
93
|
+
When /^I find a (.*) using the id of '(.*)'$/ do |type, doc_name|
|
94
|
+
klass = type.camelize.constantize
|
95
|
+
doc = instance_variable_get("@#{doc_name}")
|
96
|
+
@last_return = klass.find(doc.id)
|
86
97
|
end
|
87
98
|
|
88
99
|
When /^'(.+)' is the first (.+?) of '(.+)'$/ do |var_name, single_assoc, doc_name|
|
@@ -24,7 +24,7 @@ class Contact
|
|
24
24
|
key :interests
|
25
25
|
has_many :addresses
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
scope :rubyists, any_in(:interests => ['ruby'])
|
28
|
+
scope :contract_work, any_in(:interests => ['contract work'])
|
29
|
+
scope :in_state, lambda {|state| where('addresses.state' => state)}
|
30
30
|
end
|
@@ -6,19 +6,23 @@ def query(klass_name = nil)
|
|
6
6
|
@query ||= klass(klass_name).criteria
|
7
7
|
end
|
8
8
|
|
9
|
-
Then /^the
|
10
|
-
|
11
|
-
query.
|
9
|
+
Then /^the query result is equal to the document '(.*)'$/ do |name|
|
10
|
+
doc = instance_variable_get("@#{name}")
|
11
|
+
query.should == doc
|
12
12
|
end
|
13
13
|
|
14
14
|
Then /^one of the query results is the document '(.*)'$/ do |name|
|
15
|
-
|
16
|
-
query.any? {|
|
15
|
+
doc = instance_variable_get("@#{name}")
|
16
|
+
query.any? {|d| d == doc}.should be_true
|
17
17
|
end
|
18
18
|
|
19
|
-
Then /^the
|
20
|
-
|
21
|
-
|
19
|
+
Then /^the query result with "(.*)" == "(.*)" has a count of (.*)$/ do |key, value, count|
|
20
|
+
query.find {|r| r.has_key?(key) and r[key] == value }['count'].should == count.to_i
|
21
|
+
end
|
22
|
+
|
23
|
+
Then /^the query result with "([^\"]*)" == "([^\"]*)" has the document '(.*)'$/ do |key, value, name|
|
24
|
+
doc = instance_variable_get("@#{name}")
|
25
|
+
query.find {|r| r.has_key?(key) and r[key] == value }['group'].should include(doc)
|
22
26
|
end
|
23
27
|
|
24
28
|
Then /^the query result has (.*) documents*$/ do |count|
|
@@ -29,14 +33,13 @@ Then /^the query result has (.*) documents*$/ do |count|
|
|
29
33
|
end
|
30
34
|
end
|
31
35
|
|
32
|
-
Then /^the
|
33
|
-
|
36
|
+
Then /^the (first|last) query result is the document '(.*)'$/ do |position, name|
|
37
|
+
doc = instance_variable_get("@#{name}")
|
38
|
+
query.entries.send(position).should == doc
|
34
39
|
end
|
35
40
|
|
36
|
-
Then /^the
|
37
|
-
|
38
|
-
result = query.group
|
39
|
-
result.find {|r| r.has_key?(key) and r[key] == value }['group'].should include(object)
|
41
|
+
Then /^the size of the query result is (.*)$/ do |count|
|
42
|
+
query.to_a.size.should == count.to_i
|
40
43
|
end
|
41
44
|
|
42
45
|
Then /^the query result is the document '(.*)'$/ do |name|
|
File without changes
|
@@ -74,76 +74,55 @@ Feature: MongoDoc::Base
|
|
74
74
|
And I save the document 'jax'
|
75
75
|
|
76
76
|
Scenario: Counting results
|
77
|
-
When I query contacts with
|
77
|
+
When I query contacts with criteria all('interests' => ['ruby', 'rails', 'employment'])
|
78
78
|
Then the query result has 2 documents
|
79
79
|
|
80
80
|
Scenario: Finding contacts with interests in ruby and rails
|
81
|
-
When I query contacts with
|
82
|
-
Then the query
|
83
|
-
And one of the query results is the document 'rocketeer'
|
81
|
+
When I query contacts with criteria all('interests' => ['ruby', 'rails', 'employment'])
|
82
|
+
Then one of the query results is the document 'rocketeer'
|
84
83
|
|
85
|
-
Scenario: Finding contacts with interests in restaurants
|
86
|
-
When I query contacts with
|
87
|
-
Then the query
|
88
|
-
And one of the query results is the document 'contractor'
|
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'
|
89
87
|
|
90
88
|
Scenario: Aggregating Places
|
91
|
-
When I query places
|
92
|
-
|
93
|
-
Then the aggregate query result with "type" == "hotel" has a count of 2
|
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
|
94
91
|
|
95
92
|
Scenario: Excluding places in Neptune Beach
|
96
|
-
When I query places
|
97
|
-
|
98
|
-
Then the aggregate query result with "type" == "hotel" has a count of 1
|
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
|
99
95
|
|
100
96
|
Scenario: Using extras to limit results
|
101
|
-
When I query contacts with
|
102
|
-
And I set the query extras limit on contacts to 1
|
97
|
+
When I query contacts with criteria all('interests' => ['ruby', 'rails', 'employment']).limit(1)
|
103
98
|
Then the size of the query result is 1
|
104
99
|
|
105
100
|
Scenario: Finding the first result
|
106
|
-
When I query contacts with
|
107
|
-
Then the
|
101
|
+
When I query contacts with criteria all('interests' => ['ruby', 'rails', 'employment']).first
|
102
|
+
Then the query result is equal to the document 'hashrocket'
|
108
103
|
|
109
104
|
Scenario: Grouping places by type
|
110
|
-
When I query places
|
111
|
-
|
112
|
-
Then the group query result with "type" == "hotel" has the document 'one_ocean'
|
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'
|
113
107
|
|
114
108
|
Scenario: Selecting contacts with in operator
|
115
|
-
When I query contacts with in
|
109
|
+
When I query contacts with criteria in('interests' => ['ruby', 'rails', 'employment'])
|
116
110
|
Then the query result has 3 documents
|
117
111
|
|
118
112
|
Scenario: Selecting a contact with the id operator
|
119
113
|
When I query contacts with the 'hashrocket' id
|
120
114
|
Then the query result has 1 documents
|
121
|
-
And the
|
122
|
-
|
123
|
-
Scenario: Finding the last result
|
124
|
-
When I query contacts with every "{'interests' => ['ruby', 'rails', 'employment']}"
|
125
|
-
Then the last query result is equal to the document 'rocketeer'
|
126
|
-
|
127
|
-
Scenario: Using limit on results
|
128
|
-
When I query contacts with every "{'interests' => ['ruby', 'rails', 'employment']}"
|
129
|
-
And I set the query on contacts to limit 1
|
130
|
-
Then the size of the query result is 1
|
115
|
+
And the query result is the document 'hashrocket'
|
131
116
|
|
132
117
|
Scenario: Selecting contacts with not in operator
|
133
|
-
When I query contacts with
|
118
|
+
When I query contacts with criteria not_in('interests' => ['contract work', 'employment'])
|
134
119
|
Then the query result has 0 documents
|
135
120
|
|
136
121
|
Scenario: Ordering contacts
|
137
|
-
When I query contacts with in
|
138
|
-
|
139
|
-
|
140
|
-
Then the last query result is equal to the document 'rocketeer'
|
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'
|
141
125
|
|
142
126
|
Scenario: Using skip on results
|
143
|
-
When I query contacts with
|
144
|
-
And I set the query on contacts to skip 1
|
127
|
+
When I query contacts with criteria all('interests' => ['ruby', 'rails']).skip(1)
|
145
128
|
Then the size of the query result is 2
|
146
|
-
|
147
|
-
Scenario: All
|
148
|
-
When I query contacts with 'all'
|
149
|
-
Then the size of the query result is 3
|
data/lib/mongodoc.rb
CHANGED
@@ -2,7 +2,9 @@
|
|
2
2
|
# http://github.com/sandro
|
3
3
|
module MongoDoc
|
4
4
|
module Associations
|
5
|
-
class CollectionProxy
|
5
|
+
class CollectionProxy
|
6
|
+
include ProxyBase
|
7
|
+
|
6
8
|
# List of array methods (that are not in +Object+) that need to be
|
7
9
|
# delegated to +collection+.
|
8
10
|
ARRAY_METHODS = (Array.instance_methods - Object.instance_methods).map { |n| n.to_s }
|
@@ -1,9 +1,12 @@
|
|
1
1
|
module MongoDoc
|
2
2
|
module Associations
|
3
|
-
class DocumentProxy
|
3
|
+
class DocumentProxy
|
4
|
+
include ProxyBase
|
4
5
|
|
5
6
|
attr_reader :document
|
6
7
|
|
8
|
+
delegate :to_bson, :id, :to => :document
|
9
|
+
|
7
10
|
def _root=(root)
|
8
11
|
@_root = root
|
9
12
|
document._root = root if is_document?(document)
|
@@ -2,7 +2,9 @@ module MongoDoc
|
|
2
2
|
class InvalidEmbeddedHashKey < RuntimeError; end
|
3
3
|
|
4
4
|
module Associations
|
5
|
-
class HashProxy
|
5
|
+
class HashProxy
|
6
|
+
include ProxyBase
|
7
|
+
|
6
8
|
HASH_METHODS = (Hash.instance_methods - Object.instance_methods).map { |n| n.to_s }
|
7
9
|
|
8
10
|
MUST_DEFINE = %w[to_a inspect to_bson ==]
|
@@ -1,9 +1,11 @@
|
|
1
1
|
module MongoDoc
|
2
2
|
module Associations
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
module ProxyBase
|
4
|
+
def self.included(klass)
|
5
|
+
klass.class_eval do
|
6
|
+
attr_reader :assoc_name, :assoc_class, :_parent, :_root
|
7
|
+
end
|
8
|
+
end
|
7
9
|
|
8
10
|
def _parent=(parent)
|
9
11
|
@_parent = parent
|
data/lib/mongodoc/attributes.rb
CHANGED
@@ -51,7 +51,7 @@ module MongoDoc
|
|
51
51
|
def has_one(*args)
|
52
52
|
options = args.extract_options!
|
53
53
|
assoc_class = if class_name = options.delete(:class_name)
|
54
|
-
|
54
|
+
self.class_from_name(class_name)
|
55
55
|
end
|
56
56
|
|
57
57
|
args.each do |name|
|
@@ -75,7 +75,7 @@ module MongoDoc
|
|
75
75
|
def has_many(*args)
|
76
76
|
options = args.extract_options!
|
77
77
|
assoc_class = if class_name = options.delete(:class_name)
|
78
|
-
|
78
|
+
self.class_from_name(class_name)
|
79
79
|
end
|
80
80
|
|
81
81
|
args.each do |name|
|
@@ -84,7 +84,7 @@ module MongoDoc
|
|
84
84
|
define_method("#{name}") do
|
85
85
|
association = instance_variable_get("@#{name}")
|
86
86
|
unless association
|
87
|
-
association = Associations::CollectionProxy.new(:root => _root || self, :parent => self, :assoc_name => name, :assoc_class => assoc_class || self.class.
|
87
|
+
association = Associations::CollectionProxy.new(:root => _root || self, :parent => self, :assoc_name => name, :assoc_class => assoc_class || self.class.class_from_name(name))
|
88
88
|
instance_variable_set("@#{name}", association)
|
89
89
|
end
|
90
90
|
association
|
@@ -105,7 +105,7 @@ module MongoDoc
|
|
105
105
|
def has_hash(*args)
|
106
106
|
options = args.extract_options!
|
107
107
|
assoc_class = if class_name = options.delete(:class_name)
|
108
|
-
|
108
|
+
self.class_from_name(class_name)
|
109
109
|
end
|
110
110
|
|
111
111
|
args.each do |name|
|
@@ -114,7 +114,7 @@ module MongoDoc
|
|
114
114
|
define_method("#{name}") do
|
115
115
|
association = instance_variable_get("@#{name}")
|
116
116
|
unless association
|
117
|
-
association = Associations::HashProxy.new(:root => _root || self, :parent => self, :assoc_name => name, :assoc_class => assoc_class || self.class.
|
117
|
+
association = Associations::HashProxy.new(:root => _root || self, :parent => self, :assoc_name => name, :assoc_class => assoc_class || self.class.class_from_name(name))
|
118
118
|
instance_variable_set("@#{name}", association)
|
119
119
|
end
|
120
120
|
association
|
@@ -133,7 +133,7 @@ module MongoDoc
|
|
133
133
|
end
|
134
134
|
|
135
135
|
def type_name_with_module(type_name)
|
136
|
-
(/^::/ =~ type_name) ? type_name : "#{
|
136
|
+
(/^::/ =~ type_name) ? type_name : "#{parent}::#{type_name}"
|
137
137
|
end
|
138
138
|
end
|
139
139
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "mongoid/contexts/paging"
|
3
|
+
require "mongodoc/contexts/enumerable"
|
4
|
+
require "mongodoc/contexts/mongo"
|
5
|
+
|
6
|
+
module Mongoid
|
7
|
+
module Contexts
|
8
|
+
# Determines the context to be used for this criteria. If the class is an
|
9
|
+
# embedded document, then the context will be the array in the has_many
|
10
|
+
# association it is in. If the class is a root, then the database itself
|
11
|
+
# will be the context.
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
#
|
15
|
+
# <tt>Contexts.context_for(criteria)</tt>
|
16
|
+
def self.context_for(criteria)
|
17
|
+
if criteria.klass.respond_to?(:collection)
|
18
|
+
return MongoDoc::Contexts::Mongo.new(criteria)
|
19
|
+
end
|
20
|
+
return MongoDoc::Contexts::Enumerable.new(criteria)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module MongoDoc #:nodoc:
|
3
|
+
module Contexts #:nodoc:
|
4
|
+
class Enumerable
|
5
|
+
include Mongoid::Contexts::Paging
|
6
|
+
attr_reader :criteria
|
7
|
+
|
8
|
+
delegate :first, :last, :to => :execute
|
9
|
+
delegate :documents, :options, :selector, :to => :criteria
|
10
|
+
|
11
|
+
# Return aggregation counts of the grouped documents. This will count by
|
12
|
+
# the first field provided in the fields array.
|
13
|
+
#
|
14
|
+
# Returns:
|
15
|
+
#
|
16
|
+
# A +Hash+ with field values as keys, count as values
|
17
|
+
def aggregate
|
18
|
+
counts = {}
|
19
|
+
group.each_pair { |key, value| counts[key] = value.size }
|
20
|
+
counts
|
21
|
+
end
|
22
|
+
|
23
|
+
# Gets the number of documents in the array. Delegates to size.
|
24
|
+
def count
|
25
|
+
@count ||= documents.size
|
26
|
+
end
|
27
|
+
|
28
|
+
# Groups the documents by the first field supplied in the field options.
|
29
|
+
#
|
30
|
+
# Returns:
|
31
|
+
#
|
32
|
+
# A +Hash+ with field values as keys, arrays of documents as values.
|
33
|
+
def group
|
34
|
+
field = options[:fields].first
|
35
|
+
documents.group_by { |doc| doc.send(field) }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Enumerable implementation of execute. Returns matching documents for
|
39
|
+
# the selector, and adds options if supplied.
|
40
|
+
#
|
41
|
+
# Returns:
|
42
|
+
#
|
43
|
+
# An +Array+ of documents that matched the selector.
|
44
|
+
def execute(paginating = false)
|
45
|
+
limit(documents.select { |document| document.matches?(selector) })
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return documents based on an id search. Will handle if a single id has
|
49
|
+
# been passed or mulitple ids.
|
50
|
+
#
|
51
|
+
# Example:
|
52
|
+
#
|
53
|
+
# context.id_criteria([1, 2, 3])
|
54
|
+
#
|
55
|
+
# Returns:
|
56
|
+
#
|
57
|
+
# The single or multiple documents.
|
58
|
+
def id_criteria(params)
|
59
|
+
criteria.id(params)
|
60
|
+
result = params.is_a?(String) ? one : criteria.entries
|
61
|
+
return result
|
62
|
+
end
|
63
|
+
|
64
|
+
# Create the new enumerable context. This will need the selector and
|
65
|
+
# options from a +Criteria+ and a documents array that is the underlying
|
66
|
+
# array of embedded documents from a has many association.
|
67
|
+
#
|
68
|
+
# Example:
|
69
|
+
#
|
70
|
+
# <tt>MongoDoc::Contexts::Enumerable.new(criteria)</tt>
|
71
|
+
def initialize(criteria)
|
72
|
+
@criteria = criteria
|
73
|
+
end
|
74
|
+
|
75
|
+
# Get the largest value for the field in all the documents.
|
76
|
+
#
|
77
|
+
# Returns:
|
78
|
+
#
|
79
|
+
# The numerical largest value.
|
80
|
+
def max(field)
|
81
|
+
determine(field, :>=)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Get the smallest value for the field in all the documents.
|
85
|
+
#
|
86
|
+
# Returns:
|
87
|
+
#
|
88
|
+
# The numerical smallest value.
|
89
|
+
def min(field)
|
90
|
+
determine(field, :<=)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Get one document.
|
94
|
+
#
|
95
|
+
# Returns:
|
96
|
+
#
|
97
|
+
# The first document in the +Array+
|
98
|
+
alias :one :first
|
99
|
+
|
100
|
+
# Get the sum of the field values for all the documents.
|
101
|
+
#
|
102
|
+
# Returns:
|
103
|
+
#
|
104
|
+
# The numerical sum of all the document field values.
|
105
|
+
def sum(field)
|
106
|
+
sum = documents.inject(nil) do |memo, doc|
|
107
|
+
value = doc.send(field)
|
108
|
+
memo ? memo += value : value
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
protected
|
113
|
+
# If the field exists, perform the comparison and set if true.
|
114
|
+
def determine(field, operator)
|
115
|
+
matching = documents.inject(nil) do |memo, doc|
|
116
|
+
value = doc.send(field)
|
117
|
+
(memo && memo.send(operator, value)) ? memo : value
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Limits the result set if skip and limit options.
|
122
|
+
def limit(documents)
|
123
|
+
skip, limit = options[:skip], options[:limit]
|
124
|
+
if skip && limit
|
125
|
+
return documents.slice(skip, limit)
|
126
|
+
end
|
127
|
+
documents
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|