mongoid 0.2.5

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.
@@ -0,0 +1,5 @@
1
+ .DS_STORE
2
+ coverage/*
3
+ pkg/*
4
+ scratch_directory/*
5
+ tmp/*
@@ -0,0 +1,2 @@
1
+ 0.2.5:
2
+ - Switching pagination over to will_paginate
@@ -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.
@@ -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
@@ -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
@@ -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