mongoid 0.2.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/History.txt +2 -0
- data/MIT_LICENSE +20 -0
- data/README.textile +135 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/lib/mongoid.rb +79 -0
- data/lib/mongoid/associations/association_factory.rb +21 -0
- data/lib/mongoid/associations/belongs_to_association.rb +23 -0
- data/lib/mongoid/associations/has_many_association.rb +36 -0
- data/lib/mongoid/associations/has_one_association.rb +35 -0
- data/lib/mongoid/document.rb +208 -0
- data/lib/mongoid/extensions.rb +7 -0
- data/lib/mongoid/extensions/array/conversions.rb +13 -0
- data/lib/mongoid/extensions/object/conversions.rb +13 -0
- data/lib/mongoid/paginator.rb +22 -0
- data/mongoid.gemspec +80 -0
- data/spec/integration/mongoid/document_spec.rb +95 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +41 -0
- data/spec/unit/mongoid/associations/association_factory_spec.rb +48 -0
- data/spec/unit/mongoid/associations/belongs_to_association_spec.rb +35 -0
- data/spec/unit/mongoid/associations/has_many_association_spec.rb +126 -0
- data/spec/unit/mongoid/associations/has_one_association_spec.rb +35 -0
- data/spec/unit/mongoid/document_spec.rb +680 -0
- data/spec/unit/mongoid/extensions/array/conversions_spec.rb +14 -0
- data/spec/unit/mongoid/extensions/object/conversions_spec.rb +13 -0
- data/spec/unit/mongoid/paginator_spec.rb +74 -0
- metadata +110 -0
data/.gitignore
ADDED
data/History.txt
ADDED
data/MIT_LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Durran Jordan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,135 @@
|
|
1
|
+
<h2>Overview</h2>
|
2
|
+
|
3
|
+
<h3>About Mongoid</h3>
|
4
|
+
|
5
|
+
<p>
|
6
|
+
Mongoid is an ODM (Object-Document-Mapper) framework for MongoDB in Ruby. Mongoid differs from other
|
7
|
+
mapping frameworks in that it constrains the models into behaving in a manner appropriate for a
|
8
|
+
document database. That is to say there are no relationships between documents in the underlying datastore.
|
9
|
+
If a relationship is set up in the Model the child models are automatically embedded within the parent document
|
10
|
+
in the database. The concept of a foreign key relationship to another Document does not exist, as that is
|
11
|
+
design and thinking for a relational database, not a document database. Mongoloid does however provide
|
12
|
+
all the ActiveRecord style functionality you need, with the difference that it stores all associations
|
13
|
+
within the parent document.
|
14
|
+
</p>
|
15
|
+
|
16
|
+
<h3>Project Tracking</h3>
|
17
|
+
|
18
|
+
<a href="http://www.pivotaltracker.com/projects/27482">Mongoid on Pivotal Tracker</a>
|
19
|
+
<a href="http://groups.google.com/group/mongoid">Mongoid Google Group</a>
|
20
|
+
|
21
|
+
<h3>Compatibility</h3>
|
22
|
+
|
23
|
+
<p>
|
24
|
+
Mongoid is developed against Ruby 1.8.6, 1.8.7, 1.9.1
|
25
|
+
</p>
|
26
|
+
|
27
|
+
<h2>Mongoid in Action</h2>
|
28
|
+
|
29
|
+
Initialize Mongoid:
|
30
|
+
|
31
|
+
<pre>
|
32
|
+
Mongoid.connect_to("myapp_database_name")
|
33
|
+
</pre>
|
34
|
+
|
35
|
+
Example of a simple domain model:
|
36
|
+
|
37
|
+
<pre>
|
38
|
+
class Person < Mongoid::Document
|
39
|
+
fields :title
|
40
|
+
has_many :addresses
|
41
|
+
has_one :name
|
42
|
+
end
|
43
|
+
|
44
|
+
class Address < Mongoid::Document
|
45
|
+
fields \
|
46
|
+
:street,
|
47
|
+
:city,
|
48
|
+
:state,
|
49
|
+
:post_code
|
50
|
+
belongs_to :person
|
51
|
+
end
|
52
|
+
|
53
|
+
class Name < Mongoid::Document
|
54
|
+
fields \
|
55
|
+
:first_name,
|
56
|
+
:last_name
|
57
|
+
end
|
58
|
+
</pre>
|
59
|
+
|
60
|
+
Create a new Document:
|
61
|
+
|
62
|
+
<pre>
|
63
|
+
Person.create(:title => "Esquire")
|
64
|
+
</pre>
|
65
|
+
|
66
|
+
Save a Document:
|
67
|
+
|
68
|
+
<pre>
|
69
|
+
person.save
|
70
|
+
</pre>
|
71
|
+
|
72
|
+
Delete a Document:
|
73
|
+
|
74
|
+
<pre>
|
75
|
+
person.destroy
|
76
|
+
</pre>
|
77
|
+
|
78
|
+
Update a Document:
|
79
|
+
|
80
|
+
<pre>
|
81
|
+
person.update_attributes(:title => "Sir")
|
82
|
+
</pre>
|
83
|
+
|
84
|
+
Search for a Document in the database:
|
85
|
+
|
86
|
+
<pre>
|
87
|
+
Person.find(:all, :title => "Esquire")
|
88
|
+
|
89
|
+
Person.find(:first, :title => "Esquire")
|
90
|
+
</pre>
|
91
|
+
|
92
|
+
Paginate Document search results:
|
93
|
+
|
94
|
+
<pre>
|
95
|
+
Person.paginate(:title => "Esquire", :page => 1, :per_page => 20)
|
96
|
+
</pre>
|
97
|
+
|
98
|
+
Validations:
|
99
|
+
|
100
|
+
Mongoid supports all validations provided by Jay Fields' <tt>Validatable</tt> gem.
|
101
|
+
For more information please see the <tt>Validatable</tt> documentation.
|
102
|
+
|
103
|
+
<a href="http://validatable.rubyforge.org/">Validatable on RubyForge</a>
|
104
|
+
|
105
|
+
<h2>License</h2>
|
106
|
+
|
107
|
+
<p>
|
108
|
+
Copyright (c) 2009 Durran Jordan
|
109
|
+
</p>
|
110
|
+
<p>
|
111
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
112
|
+
a copy of this software and associated documentation files (the
|
113
|
+
"Software"), to deal in the Software without restriction, including
|
114
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
115
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
116
|
+
permit persons to whom the Software is furnished to do so, subject to
|
117
|
+
the following conditions:
|
118
|
+
</p>
|
119
|
+
<p>
|
120
|
+
The above copyright notice and this permission notice shall be
|
121
|
+
included in all copies or substantial portions of the Software.
|
122
|
+
</p>
|
123
|
+
<p>
|
124
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
125
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
126
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
127
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
128
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
129
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
130
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
131
|
+
</p>
|
132
|
+
|
133
|
+
<h2>Credits</h2>
|
134
|
+
|
135
|
+
Durran Jordan: durran at gmail dot com
|
data/Rakefile
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require "rubygems"
|
2
|
+
require "rake"
|
3
|
+
require "rake/rdoctask"
|
4
|
+
require "spec/rake/spectask"
|
5
|
+
require "metric_fu"
|
6
|
+
|
7
|
+
begin
|
8
|
+
require "jeweler"
|
9
|
+
Jeweler::Tasks.new do |gem|
|
10
|
+
gem.name = "mongoid"
|
11
|
+
gem.summary = %Q{Mongoid}
|
12
|
+
gem.email = "durran@gmail.com"
|
13
|
+
gem.homepage = "http://github.com/durran/mongoid"
|
14
|
+
gem.authors = ["Durran Jordan"]
|
15
|
+
gem.add_dependency "activesupport"
|
16
|
+
gem.add_dependency "mongodb-mongo"
|
17
|
+
end
|
18
|
+
|
19
|
+
rescue LoadError
|
20
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
21
|
+
end
|
22
|
+
|
23
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
24
|
+
spec.libs << "lib" << "spec"
|
25
|
+
spec.pattern = "spec/**/*_spec.rb"
|
26
|
+
spec.spec_opts = ['--options', "spec/spec.opts"]
|
27
|
+
end
|
28
|
+
|
29
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
30
|
+
spec.libs << "lib" << "spec"
|
31
|
+
spec.pattern = "spec/**/*_spec.rb"
|
32
|
+
spec.spec_opts = ['--options', "spec/spec.opts"]
|
33
|
+
spec.rcov = true
|
34
|
+
end
|
35
|
+
|
36
|
+
Rake::RDocTask.new do |rdoc|
|
37
|
+
if File.exist?("VERSION.yml")
|
38
|
+
config = YAML.load(File.read("VERSION.yml"))
|
39
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
40
|
+
else
|
41
|
+
version = ""
|
42
|
+
end
|
43
|
+
rdoc.rdoc_dir = "rdoc"
|
44
|
+
rdoc.title = "my_emma #{version}"
|
45
|
+
rdoc.rdoc_files.include("README*")
|
46
|
+
rdoc.rdoc_files.include("lib/**/*.rb")
|
47
|
+
end
|
48
|
+
|
49
|
+
task :default => ["rcov", "metrics:all"]
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.2.5
|
data/lib/mongoid.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# Copyright (c) 2009 Durran Jordan
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
require "rubygems"
|
22
|
+
|
23
|
+
gem "activesupport", "2.3.4"
|
24
|
+
gem "mongodb-mongo", "0.14.1"
|
25
|
+
gem "durran-validatable", "1.7.5"
|
26
|
+
gem "mislav-will_paginate", "2.3.11"
|
27
|
+
|
28
|
+
require "validatable"
|
29
|
+
require "active_support/callbacks"
|
30
|
+
require "active_support/core_ext"
|
31
|
+
require "delegate"
|
32
|
+
require "will_paginate/collection"
|
33
|
+
require "mongo"
|
34
|
+
require "mongoid/associations/association_factory"
|
35
|
+
require "mongoid/associations/belongs_to_association"
|
36
|
+
require "mongoid/associations/has_many_association"
|
37
|
+
require "mongoid/associations/has_one_association"
|
38
|
+
require "mongoid/extensions/array/conversions"
|
39
|
+
require "mongoid/extensions/object/conversions"
|
40
|
+
require "mongoid/extensions"
|
41
|
+
require "mongoid/document"
|
42
|
+
require "mongoid/paginator"
|
43
|
+
|
44
|
+
module Mongoid
|
45
|
+
|
46
|
+
# Thrown when the database connection has not been set up.
|
47
|
+
class NoConnectionError < RuntimeError
|
48
|
+
end
|
49
|
+
|
50
|
+
# Thrown when :document_class is not provided in the attributes
|
51
|
+
# hash when creating a new Document
|
52
|
+
class ClassNotProvidedError < RuntimeError
|
53
|
+
end
|
54
|
+
|
55
|
+
# Thrown when an association is defined on the class, but the
|
56
|
+
# attribute in the hash is not an Array or Hash.
|
57
|
+
class TypeMismatchError < RuntimeError
|
58
|
+
end
|
59
|
+
|
60
|
+
# Thrown when an association is defined that is not valid. Must
|
61
|
+
# be belongs_to, has_many, has_one
|
62
|
+
class InvalidAssociationError < RuntimeError
|
63
|
+
end
|
64
|
+
|
65
|
+
# Connect to the database name supplied. This should be run
|
66
|
+
# for initial setup, potentially in a rails initializer.
|
67
|
+
def self.connect_to(name)
|
68
|
+
@@connection ||= Mongo::Connection.new
|
69
|
+
@@database ||= @@connection.db(name)
|
70
|
+
end
|
71
|
+
|
72
|
+
# Get the MongoDB database. If initialization via Mongoid.connect_to()
|
73
|
+
# has not happened, an exception will occur.
|
74
|
+
def self.database
|
75
|
+
raise NoConnectionError unless @@database
|
76
|
+
@@database
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Mongoid #:nodoc:
|
2
|
+
module Associations #:nodoc:
|
3
|
+
class AssociationFactory #:nodoc:
|
4
|
+
|
5
|
+
# Creates a new association, based on the type provided and
|
6
|
+
# passes the name and document into the newly instantiated
|
7
|
+
# association.
|
8
|
+
#
|
9
|
+
# If the type is invalid a InvalidAssociationError will be thrown.
|
10
|
+
def self.create(association_type, association_name, document)
|
11
|
+
case association_type
|
12
|
+
when :belongs_to then BelongsToAssociation.new(document)
|
13
|
+
when :has_many then HasManyAssociation.new(association_name, document)
|
14
|
+
when :has_one then HasOneAssociation.new(association_name, document)
|
15
|
+
else raise InvalidAssociationError
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Mongoid #:nodoc:
|
2
|
+
module Associations #:nodoc:
|
3
|
+
class BelongsToAssociation #:nodoc:
|
4
|
+
|
5
|
+
# Creates the new association by setting the internal
|
6
|
+
# document as the passed in Document. This should be the
|
7
|
+
# parent.
|
8
|
+
#
|
9
|
+
# All method calls on this object will then be delegated
|
10
|
+
# to the internal document itself.
|
11
|
+
def initialize(document)
|
12
|
+
@document = document.parent
|
13
|
+
end
|
14
|
+
|
15
|
+
# All calls to this association will be delegated straight
|
16
|
+
# to the encapsulated document.
|
17
|
+
def method_missing(method, *args)
|
18
|
+
@document.send(method, *args)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Mongoid #:nodoc:
|
2
|
+
module Associations #:nodoc:
|
3
|
+
class HasManyAssociation < DelegateClass(Array) #:nodoc:
|
4
|
+
|
5
|
+
# Creates the new association by finding the attributes in
|
6
|
+
# the parent document with its name, and instantiating a
|
7
|
+
# new document for each one found. These will then be put in an
|
8
|
+
# internal array.
|
9
|
+
#
|
10
|
+
# This then delegated all methods to the array class since this is
|
11
|
+
# essentially a proxy to an array itself.
|
12
|
+
def initialize(association_name, document)
|
13
|
+
@klass = association_name.to_s.classify.constantize
|
14
|
+
attributes = document.attributes[association_name]
|
15
|
+
@documents = attributes ? attributes.collect do |attribute|
|
16
|
+
child = @klass.new(attribute)
|
17
|
+
child.parent = document
|
18
|
+
child
|
19
|
+
end : []
|
20
|
+
super(@documents)
|
21
|
+
end
|
22
|
+
|
23
|
+
# Builds a new Document and adds it to the association collection. The
|
24
|
+
# document created will be of the same class as the others in the
|
25
|
+
# association, and the attributes will be passed into the constructor.
|
26
|
+
#
|
27
|
+
# Returns the newly created object.
|
28
|
+
def build(attributes)
|
29
|
+
object = @klass.new(attributes)
|
30
|
+
push(object)
|
31
|
+
object
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Mongoid #:nodoc:
|
2
|
+
module Associations #:nodoc:
|
3
|
+
class HasOneAssociation #:nodoc:
|
4
|
+
|
5
|
+
attr_reader :document
|
6
|
+
delegate :valid?, :to => :document
|
7
|
+
|
8
|
+
# Creates the new association by finding the attributes in
|
9
|
+
# the parent document with its name, and instantiating a
|
10
|
+
# new document for it.
|
11
|
+
#
|
12
|
+
# All method calls on this object will then be delegated
|
13
|
+
# to the internal document itself.
|
14
|
+
def initialize(association_name, document)
|
15
|
+
klass = association_name.to_s.titleize.constantize
|
16
|
+
attributes = document.attributes[association_name]
|
17
|
+
@document = klass.new(attributes)
|
18
|
+
@document.parent = document
|
19
|
+
decorate!
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def decorate!
|
24
|
+
@document.public_methods(false).each do |method|
|
25
|
+
(class << self; self; end).class_eval do
|
26
|
+
define_method method do |*args|
|
27
|
+
@document.send method, *args
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
module Mongoid #:nodoc:
|
2
|
+
class Document #:nodoc:
|
3
|
+
include ActiveSupport::Callbacks
|
4
|
+
include Validatable
|
5
|
+
|
6
|
+
AGGREGATE_REDUCE = "function(obj, prev) { prev.count++; }"
|
7
|
+
GROUP_BY_REDUCE = "function(obj, prev) { prev.group.push(obj); }"
|
8
|
+
|
9
|
+
attr_reader :attributes, :parent
|
10
|
+
|
11
|
+
define_callbacks \
|
12
|
+
:after_create,
|
13
|
+
:after_save,
|
14
|
+
:before_create,
|
15
|
+
:before_save
|
16
|
+
|
17
|
+
class << self
|
18
|
+
|
19
|
+
# Get an aggregate count for the supplied group of fields and the
|
20
|
+
# selector that is provided.
|
21
|
+
def aggregate(fields, selector)
|
22
|
+
collection.group(fields, selector, { :count => 0 }, AGGREGATE_REDUCE)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Create an association to a parent Document.
|
26
|
+
def belongs_to(association_name)
|
27
|
+
add_association(:belongs_to, association_name.to_s.classify, association_name)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Get the Mongo::Collection associated with this Document.
|
31
|
+
def collection
|
32
|
+
@collection_name = self.to_s.demodulize.tableize
|
33
|
+
@collection ||= Mongoid.database.collection(@collection_name)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Create a new Document with the supplied attribtues, and insert it into the database.
|
37
|
+
def create(attributes = {})
|
38
|
+
new(attributes).save
|
39
|
+
end
|
40
|
+
|
41
|
+
# Defines all the fields that are accessable on the Document
|
42
|
+
# For each field that is defined, a getter and setter will be
|
43
|
+
# added as an instance method to the Document.
|
44
|
+
def fields(*names)
|
45
|
+
@fields = []
|
46
|
+
names.flatten.each do |name|
|
47
|
+
@fields << name
|
48
|
+
define_method(name) { read_attribute(name) }
|
49
|
+
define_method("#{name}=") { |value| write_attribute(name, value) }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Find all Documents in several ways.
|
54
|
+
# Model.find(:first, :attribute => "value")
|
55
|
+
# Model.find(:all, :attribute => "value")
|
56
|
+
def find(*args)
|
57
|
+
type, selector = args[0], args[1]
|
58
|
+
case type
|
59
|
+
when :all then find_all(selector[:conditions])
|
60
|
+
when :first then find_first(selector[:conditions])
|
61
|
+
else find_first(Mongo::ObjectID.from_string(type.to_s))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Find a single Document given the passed selector, which is a Hash of attributes that
|
66
|
+
# must match the Document in the database exactly.
|
67
|
+
def find_first(selector = nil)
|
68
|
+
new(collection.find_one(selector))
|
69
|
+
end
|
70
|
+
|
71
|
+
# Find all Documents given the passed selector, which is a Hash of attributes that
|
72
|
+
# must match the Document in the database exactly.
|
73
|
+
def find_all(selector = nil)
|
74
|
+
collection.find(selector).collect { |doc| new(doc) }
|
75
|
+
end
|
76
|
+
|
77
|
+
# Find all Documents given the supplied criteria, grouped by the fields
|
78
|
+
# provided.
|
79
|
+
def group_by(fields, selector)
|
80
|
+
collection.group(fields, selector, { :group => [] }, GROUP_BY_REDUCE).collect do |docs|
|
81
|
+
group!(docs)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Create a one-to-many association between Documents.
|
86
|
+
def has_many(association_name)
|
87
|
+
add_association(:has_many, association_name.to_s.classify, association_name)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Create a one-to-many association between Documents.
|
91
|
+
def has_one(association_name)
|
92
|
+
add_association(:has_one, association_name.to_s.titleize, association_name)
|
93
|
+
end
|
94
|
+
|
95
|
+
# Adds an index on the field specified. Options can be :unique => true or
|
96
|
+
# :unique => false. It will default to the latter.
|
97
|
+
def index(name, options = { :unique => false })
|
98
|
+
collection.create_index(name, options)
|
99
|
+
end
|
100
|
+
|
101
|
+
# Find all documents in paginated fashion given the supplied arguments.
|
102
|
+
# If no parameters are passed just default to offset 0 and limit 20.
|
103
|
+
def paginate(selector = {}, params = {})
|
104
|
+
WillPaginate::Collection.create(
|
105
|
+
params[:page] || 1,
|
106
|
+
params[:per_page] || 20,
|
107
|
+
0) do |pager|
|
108
|
+
results = collection.find(selector[:conditions], { :limit => pager.per_page, :offset => pager.offset })
|
109
|
+
pager.total_entries = results.count
|
110
|
+
pager.replace(results.collect { |doc| new(doc) })
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
# Get the Mongo::Collection associated with this Document.
|
117
|
+
def collection
|
118
|
+
self.class.collection
|
119
|
+
end
|
120
|
+
|
121
|
+
# Delete this Document from the database.
|
122
|
+
def destroy
|
123
|
+
collection.remove(:_id => id)
|
124
|
+
end
|
125
|
+
|
126
|
+
# Get the Mongo::ObjectID associated with this object.
|
127
|
+
# This is in essence the primary key.
|
128
|
+
def id
|
129
|
+
@attributes[:_id]
|
130
|
+
end
|
131
|
+
|
132
|
+
# Instantiate a new Document, setting the Document's attirbutes if given.
|
133
|
+
# If no attributes are provided, they will be initialized with an empty Hash.
|
134
|
+
def initialize(attributes = {})
|
135
|
+
@attributes = attributes.symbolize_keys if attributes
|
136
|
+
@attributes = {} unless attributes
|
137
|
+
end
|
138
|
+
|
139
|
+
# Returns true is the Document has not been persisted to the database, false if it has.
|
140
|
+
def new_record?
|
141
|
+
@attributes[:_id].nil?
|
142
|
+
end
|
143
|
+
|
144
|
+
# Set the parent to this document.
|
145
|
+
def parent=(document)
|
146
|
+
@parent = document
|
147
|
+
end
|
148
|
+
|
149
|
+
# Save this document to the database. If this document is the root document
|
150
|
+
# in the object graph, it will save itself, and return self. If the
|
151
|
+
# document is embedded within another document, or is multiple levels down
|
152
|
+
# the tree, the root object will get saved, and return itself.
|
153
|
+
def save
|
154
|
+
if @parent
|
155
|
+
@parent.save
|
156
|
+
else
|
157
|
+
run_callbacks(:before_save)
|
158
|
+
collection.save(@attributes)
|
159
|
+
run_callbacks(:after_save)
|
160
|
+
return self
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Returns the id of the Document
|
165
|
+
def to_param
|
166
|
+
id.to_s
|
167
|
+
end
|
168
|
+
|
169
|
+
# Update the attributes of this Document and return true
|
170
|
+
def update_attributes(attributes)
|
171
|
+
@attributes = attributes.symbolize_keys!; save; true
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
|
176
|
+
class << self
|
177
|
+
|
178
|
+
# Adds the association to the associations hash with the type as the key,
|
179
|
+
# then adds the accessors for the association.
|
180
|
+
def add_association(type, class_name, name)
|
181
|
+
define_method(name) do
|
182
|
+
Mongoid::Associations::AssociationFactory.create(type, name, self)
|
183
|
+
end
|
184
|
+
define_method("#{name}=") do |object|
|
185
|
+
@attributes[name] = object.mongoidize
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Takes the supplied raw grouping of documents and alters it to a
|
190
|
+
# grouping of actual document objects.
|
191
|
+
def group!(docs)
|
192
|
+
docs["group"] = docs["group"].collect { |attrs| new(attrs) }; docs
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
|
197
|
+
# Read from the attributes hash.
|
198
|
+
def read_attribute(name)
|
199
|
+
@attributes[name.to_sym]
|
200
|
+
end
|
201
|
+
|
202
|
+
# Write to the attributes hash.
|
203
|
+
def write_attribute(name, value)
|
204
|
+
@attributes[name.to_sym] = value
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
end
|