mongoid 0.12.0 → 1.0.0

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