mongoid 2.0.0.beta.7 → 2.0.0.beta.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -19,13 +19,6 @@
19
19
  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
20
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
21
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
- require "rubygems"
23
-
24
- gem "activemodel", "~>3.0.0.beta"
25
- gem "tzinfo", "~>0.3.22"
26
- gem "will_paginate", "~>3.0.pre"
27
- gem "mongo", "~>1.0.1"
28
- gem "bson", "~>1.0.1"
29
22
 
30
23
  require "delegate"
31
24
  require "singleton"
@@ -47,7 +40,7 @@ require "active_model/validator"
47
40
  require "active_model/validations"
48
41
  require "will_paginate/collection"
49
42
  require "mongo"
50
- require "mongoid/observable"
43
+ require "mongoid/extensions"
51
44
  require "mongoid/associations"
52
45
  require "mongoid/atomicity"
53
46
  require "mongoid/attributes"
@@ -60,7 +53,6 @@ require "mongoid/criteria"
60
53
  require "mongoid/cursor"
61
54
  require "mongoid/deprecation"
62
55
  require "mongoid/dirty"
63
- require "mongoid/extensions"
64
56
  require "mongoid/extras"
65
57
  require "mongoid/errors"
66
58
  require "mongoid/factory"
@@ -82,6 +74,7 @@ require "mongoid/timestamps"
82
74
  require "mongoid/validations"
83
75
  require "mongoid/versioning"
84
76
  require "mongoid/components"
77
+ require "mongoid/paranoia"
85
78
  require "mongoid/document"
86
79
 
87
80
  # add railtie
@@ -55,8 +55,10 @@ module Mongoid #:nodoc:
55
55
  #
56
56
  # A new +EmbeddedIn+ association proxy.
57
57
  def update(target, child, options)
58
- child.parentize(target, determine_name(target, options))
58
+ inverse = determine_name(target, options)
59
+ child.parentize(target, inverse)
59
60
  child.notify
61
+ target.unmemoize(inverse)
60
62
  instantiate(child, options)
61
63
  end
62
64
 
@@ -41,11 +41,24 @@ module Mongoid #:nodoc:
41
41
  def clear
42
42
  unless @target.empty?
43
43
  document = @target.first
44
- document.notify_observers(document, true)
44
+ document._parent.update_child(document, true) if (document._parent)
45
45
  @target.clear
46
46
  end
47
47
  end
48
48
 
49
+ # Returns a count of the number of documents in the association that have
50
+ # actually been persisted to the database.
51
+ #
52
+ # Use #size if you want the total number of documents.
53
+ #
54
+ # Returns:
55
+ #
56
+ # The total number of persisted embedded docs, as flagged by the
57
+ # #persisted? method.
58
+ def count
59
+ @target.select(&:persisted?).size
60
+ end
61
+
49
62
  # Creates a new Document and adds it to the association collection. The
50
63
  # document created will be of the same class as the others in the
51
64
  # association, and the attributes will be passed into the constructor and
@@ -69,8 +82,7 @@ module Mongoid #:nodoc:
69
82
  # The newly created Document.
70
83
  def create!(attrs = {}, type = nil)
71
84
  document = create(attrs, type)
72
- errors = document.errors
73
- raise Errors::Validations.new(errors) unless errors.empty?
85
+ raise Errors::Validations.new(document) unless document.errors.empty?
74
86
  document
75
87
  end
76
88
 
@@ -174,7 +186,7 @@ module Mongoid #:nodoc:
174
186
  else
175
187
  build(attrs)
176
188
  end
177
- end
189
+ end; self
178
190
  end
179
191
 
180
192
  # Paginate the association. Will create a new criteria, set the documents
@@ -43,7 +43,11 @@ module Mongoid #:nodoc:
43
43
  #
44
44
  # A new target document.
45
45
  def nested_build(attributes, options = nil)
46
- build(attributes) unless @target.blank? && options[:update_only]
46
+ unless @target.blank? && options[:update_only]
47
+ (attributes || {}).each do |key, value|
48
+ @target.write_attribute(key, value)
49
+ end
50
+ end; @target
47
51
  end
48
52
 
49
53
  class << self
@@ -2,6 +2,10 @@
2
2
  module Mongoid #:nodoc:
3
3
  module Attributes
4
4
  extend ActiveSupport::Concern
5
+ included do
6
+ class_inheritable_accessor :_protected_fields
7
+ self._protected_fields = []
8
+ end
5
9
 
6
10
  # Get the id associated with this object. This will pull the _id value out
7
11
  # of the attributes +Hash+.
@@ -38,7 +42,15 @@ module Mongoid #:nodoc:
38
42
  if set_allowed?(key)
39
43
  write_attribute(key, value)
40
44
  elsif write_allowed?(key)
41
- send("#{key}=", value)
45
+ if associations.include?(key.to_s) and associations[key.to_s].embedded? and value.is_a?(Hash)
46
+ if association = send(key)
47
+ association.nested_build(value)
48
+ else
49
+ send("build_#{key}", value)
50
+ end
51
+ else
52
+ send("#{key}=", value)
53
+ end
42
54
  end
43
55
  end
44
56
  setup_modifications
@@ -76,6 +88,16 @@ module Mongoid #:nodoc:
76
88
  modify(access, @attributes.delete(name.to_s), nil)
77
89
  end
78
90
 
91
+ # Returns true when attribute is present.
92
+ #
93
+ # Options:
94
+ #
95
+ # name: The name of the attribute to request presence on.
96
+ def attribute_present?(name)
97
+ value = read_attribute(name)
98
+ !value.blank?
99
+ end
100
+
79
101
  # Returns the object type. This corresponds to the name of the class that
80
102
  # this +Document+ is, which is used in determining the class to
81
103
  # instantiate in various cases.
@@ -161,15 +183,15 @@ module Mongoid #:nodoc:
161
183
 
162
184
  # Used when supplying a :limit as an option to accepts_nested_attributes_for
163
185
  def limit(attributes, name, options)
164
- raise Mongoid::Errors::TooManyNestedAttributeRecords.new(name, options[:limit]) if options[:limit] && attributes.size > options[:limit]
186
+ if options[:limit] && attributes.size > options[:limit]
187
+ raise Mongoid::Errors::TooManyNestedAttributeRecords.new(name, options[:limit])
188
+ end
165
189
  end
166
190
 
167
191
  # Return true if writing to the given field is allowed
168
192
  def write_allowed?(key)
169
193
  name = key.to_s
170
- existing = fields[name]
171
- return true unless existing
172
- existing.accessible?
194
+ !self._protected_fields.include?(name)
173
195
  end
174
196
 
175
197
  module ClassMethods
@@ -202,6 +224,19 @@ module Mongoid #:nodoc:
202
224
  end
203
225
  end
204
226
  end
227
+
228
+ # Defines fields that cannot be set via mass assignment.
229
+ #
230
+ # Example:
231
+ #
232
+ # class Person
233
+ # include Mongoid::Document
234
+ # field :security_code
235
+ # attr_protected :security_code
236
+ # end
237
+ def attr_protected(*names)
238
+ _protected_fields.concat(names.flatten.map(&:to_s))
239
+ end
205
240
  end
206
241
  end
207
242
  end
@@ -21,7 +21,6 @@ module Mongoid #:nodoc
21
21
  include Mongoid::Indexes
22
22
  include Mongoid::Matchers
23
23
  include Mongoid::Memoization
24
- include Mongoid::Observable
25
24
  include Mongoid::Paths
26
25
  include Mongoid::Persistence
27
26
  include Mongoid::State
@@ -160,7 +160,10 @@ module Mongoid #:nodoc:
160
160
  # Returns the selector and options as a +Hash+ that would be passed to a
161
161
  # scope for use with named scopes.
162
162
  def scoped
163
- { :where => @selector }.merge(@options)
163
+ scope_options = @options.dup
164
+ sorting = scope_options.delete(:sort)
165
+ scope_options[:order_by] = sorting if sorting
166
+ { :where => @selector }.merge(scope_options)
164
167
  end
165
168
 
166
169
  # Translate the supplied arguments into a +Criteria+ object.
@@ -2,6 +2,21 @@
2
2
  module Mongoid #:nodoc:
3
3
  module Criterion #:nodoc:
4
4
  module Optional
5
+
6
+ # Adds fields to be sorted in ascending order. Will add them in the order
7
+ # they were passed into the method.
8
+ #
9
+ # Example:
10
+ #
11
+ # <tt>criteria.ascending(:title, :dob)</tt>
12
+ def ascending(*fields)
13
+ @options[:sort] = [] unless @options[:sort] || fields.first.nil?
14
+ fields.flatten.each { |field| @options[:sort] << [ field, :asc ] }
15
+ self
16
+ end
17
+
18
+ alias :asc :ascending
19
+
5
20
  # Tells the criteria that the cursor that gets returned needs to be
6
21
  # cached. This is so multiple iterations don't hit the database multiple
7
22
  # times, however this is not advisable when working with large data sets
@@ -23,6 +38,20 @@ module Mongoid #:nodoc:
23
38
  @options[:cache] == true
24
39
  end
25
40
 
41
+ # Adds fields to be sorted in descending order. Will add them in the order
42
+ # they were passed into the method.
43
+ #
44
+ # Example:
45
+ #
46
+ # <tt>criteria.descending(:title, :dob)</tt>
47
+ def descending(*fields)
48
+ @options[:sort] = [] unless @options[:sort] || fields.first.nil?
49
+ fields.flatten.each { |field| @options[:sort] << [ field, :desc ] }
50
+ self
51
+ end
52
+
53
+ alias :desc :descending
54
+
26
55
  # Flags the criteria to execute against a read-only slave in the pool
27
56
  # instead of master.
28
57
  #
@@ -110,8 +139,15 @@ module Mongoid #:nodoc:
110
139
  # <tt>criteria.order_by([[:field1, :asc], [:field2, :desc]])</tt>
111
140
  #
112
141
  # Returns: <tt>self</tt>
113
- def order_by(params = [])
114
- @options[:sort] = params; self
142
+ def order_by(*args)
143
+ @options[:sort] = [] unless @options[:sort] || args.first.nil?
144
+ arguments = args.first
145
+ case arguments
146
+ when Hash then arguments.each { |field, direction| @options[:sort] << [ field, direction ] }
147
+ when Array then @options[:sort].concat(arguments)
148
+ when Complex
149
+ args.flatten.each { |complex| @options[:sort] << [ complex.key, complex.operator.to_sym ] }
150
+ end; self
115
151
  end
116
152
 
117
153
  # Adds a criterion to the +Criteria+ that specifies how many results to skip
@@ -141,13 +141,13 @@ module Mongoid #:nodoc:
141
141
  "#<#{self.class.name} _id: #{id}, #{attrs * ', '}>"
142
142
  end
143
143
 
144
- # Notify observers of an update.
144
+ # Notify parent of an update.
145
145
  #
146
146
  # Example:
147
147
  #
148
148
  # <tt>person.notify</tt>
149
149
  def notify
150
- notify_observers(self)
150
+ _parent.update_child(self) if _parent
151
151
  end
152
152
 
153
153
  # Return the attributes hash.
@@ -178,7 +178,7 @@ module Mongoid #:nodoc:
178
178
  [ self ]
179
179
  end
180
180
 
181
- # Observe a notify call from a child +Document+. This will either update
181
+ # Recieve a notify call from a child +Document+. This will either update
182
182
  # existing attributes on the +Document+ or clear them out for the child if
183
183
  # the clear boolean is provided.
184
184
  #
@@ -186,18 +186,15 @@ module Mongoid #:nodoc:
186
186
  #
187
187
  # child: The child +Document+ that sent the notification.
188
188
  # clear: Will clear out the child's attributes if set to true.
189
- #
190
- # This will also cause the observing +Document+ to notify it's parent if
191
- # there is any.
192
- def observe(child, clear = false)
189
+ def update_child(child, clear = false)
193
190
  name = child.association_name
194
191
  attrs = child.instance_variable_get(:@attributes)
195
192
  if clear
196
193
  @attributes.delete(name)
197
194
  else
195
+ # check good for array only
198
196
  @attributes.insert(name, attrs) unless @attributes[name] && @attributes[name].include?(attrs)
199
197
  end
200
- notify
201
198
  end
202
199
  end
203
200
  end
@@ -2,6 +2,9 @@
2
2
  module Mongoid #:nodoc
3
3
  module Errors #:nodoc
4
4
 
5
+ # Default parent Mongoid error for all custom errors
6
+ class MongoidError < StandardError; end
7
+
5
8
  # Raised when querying the database for a document by a specific id which
6
9
  # does not exist. If multiple ids were passed then it will display all of
7
10
  # those.
@@ -9,12 +12,12 @@ module Mongoid #:nodoc
9
12
  # Example:
10
13
  #
11
14
  # <tt>DocumentNotFound.new(Person, ["1", "2"])</tt>
12
- class DocumentNotFound < RuntimeError
15
+ class DocumentNotFound < MongoidError
16
+ attr_reader :klass, :indentifiers
13
17
  def initialize(klass, ids)
14
- @klass, @identifier = klass, ids.is_a?(Array) ? ids.join(", ") : ids
15
- end
16
- def message
17
- "Document not found for class #{@klass} and id(s) #{@identifier}"
18
+ @klass = klass
19
+ @identifiers = ids.is_a?(Array) ? ids.join(", ") : ids
20
+ super("Document not found for class #{@klass} with id(s) #{@identifiers}")
18
21
  end
19
22
  end
20
23
 
@@ -23,7 +26,7 @@ module Mongoid #:nodoc
23
26
  # Example:
24
27
  #
25
28
  # <tt>InvalidOptions.new</tt>
26
- class InvalidOptions < RuntimeError; end
29
+ class InvalidOptions < MongoidError; end
27
30
 
28
31
  # Raised when the database connection has not been set up properly, either
29
32
  # by attempting to set an object on the db that is not a +Mongo::DB+, or
@@ -32,12 +35,24 @@ module Mongoid #:nodoc
32
35
  # Example:
33
36
  #
34
37
  # <tt>InvalidDatabase.new("Not a DB")</tt>
35
- class InvalidDatabase < RuntimeError
38
+ class InvalidDatabase < MongoidError
39
+ attr_reader :database
36
40
  def initialize(database)
37
41
  @database = database
42
+ super("Database should be a Mongo::DB, not #{@database.class.name}")
38
43
  end
39
- def message
40
- "Database should be a Mongo::DB, not #{@database.class.name}"
44
+ end
45
+
46
+ # Raised when trying to get or set a value for a defined field, where the
47
+ # type of the object does not match the defined field type.
48
+ #
49
+ # Example:
50
+ #
51
+ # <tt>InvalidType.new(Array, "Not an Array")</tt>
52
+ class InvalidType < MongoidError
53
+ def initialize(klass, value)
54
+ super("Field was defined as a(n) #{klass.name}, but received a #{value.class.name} " +
55
+ "with the value #{value.inspect}.")
41
56
  end
42
57
  end
43
58
 
@@ -46,12 +61,9 @@ module Mongoid #:nodoc
46
61
  # Example:
47
62
  #
48
63
  # <tt>UnsupportedVersion.new(Mongo::ServerVersion.new("1.3.1"))</tt>
49
- class UnsupportedVersion < RuntimeError
64
+ class UnsupportedVersion < MongoidError
50
65
  def initialize(version)
51
- @version = version
52
- end
53
- def message
54
- "MongoDB #{@version} not supported, please upgrade to #{Mongoid::MONGODB_VERSION}"
66
+ super("MongoDB #{version} not supported, please upgrade to #{Mongoid::MONGODB_VERSION}")
55
67
  end
56
68
  end
57
69
 
@@ -61,12 +73,11 @@ module Mongoid #:nodoc
61
73
  # Example:
62
74
  #
63
75
  # <tt>Validations.new(person.errors)</tt>
64
- class Validations < RuntimeError
65
- def initialize(errors)
66
- @errors = errors
67
- end
68
- def message
69
- "Validation Failed: " + @errors.full_messages.join(", ")
76
+ class Validations < MongoidError
77
+ attr_reader :document
78
+ def initialize(document)
79
+ @document = document
80
+ super("Validation Failed: #{@document.errors.full_messages.join(", ")}")
70
81
  end
71
82
  end
72
83
 
@@ -76,14 +87,13 @@ module Mongoid #:nodoc
76
87
  # Example:
77
88
  #
78
89
  # <tt>InvalidCollection.new(Address)</tt>
79
- class InvalidCollection < RuntimeError
90
+ class InvalidCollection < MongoidError
91
+ attr_reader :klass
80
92
  def initialize(klass)
81
93
  @klass = klass
82
- end
83
- def message
84
- "Access to the collection for #{@klass.name} is not allowed " +
85
- "since it is an embedded document, please access a collection from " +
86
- "the root document"
94
+ super("Access to the collection for #{@klass.name} is not allowed " +
95
+ "since it is an embedded document, please access a collection from " +
96
+ "the root document")
87
97
  end
88
98
  end
89
99
 
@@ -93,15 +103,14 @@ module Mongoid #:nodoc
93
103
  # Example:
94
104
  #
95
105
  # <tt>InvalidField.new('collection')</tt>
96
- class InvalidField < RuntimeError
106
+ class InvalidField < MongoidError
107
+ attr_reader :name
97
108
  def initialize(name)
98
109
  @name = name
99
- end
100
- def message
101
- "Defining a field named '#{@name}' is not allowed. " +
102
- "Do not define fields that conflict with Mongoid internal attributes " +
103
- "or method names. Use Document#instance_methods to see what " +
104
- "names this includes."
110
+ super("Defining a field named '#{@name}' is not allowed. " +
111
+ "Do not define fields that conflict with Mongoid internal attributes " +
112
+ "or method names. Use Document#instance_methods to see what " +
113
+ "names this includes.")
105
114
  end
106
115
  end
107
116
 
@@ -111,13 +120,11 @@ module Mongoid #:nodoc
111
120
  # Example:
112
121
  #
113
122
  #<tt>TooManyNestedAttributeRecords.new('association', limit)
114
- class TooManyNestedAttributeRecords < RuntimeError
123
+ class TooManyNestedAttributeRecords < MongoidError
124
+ attr_reader :association, :limit
115
125
  def initialize(association, limit)
116
- @association = association.to_s.humanize.capitalize
117
- @limit = limit
118
- end
119
- def message
120
- "Accept Nested Attributes for #{@association} is limited to #{@limit} records"
126
+ @association, @limit = association.to_s.humanize.capitalize, limit
127
+ super("Accept Nested Attributes for #{@association} is limited to #{@limit} records")
121
128
  end
122
129
  end
123
130
  end
@@ -11,6 +11,7 @@ require "mongoid/extensions/binary/conversions"
11
11
  require "mongoid/extensions/boolean/conversions"
12
12
  require "mongoid/extensions/date/conversions"
13
13
  require "mongoid/extensions/datetime/conversions"
14
+ require "mongoid/extensions/false_class/equality"
14
15
  require "mongoid/extensions/float/conversions"
15
16
  require "mongoid/extensions/hash/accessors"
16
17
  require "mongoid/extensions/hash/assimilation"
@@ -24,6 +25,7 @@ require "mongoid/extensions/proc/scoping"
24
25
  require "mongoid/extensions/string/conversions"
25
26
  require "mongoid/extensions/string/inflections"
26
27
  require "mongoid/extensions/symbol/inflections"
28
+ require "mongoid/extensions/true_class/equality"
27
29
  require "mongoid/extensions/objectid/conversions"
28
30
 
29
31
  class Array #:nodoc
@@ -46,7 +48,7 @@ class Binary #:nodoc
46
48
  end
47
49
 
48
50
  class Boolean #:nodoc
49
- extend Mongoid::Extensions::Boolean::Conversions
51
+ include Mongoid::Extensions::Boolean::Conversions
50
52
  end
51
53
 
52
54
  class DateTime #:nodoc
@@ -59,6 +61,10 @@ class Date #:nodoc
59
61
  extend Mongoid::Extensions::Date::Conversions
60
62
  end
61
63
 
64
+ class FalseClass #:nodoc
65
+ include Mongoid::Extensions::FalseClass::Equality
66
+ end
67
+
62
68
  class Float #:nodoc
63
69
  extend Mongoid::Extensions::Float::Conversions
64
70
  end
@@ -101,6 +107,10 @@ class Time #:nodoc
101
107
  extend Mongoid::Extensions::TimeConversions
102
108
  end
103
109
 
110
+ class TrueClass #:nodoc
111
+ include Mongoid::Extensions::TrueClass::Equality
112
+ end
113
+
104
114
  class BSON::ObjectID #:nodoc
105
115
  extend Mongoid::Extensions::ObjectID::Conversions
106
116
  end
@@ -6,18 +6,14 @@ module Mongoid #:nodoc:
6
6
  module Conversions #:nodoc:
7
7
  extend ActiveSupport::Concern
8
8
 
9
- # Converts this array into an array of hashes.
10
- def mongoidize
11
- collect { |obj| obj.attributes }
12
- end
13
-
14
9
  module ClassMethods #:nodoc:
15
- def get(value)
16
- value
17
- end
18
- def set(value)
10
+ def raise_or_return(value)
11
+ raise Mongoid::Errors::InvalidType.new(::Array, value) unless value.is_a?(::Array)
19
12
  value
20
13
  end
14
+
15
+ alias :get :raise_or_return
16
+ alias :set :raise_or_return
21
17
  end
22
18
  end
23
19
  end
@@ -3,18 +3,23 @@ module Mongoid #:nodoc:
3
3
  module Extensions #:nodoc:
4
4
  module Boolean #:nodoc:
5
5
  module Conversions #:nodoc:
6
+ extend ActiveSupport::Concern
6
7
 
7
8
  BOOLEAN_MAP = {
8
9
  true => true, "true" => true, "TRUE" => true, "1" => true, 1 => true, 1.0 => true,
9
10
  false => false, "false" => false, "FALSE" => false, "0" => false, 0 => false, 0.0 => false
10
11
  }
11
12
 
12
- def set(value)
13
- value = BOOLEAN_MAP[value]
14
- value.nil? ? nil : value
15
- end
16
- def get(value)
17
- value
13
+ module ClassMethods #:nodoc
14
+
15
+ def set(value)
16
+ value = BOOLEAN_MAP[value]
17
+ value.nil? ? nil : value
18
+ end
19
+
20
+ def get(value)
21
+ value
22
+ end
18
23
  end
19
24
  end
20
25
  end
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module FalseClass #:nodoc:
5
+ module Equality #:nodoc:
6
+ def is_a?(other)
7
+ return true if other.name == "Boolean"
8
+ super(other)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -21,7 +21,11 @@ module Mongoid #:nodoc:
21
21
  def insert(key, attrs)
22
22
  elements = self[key]
23
23
  if elements
24
- elements.merge!(attrs)
24
+ if elements.kind_of?(::Array)
25
+ elements.merge!(attrs)
26
+ else
27
+ self[key] = attrs.reverse_merge!(elements)
28
+ end
25
29
  else
26
30
  self[key] = key.singular? ? attrs : [attrs]
27
31
  end
@@ -27,11 +27,12 @@ module Mongoid #:nodoc:
27
27
  protected
28
28
 
29
29
  def init(parent, child, options)
30
- child._parent = parent
30
+ child.parentize(parent, options.name)
31
31
  child.write_attributes(self)
32
32
  child.identify
33
33
  child.reset_modifications
34
- child.assimilate(parent, options)
34
+ child.notify
35
+ child
35
36
  end
36
37
  end
37
38
  end
@@ -23,8 +23,9 @@ module Mongoid #:nodoc:
23
23
  to_s.plural?
24
24
  end
25
25
 
26
- [ "gt", "lt", "gte", "lte", "ne", "near", "in", "nin", "mod", "all",
27
- "size", "exists", "within", ["matches","elemMatch"] ].each do |oper|
26
+ [ "asc", "ascending", "desc", "descending", "gt", "lt", "gte",
27
+ "lte", "ne", "near", "in", "nin", "mod", "all", "size", "exists",
28
+ "within", ["matches","elemMatch"] ].each do |oper|
28
29
  m, oper = oper
29
30
  oper = m unless oper
30
31
  class_eval <<-OPERATORS
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module TrueClass #:nodoc:
5
+ module Equality #:nodoc:
6
+ def is_a?(other)
7
+ return true if other.name == "Boolean"
8
+ super(other)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -43,12 +43,12 @@ module Mongoid #:nodoc:
43
43
  # <tt>Field.new(:score, :default => 0)</tt>
44
44
  def initialize(name, options = {})
45
45
  check_name!(name)
46
+ @type = options[:type] || String
46
47
  @name, @default = name, options[:default]
47
48
  @copyable = (@default.is_a?(Array) || @default.is_a?(Hash))
48
- @type = options[:type] || String
49
49
  @accessible = options.has_key?(:accessible) ? options[:accessible] : true
50
-
51
50
  @options = options
51
+ check_default!
52
52
  end
53
53
 
54
54
  # Used for setting an object in the attributes hash. If nil is provided the
@@ -70,7 +70,14 @@ module Mongoid #:nodoc:
70
70
 
71
71
  # Check if the name is valid.
72
72
  def check_name!(name)
73
- raise Errors::InvalidField.new(name) if Mongoid.destructive_fields.include?(name.to_s)
73
+ raise Mongoid::Errors::InvalidField.new(name) if Mongoid.destructive_fields.include?(name.to_s)
74
+ end
75
+
76
+ def check_default!
77
+ return if @default.is_a?(Proc)
78
+ if !@default.nil? && !@default.is_a?(@type)
79
+ raise Mongoid::Errors::InvalidType.new(@type, @default)
80
+ end
74
81
  end
75
82
  end
76
83
  end
@@ -27,6 +27,7 @@ module Mongoid #:nodoc
27
27
  def field(name, options = {})
28
28
  access = name.to_s
29
29
  set_field(access, options)
30
+ attr_protected name if options[:accessible] == false
30
31
  end
31
32
 
32
33
  # Returns the default values for the fields on the document
@@ -25,7 +25,7 @@ module Mongoid #:nodoc:
25
25
  # Returns a count of matching records in the database based on the
26
26
  # provided arguments.
27
27
  #
28
- # <tt>Person.count(:first, :conditions => { :attribute => "value" })</tt>
28
+ # <tt>Person.count(:conditions => { :attribute => "value" })</tt>
29
29
  def count(*args)
30
30
  Criteria.translate(self, *args).count
31
31
  end
@@ -33,7 +33,7 @@ module Mongoid #:nodoc:
33
33
  # Returns true if there are on document in database based on the
34
34
  # provided arguments.
35
35
  #
36
- # <tt>Person.exists?(:first, :conditions => { :attribute => "value" })</tt>
36
+ # <tt>Person.exists?(:conditions => { :attribute => "value" })</tt>
37
37
  def exists?(*args)
38
38
  Criteria.translate(self, *args).limit(1).count == 1
39
39
  end
@@ -47,8 +47,7 @@ module Mongoid #:nodoc
47
47
  end
48
48
 
49
49
  # Sets up a child/parent association. This is used for newly created
50
- # objects so they can be properly added to the graph and have the parent
51
- # observers set up properly.
50
+ # objects so they can be properly added to the graph.
52
51
  #
53
52
  # Options:
54
53
  #
@@ -61,7 +60,6 @@ module Mongoid #:nodoc
61
60
  def parentize(object, association_name)
62
61
  self._parent = object
63
62
  self.association_name = association_name.to_s
64
- add_observer(object)
65
63
  end
66
64
 
67
65
  # Return the root +Document+ in the object graph. If the current +Document+
@@ -0,0 +1,106 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Paranoid #:nodoc:
4
+ # Find deleted documents
5
+ #
6
+ # Examples:
7
+ #
8
+ # <tt>Person.deleted</tt> # all deleted employees
9
+ # <tt>Company.first.employees.deleted</tt> # works with a join
10
+ # <tt>Person.deleted.find("4c188dea7b17235a2a000001").first</tt> # retrieve by id a deleted person
11
+ def deleted
12
+ where(:deleted_at.exists => false)
13
+ end
14
+ end
15
+
16
+ # Include this module to get soft deletion of root level documents.
17
+ # This will add a deleted_at field to the +Document+, managed automatically.
18
+ # Potentially incompatible with unique indices. (if collisions with deleted items)
19
+ #
20
+ # To use:
21
+ #
22
+ # class Person
23
+ # include Mongoid::Document
24
+ # include Mongoid::Paranoia
25
+ # end
26
+ module Paranoia
27
+ extend ActiveSupport::Concern
28
+
29
+ included do
30
+ Mongoid::Criteria.send(:include, Mongoid::Paranoid)
31
+ field :deleted_at, :type => Time
32
+ end
33
+
34
+ # Delete the paranoid +Document+ from the database completely. This will
35
+ # run the destroy callbacks.
36
+ #
37
+ # Example:
38
+ #
39
+ # <tt>document.destroy!</tt>
40
+ def destroy!
41
+ run_callbacks(:destroy) { delete! }
42
+ end
43
+
44
+ # Delete the paranoid +Document+ from the database completely.
45
+ #
46
+ # Example:
47
+ #
48
+ # <tt>document.delete!</tt>
49
+ def delete!
50
+ @destroyed = true
51
+ Mongoid::Persistence::Remove.new(self).persist
52
+ end
53
+
54
+ # Delete the +Document+, will set the deleted_at timestamp and not actually
55
+ # delete it.
56
+ #
57
+ # Example:
58
+ #
59
+ # <tt>document._remove</tt>
60
+ #
61
+ # Returns:
62
+ #
63
+ # true
64
+ def _remove
65
+ now = Time.now
66
+ collection.update({ :_id => self.id }, { '$set' => { :deleted_at => Time.now } })
67
+ @attributes["deleted_at"] = now
68
+ true
69
+ end
70
+
71
+ alias :delete :_remove
72
+
73
+ # Determines if this document is destroyed.
74
+ #
75
+ # Returns:
76
+ #
77
+ # true if the +Document+ was destroyed.
78
+ def destroyed?
79
+ @destroyed || !!deleted_at
80
+ end
81
+
82
+ # Restores a previously soft-deleted document. Handles this by removing the
83
+ # deleted_at flag.
84
+ #
85
+ # Example:
86
+ #
87
+ # <tt>document.restore</tt>
88
+ def restore
89
+ collection.update({ :_id => self.id }, { '$unset' => { :deleted_at => true } })
90
+ @attributes.delete("deleted_at")
91
+ end
92
+
93
+ module ClassMethods #:nodoc:
94
+
95
+ # Override the default +Criteria+ accessor to only get existing
96
+ # documents.
97
+ #
98
+ # Returns:
99
+ #
100
+ # A +Criteria+ for deleted_at not existing.
101
+ def criteria
102
+ super.where(:deleted_at.exists => false)
103
+ end
104
+ end
105
+ end
106
+ end
@@ -209,7 +209,7 @@ module Mongoid #:nodoc:
209
209
 
210
210
  # Raise an error if validation failed.
211
211
  def fail_validate!(document)
212
- raise Errors::Validations.new(document.errors)
212
+ raise Errors::Validations.new(document)
213
213
  end
214
214
  end
215
215
  end
@@ -27,6 +27,7 @@ module Mongoid #:nodoc:
27
27
  @document.run_callbacks(:save) do
28
28
  if insert
29
29
  @document.new_record = false
30
+ @document._children.each { |child| child.new_record = false }
30
31
  @document.move_changes
31
32
  end
32
33
  end
@@ -33,10 +33,11 @@ module Mongoid #:nodoc:
33
33
  else
34
34
  update = { @document._inserter => { @document._position => @document.raw_attributes } }
35
35
  @collection.update(parent._selector, update, @options.merge(:multi => false))
36
+ @document.new_record = false
36
37
  end
37
38
  end
38
39
  end
39
- @document.new_record = false; @document
40
+ @document
40
41
  end
41
42
  end
42
43
  end
@@ -7,6 +7,9 @@ module Mongoid #:nodoc:
7
7
  field :updated_at, :type => Time
8
8
  set_callback :create, :before, :set_created_at
9
9
  set_callback :save, :before, :set_updated_at
10
+
11
+ class_inheritable_accessor :record_timestamps, :instance_writer => false
12
+ self.record_timestamps = true
10
13
  end
11
14
 
12
15
  # Update the created_at field on the Document to the current time. This is
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc
3
- VERSION = "2.0.0.beta.7"
3
+ VERSION = "2.0.0.beta.8"
4
4
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid
3
3
  version: !ruby/object:Gem::Version
4
- hash: 62196461
4
+ hash: 62196467
5
5
  prerelease: true
6
6
  segments:
7
7
  - 2
8
8
  - 0
9
9
  - 0
10
10
  - beta
11
- - 7
12
- version: 2.0.0.beta.7
11
+ - 8
12
+ version: 2.0.0.beta.8
13
13
  platform: ruby
14
14
  authors:
15
15
  - Durran Jordan
@@ -77,12 +77,12 @@ dependencies:
77
77
  requirements:
78
78
  - - ~>
79
79
  - !ruby/object:Gem::Version
80
- hash: 21
80
+ hash: 17
81
81
  segments:
82
82
  - 1
83
83
  - 0
84
- - 1
85
- version: 1.0.1
84
+ - 3
85
+ version: 1.0.3
86
86
  type: :runtime
87
87
  version_requirements: *id004
88
88
  - !ruby/object:Gem::Dependency
@@ -93,12 +93,12 @@ dependencies:
93
93
  requirements:
94
94
  - - ~>
95
95
  - !ruby/object:Gem::Version
96
- hash: 21
96
+ hash: 17
97
97
  segments:
98
98
  - 1
99
99
  - 0
100
- - 1
101
- version: 1.0.1
100
+ - 3
101
+ version: 1.0.3
102
102
  type: :runtime
103
103
  version_requirements: *id005
104
104
  - !ruby/object:Gem::Dependency
@@ -193,6 +193,7 @@ files:
193
193
  - lib/mongoid/extensions/boolean/conversions.rb
194
194
  - lib/mongoid/extensions/date/conversions.rb
195
195
  - lib/mongoid/extensions/datetime/conversions.rb
196
+ - lib/mongoid/extensions/false_class/equality.rb
196
197
  - lib/mongoid/extensions/float/conversions.rb
197
198
  - lib/mongoid/extensions/hash/accessors.rb
198
199
  - lib/mongoid/extensions/hash/assimilation.rb
@@ -209,6 +210,7 @@ files:
209
210
  - lib/mongoid/extensions/string/inflections.rb
210
211
  - lib/mongoid/extensions/symbol/inflections.rb
211
212
  - lib/mongoid/extensions/time_conversions.rb
213
+ - lib/mongoid/extensions/true_class/equality.rb
212
214
  - lib/mongoid/extensions.rb
213
215
  - lib/mongoid/extras.rb
214
216
  - lib/mongoid/factory.rb
@@ -234,7 +236,7 @@ files:
234
236
  - lib/mongoid/matchers.rb
235
237
  - lib/mongoid/memoization.rb
236
238
  - lib/mongoid/named_scope.rb
237
- - lib/mongoid/observable.rb
239
+ - lib/mongoid/paranoia.rb
238
240
  - lib/mongoid/paths.rb
239
241
  - lib/mongoid/persistence/command.rb
240
242
  - lib/mongoid/persistence/insert.rb
@@ -1,30 +0,0 @@
1
- # encoding: utf-8
2
- module Mongoid #:nodoc:
3
- module Observable #:nodoc:
4
- extend ActiveSupport::Concern
5
- included do
6
- attr_reader :observers
7
- end
8
-
9
- # Add an observer to this object. This mimics the standard Ruby observable
10
- # library.
11
- #
12
- # Example:
13
- #
14
- # <tt>address.add_observer(person)</tt>
15
- def add_observer(object)
16
- @observers ||= []
17
- @observers.push(object)
18
- end
19
-
20
- # Notify all the objects observing this object of an update. All observers
21
- # need to respond to the update method in order to handle this.
22
- #
23
- # Example:
24
- #
25
- # <tt>document.notify_observers(self)</tt>
26
- def notify_observers(*args)
27
- @observers.dup.each { |observer| observer.observe(*args) } if @observers
28
- end
29
- end
30
- end