mongoid 2.0.0.beta.9 → 2.0.0.beta.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/README.rdoc +2 -4
  2. data/lib/mongoid.rb +2 -1
  3. data/lib/mongoid/associations.rb +2 -3
  4. data/lib/mongoid/associations/embeds_many.rb +6 -2
  5. data/lib/mongoid/associations/embeds_one.rb +1 -3
  6. data/lib/mongoid/associations/options.rb +11 -10
  7. data/lib/mongoid/associations/references_many.rb +27 -4
  8. data/lib/mongoid/associations/references_many_as_array.rb +24 -4
  9. data/lib/mongoid/associations/references_one.rb +1 -2
  10. data/lib/mongoid/atomicity.rb +17 -4
  11. data/lib/mongoid/config.rb +97 -20
  12. data/lib/mongoid/contexts/enumerable.rb +9 -4
  13. data/lib/mongoid/contexts/mongo.rb +1 -1
  14. data/lib/mongoid/criteria.rb +7 -1
  15. data/lib/mongoid/criterion/inclusion.rb +12 -5
  16. data/lib/mongoid/criterion/optional.rb +26 -3
  17. data/lib/mongoid/cursor.rb +0 -1
  18. data/lib/mongoid/deprecation.rb +1 -2
  19. data/lib/mongoid/document.rb +25 -3
  20. data/lib/mongoid/extensions.rb +2 -1
  21. data/lib/mongoid/extensions/float/conversions.rb +1 -1
  22. data/lib/mongoid/extensions/integer/conversions.rb +1 -1
  23. data/lib/mongoid/extensions/symbol/conversions.rb +21 -0
  24. data/lib/mongoid/finders.rb +3 -2
  25. data/lib/mongoid/indexes.rb +17 -6
  26. data/lib/mongoid/logger.rb +19 -0
  27. data/lib/mongoid/paranoia.rb +12 -14
  28. data/lib/mongoid/persistence/remove_embedded.rb +0 -4
  29. data/lib/mongoid/persistence/update.rb +3 -0
  30. data/lib/mongoid/railtie.rb +37 -4
  31. data/lib/mongoid/railties/database.rake +19 -1
  32. data/lib/mongoid/validations/associated.rb +1 -1
  33. data/lib/mongoid/validations/locale/en.yml +4 -3
  34. data/lib/mongoid/validations/uniqueness.rb +19 -8
  35. data/lib/mongoid/version.rb +1 -1
  36. metadata +50 -18
  37. data/lib/mongoid/extensions/array/aliasing.rb +0 -4
@@ -36,7 +36,7 @@ module Mongoid #:nodoc:
36
36
 
37
37
  # Gets the number of documents in the array. Delegates to size.
38
38
  def count
39
- @count ||= documents.size
39
+ @count ||= filter.size
40
40
  end
41
41
 
42
42
  # Gets an array of distinct values for the supplied field across the
@@ -56,7 +56,7 @@ module Mongoid #:nodoc:
56
56
  #
57
57
  # An +Array+ of documents that matched the selector.
58
58
  def execute(paginating = false)
59
- limit(documents.select { |document| document.matches?(selector) })
59
+ limit(filter) || []
60
60
  end
61
61
 
62
62
  # Groups the documents by the first field supplied in the field options.
@@ -66,7 +66,7 @@ module Mongoid #:nodoc:
66
66
  # A +Hash+ with field values as keys, arrays of documents as values.
67
67
  def group
68
68
  field = options[:fields].first
69
- documents.group_by { |doc| doc.send(field) }
69
+ execute.group_by { |doc| doc.send(field) }
70
70
  end
71
71
 
72
72
  # Create the new enumerable context. This will need the selector and
@@ -121,13 +121,18 @@ module Mongoid #:nodoc:
121
121
  #
122
122
  # The numerical sum of all the document field values.
123
123
  def sum(field)
124
- sum = documents.inject(nil) do |memo, doc|
124
+ sum = execute.inject(nil) do |memo, doc|
125
125
  value = doc.send(field)
126
126
  memo ? memo += value : value
127
127
  end
128
128
  end
129
129
 
130
130
  protected
131
+ # Filters the documents against the criteria's selector
132
+ def filter
133
+ documents.select { |document| document.matches?(selector) }
134
+ end
135
+
131
136
  # If the field exists, perform the comparison and set if true.
132
137
  def determine(field, operator)
133
138
  matching = documents.inject(nil) do |memo, doc|
@@ -133,7 +133,7 @@ module Mongoid #:nodoc:
133
133
  # <tt>Mongoid::Contexts::Mongo.new(criteria)</tt>
134
134
  def initialize(criteria)
135
135
  @criteria = criteria
136
- if klass.hereditary
136
+ if klass.hereditary && !criteria.selector.keys.include?(:_type)
137
137
  criteria.in(:_type => criteria.klass._types)
138
138
  end
139
139
  criteria.enslave if klass.enslaved?
@@ -63,6 +63,12 @@ module Mongoid #:nodoc:
63
63
  end
64
64
  end
65
65
 
66
+ # Returns true if the supplied +Object+ is an instance of +Criteria+ or
67
+ # +Scope+.
68
+ def self.===(other)
69
+ super || Scope === other
70
+ end
71
+
66
72
  # Return or create the context in which this criteria should be executed.
67
73
  #
68
74
  # This will return an Enumerable context if the class is embedded,
@@ -148,7 +154,7 @@ module Mongoid #:nodoc:
148
154
  def method_missing(name, *args)
149
155
  if @klass.respond_to?(name)
150
156
  new_scope = @klass.send(name, *args)
151
- new_scope.merge(self)
157
+ new_scope.merge(self) if Criteria === new_scope
152
158
  return new_scope
153
159
  else
154
160
  return entries.send(name, *args)
@@ -97,12 +97,19 @@ module Mongoid #:nodoc:
97
97
  #
98
98
  # Returns: <tt>self</tt>
99
99
  def where(selector = nil)
100
- case selector
101
- when String
102
- @selector.update("$where" => selector)
103
- else
104
- @selector.update(selector ? selector.expand_complex_criteria : {})
100
+ selector = case selector
101
+ when String then {"$where" => selector}
102
+ else selector ? selector.expand_complex_criteria : {}
105
103
  end
104
+
105
+ selector.each_pair do |key, value|
106
+ if @selector.has_key?(key) && @selector[key].respond_to?(:merge!)
107
+ @selector[key].merge!(value)
108
+ else
109
+ @selector[key] = value
110
+ end
111
+ end
112
+
106
113
  self
107
114
  end
108
115
  end
@@ -91,15 +91,21 @@ module Mongoid #:nodoc:
91
91
  #
92
92
  # Options:
93
93
  #
94
- # object_id: A +String+ representation of a <tt>BSON::ObjectID</tt>
94
+ # object_id: A single id or an array of ids in +String+ or <tt>BSON::ObjectID</tt> format
95
95
  #
96
96
  # Example:
97
97
  #
98
98
  # <tt>criteria.id("4ab2bc4b8ad548971900005c")</tt>
99
+ # <tt>criteria.id(["4ab2bc4b8ad548971900005c", "4c454e7ebf4b98032d000001"])</tt>
99
100
  #
100
101
  # Returns: <tt>self</tt>
101
- def id(*args)
102
- (args.flatten.size > 1) ? self.in(:_id => args.flatten) : (@selector[:_id] = args.first)
102
+ def id(*ids)
103
+ ids.flatten!
104
+ if ids.size > 1
105
+ self.in(:_id => Mongoid.convert_to_object_id(ids, self.klass.primary_key.nil?))
106
+ else
107
+ @selector[:_id] = Mongoid.convert_to_object_id(ids.first, self.klass.primary_key.nil?)
108
+ end
103
109
  self
104
110
  end
105
111
 
@@ -167,6 +173,23 @@ module Mongoid #:nodoc:
167
173
  def skip(value = 0)
168
174
  @options[:skip] = value; self
169
175
  end
176
+
177
+ # Adds a criterion to the +Criteria+ that specifies a type or an Array of type that must be matched.
178
+ #
179
+ # Options:
180
+ #
181
+ # types : An +Array+ of types of a +String+ representing the Type of you search
182
+ #
183
+ # Example:
184
+ #
185
+ # <tt>criteria.type('Browser')</tt>
186
+ # <tt>criteria.type(['Firefox', 'Browser'])</tt>
187
+ #
188
+ # Returns: <tt>self</tt>
189
+ def type(types)
190
+ types = [types] unless types.is_a?(Array)
191
+ self.in(:_type => types)
192
+ end
170
193
  end
171
194
  end
172
195
  end
@@ -5,7 +5,6 @@ module Mongoid #:nodoc
5
5
  # Operations on the Mongo::Cursor object that will not get overriden by the
6
6
  # Mongoid::Cursor are defined here.
7
7
  OPERATIONS = [
8
- :admin,
9
8
  :close,
10
9
  :closed?,
11
10
  :count,
@@ -14,9 +14,8 @@ module Mongoid #:nodoc:
14
14
  end
15
15
 
16
16
  protected
17
- # Instantiate a new logger to stdout or a rails logger if available.
18
17
  def initialize
19
- @logger = defined?(Rails) ? Rails.logger : Logger.new($stdout)
18
+ @logger = Mongoid::Logger.new
20
19
  end
21
20
  end
22
21
  end
@@ -11,6 +11,10 @@ module Mongoid #:nodoc:
11
11
  attr_reader :new_record
12
12
 
13
13
  delegate :primary_key, :to => "self.class"
14
+
15
+ unless self.instance_of?(Class) and self.name == ""
16
+ (@@descendants ||= {})[self] = :seen
17
+ end
14
18
  end
15
19
 
16
20
  module ClassMethods #:nodoc:
@@ -58,12 +62,17 @@ module Mongoid #:nodoc:
58
62
  set_callback :save, :before, :identify
59
63
  end
60
64
 
65
+ # Returns the classes that have included Mongoid::Document
66
+ def self.descendents
67
+ (@@descendants ||= {}).keys
68
+ end
69
+
61
70
  # Returns all types to query for when using this class as the base.
62
71
  # *subclasses* is from activesupport. Note that a bug in *subclasses*
63
72
  # causes the first call to only return direct children, hence
64
73
  # the double call and unique.
65
74
  def _types
66
- @_type ||= [subclasses + subclasses + [self.name]].flatten.uniq
75
+ @_type ||= [subclasses + subclasses + [self.name]].flatten.uniq.map(&:to_s)
67
76
  end
68
77
  end
69
78
 
@@ -169,10 +178,23 @@ module Mongoid #:nodoc:
169
178
  # memoized association and notify the parent of the change.
170
179
  def remove(child)
171
180
  name = child.association_name
172
- reset(name) { @attributes.remove(name, child.raw_attributes) }
173
- notify
181
+ if @building_nested
182
+ @attributes.remove(name, child.raw_attributes)
183
+ else
184
+ reset(name) do
185
+ @attributes.remove(name, child.raw_attributes)
186
+ @attributes[name]
187
+ end
188
+ notify
189
+ end
174
190
  end
175
191
 
192
+ # def remove_without_reset
193
+ # name = child.association_name
194
+ # @attributes.remove(name, child.raw_attributes)
195
+ # notify
196
+ # end
197
+
176
198
  # Return an array with this +Document+ only in it.
177
199
  def to_a
178
200
  [ self ]
@@ -1,7 +1,6 @@
1
1
  # encoding: utf-8
2
2
  require "mongoid/extensions/time_conversions"
3
3
  require "mongoid/extensions/array/accessors"
4
- require "mongoid/extensions/array/aliasing"
5
4
  require "mongoid/extensions/array/assimilation"
6
5
  require "mongoid/extensions/array/conversions"
7
6
  require "mongoid/extensions/array/parentization"
@@ -25,6 +24,7 @@ require "mongoid/extensions/proc/scoping"
25
24
  require "mongoid/extensions/string/conversions"
26
25
  require "mongoid/extensions/string/inflections"
27
26
  require "mongoid/extensions/symbol/inflections"
27
+ require "mongoid/extensions/symbol/conversions"
28
28
  require "mongoid/extensions/true_class/equality"
29
29
  require "mongoid/extensions/objectid/conversions"
30
30
 
@@ -101,6 +101,7 @@ end
101
101
  class Symbol #:nodoc
102
102
  remove_method :size if instance_methods.include? :size # temporal fix for ruby 1.9
103
103
  include Mongoid::Extensions::Symbol::Inflections
104
+ include Mongoid::Extensions::Symbol::Conversions
104
105
  end
105
106
 
106
107
  class Time #:nodoc
@@ -8,7 +8,7 @@ module Mongoid #:nodoc:
8
8
  begin
9
9
  Float(value)
10
10
  rescue ArgumentError => e
11
- puts(e.message); value
11
+ value
12
12
  end
13
13
  end
14
14
  def get(value)
@@ -8,7 +8,7 @@ module Mongoid #:nodoc:
8
8
  begin
9
9
  Integer(value)
10
10
  rescue ArgumentError => e
11
- puts(e.message); value
11
+ value
12
12
  end
13
13
  end
14
14
  def get(value)
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Extensions #:nodoc:
4
+ module Symbol#:nodoc:
5
+ # This module converts objects into mongoid related objects.
6
+ module Conversions #:nodoc:
7
+ extend ActiveSupport::Concern
8
+
9
+ module ClassMethods
10
+ def set(value)
11
+ (value.nil? or (value.respond_to?(:empty?) && value.empty?)) ? nil : value.to_sym
12
+ end
13
+
14
+ def get(value)
15
+ value
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -4,8 +4,9 @@ module Mongoid #:nodoc:
4
4
 
5
5
  # Delegate to the criteria methods that are natural for creating a new
6
6
  # criteria.
7
- [ :all_in, :any_in, :avg, :excludes, :limit, :max, :min,
8
- :not_in, :only, :order_by, :skip, :sum, :where ].each do |name|
7
+ [ :all_in, :any_in, :asc, :ascending, :avg, :desc, :descending,
8
+ :excludes, :limit, :max, :min, :not_in, :only, :order_by,
9
+ :skip, :sum, :where, :near ].each do |name|
9
10
  define_method(name) do |*args|
10
11
  criteria.send(name, *args)
11
12
  end
@@ -3,24 +3,35 @@ module Mongoid #:nodoc
3
3
  module Indexes #:nodoc
4
4
  extend ActiveSupport::Concern
5
5
  included do
6
- cattr_accessor :indexed
7
- self.indexed = false
6
+ cattr_accessor :index_options
7
+ self.index_options = {}
8
8
  end
9
9
 
10
10
  module ClassMethods #:nodoc
11
+
12
+ # Send the actual index creation comments to the MongoDB driver
13
+ def create_indexes
14
+ return unless index_options
15
+
16
+ index_options.each do |name, options|
17
+ self._collection.create_index(name, options)
18
+ end
19
+ end
20
+
11
21
  # Add the default indexes to the root document if they do not already
12
22
  # exist. Currently this is only _type.
13
23
  def add_indexes
14
- if hereditary && !indexed
15
- self._collection.create_index(:_type, :unique => false, :background => true)
16
- self.indexed = true
24
+ if hereditary && !index_options[:_type]
25
+ self.index_options[:_type] = {:unique => false, :background => true}
17
26
  end
27
+ create_indexes if Mongoid.autocreate_indexes
18
28
  end
19
29
 
20
30
  # Adds an index on the field specified. Options can be :unique => true or
21
31
  # :unique => false. It will default to the latter.
22
32
  def index(name, options = { :unique => false })
23
- collection.create_index(name, options)
33
+ self.index_options[name] = options
34
+ create_indexes if Mongoid.autocreate_indexes
24
35
  end
25
36
  end
26
37
  end
@@ -0,0 +1,19 @@
1
+ module Mongoid
2
+ class Logger
3
+
4
+ delegate :info, :debug, :error, :fatal, :unknown, :to => :logger, :allow_nil => true
5
+
6
+ def warn(message)
7
+ logger.warn(message) if logger && logger.respond_to?(:warn)
8
+ end
9
+
10
+ def logger
11
+ Mongoid.logger
12
+ end
13
+
14
+ def inspect
15
+ "#<Mongoid::Logger:0x#{object_id.to_s(16)} @logger=#{logger.inspect}>"
16
+ end
17
+
18
+ end
19
+ end
@@ -1,18 +1,5 @@
1
1
  # encoding: utf-8
2
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
3
  # Include this module to get soft deletion of root level documents.
17
4
  # This will add a deleted_at field to the +Document+, managed automatically.
18
5
  # Potentially incompatible with unique indices. (if collisions with deleted items)
@@ -27,7 +14,6 @@ module Mongoid #:nodoc:
27
14
  extend ActiveSupport::Concern
28
15
 
29
16
  included do
30
- Mongoid::Criteria.send(:include, Mongoid::Paranoid)
31
17
  field :deleted_at, :type => Time
32
18
  end
33
19
 
@@ -101,6 +87,18 @@ module Mongoid #:nodoc:
101
87
  def criteria
102
88
  super.where(:deleted_at.exists => false)
103
89
  end
90
+
91
+ # Find deleted documents
92
+ #
93
+ # Examples:
94
+ #
95
+ # <tt>Person.deleted</tt> # all deleted employees
96
+ # <tt>Company.first.employees.deleted</tt> # works with a join
97
+ # <tt>Person.deleted.find("4c188dea7b17235a2a000001").first</tt> # retrieve by id a deleted person
98
+ def deleted
99
+ where(:deleted_at.exists => true)
100
+ end
101
+
104
102
  end
105
103
  end
106
104
  end
@@ -11,10 +11,6 @@ module Mongoid #:nodoc:
11
11
  # false
12
12
  # );
13
13
  class RemoveEmbedded < Command
14
- # Insert the new document in the database. If the document's parent is a
15
- # new record, we will call save on the parent, otherwise we will $push
16
- # the document onto the parent.
17
- #
18
14
  # Remove the document from the database. If the parent is a new record,
19
15
  # it will get removed in Ruby only. If the parent is not a new record
20
16
  # then either an $unset or $set will occur, depending if it's an
@@ -60,7 +60,10 @@ module Mongoid #:nodoc:
60
60
  def update
61
61
  updates = @document._updates
62
62
  unless updates.empty?
63
+ other_pushes = updates.delete(:other)
64
+
63
65
  @collection.update(@document._selector, updates, @options.merge(:multi => false))
66
+ @collection.update(@document._selector, { "$pushAll" => other_pushes }, @options.merge(:multi => false)) if other_pushes
64
67
  end; true
65
68
  end
66
69
  end
@@ -1,17 +1,28 @@
1
+ require "singleton"
1
2
  require "rails"
3
+ require "mongoid/config"
2
4
  module Rails #:nodoc:
3
5
  module Mongoid #:nodoc:
4
6
  class Railtie < Rails::Railtie #:nodoc:
5
7
 
6
- # do we want a custom log subscriber for mongoid?
7
- # log_subscriber :mongoid, ::Mongoid::Railties::LogSubscriber.new
8
-
9
8
  config.generators.orm :mongoid, :migration => false
10
9
 
11
10
  rake_tasks do
12
11
  load "mongoid/railties/database.rake"
13
12
  end
14
13
 
14
+ # Exposes Mongoid's configuration to the Rails application configuration.
15
+ #
16
+ # Example:
17
+ #
18
+ # module MyApplication
19
+ # class Application < Rails::Application
20
+ # config.mongoid.logger = Logger.new($stdout, :warn)
21
+ # config.mongoid.reconnect_time = 10
22
+ # end
23
+ # end
24
+ config.mongoid = ::Mongoid::Config.instance
25
+
15
26
  # Initialize Mongoid. This will look for a mongoid.yml in the config
16
27
  # directory and configure mongoid appropriately.
17
28
  #
@@ -51,6 +62,28 @@ module Rails #:nodoc:
51
62
  end
52
63
  end
53
64
  end
65
+
66
+ initializer "reconnect to master if application is preloaded" do
67
+ config.after_initialize do
68
+ # Unicorn clears the START_CTX when a worker is forked, so if we have
69
+ # data in START_CTX then we know we're being preloaded. Unicorn does
70
+ # not provide application-level hooks for executing code after the
71
+ # process has forked, so we reconnect lazily.
72
+ if defined?(Unicorn) && !Unicorn::HttpServer::START_CTX.empty?
73
+ ::Mongoid.reconnect!(false)
74
+ end
75
+
76
+ # Passenger provides the :starting_worker_process event for executing
77
+ # code after it has forked, so we use that and reconnect immediately.
78
+ if defined?(PhusionPassenger)
79
+ PhusionPassenger.on_event(:starting_worker_process) do |forked|
80
+ if forked
81
+ ::Mongoid.reconnect!
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
54
87
  end
55
88
  end
56
- end
89
+ end