mongoid 4.0.0.alpha2 → 4.0.0.beta1

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 (104) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +55 -0
  3. data/README.md +3 -3
  4. data/lib/config/locales/en.yml +13 -0
  5. data/lib/mongoid.rb +3 -1
  6. data/lib/mongoid/atomic.rb +1 -1
  7. data/lib/mongoid/atomic/paths/embedded/many.rb +1 -1
  8. data/lib/mongoid/atomic/paths/embedded/one.rb +1 -1
  9. data/lib/mongoid/attributes.rb +23 -1
  10. data/lib/mongoid/attributes/processing.rb +1 -1
  11. data/lib/mongoid/composable.rb +3 -2
  12. data/lib/mongoid/contextual/command.rb +0 -26
  13. data/lib/mongoid/contextual/geo_near.rb +1 -1
  14. data/lib/mongoid/contextual/mongo.rb +6 -29
  15. data/lib/mongoid/contextual/text_search.rb +3 -5
  16. data/lib/mongoid/criteria.rb +1 -1
  17. data/lib/mongoid/criteria/modifiable.rb +27 -7
  18. data/lib/mongoid/criteria/permission.rb +70 -0
  19. data/lib/mongoid/document.rb +5 -6
  20. data/lib/mongoid/errors.rb +2 -0
  21. data/lib/mongoid/errors/document_not_destroyed.rb +25 -0
  22. data/lib/mongoid/errors/readonly_document.rb +24 -0
  23. data/lib/mongoid/extensions/boolean.rb +1 -0
  24. data/lib/mongoid/extensions/hash.rb +1 -1
  25. data/lib/mongoid/factory.rb +5 -3
  26. data/lib/mongoid/fields.rb +32 -0
  27. data/lib/mongoid/fields/localized.rb +1 -1
  28. data/lib/mongoid/fields/standard.rb +1 -1
  29. data/lib/mongoid/findable.rb +1 -0
  30. data/lib/mongoid/interceptable.rb +11 -6
  31. data/lib/mongoid/log_subscriber.rb +34 -1
  32. data/lib/mongoid/persistable/deletable.rb +1 -0
  33. data/lib/mongoid/persistable/destroyable.rb +7 -2
  34. data/lib/mongoid/persistable/updatable.rb +27 -26
  35. data/lib/mongoid/query_cache.rb +246 -0
  36. data/lib/mongoid/railties/database.rake +4 -26
  37. data/lib/mongoid/relations.rb +8 -22
  38. data/lib/mongoid/relations/accessors.rb +0 -3
  39. data/lib/mongoid/relations/binding.rb +1 -1
  40. data/lib/mongoid/relations/bindings/embedded/in.rb +1 -1
  41. data/lib/mongoid/relations/eager.rb +5 -6
  42. data/lib/mongoid/relations/eager/base.rb +97 -5
  43. data/lib/mongoid/relations/eager/belongs_to.rb +1 -0
  44. data/lib/mongoid/relations/eager/has_and_belongs_to_many.rb +16 -9
  45. data/lib/mongoid/relations/eager/has_many.rb +1 -0
  46. data/lib/mongoid/relations/eager/has_one.rb +1 -0
  47. data/lib/mongoid/relations/embedded/batchable.rb +1 -1
  48. data/lib/mongoid/relations/embedded/in.rb +4 -4
  49. data/lib/mongoid/relations/embedded/many.rb +7 -5
  50. data/lib/mongoid/relations/embedded/one.rb +1 -1
  51. data/lib/mongoid/relations/macros.rb +1 -0
  52. data/lib/mongoid/relations/marshalable.rb +3 -3
  53. data/lib/mongoid/relations/proxy.rb +12 -10
  54. data/lib/mongoid/relations/referenced/in.rb +2 -2
  55. data/lib/mongoid/relations/referenced/many.rb +9 -9
  56. data/lib/mongoid/relations/referenced/many_to_many.rb +7 -7
  57. data/lib/mongoid/relations/referenced/one.rb +4 -4
  58. data/lib/mongoid/{state.rb → stateful.rb} +13 -1
  59. data/lib/mongoid/tasks/database.rake +31 -0
  60. data/lib/mongoid/tasks/database.rb +107 -0
  61. data/lib/mongoid/threaded.rb +0 -47
  62. data/lib/mongoid/validatable/uniqueness.rb +4 -16
  63. data/lib/mongoid/version.rb +1 -1
  64. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +0 -3
  65. data/lib/rails/mongoid.rb +0 -124
  66. data/spec/app/models/edit.rb +5 -0
  67. data/spec/app/models/even.rb +7 -0
  68. data/spec/app/models/line_item.rb +1 -1
  69. data/spec/app/models/note.rb +2 -0
  70. data/spec/app/models/odd.rb +7 -0
  71. data/spec/app/models/record.rb +5 -0
  72. data/spec/app/models/wiki_page.rb +1 -1
  73. data/spec/mongoid/attributes_spec.rb +76 -1
  74. data/spec/mongoid/changeable_spec.rb +6 -2
  75. data/spec/mongoid/contextual/mongo_spec.rb +3 -1
  76. data/spec/mongoid/contextual/text_search_spec.rb +3 -1
  77. data/spec/mongoid/criteria/modifiable_spec.rb +192 -0
  78. data/spec/mongoid/criteria_spec.rb +6 -2
  79. data/spec/mongoid/errors/document_not_destroyed_spec.rb +33 -0
  80. data/spec/mongoid/errors/readonly_document_spec.rb +29 -0
  81. data/spec/mongoid/fields/localized_spec.rb +15 -0
  82. data/spec/mongoid/fields_spec.rb +88 -2
  83. data/spec/mongoid/log_subscriber_spec.rb +3 -3
  84. data/spec/mongoid/persistable/deletable_spec.rb +14 -1
  85. data/spec/mongoid/persistable/destroyable_spec.rb +45 -1
  86. data/spec/mongoid/persistable/savable_spec.rb +34 -5
  87. data/spec/mongoid/query_cache_spec.rb +197 -0
  88. data/spec/mongoid/relations/bindings/embedded/in_spec.rb +2 -2
  89. data/spec/mongoid/relations/builders/referenced/many_spec.rb +1 -1
  90. data/spec/mongoid/relations/eager/has_and_belongs_to_many_spec.rb +11 -37
  91. data/spec/mongoid/relations/eager/has_one_spec.rb +1 -1
  92. data/spec/mongoid/relations/embedded/in_spec.rb +1 -1
  93. data/spec/mongoid/relations/embedded/many_spec.rb +10 -10
  94. data/spec/mongoid/relations/embedded/one_spec.rb +10 -2
  95. data/spec/mongoid/relations/referenced/in_spec.rb +1 -1
  96. data/spec/mongoid/relations/referenced/many_spec.rb +37 -2
  97. data/spec/mongoid/relations/touchable_spec.rb +20 -0
  98. data/spec/mongoid/{state_spec.rb → stateful_spec.rb} +26 -1
  99. data/spec/mongoid/tasks/database_rake_spec.rb +285 -0
  100. data/spec/mongoid/tasks/database_spec.rb +148 -0
  101. data/spec/mongoid/validatable/uniqueness_spec.rb +7 -0
  102. data/spec/rails/mongoid_spec.rb +0 -316
  103. data/spec/spec_helper.rb +1 -0
  104. metadata +30 -8
@@ -0,0 +1,31 @@
1
+ namespace :db do
2
+ namespace :mongoid do
3
+ task :load_models do
4
+ end
5
+
6
+ desc "Create the indexes defined on your mongoid models"
7
+ task :create_indexes => [:environment, :load_models] do
8
+ ::Mongoid::Tasks::Database.create_indexes
9
+ end
10
+
11
+ desc "Remove indexes that exist in the database but aren't specified on the models"
12
+ task :remove_undefined_indexes => [:environment, :load_models] do
13
+ ::Mongoid::Tasks::Database.remove_undefined_indexes
14
+ end
15
+
16
+ desc "Remove the indexes defined on your mongoid models without questions!"
17
+ task :remove_indexes => [:environment, :load_models] do
18
+ ::Mongoid::Tasks::Database.remove_indexes
19
+ end
20
+
21
+ desc "Drops the default session"
22
+ task :drop => :environment do
23
+ ::Mongoid::Sessions.default.drop
24
+ end
25
+
26
+ desc "Drop all collections except the system collections"
27
+ task :purge => :environment do
28
+ ::Mongoid.purge!
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,107 @@
1
+ # encoding: utf-8
2
+ module Mongoid
3
+ module Tasks
4
+ module Database
5
+ extend self
6
+
7
+ # Create indexes for each model given the provided globs and the class is
8
+ # not embedded.
9
+ #
10
+ # @example Create all the indexes.
11
+ # Mongoid::Tasks::Database.create_indexes
12
+ #
13
+ # @return [ Array<Class> ] The indexed models.
14
+ #
15
+ # @since 2.1.0
16
+ def create_indexes(models = ::Mongoid.models)
17
+ models.each do |model|
18
+ next if model.index_specifications.empty?
19
+ unless model.embedded?
20
+ model.create_indexes
21
+ logger.info("MONGOID: Created indexes on #{model}:")
22
+ model.index_specifications.each do |spec|
23
+ logger.info("MONGOID: Index: #{spec.key}, Options: #{spec.options}")
24
+ end
25
+ model
26
+ else
27
+ logger.info("MONGOID: Index ignored on: #{model}, please define in the root model.")
28
+ nil
29
+ end
30
+ end.compact
31
+ end
32
+
33
+ # Return the list of indexes by model that exist in the database but aren't
34
+ # specified on the models.
35
+ #
36
+ # @example Return the list of unused indexes.
37
+ # Mongoid::Tasks::Database.undefined_indexes
38
+ #
39
+ # @return Hash{Class => Array(Hash)} The list of undefined indexes by model.
40
+ def undefined_indexes(models = ::Mongoid.models)
41
+ undefined_by_model = {}
42
+
43
+ models.each do |model|
44
+ unless model.embedded?
45
+ model.collection.indexes.each do |index|
46
+ # ignore default index
47
+ unless index['name'] == '_id_'
48
+ key = index['key'].symbolize_keys
49
+ spec = model.index_specification(key)
50
+ unless spec
51
+ # index not specified
52
+ undefined_by_model[model] ||= []
53
+ undefined_by_model[model] << index
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ undefined_by_model
61
+ end
62
+
63
+ # Remove indexes that exist in the database but aren't specified on the
64
+ # models.
65
+ #
66
+ # @example Remove undefined indexes.
67
+ # Mongoid::Tasks::Database.remove_undefined_indexes
68
+ #
69
+ # @return [ Hash{Class => Array(Hash)}] The list of indexes that were removed by model.
70
+ #
71
+ # @since 4.0.0
72
+ def remove_undefined_indexes(models = ::Mongoid.models)
73
+ undefined_indexes(models).each do |model, indexes|
74
+ indexes.each do |index|
75
+ key = index['key'].symbolize_keys
76
+ model.collection.indexes.drop(key)
77
+ logger.info("MONGOID: Removing index: #{index['name']} on #{model}.")
78
+ end
79
+ end
80
+ end
81
+
82
+ # Remove indexes for each model given the provided globs and the class is
83
+ # not embedded.
84
+ #
85
+ # @example Remove all the indexes.
86
+ # Mongoid::Tasks::Database.remove_indexes
87
+ #
88
+ # @return [ Array<Class> ] The un-indexed models.
89
+ #
90
+ def remove_indexes(models = ::Mongoid.models)
91
+ models.each do |model|
92
+ next if model.embedded?
93
+ indexes = model.collection.indexes.map{ |doc| doc["name"] }
94
+ indexes.delete_one("_id_")
95
+ model.remove_indexes
96
+ logger.info("MONGOID: Removing indexes on: #{model} for: #{indexes.join(', ')}.")
97
+ model
98
+ end.compact
99
+ end
100
+
101
+ private
102
+ def logger
103
+ @logger ||= Logger.new($stdout)
104
+ end
105
+ end
106
+ end
107
+ end
@@ -150,53 +150,6 @@ module Mongoid
150
150
  validations_for(document.class).delete_one(document.id)
151
151
  end
152
152
 
153
- # Get the field selection options from the current thread.
154
- #
155
- # @example Get the field selection options.
156
- # Threaded.selection
157
- #
158
- # @param [ Integer ] criteria_instance_id The criteria instance id.
159
- #
160
- # @return [ Hash ] The field selection.
161
- #
162
- # @since 2.4.4
163
- def selection(criteria_instance_id)
164
- selections = Thread.current["[mongoid][selections]"]
165
- selections[criteria_instance_id] if selections
166
- end
167
-
168
- # Set the field selection on the current thread.
169
- #
170
- # @example Set the field selection.
171
- # Threaded.set_selection(Person, { field: 1 })
172
- #
173
- # @param [ Integer ] criteria_instance_id The criteria instance id.
174
- # @param [ Hash ] value The current field selection.
175
- #
176
- # @return [ Hash ] The field selection.
177
- #
178
- # @since 2.4.4
179
- def set_selection(criteria_instance_id, value)
180
- Thread.current["[mongoid][selections]"] ||= {}
181
- Thread.current["[mongoid][selections]"][criteria_instance_id] = value
182
- end
183
-
184
- # Delete the field selection on the current thread.
185
- #
186
- # @example Delete the field selection.
187
- # Threaded.delete_selection(Person)
188
- #
189
- # @param [ Integer ] criteria_instance_id The criteria instance id.
190
- #
191
- # @return [ Boolean ] Whether there was a field selection.
192
- #
193
- # @since 3.0.7
194
- def delete_selection(criteria_instance_id)
195
- selections = Thread.current["[mongoid][selections]"]
196
- return false unless selections
197
- !!selections.delete(criteria_instance_id)
198
- end
199
-
200
153
  # Get the global session override.
201
154
  #
202
155
  # @example Get the global session override.
@@ -26,20 +26,6 @@ module Mongoid
26
26
  class UniquenessValidator < ActiveModel::EachValidator
27
27
  include Queryable
28
28
 
29
- attr_reader :klass
30
-
31
- # Unfortunately, we have to tie Uniqueness validators to a class.
32
- #
33
- # @example Setup the validator.
34
- # UniquenessValidator.new.setup(Person)
35
- #
36
- # @param [ Class ] klass The class getting validated.
37
- #
38
- # @since 1.0.0
39
- def setup(klass)
40
- @klass = klass
41
- end
42
-
43
29
  # Validate the document for uniqueness violations.
44
30
  #
45
31
  # @example Validate the document.
@@ -241,7 +227,7 @@ module Mongoid
241
227
  def to_validate(document, attribute, value)
242
228
  metadata = document.relations[attribute.to_s]
243
229
  if metadata && metadata.stores_foreign_key?
244
- [ metadata.foreign_key, value.id ]
230
+ [ metadata.foreign_key, value && value.id ]
245
231
  else
246
232
  [ attribute, value ]
247
233
  end
@@ -280,7 +266,9 @@ module Mongoid
280
266
  #
281
267
  # @since 2.4.10
282
268
  def validate_root(document, attribute, value)
283
- criteria = create_criteria(klass || document.class, document, attribute, value)
269
+ klass = document.class
270
+ klass = klass.superclass while !klass.validators.include?(self)
271
+ criteria = create_criteria(klass, document, attribute, value)
284
272
  criteria = criteria.merge(options[:conditions].call) if options[:conditions]
285
273
 
286
274
  if criteria.with(persistence_options(criteria)).exists?
@@ -1,4 +1,4 @@
1
1
  # encoding: utf-8
2
2
  module Mongoid
3
- VERSION = "4.0.0.alpha2"
3
+ VERSION = "4.0.0.beta1"
4
4
  end
@@ -29,9 +29,6 @@ development:
29
29
  # retry_interval: 1
30
30
  # Configure Mongoid specific options. (optional)
31
31
  options:
32
- # Enable the identity map, needed for eager loading. (default: false)
33
- # identity_map_enabled: false
34
-
35
32
  # Includes the root model name in json serialization. (default: false)
36
33
  # include_root_in_json: false
37
34
 
@@ -3,100 +3,6 @@ module Rails
3
3
  module Mongoid
4
4
  extend self
5
5
 
6
- # Create indexes for each model given the provided globs and the class is
7
- # not embedded.
8
- #
9
- # @example Create all the indexes.
10
- # Rails::Mongoid.create_indexes
11
- #
12
- # @return [ Array<Class> ] The indexed models.
13
- #
14
- # @since 2.1.0
15
- def create_indexes
16
- ::Mongoid.models.each do |model|
17
- next if model.index_specifications.empty?
18
- unless model.embedded?
19
- model.create_indexes
20
- logger.info("MONGOID: Created indexes on #{model}:")
21
- model.index_specifications.each do |spec|
22
- logger.info("MONGOID: Index: #{spec.key}, Options: #{spec.options}")
23
- end
24
- model
25
- else
26
- logger.info("MONGOID: Index ignored on: #{model}, please define in the root model.")
27
- nil
28
- end
29
- end.compact
30
- end
31
-
32
- # Return the list of indexes by model that exist in the database but aren't
33
- # specified on the models.
34
- #
35
- # @example Return the list of unused indexes.
36
- # Rails::Mongoid.undefined_indexes
37
- #
38
- # @return Hash{Class => Array(Hash)} The list of undefined indexes by model.
39
- def undefined_indexes
40
- undefined_by_model = {}
41
-
42
- ::Mongoid.models.each do |model|
43
- unless model.embedded?
44
- model.collection.indexes.each do |index|
45
- # ignore default index
46
- unless index['name'] == '_id_'
47
- key = index['key'].symbolize_keys
48
- spec = model.index_specification(key)
49
- unless spec
50
- # index not specified
51
- undefined_by_model[model] ||= []
52
- undefined_by_model[model] << index
53
- end
54
- end
55
- end
56
- end
57
- end
58
-
59
- undefined_by_model
60
- end
61
-
62
- # Remove indexes that exist in the database but aren't specified on the
63
- # models.
64
- #
65
- # @example Remove undefined indexes.
66
- # Rails::Mongoid.remove_undefined_indexes
67
- #
68
- # @return [ Hash{Class => Array(Hash)}] The list of indexes that were removed by model.
69
- #
70
- # @since 4.0.0
71
- def remove_undefined_indexes
72
- undefined_indexes.each do |model, indexes|
73
- indexes.each do |index|
74
- key = index['key'].symbolize_keys
75
- model.collection.indexes.drop(key)
76
- logger.info("MONGOID: Removing index: #{index['name']} on #{model}.")
77
- end
78
- end
79
- end
80
-
81
- # Remove indexes for each model given the provided globs and the class is
82
- # not embedded.
83
- #
84
- # @example Remove all the indexes.
85
- # Rails::Mongoid.remove_indexes
86
- #
87
- # @return [ Array<Class> ] The un-indexed models.
88
- #
89
- def remove_indexes
90
- ::Mongoid.models.each do |model|
91
- next if model.embedded?
92
- indexes = model.collection.indexes.map{ |doc| doc["name"] }
93
- indexes.delete_one("_id_")
94
- model.remove_indexes
95
- logger.info("MONGOID: Removing indexes on: #{model} for: #{indexes.join(', ')}.")
96
- model
97
- end.compact
98
- end
99
-
100
6
  # Use the application configuration to get every model and require it, so
101
7
  # that indexing and inheritance work in both development and production
102
8
  # with the same results.
@@ -146,35 +52,5 @@ module Rails
146
52
  Logger.new($stdout).warn(e.message)
147
53
  end
148
54
  end
149
-
150
- # Given the provided file name, determine the model and return the class.
151
- #
152
- # @example Determine the model from the file.
153
- # Rails::Mongoid.determine_model("app/models/person.rb")
154
- #
155
- # @param [ String ] file The filename.
156
- #
157
- # @return [ Class ] The model.
158
- #
159
- # @since 2.1.0
160
- def determine_model(file, logger)
161
- return nil unless file =~ /app\/models\/(.*).rb$/
162
- return nil unless logger
163
-
164
- model_path = $1.split('/')
165
- begin
166
- parts = model_path.map { |path| path.camelize }
167
- name = parts.join("::")
168
- klass = name.constantize
169
- rescue NameError, LoadError
170
- logger.info("MONGOID: Attempted to constantize #{name}, trying without namespacing.")
171
- klass = parts.last.constantize rescue nil
172
- end
173
- klass if klass && klass.ancestors.include?(::Mongoid::Document)
174
- end
175
-
176
- def logger
177
- @logger ||= Logger.new($stdout)
178
- end
179
55
  end
180
56
  end
@@ -0,0 +1,5 @@
1
+ class Edit
2
+ include Mongoid::Document
3
+ include Mongoid::Timestamps::Updated
4
+ embedded_in :wiki_page, touch: true
5
+ end
@@ -0,0 +1,7 @@
1
+ class Even
2
+ include Mongoid::Document
3
+ field :name
4
+
5
+ belongs_to :parent, class_name: 'Odd', inverse_of: :evens
6
+ has_many :odds, inverse_of: :parent, autosave: true
7
+ end
@@ -2,5 +2,5 @@ class LineItem
2
2
  include Mongoid::Document
3
3
  embedded_in :purchase
4
4
  belongs_to :product, polymorphic: true
5
- validates :product, presence: true
5
+ validates :product, presence: true, uniqueness: { scope: :product }
6
6
  end
@@ -6,6 +6,8 @@ class Note
6
6
 
7
7
  after_save :update_saved
8
8
 
9
+ scope :permanent, ->{ where(saved: true) }
10
+
9
11
  def update_saved
10
12
  self.saved = true
11
13
  end
@@ -0,0 +1,7 @@
1
+ class Odd
2
+ include Mongoid::Document
3
+ field :name
4
+
5
+ belongs_to :parent, class_name: 'Even', inverse_of: :odds
6
+ has_many :evens, inverse_of: :parent
7
+ end
@@ -43,4 +43,9 @@ class Record
43
43
  def access_band
44
44
  band.name
45
45
  end
46
+
47
+ def dont_call_me_twice
48
+ end
49
+
50
+ validate { dont_call_me_twice }
46
51
  end
@@ -6,8 +6,8 @@ class WikiPage
6
6
  field :transient_property, type: String
7
7
  field :author, type: String
8
8
  field :description, type: String, localize: true
9
- max_versions 5
10
9
 
10
+ embeds_many :edits, validate: false
11
11
  has_many :comments, dependent: :destroy, validate: false
12
12
  has_many :child_pages, class_name: "WikiPage", dependent: :delete, inverse_of: :parent_pages
13
13
  belongs_to :parent_pages, class_name: "WikiPage", inverse_of: :child_pages