mongoid 0.12.0 → 1.0.0

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/HISTORY CHANGED
@@ -1,3 +1,15 @@
1
+ === 1.0.0
2
+ - Validations cleanup: Only before_validation and
3
+ after_validation callbacks are supported now.
4
+
5
+ - Dynamic fields have reader and writer methods
6
+ again, but only for instances where the dynamic
7
+ attribute exists.
8
+
9
+ - Lots of refactoring, mostly coverting common
10
+ fucntionality into modules for the upcoming rails
11
+ 2 and 3 gem split.
12
+
1
13
  === 0.12.0
2
14
  - Has one now works as expected:
3
15
 
@@ -0,0 +1,56 @@
1
+ = Overview
2
+
3
+ == About Mongoid
4
+
5
+ Mongoid is an ODM (Object-Document-Mapper) framework for MongoDB in Ruby.
6
+
7
+ == Project Tracking
8
+
9
+ * {Mongoid on Pivotal Tracker}[http://www.pivotaltracker.com/projects/27482]
10
+ * {Mongoid Google Group}[http://groups.google.com/group/mongoid]
11
+ * {Mongoid on CI Joe}[http://mongoid.org:4444/]
12
+ * {Mongoid Website and Documentation}[http://mongoid.org]
13
+
14
+ == Compatibility
15
+
16
+ Mongoid is developed against Ruby 1.8.6, 1.8.7, 1.9.1, 1.9.2
17
+
18
+ Note API changes will be frequent until 1.0, releases will be
19
+ frequent, and backwards compatibility will not be supported. Do not
20
+ expect the gem to be of full production quality until that
21
+ point. However, once 1.0 is released I will be providing backwards
22
+ compatibility through each minor version upgrade, as well as detailed
23
+ release notes and documentation with each release.
24
+
25
+ = Documentation
26
+
27
+ Please see the new Mongoid website for up-to-date documentation:
28
+ {mongoid.org}[http://mongoid.org]
29
+
30
+ = License
31
+
32
+ Copyright (c) 2009 Durran Jordan
33
+
34
+ Permission is hereby granted, free of charge, to any person obtaining
35
+ a copy of this software and associated documentation files (the
36
+ "Software"), to deal in the Software without restriction, including
37
+ without limitation the rights to use, copy, modify, merge, publish,
38
+ distribute, sublicense, and/or sell copies of the Software, and to
39
+ permit persons to whom the Software is furnished to do so, subject to
40
+ the following conditions:
41
+
42
+ The above copyright notice and this permission notice shall be
43
+ included in all copies or substantial portions of the Software.
44
+
45
+
46
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
47
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
48
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
49
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
50
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
51
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
52
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
53
+
54
+ = Credits
55
+
56
+ Durran Jordan: durran at gmail dot com
data/Rakefile CHANGED
@@ -14,7 +14,7 @@ begin
14
14
 
15
15
  gem.add_dependency("activesupport", ">= 2.2.2")
16
16
  gem.add_dependency("mongo", ">= 0.18.2")
17
- gem.add_dependency("durran-validatable", ">= 1.8.4")
17
+ gem.add_dependency("durran-validatable", ">= 2.0.0")
18
18
  gem.add_dependency("leshill-will_paginate", ">= 2.3.11")
19
19
 
20
20
  gem.add_development_dependency("rspec", ">= 1.2.9")
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.12.0
1
+ 1.0.0
@@ -23,7 +23,7 @@ require "rubygems"
23
23
 
24
24
  gem "activesupport", ">= 2.2.2"
25
25
  gem "mongo", ">= 0.18.2"
26
- gem "durran-validatable", ">= 1.8.4"
26
+ gem "durran-validatable", ">= 2.0.0"
27
27
  gem "leshill-will_paginate", ">= 2.3.11"
28
28
 
29
29
  require "delegate"
@@ -40,17 +40,21 @@ require "mongo"
40
40
  require "mongoid/associations"
41
41
  require "mongoid/associations/options"
42
42
  require "mongoid/attributes"
43
+ require "mongoid/callbacks"
43
44
  require "mongoid/commands"
44
45
  require "mongoid/config"
45
46
  require "mongoid/complex_criterion"
46
47
  require "mongoid/criteria"
47
48
  require "mongoid/extensions"
48
49
  require "mongoid/errors"
49
- require "mongoid/field"
50
+ require "mongoid/fields/field"
51
+ require "mongoid/fields"
50
52
  require "mongoid/finders"
53
+ require "mongoid/indexes"
51
54
  require "mongoid/memoization"
52
55
  require "mongoid/timestamps"
53
56
  require "mongoid/versioning"
57
+ require "mongoid/components"
54
58
  require "mongoid/document"
55
59
 
56
60
  module Mongoid #:nodoc
@@ -50,11 +50,13 @@ module Mongoid # :nodoc:
50
50
  #
51
51
  # Example:
52
52
  #
53
- # class Person < Mongoid::Document
53
+ # class Person
54
+ # include Mongoid::Document
54
55
  # has_many :addresses
55
56
  # end
56
57
  #
57
- # class Address < Mongoid::Document
58
+ # class Address
59
+ # include Mongoid::Document
58
60
  # belongs_to :person, :inverse_of => :addresses
59
61
  # end
60
62
  def belongs_to(name, options = {})
@@ -77,7 +79,8 @@ module Mongoid # :nodoc:
77
79
  #
78
80
  # Example:
79
81
  #
80
- # class Game < Mongoid::Document
82
+ # class Game
83
+ # include Mongoid::Document
81
84
  # belongs_to_related :person
82
85
  # end
83
86
  #
@@ -99,11 +102,13 @@ module Mongoid # :nodoc:
99
102
  #
100
103
  # Example:
101
104
  #
102
- # class Person < Mongoid::Document
105
+ # class Person
106
+ # include Mongoid::Document
103
107
  # has_many :addresses
104
108
  # end
105
109
  #
106
- # class Address < Mongoid::Document
110
+ # class Address
111
+ # include Mongoid::Document
107
112
  # belongs_to :person, :inverse_of => :addresses
108
113
  # end
109
114
  def has_many(name, options = {})
@@ -122,7 +127,8 @@ module Mongoid # :nodoc:
122
127
  #
123
128
  # Example:
124
129
  #
125
- # class Person < Mongoid::Document
130
+ # class Person
131
+ # include Mongoid::Document
126
132
  # has_many_related :posts
127
133
  # end
128
134
  #
@@ -146,11 +152,13 @@ module Mongoid # :nodoc:
146
152
  #
147
153
  # Example:
148
154
  #
149
- # class Person < Mongoid::Document
155
+ # class Person
156
+ # include Mongoid::Document
150
157
  # has_many :addresses
151
158
  # end
152
159
  #
153
- # class Address < Mongoid::Document
160
+ # class Address
161
+ # include Mongoid::Document
154
162
  # belongs_to :person
155
163
  # end
156
164
  def has_one(name, options = {})
@@ -170,7 +178,8 @@ module Mongoid # :nodoc:
170
178
  #
171
179
  # Example:
172
180
  #
173
- # class Person < Mongoid::Document
181
+ # class Person
182
+ # include Mongoid::Document
174
183
  # has_one_related :game
175
184
  # end
176
185
  def has_one_related(name, options = {})
@@ -8,6 +8,27 @@ module Mongoid #:nodoc:
8
8
  end
9
9
  end
10
10
  module InstanceMethods
11
+ # Get the id associated with this object. This will pull the _id value out
12
+ # of the attributes +Hash+.
13
+ def id
14
+ @attributes[:_id]
15
+ end
16
+
17
+ # Set the id of the +Document+ to a new one.
18
+ def id=(new_id)
19
+ @attributes[:_id] = new_id
20
+ end
21
+
22
+ alias :_id :id
23
+ alias :_id= :id=
24
+
25
+ # Used for allowing accessor methods for dynamic attributes.
26
+ def method_missing(name, *args)
27
+ attr = name.to_s
28
+ return super unless @attributes.has_key?(attr.reader)
29
+ attr.writer? ? (@attributes[attr.reader] = *args) : @attributes[attr.reader]
30
+ end
31
+
11
32
  # Process the provided attributes casting them to their proper values if a
12
33
  # field exists for them on the +Document+. This will be limited to only the
13
34
  # attributes provided in the suppied +Hash+ so that no extra nil values get
@@ -17,7 +38,7 @@ module Mongoid #:nodoc:
17
38
  if Mongoid.allow_dynamic_fields && !respond_to?("#{key}=")
18
39
  @attributes[key] = value
19
40
  else
20
- send("#{key}=", value) unless (value.is_a?(String) && value.blank?)
41
+ set_unless_blank(key, value)
21
42
  end
22
43
  end
23
44
  end
@@ -50,6 +71,18 @@ module Mongoid #:nodoc:
50
71
  @attributes.delete(name)
51
72
  end
52
73
 
74
+ # Returns the object type. This corresponds to the name of the class that
75
+ # this +Document+ is, which is used in determining the class to
76
+ # instantiate in various cases.
77
+ def _type
78
+ @attributes[:_type]
79
+ end
80
+
81
+ # Set the type of the +Document+. This should be the name of the class.
82
+ def _type=(new_type)
83
+ @attributes[:_type] = new_type
84
+ end
85
+
53
86
  # Write a single attribute to the +Document+ attribute +Hash+. This will
54
87
  # also fire the before and after update callbacks, and perform any
55
88
  # necessary typecasting.
@@ -102,6 +135,11 @@ module Mongoid #:nodoc:
102
135
  end
103
136
  end
104
137
  end
138
+
139
+ # Sets the attirbute value unless it is a blank string.
140
+ def set_unless_blank(name, value)
141
+ send("#{name}=", value) unless (value.is_a?(String) && value.blank?)
142
+ end
105
143
  end
106
144
 
107
145
  module ClassMethods
@@ -110,7 +148,8 @@ module Mongoid #:nodoc:
110
148
  #
111
149
  # Example:
112
150
  #
113
- # class Person < Mongoid::Document
151
+ # class Person
152
+ # include Mongoid::Document
114
153
  # has_one :name
115
154
  # has_many :addresses
116
155
  #
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Callbacks
4
+ def self.included(base)
5
+ base.class_eval do
6
+ include ActiveSupport::Callbacks
7
+
8
+ # Define all the callbacks that are accepted by the document.
9
+ define_callbacks \
10
+ :before_create,
11
+ :after_create,
12
+ :before_destroy,
13
+ :after_destroy,
14
+ :before_save,
15
+ :after_save,
16
+ :before_update,
17
+ :after_update,
18
+ :before_validation,
19
+ :after_validation
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc
3
+ module Components #:nodoc
4
+ def self.included(base)
5
+ base.class_eval do
6
+ # All modules that a +Document+ is composed of are defined in this
7
+ # module, to keep the document class from getting too cluttered.
8
+ include Associations
9
+ include Attributes
10
+ include Callbacks
11
+ include Commands
12
+ include Fields
13
+ include Indexes
14
+ include Memoization
15
+ include Observable
16
+ include Validatable
17
+ extend Finders
18
+ end
19
+ end
20
+ end
21
+ end
@@ -311,7 +311,8 @@ module Mongoid #:nodoc:
311
311
  #
312
312
  # Example:
313
313
  #
314
- # class Person < Mongoid::Document
314
+ # class Person
315
+ # include Mongoid::Document
315
316
  # field :title
316
317
  # field :terms, :type => Boolean, :default => false
317
318
  #
@@ -3,49 +3,22 @@ module Mongoid #:nodoc:
3
3
  module Document
4
4
  def self.included(base)
5
5
  base.class_eval do
6
- include ActiveSupport::Callbacks
7
- include Associations, Attributes, Commands, Memoization, Observable, Validatable
6
+ include Components
8
7
  include InstanceMethods
9
-
10
8
  extend ClassMethods
11
- extend Finders
12
-
13
- # Set up the class attributes that must be available to all subclasses.
14
- # These include defaults, fields
15
- class_inheritable_accessor :defaults, :fields
16
9
 
17
- # The same collection is used for the entire class hierarchy.
18
- cattr_accessor :_collection, :collection_name, :embedded, :primary_key, :indexed
10
+ cattr_accessor :_collection, :collection_name, :embedded, :primary_key
19
11
 
20
- # Set the initial values. Defaults and fields get set to a
21
- # +HashWithIndifferentAccess+ while the collection name will get set to
22
- # the demodulized class.
23
12
  self.collection_name = self.to_s.underscore.tableize.gsub("/", "_")
24
- self.defaults = {}.with_indifferent_access
25
- self.fields = {}.with_indifferent_access
26
- self.indexed = false
27
13
 
28
14
  attr_accessor :association_name, :_parent
29
15
  attr_reader :attributes, :new_record
30
16
 
31
- delegate :collection, :defaults, :embedded?, :fields, :primary_key, :to => "self.class"
32
-
33
- # Define all the callbacks that are accepted by the document.
34
- define_callbacks :before_create, :before_destroy, :before_save, :before_update, :before_validation
35
- define_callbacks :after_create, :after_destroy, :after_save, :after_update, :after_validation
17
+ delegate :collection, :embedded?, :primary_key, :to => "self.class"
36
18
  end
37
19
  end
38
20
 
39
21
  module ClassMethods
40
- # Add the default indexes to the root document if they do not already
41
- # exist. Currently this is only _type.
42
- def add_indexes
43
- unless indexed
44
- self._collection.create_index(:_type, false)
45
- self.indexed = true
46
- end
47
- end
48
-
49
22
  # Returns the collection associated with this +Document+. If the
50
23
  # document is embedded, there will be no collection associated
51
24
  # with it.
@@ -57,40 +30,31 @@ module Mongoid #:nodoc:
57
30
  add_indexes; self._collection
58
31
  end
59
32
 
60
- # return true if the +Document+ is embedded in another +Documnet+.
33
+ # return true if the +Document+ is embedded in another +Documnet+. The
34
+ # embedded flag is set by declaring a belongs_to association.
35
+ #
36
+ # Example:
37
+ #
38
+ # <tt>Address.embedded?</tt>
61
39
  def embedded?
62
40
  self.embedded == true
63
41
  end
64
42
 
65
- # Defines all the fields that are accessable on the Document
66
- # For each field that is defined, a getter and setter will be
67
- # added as an instance method to the Document.
68
- #
69
- # Options:
70
- #
71
- # name: The name of the field, as a +Symbol+.
72
- # options: A +Hash+ of options to supply to the +Field+.
43
+ # Returns a human readable version of the class.
73
44
  #
74
45
  # Example:
75
46
  #
76
- # <tt>field :score, :default => 0</tt>
77
- def field(name, options = {})
78
- set_field(name, options)
79
- set_default(name, options)
80
- end
81
-
82
- # Returns a human readable version of the class.
47
+ # <tt>MixedDrink.human_name # returns "Mixed Drink"</tt>
83
48
  def human_name
84
49
  name.underscore.humanize
85
50
  end
86
51
 
87
- # Adds an index on the field specified. Options can be :unique => true or
88
- # :unique => false. It will default to the latter.
89
- def index(name, options = { :unique => false })
90
- collection.create_index(name, options[:unique])
91
- end
92
-
93
- # Instantiate a new object, only when loaded from the database.
52
+ # Instantiate a new object, only when loaded from the database or when
53
+ # the attributes have already been typecast.
54
+ #
55
+ # Example:
56
+ #
57
+ # <tt>Person.instantiate(:title => "Sir", :age => 30)</tt>
94
58
  def instantiate(attrs = {}, allocating = false)
95
59
  attributes = attrs.with_indifferent_access
96
60
  if attributes[:_id] || allocating
@@ -105,14 +69,26 @@ module Mongoid #:nodoc:
105
69
  # Defines the field that will be used for the id of this +Document+. This
106
70
  # set the id of this +Document+ before save to a parameterized version of
107
71
  # the field that was supplied. This is good for use for readable URLS in
108
- # web applications and *MUST* be defined on documents that are embedded
109
- # in order for proper updates in has_may associations.
72
+ # web applications.
73
+ #
74
+ # Example:
75
+ #
76
+ # class Person
77
+ # include Mongoid::Document
78
+ # field :first_name
79
+ # field :last_name
80
+ # key :first_name, :last_name
81
+ # end
110
82
  def key(*fields)
111
83
  self.primary_key = fields
112
84
  before_save :generate_key
113
85
  end
114
86
 
115
87
  # Macro for setting the collection name to store in.
88
+ #
89
+ # Example:
90
+ #
91
+ # <tt>Person.store_in :populdation</tt>
116
92
  def store_in(name)
117
93
  self.collection_name = name.to_s
118
94
  self._collection = Mongoid.database.collection(name.to_s)
@@ -123,28 +99,6 @@ module Mongoid #:nodoc:
123
99
  @_type ||= (self.subclasses + [ self.name ])
124
100
  end
125
101
 
126
- protected
127
-
128
- # Define a field attribute for the +Document+.
129
- def set_field(name, options = {})
130
- meth = options.delete(:as) || name
131
- fields[name] = Field.new(name.to_s, options)
132
- create_accessors(name, meth, options)
133
- end
134
-
135
- # Create the field accessors.
136
- def create_accessors(name, meth, options = {})
137
- define_method(meth) { read_attribute(name) }
138
- define_method("#{meth}=") { |value| write_attribute(name, value) }
139
- define_method("#{meth}?") { read_attribute(name) == true } if options[:type] == Boolean
140
- end
141
-
142
- # Set up a default value for a field.
143
- def set_default(name, options = {})
144
- value = options[:default]
145
- defaults[name] = value if value
146
- end
147
-
148
102
  end
149
103
 
150
104
  module InstanceMethods
@@ -180,20 +134,6 @@ module Mongoid #:nodoc:
180
134
  self.class.instantiate(@attributes.except(:_id).except(:versions).dup, true)
181
135
  end
182
136
 
183
- # Get the id associated with this object. This will pull the _id value out
184
- # of the attributes +Hash+.
185
- def id
186
- @attributes[:_id]
187
- end
188
-
189
- # Set the id
190
- def id=(new_id)
191
- @attributes[:_id] = new_id
192
- end
193
-
194
- alias :_id :id
195
- alias :_id= :id=
196
-
197
137
  # Instantiate a new +Document+, setting the Document's attributes if
198
138
  # given. If no attributes are provided, they will be initialized with
199
139
  # an empty +Hash+.
@@ -271,8 +211,7 @@ module Mongoid #:nodoc:
271
211
  # memoized association and notify the parent of the change.
272
212
  def remove(child)
273
213
  name = child.association_name
274
- @attributes.remove(name, child.attributes)
275
- remove_instance_variable("@#{name}")
214
+ reset(name) { @attributes.remove(name, child.attributes) }
276
215
  notify
277
216
  end
278
217
 
@@ -294,16 +233,6 @@ module Mongoid #:nodoc:
294
233
  id
295
234
  end
296
235
 
297
- # Returns the object type.
298
- def _type
299
- @attributes[:_type]
300
- end
301
-
302
- # Set the type.
303
- def _type=(new_type)
304
- @attributes[:_type] = new_type
305
- end
306
-
307
236
  # Observe a notify call from a child +Document+. This will either update
308
237
  # existing attributes on the +Document+ or clear them out for the child if
309
238
  # the clear boolean is provided.
@@ -325,15 +254,6 @@ module Mongoid #:nodoc:
325
254
  notify
326
255
  end
327
256
 
328
- # Needs to run the appropriate callbacks the delegate up to the validatable
329
- # gem.
330
- def valid?
331
- run_callbacks(:before_validation)
332
- result = super
333
- run_callbacks(:after_validation)
334
- result
335
- end
336
-
337
257
  protected
338
258
  def generate_key
339
259
  if primary_key