mongoid 2.0.0.beta.15 → 2.0.0.beta.16

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.
Files changed (66) hide show
  1. data/lib/config/locales/en.yml +40 -0
  2. data/lib/config/locales/es.yml +41 -0
  3. data/lib/config/locales/fr.yml +42 -0
  4. data/lib/config/locales/it.yml +39 -0
  5. data/lib/config/locales/pl.yml +39 -0
  6. data/lib/config/locales/pt.yml +40 -0
  7. data/lib/config/locales/sv.yml +40 -0
  8. data/lib/mongoid.rb +7 -2
  9. data/lib/mongoid/associations.rb +16 -9
  10. data/lib/mongoid/associations/embedded_in.rb +11 -0
  11. data/lib/mongoid/associations/embeds_many.rb +28 -2
  12. data/lib/mongoid/associations/embeds_one.rb +18 -2
  13. data/lib/mongoid/associations/proxy.rb +28 -1
  14. data/lib/mongoid/associations/referenced_in.rb +10 -0
  15. data/lib/mongoid/associations/references_many.rb +10 -7
  16. data/lib/mongoid/associations/references_many_as_array.rb +29 -0
  17. data/lib/mongoid/associations/references_one.rb +9 -1
  18. data/lib/mongoid/attributes.rb +13 -3
  19. data/lib/mongoid/callbacks.rb +1 -0
  20. data/lib/mongoid/collections.rb +1 -1
  21. data/lib/mongoid/components.rb +1 -0
  22. data/lib/mongoid/config.rb +16 -1
  23. data/lib/mongoid/contexts/enumerable.rb +28 -1
  24. data/lib/mongoid/contexts/enumerable/sort.rb +43 -0
  25. data/lib/mongoid/contexts/mongo.rb +13 -1
  26. data/lib/mongoid/criteria.rb +4 -2
  27. data/lib/mongoid/criterion/inclusion.rb +22 -1
  28. data/lib/mongoid/criterion/optional.rb +14 -39
  29. data/lib/mongoid/criterion/selector.rb +65 -0
  30. data/lib/mongoid/document.rb +5 -11
  31. data/lib/mongoid/errors.rb +10 -130
  32. data/lib/mongoid/errors/document_not_found.rb +29 -0
  33. data/lib/mongoid/errors/invalid_collection.rb +19 -0
  34. data/lib/mongoid/errors/invalid_database.rb +20 -0
  35. data/lib/mongoid/errors/invalid_field.rb +19 -0
  36. data/lib/mongoid/errors/invalid_options.rb +16 -0
  37. data/lib/mongoid/errors/invalid_type.rb +26 -0
  38. data/lib/mongoid/errors/mongoid_error.rb +27 -0
  39. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +21 -0
  40. data/lib/mongoid/errors/unsupported_version.rb +21 -0
  41. data/lib/mongoid/errors/validations.rb +22 -0
  42. data/lib/mongoid/extensions/hash/assimilation.rb +1 -1
  43. data/lib/mongoid/extensions/hash/criteria_helpers.rb +5 -3
  44. data/lib/mongoid/extensions/object/conversions.rb +5 -1
  45. data/lib/mongoid/extensions/objectid/conversions.rb +43 -1
  46. data/lib/mongoid/field.rb +13 -6
  47. data/lib/mongoid/finders.rb +1 -1
  48. data/lib/mongoid/hierarchy.rb +9 -4
  49. data/lib/mongoid/identity.rb +2 -2
  50. data/lib/mongoid/indexes.rb +1 -1
  51. data/lib/mongoid/matchers/default.rb +1 -1
  52. data/lib/mongoid/modifiers.rb +24 -0
  53. data/lib/mongoid/modifiers/command.rb +18 -0
  54. data/lib/mongoid/modifiers/inc.rb +24 -0
  55. data/lib/mongoid/persistence/command.rb +2 -10
  56. data/lib/mongoid/persistence/remove_all.rb +1 -1
  57. data/lib/mongoid/railtie.rb +2 -0
  58. data/lib/mongoid/railties/database.rake +102 -11
  59. data/lib/mongoid/railties/document.rb +12 -0
  60. data/lib/mongoid/safe.rb +13 -0
  61. data/lib/mongoid/safety.rb +12 -0
  62. data/lib/mongoid/validations.rb +0 -4
  63. data/lib/mongoid/version.rb +1 -1
  64. data/lib/mongoid/versioning.rb +11 -1
  65. metadata +55 -28
  66. data/lib/mongoid/validations/locale/en.yml +0 -5
@@ -0,0 +1,27 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc
3
+ module Errors #:nodoc
4
+
5
+ # Default parent Mongoid error for all custom errors. This handles the base
6
+ # key for the translations and provides the convenience method for
7
+ # translating the messages.
8
+ class MongoidError < StandardError
9
+ BASE_KEY = "mongoid.errors.messages"
10
+
11
+ # Given the key of the specific error and the options hash, translate the
12
+ # message.
13
+ #
14
+ # Options:
15
+ #
16
+ # key: The key of the error in the locales.
17
+ # options: The objects to pass to create the message.
18
+ #
19
+ # Returns:
20
+ #
21
+ # A localized error message string.
22
+ def translate(key, options)
23
+ I18n.translate("#{BASE_KEY}.#{key}", options)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,21 @@
1
+ module Mongoid #:nodoc
2
+ module Errors #:nodoc
3
+
4
+ # This error is raised when trying to create set nested records above the
5
+ # specified :limit
6
+ #
7
+ # Example:
8
+ #
9
+ #<tt>TooManyNestedAttributeRecords.new('association', limit)
10
+ class TooManyNestedAttributeRecords < MongoidError
11
+ def initialize(association, limit)
12
+ super(
13
+ translate(
14
+ "too_many_nested_attribute_records",
15
+ { :association => association, :limit => limit }
16
+ )
17
+ )
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc
3
+ module Errors #:nodoc
4
+
5
+ # Raised when the database version is not supported by Mongoid.
6
+ #
7
+ # Example:
8
+ #
9
+ # <tt>UnsupportedVersion.new(Mongo::ServerVersion.new("1.3.1"))</tt>
10
+ class UnsupportedVersion < MongoidError
11
+ def initialize(version)
12
+ super(
13
+ translate(
14
+ "unsupported_version",
15
+ { :version => version, :mongo_version => Mongoid::MONGODB_VERSION }
16
+ )
17
+ )
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc
3
+ module Errors #:nodoc
4
+
5
+ # Raised when a persisence method ending in ! fails validation. The message
6
+ # will contain the full error messages from the +Document+ in question.
7
+ #
8
+ # Example:
9
+ #
10
+ # <tt>Validations.new(person.errors)</tt>
11
+ class Validations < MongoidError
12
+ def initialize(document)
13
+ super(
14
+ translate(
15
+ "validations",
16
+ { :errors => document.errors.full_messages.join(", ") }
17
+ )
18
+ )
19
+ end
20
+ end
21
+ end
22
+ end
@@ -20,7 +20,7 @@ module Mongoid #:nodoc:
20
20
  def assimilate(parent, options, type = nil)
21
21
  klass = self.klass || (type ? type : options.klass)
22
22
  child = klass.instantiate("_id" => self["_id"])
23
- self.merge("_type" => klass.name) if klass.hereditary
23
+ self.merge("_type" => klass.name) if klass.hereditary?
24
24
  init(parent, child, options)
25
25
  end
26
26
 
@@ -5,9 +5,11 @@ module Mongoid #:nodoc:
5
5
  module CriteriaHelpers #:nodoc:
6
6
  def expand_complex_criteria
7
7
  hsh = {}
8
- self.each_pair do |k,v|
9
- if k.class == Mongoid::Criterion::Complex
10
- hsh[k.key] = {"$#{k.operator}" => v}
8
+ each_pair do |k,v|
9
+ case k
10
+ when Mongoid::Criterion::Complex
11
+ hsh[k.key] ||= {}
12
+ hsh[k.key].merge!({"$#{k.operator}" => v})
11
13
  else
12
14
  hsh[k] = v
13
15
  end
@@ -12,7 +12,11 @@ module Mongoid #:nodoc:
12
12
  end
13
13
 
14
14
  def get(value)
15
- value ? self.instantiate(value) : value
15
+ if value && respond_to?(:instantiate)
16
+ instantiate(value)
17
+ else
18
+ value
19
+ end
16
20
  end
17
21
  end
18
22
  end
@@ -3,12 +3,54 @@ module Mongoid #:nodoc:
3
3
  module Extensions #:nodoc:
4
4
  module ObjectID #:nodoc:
5
5
  module Conversions #:nodoc:
6
+
6
7
  def set(value)
7
- value
8
+ if value.is_a?(::String)
9
+ BSON::ObjectID.from_string(value) unless value.blank?
10
+ else
11
+ value
12
+ end
8
13
  end
14
+
9
15
  def get(value)
10
16
  value
11
17
  end
18
+
19
+ # If the document is using BSON::ObjectIDs the convert the argument to
20
+ # either an object id or an array of them if the supplied argument is an
21
+ # Array. Otherwise just return.
22
+ #
23
+ # Options:
24
+ # args: A +String+ or an +Array+ convert to +BSON::ObjectID+
25
+ # cast: A +Boolean+ define if we can or not cast to BSON::ObjectID.
26
+ # If false, we use the default type of args
27
+ #
28
+ # Example:
29
+ #
30
+ # <tt>Mongoid.cast_ids!("4ab2bc4b8ad548971900005c", true)</tt>
31
+ # <tt>Mongoid.cast_ids!(["4ab2bc4b8ad548971900005c"])</tt>
32
+ #
33
+ # Returns:
34
+ #
35
+ # If using object ids:
36
+ # An +Array+ of +BSON::ObjectID+ of each element if params is an +Array+
37
+ # A +BSON::ObjectID+ from params if params is +String+
38
+ # Otherwise:
39
+ # <tt>args</tt>
40
+ def cast!(klass, args, cast = true)
41
+ if !klass.using_object_ids? || args.is_a?(::BSON::ObjectID) || !cast
42
+ return args
43
+ end
44
+ if args.is_a?(::String)
45
+ ::BSON::ObjectID(args)
46
+ elsif args.is_a?(::Array)
47
+ args.map{ |a|
48
+ a.is_a?(::BSON::ObjectID) ? a : ::BSON::ObjectID(a)
49
+ }
50
+ else
51
+ args
52
+ end
53
+ end
12
54
  end
13
55
  end
14
56
  end
@@ -1,13 +1,13 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid #:nodoc:
3
3
  class Field
4
- attr_reader :name, :type
4
+ attr_reader :klass, :name, :type
5
5
 
6
6
  # Get the declared options for this field
7
7
  #
8
8
  # Returns:
9
9
  #
10
- # a hash of options
10
+ # a hash of options
11
11
  def options
12
12
  @options
13
13
  end
@@ -16,9 +16,9 @@ module Mongoid #:nodoc:
16
16
  #
17
17
  # Returns:
18
18
  #
19
- # The primitive value or a copy of the default.
19
+ # The typecast default value.
20
20
  def default
21
- copy
21
+ copy.respond_to?(:call) ? copy : set(copy)
22
22
  end
23
23
 
24
24
  # Create the new field with a name and optional additional options. Valid
@@ -44,7 +44,12 @@ module Mongoid #:nodoc:
44
44
  # Used for setting an object in the attributes hash. If nil is provided the
45
45
  # default will get returned if it exists.
46
46
  def set(object)
47
- type.set(object)
47
+ unless @options[:identity]
48
+ type.set(object)
49
+ else
50
+ inverse = @options[:inverse_class_name].constantize
51
+ object.blank? ? type.set(object) : BSON::ObjectID.cast!(inverse, object)
52
+ end
48
53
  end
49
54
 
50
55
  # Used for retrieving the object out of the attributes hash.
@@ -60,7 +65,9 @@ module Mongoid #:nodoc:
60
65
 
61
66
  # Check if the name is valid.
62
67
  def check_name!(name)
63
- raise Mongoid::Errors::InvalidField.new(name) if Mongoid.destructive_fields.include?(name.to_s)
68
+ if Mongoid.destructive_fields.include?(name.to_s)
69
+ raise Mongoid::Errors::InvalidField.new(name)
70
+ end
64
71
  end
65
72
 
66
73
  def check_default!
@@ -4,7 +4,7 @@ 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, :asc, :ascending, :avg, :desc, :descending,
7
+ [ :all_in, :any_in, :any_of, :asc, :ascending, :avg, :desc, :descending,
8
8
  :excludes, :limit, :max, :min, :not_in, :only, :order_by,
9
9
  :skip, :sum, :where, :near ].each do |name|
10
10
  define_method(name) do |*args|
@@ -3,12 +3,17 @@ module Mongoid #:nodoc
3
3
  module Hierarchy #:nodoc
4
4
  extend ActiveSupport::Concern
5
5
  included do
6
- cattr_accessor :hereditary
7
- self.hereditary = false
8
-
9
6
  attr_accessor :_parent
10
7
  end
11
8
 
9
+ module ClassMethods #:nodoc:
10
+ # Returns <tt>true</tt> if the document inherits from another
11
+ # Mongoid::Document.
12
+ def hereditary?
13
+ Mongoid::Document > superclass
14
+ end
15
+ end
16
+
12
17
  module InstanceMethods #:nodoc:
13
18
 
14
19
  # Get all child +Documents+ to this +Document+, going n levels deep if
@@ -43,7 +48,7 @@ module Mongoid #:nodoc
43
48
  #
44
49
  # <tt>true</tt> if inheritance used, <tt>false</tt> if not.
45
50
  def hereditary?
46
- !!self.hereditary
51
+ self.class.hereditary?
47
52
  end
48
53
 
49
54
  # Sets up a child/parent association. This is used for newly created
@@ -34,9 +34,9 @@ module Mongoid #:nodoc:
34
34
  @document.id = generate_id if @document.id.blank?
35
35
  end
36
36
 
37
- # Set the _type field on the @document.ment.
37
+ # Set the _type field on the @document.
38
38
  def type!
39
- @document._type = @document.class.name if @document.hereditary?
39
+ @document._type = @document.class.name if @document.hereditary? || @document.class.descendants.any?
40
40
  end
41
41
 
42
42
  # Generates the composite key for a @document.ment.
@@ -21,7 +21,7 @@ module Mongoid #:nodoc
21
21
  # Add the default indexes to the root document if they do not already
22
22
  # exist. Currently this is only _type.
23
23
  def add_indexes
24
- if hereditary && !index_options[:_type]
24
+ if hereditary? && !index_options[:_type]
25
25
  self.index_options[:_type] = {:unique => false, :background => true}
26
26
  end
27
27
  create_indexes if Mongoid.autocreate_indexes
@@ -8,7 +8,7 @@ module Mongoid #:nodoc:
8
8
  end
9
9
  # Return true if the attribute and value are equal.
10
10
  def matches?(value)
11
- @attribute == value
11
+ value === @attribute
12
12
  end
13
13
 
14
14
  protected
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+ require "mongoid/modifiers/command"
3
+ require "mongoid/modifiers/inc"
4
+
5
+ module Mongoid #:nodoc:
6
+ module Modifiers #:nodoc:
7
+ extend ActiveSupport::Concern
8
+
9
+ # Increment the field by the provided value, else if it doesn't exists set
10
+ # it to that value.
11
+ #
12
+ # Options:
13
+ #
14
+ # field: The field to increment.
15
+ # value: The value to increment by.
16
+ # options: Options to pass through to the driver.
17
+ def inc(field, value, options = {})
18
+ current = send(field)
19
+ write_attribute(field, (current ? (current + value) : value))
20
+ Inc.new(self, options).persist(field, value)
21
+ current + value
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Modifiers #:nodoc:
4
+ class Command #:nodoc:
5
+ include Mongoid::Safe
6
+
7
+ # Instantiate the new $inc modifier.
8
+ #
9
+ # Options:
10
+ #
11
+ # klass: The class to get the collection from.
12
+ # options: The options to get passed through to the driver.
13
+ def initialize(document, options = {})
14
+ @document, @options = document, options
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,24 @@
1
+ # encoding: utf-8
2
+ module Mongoid #:nodoc:
3
+ module Modifiers #:nodoc:
4
+ class Inc < Command #:nodoc:
5
+
6
+ # Execute the persistence operation. This will increment the provided
7
+ # field by the supplied value. If no field exists, it will be created and
8
+ # set to the value provided.
9
+ #
10
+ # Options:
11
+ #
12
+ # field: The field to increment.
13
+ # value: The number to increment by.
14
+ def persist(field, value)
15
+ @document.collection.update(
16
+ @document._selector,
17
+ { "$inc" => { field => value } },
18
+ :safe => safe_mode?(@options),
19
+ :multi => false
20
+ )
21
+ end
22
+ end
23
+ end
24
+ end
@@ -4,6 +4,8 @@ module Mongoid #:nodoc:
4
4
  # Persistence commands extend from this class to get basic functionality on
5
5
  # initialization.
6
6
  class Command
7
+ include Mongoid::Safe
8
+
7
9
  attr_reader \
8
10
  :collection,
9
11
  :document,
@@ -36,16 +38,6 @@ module Mongoid #:nodoc:
36
38
  @validate = (validate.nil? ? true : validate)
37
39
  @options = { :safe => safe_mode?(options) }
38
40
  end
39
-
40
- protected
41
- # Determine based on configuration if we are persisting in safe mode or
42
- # not.
43
- #
44
- # The query option will always override the global configuration.
45
- def safe_mode?(options)
46
- safe = options[:safe]
47
- safe.nil? ? Mongoid.persist_in_safe_mode : safe
48
- end
49
41
  end
50
42
  end
51
43
  end
@@ -28,7 +28,7 @@ module Mongoid #:nodoc:
28
28
  protected
29
29
  # Remove the document from the database.
30
30
  def remove
31
- selector = (@klass.hereditary ? @selector.merge(:_type => @klass.name) : @selector)
31
+ selector = (@klass.hereditary? ? @selector.merge(:_type => @klass.name) : @selector)
32
32
  count = @collection.find(selector).count
33
33
  @collection.remove(selector, @options)
34
34
  count
@@ -1,6 +1,8 @@
1
1
  # encoding: utf-8
2
2
  require "singleton"
3
+ require "mongoid"
3
4
  require "mongoid/config"
5
+ require "mongoid/railties/document"
4
6
  require "rails"
5
7
  require "rails/mongoid"
6
8
 
@@ -18,7 +18,7 @@ namespace :db do
18
18
 
19
19
  if not Rake::Task.task_defined?("db:setup")
20
20
  desc 'Create the database, and initialize with the seed data'
21
- task :setup => [ 'db:create', 'db:create_indexes', 'db:seed' ]
21
+ task :setup => [ 'db:create', 'db:mongoid:create_indexes', 'db:seed' ]
22
22
  end
23
23
 
24
24
  if not Rake::Task.task_defined?("db:reseed")
@@ -55,25 +55,116 @@ namespace :db do
55
55
  end
56
56
 
57
57
  if not Rake::Task.task_defined?("db:create_indexes")
58
- desc 'Create the indexes defined on your mongoid models'
59
- task :create_indexes => :environment do
58
+ task :create_indexes => "mongoid:create_indexes"
59
+ end
60
+
61
+ namespace :mongoid do
62
+ # gets a list of the mongoid models defined in the app/models directory
63
+ def get_mongoid_models
60
64
  documents = []
61
65
  Dir.glob("app/models/**/*.rb").sort.each do |file|
62
- model = file.match(/\/(\w+).rb$/)[1]
63
- klass = model.classify.constantize
66
+ model_path = file[0..-4].split('/')[2..-1]
64
67
  begin
65
- documents << klass unless klass.embedded
68
+ klass = model_path.map(&:classify).join('::').constantize
69
+ if klass.ancestors.include?(Mongoid::Document) && !klass.embedded
70
+ documents << klass
71
+ end
66
72
  rescue => e
67
73
  # Just for non-mongoid objects that dont have the embedded
68
74
  # attribute at the class level.
69
75
  end
70
76
  end
71
- ::Rails::Mongoid.index_children(documents)
77
+ documents
72
78
  end
73
- end
74
79
 
80
+ desc 'Create the indexes defined on your mongoid models'
81
+ task :create_indexes => :environment do
82
+ ::Rails::Mongoid.index_children(get_mongoid_models)
83
+ end
84
+
85
+ def convert_ids(obj)
86
+ if obj.is_a?(String) && obj =~ /^[a-f0-9]{24}$/
87
+ BSON::ObjectID(obj)
88
+ elsif obj.is_a?(Array)
89
+ obj.map do |v|
90
+ convert_ids(v)
91
+ end
92
+ elsif obj.is_a?(Hash)
93
+ obj.each do |k, v|
94
+ obj[k] = convert_ids(v)
95
+ end
96
+ else
97
+ obj
98
+ end
99
+ end
100
+
101
+ desc "Convert string objectids in mongo database to ObjectID type"
102
+ task :objectid_convert => :environment do
103
+ documents = get_mongoid_models
104
+ documents.each do |document|
105
+ puts "Converting #{document.to_s} to use ObjectIDs"
106
+
107
+ # get old collection
108
+ collection_name = document.collection.name
109
+ collection = Mongoid.master.collection(collection_name)
110
+
111
+ # get new collection (a clean one)
112
+ collection.db["#{collection_name}_new"].drop
113
+ new_collection = collection.db["#{collection_name}_new"]
114
+
115
+ # convert collection documents
116
+ collection.find({}, :timeout => false, :sort => "_id") do |cursor|
117
+ cursor.each do |doc|
118
+ new_doc = convert_ids(doc)
119
+ new_collection.insert(new_doc, :safe => true)
120
+ end
121
+ end
122
+
123
+ puts "Done! Converted collection is in #{new_collection.name}\n\n"
124
+ end
125
+
126
+ # no errors. great! now rename _new to collection_name
127
+ documents.each do |document|
128
+ collection_name = document.collection.name
129
+ collection = Mongoid.master.collection(collection_name)
130
+ new_collection = collection.db["#{collection_name}_new"]
131
+
132
+ # swap collection to _old
133
+ puts "Moving #{collection.name} to #{collection_name}_old"
134
+ collection.db["#{collection_name}_old"].drop
135
+
136
+ begin
137
+ collection.rename("#{collection_name}_old")
138
+ rescue Exception => e
139
+ puts "Unable to rename database #{collection_name} to #{collection_name}_old"
140
+ puts "reason: #{e.message}\n\n"
141
+ end
142
+
143
+ # swap _new to collection
144
+ puts "Moving #{new_collection.name} to #{collection_name}\n\n"
145
+
146
+ begin
147
+ new_collection.rename(collection_name)
148
+ rescue Exception => e
149
+ puts "Unable to rename database #{new_collection.name} to #{collection_name}_old"
150
+ puts "reason: #{e.message}\n\n"
151
+ end
152
+ end
153
+
154
+ puts "DONE! Run `rake db:mongoid:cleanup_old_collections` to remove old collections"
155
+ end
156
+
157
+ desc "Clean up old collections backed up by objectid_convert"
158
+ task :cleanup_old_collections => :environment do
159
+ get_mongoid_models.each do |document|
160
+ collection = document.collection
161
+ collection.db["#{collection.name}_old"].drop
162
+ end
163
+ end
164
+
165
+ ########
166
+ # TODO: lots more useful db tasks can be added here. stuff like copyDatabase, etc
167
+ ########
168
+ end
75
169
 
76
- ########
77
- # TODO: lots more useful db tasks can be added here. stuff like copyDatabase, etc
78
- ########
79
170
  end