Skip to content

Commit

Permalink
Merge remote-tracking branch 'remotes/origin/master' into custom-fiel…
Browse files Browse the repository at this point in the history
…d-type-support
  • Loading branch information
johnnyshields committed Apr 13, 2022
2 parents 22222c6 + cd23359 commit 3cf2f23
Show file tree
Hide file tree
Showing 23 changed files with 372 additions and 95 deletions.
6 changes: 6 additions & 0 deletions docs/release-notes/mongoid-8.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,12 @@ as well as ActiveRecord-compatible ``previously_new_record?`` and
user.previously_persisted? # => true


Removed ``Document#to_a`` method
--------------------------------

The ``Document#to_a`` method has been removed in Mongoid 8.


Setting Field Type as a Class is Deprecated
-------------------------------------------

Expand Down
1 change: 1 addition & 0 deletions docs/tutorials.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ Tutorials
tutorials/getting-started-sinatra
tutorials/getting-started-rails
tutorials/documents
tutorials/common-errors
49 changes: 49 additions & 0 deletions docs/tutorials/common-errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
*************
Common Errors
*************

.. default-domain:: mongodb

.. contents:: On this page
:local:
:backlinks: none
:depth: 2
:class: singlecol

Mongoid/Moped Authentication Error: failed with error 13
========================================================

If you are encountering the following error:

.. code-block:: ruby

Moped::Errors::OperationFailure: The operation: #<Moped::Protocol::Command
@length=83
@request_id=5
@response_to=0
@op_code=2004
@flags=[]
@full_collection_name="mongose_development.$cmd"
@skip=0
@limit=-1
@selector={:getlasterror=>1, :w=>1}
@fields=nil>
failed with error 13: "not authorized for insert on mongose_development.people"

See https://github.com/mongodb/mongo/blob/master/docs/errors.md
for details about this error.
from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/bundler/gems/moped-10abbf3eac37/lib/moped/operation/read.rb:50:in `block in execute'
from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/bundler/gems/moped-10abbf3eac37/lib/moped/node.rb:594:in `[]'
from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/bundler/gems/moped-10abbf3eac37/lib/moped/node.rb:594:in `block (2 levels) in flush'
from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/bundler/gems/moped-10abbf3eac37/lib/moped/node.rb:593:in `map'
from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/bundler/gems/moped-10abbf3eac37/lib/moped/node.rb:593:in `block in flush'
from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/bundler/gems/moped-10abbf3eac37/lib/moped/node.rb:617:in `block in logging'
from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/notifications.rb:164:in `block in instrument'
from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/notifications/instrumenter.rb:20:in `instrumen

This error is caused by Moped, a Ruby driver that is no longer in use by
Mongoid. Upgrading to Mongoid 5+ should fix this issue.

You can find more information about this issue here:
`MONGOID-4067 <https://jira.mongodb.org/browse/MONGOID-4067>`_.

1 change: 1 addition & 0 deletions lib/mongoid/association/accessors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,7 @@ def self.define_setter!(association)
# @return [ Class ] The class being set up.
def self.define_ids_setter!(association)
ids_method = "#{association.name.to_s.singularize}_ids="
association.inverse_class.aliased_associations[ids_method.chop] = association.name.to_s
association.inverse_class.tap do |klass|
klass.re_define_method(ids_method) do |ids|
send(association.setter, association.relation_class.find(ids.reject(&:blank?)))
Expand Down
26 changes: 15 additions & 11 deletions lib/mongoid/association/eager_loadable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,26 +24,30 @@ def eager_load(docs)
# recursively to load the associations of the given documents'
# subdocuments.
#
# @param [ Array<Association> ] association The associations to load.
# @param [ Array<Association> ] associations The associations to load.
# @param [ Array<Document> ] document The documents.
def preload(associations, docs)
assoc_map = associations.group_by(&:inverse_class_name)
docs_map = { klass.to_s => docs.to_set }
docs_map = {}
queue = [ klass.to_s ]

while klass = queue.shift
if as = assoc_map.delete(klass)
as.group_by(&:relation)
.each do |relation, assocs|
assocs.each { |a| queue << a.class_name }
as.each do |assoc|
queue << assoc.class_name

# If this class is nested in the inclusion tree, only load documents
# for the association above it. If there is no parent association,
# we will include documents from the documents passed to this method.
ds = docs
if assoc.parent_inclusions.length > 0
ds = assoc.parent_inclusions.map{ |p| docs_map[p].to_a }.flatten
end

docs = docs_map[klass] || []
res = relation.eager_loader(assocs, docs.to_a).run
res = assoc.relation.eager_loader([assoc], ds).run

res.group_by(&:class).each do |k, vs|
docs_map[k.to_s] ||= [].to_set
docs_map[k.to_s].merge(vs)
end
docs_map[assoc.name] ||= [].to_set
docs_map[assoc.name].merge(res)
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/mongoid/association/macros.rb
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ def define_association!(macro_name, name, options = {}, &block)
Association::MACRO_MAPPING[macro_name].new(self, name, options, &block).tap do |assoc|
assoc.setup!
self.relations = self.relations.merge(name => assoc)
if assoc.respond_to?(:store_as) && assoc.store_as != name
if assoc.embedded? && assoc.respond_to?(:store_as) && assoc.store_as != name
self.aliased_associations[assoc.store_as] = name
end
end
Expand Down
7 changes: 7 additions & 0 deletions lib/mongoid/association/relatable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,13 @@ def validate?
end
end

# @return [ Array<String> ] The associations above this one in the inclusion tree.
attr_accessor :parent_inclusions

def parent_inclusions
@parent_inclusions ||= []
end

private

# Gets the model classes with inverse associations of this model. This is used to determine
Expand Down
5 changes: 3 additions & 2 deletions lib/mongoid/attributes/processing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,12 @@ def process_attributes(attrs = nil)
# @return [ true, false ] True if pending, false if not.
def pending_attribute?(key, value)
name = key.to_s
if relations.has_key?(name)
aliased = aliased_associations[name] if aliased_associations.key?(name)
if relations.has_key?(aliased)
pending_relations[name] = value
return true
end
if nested_attributes.has_key?(name)
if nested_attributes.has_key?(aliased)
pending_nested[name] = value
return true
end
Expand Down
44 changes: 24 additions & 20 deletions lib/mongoid/criteria/includable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,19 @@ module Includable
#
# @return [ Criteria ] The cloned criteria.
def includes(*relations)
extract_includes_list(klass, relations)
extract_includes_list(klass, nil, relations)
clone
end

# Get a list of criteria that are to be executed for eager loading.
#
# @example Get the eager loading inclusions.
# Person.includes(:game).inclusions
#
# @return [ Array<Association> ] The inclusions.
def inclusions
@inclusions ||= []
end

# Set the inclusions for the criteria.
#
# @example Set the inclusions.
# criteria.inclusions = [ association ]
#
# @param [ Array<Association> ] value The inclusions.
#
# @return [ Array<Association> ] The new inclusions.
Expand All @@ -56,30 +50,40 @@ def inclusions=(value)

# Add an inclusion definition to the list of inclusions for the criteria.
#
# @example Add an inclusion.
# criteria.add_inclusion(Person, :posts)
#
# @param [ Class, String, Symbol ] _klass The class or string/symbol of the class name.
# @param [ Symbol ] association The association.
#
# @raise [ Errors::InvalidIncludes ] If no association is found.
def add_inclusion(_klass, association)
inclusions.push(association) unless inclusions.include?(association)
# @param [ Association ] association The association.
# @param [ String ] parent The name of the association above this one in
# the inclusion tree, if it is a nested inclusion.
def add_inclusion(association, parent = nil)
if assoc = inclusions.detect { |a| a == association }
assoc.parent_inclusions.push(parent) if parent
else
assoc = association.dup
assoc.parent_inclusions = []
assoc.parent_inclusions.push(parent) if parent
inclusions.push(assoc)
end
end

def extract_includes_list(_parent_class, *relations_list)
# Iterate through the list of relations and create the inclusions list.
#
# @param [ Class, String, Symbol ] _parent_class The class from which the
# association originates.
# @param [ String ] parent The name of the association above this one in
# the inclusion tree, if it is a nested inclusion.
# @param relations_list The names of the associations to eager load.
def extract_includes_list(_parent_class, parent, *relations_list)
relations_list.flatten.each do |relation_object|
if relation_object.is_a?(Hash)
relation_object.each do |relation, _includes|
association = _parent_class.reflect_on_association(relation)
raise Errors::InvalidIncludes.new(_klass, [ relation ]) unless association
add_inclusion(_parent_class, association)
extract_includes_list(association.klass, _includes)
add_inclusion(association, parent)
extract_includes_list(association.klass, association.name, _includes)
end
else
association = _parent_class.reflect_on_association(relation_object)
raise Errors::InvalidIncludes.new(_parent_class, [ relation_object ]) unless association
add_inclusion(_parent_class, association)
add_inclusion(association, parent)
end
end
end
Expand Down
10 changes: 0 additions & 10 deletions lib/mongoid/document.rb
Original file line number Diff line number Diff line change
Expand Up @@ -124,16 +124,6 @@ def to_key
(persisted? || destroyed?) ? [ _id.to_s ] : nil
end

# Return an array with this +Document+ only in it.
#
# @example Return the document in an array.
# document.to_a
#
# @return [ Array<Document> ] An array with the document as its only item.
def to_a
[ self ]
end

# Return a hash of the entire document hierarchy from this document and
# below. Used when the attributes are needed for everything and not just
# the current document.
Expand Down
17 changes: 16 additions & 1 deletion lib/mongoid/persistence_context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,21 @@ def ==(other)
options == other.options
end

# Whether the client of the context can be reused later, and therefore should
# not be closed.
#
# If the persistence context is requested with :client option only, it means
# that the context should use a client configured in mongoid.yml.
# Such clients should not be closed when the context is cleared since they
# will be reused later.
#
# @return [ true | false ] True if client can be reused, otherwise false.
#
# @api private
def reusable_client?
@options.keys == [:client]
end

private

def set_options!(opts)
Expand Down Expand Up @@ -211,7 +226,7 @@ def get(object)
def clear(object, cluster = nil, original_context = nil)
if context = get(object)
unless cluster.nil? || context.cluster.equal?(cluster)
context.client.close
context.client.close unless context.reusable_client?
end
end
ensure
Expand Down
5 changes: 4 additions & 1 deletion lib/mongoid/traversable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,10 @@ def collect_descendants
to_expand = []
expanding.each do |child|
next if expanded[child]
expanded[child] = true
# Don't mark expanded if _id is nil, since documents are compared by
# their _ids, multiple embedded documents with nil ids will compare
# equally, and some documents will not be expanded.
expanded[child] = true if child._id
children << child
to_expand += child._children
end
Expand Down
28 changes: 28 additions & 0 deletions spec/integration/associations/embedded_dirty_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,34 @@
it 'performs dirty tracking efficiently' do
subject.changed?.should be false
end

it 'calculates the descendants properly' do
expect(subject._descendants.length).to eq(40)
end
end

context 'when association is cyclic and the _id is nil' do
before do
# create deeply nested record
a = EmmOuter.create(level: 0)
level = 1
iter = a.inners.create(id: nil, level: level)
loop do
iter.friends.create(id: nil, level: (level += 1))
iter = iter.friends[0]
break if level == 40
end
end

let(:subject) { EmmOuter.first }

it 'performs dirty tracking efficiently' do
subject.changed?.should be false
end

it 'calculates the descendants properly' do
expect(subject._descendants.length).to eq(40)
end
end
end
end
3 changes: 2 additions & 1 deletion spec/mongoid/association/eager_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,8 @@ class Voucher
end

it "runs the has_one preload" do
expect(Mongoid::Association::Referenced::HasOne::Eager).to receive(:new).with([book_association, cat_association], docs).once.and_call_original
expect(Mongoid::Association::Referenced::HasOne::Eager).to receive(:new).with([ book_association ], docs).once.and_call_original
expect(Mongoid::Association::Referenced::HasOne::Eager).to receive(:new).with([ cat_association ], docs).once.and_call_original
context.eager_load(docs)
end
end
Expand Down
15 changes: 15 additions & 0 deletions spec/mongoid/association/referenced/belongs_to/proxy_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require "spec_helper"
require_relative '../belongs_to_models.rb'

describe Mongoid::Association::Referenced::BelongsTo::Proxy do

Expand Down Expand Up @@ -1370,6 +1371,20 @@ class C
game.person.set_personal_data(ssn: '123', age: 25)
end.not_to raise_error
end
end

# This is a very specific case, see MONGOID-5089 for more details.
context "when required is false, child is an orphan, and parent has explicit _id" do
let(:comment) { BTMComment.create! }
let(:article) do
BTMArticle.new(
comment_ids: [comment.id],
id: 1
)
end

it "uses the correct explicit id" do
expect(article.comments.first.article_id).to eq(1)
end
end
end
11 changes: 11 additions & 0 deletions spec/mongoid/association/referenced/belongs_to_models.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true

class BTMArticle
include Mongoid::Document
has_many :comments, class_name: "BTMComment"
end

class BTMComment
include Mongoid::Document
belongs_to :article, class_name: "BTMArticle", required: false
end
Loading

0 comments on commit 3cf2f23

Please sign in to comment.