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

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