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

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.
@@ -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