mongoid 4.0.0.alpha2 → 4.0.0.beta1

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