-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
MONGOID-5654: refactor and deprecate Hash#__consolidate__ (#5740)
* refactor and deprecate Hash#__consolidate__ * fix linter complaints * another minor refactoring for optimization * remove pre-refactoring docs * fix failing specs
- Loading branch information
Showing
5 changed files
with
181 additions
and
121 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
# frozen_string_literal: true | ||
|
||
module Mongoid | ||
# A singleton class to assist with preparing attributes for atomic | ||
# updates. | ||
# | ||
# Once the deprecated Hash#__consolidate__ method is removed entirely, | ||
# these methods may be moved into Mongoid::Contextual::Mongo as private | ||
# methods. | ||
# | ||
# @api private | ||
class AtomicUpdatePreparer | ||
class << self | ||
# Convert the key/values in the attributes into a hash of atomic updates. | ||
# Non-operator keys are assumed to use $set operation. | ||
# | ||
# @param [ Class ] klass The model class. | ||
# @param [ Hash ] attributes The attributes to convert. | ||
# | ||
# @return [ Hash ] The prepared atomic updates. | ||
def prepare(attributes, klass) | ||
attributes.each_pair.with_object({}) do |(key, value), atomic_updates| | ||
key = klass.database_field_name(key.to_s) | ||
|
||
if key.to_s.start_with?('$') | ||
(atomic_updates[key] ||= {}).update(prepare_operation(klass, key, value)) | ||
else | ||
(atomic_updates['$set'] ||= {})[key] = mongoize_for(key, klass, key, value) | ||
end | ||
end | ||
end | ||
|
||
private | ||
|
||
# Treats the key as if it were a MongoDB operator and prepares | ||
# the value accordingly. | ||
# | ||
# @param [ Class ] klass the model class | ||
# @param [ String | Symbol ] key the operator | ||
# @param [ Hash ] value the operand | ||
# | ||
# @return [ Hash ] the prepared value. | ||
def prepare_operation(klass, key, value) | ||
value.each_with_object({}) do |(key2, value2), hash| | ||
key2 = klass.database_field_name(key2) | ||
hash[key2] = value_for(key, klass, value2) | ||
end | ||
end | ||
|
||
# Get the value for the provided operator, klass, key and value. | ||
# | ||
# This is necessary for special cases like $rename, $addToSet and $push. | ||
# | ||
# @param [ String ] operator The operator. | ||
# @param [ Class ] klass The model class. | ||
# @param [ Object ] value The original value. | ||
# | ||
# @return [ Object ] Value prepared for the provided operator. | ||
def value_for(operator, klass, value) | ||
case operator | ||
when '$rename' then value.to_s | ||
when '$addToSet', '$push' then value.mongoize | ||
else mongoize_for(operator, klass, operator, value) | ||
end | ||
end | ||
|
||
# Mongoize for the klass, key and value. | ||
# | ||
# @param [ String ] operator The operator. | ||
# @param [ Class ] klass The model class. | ||
# @param [ String | Symbol ] key The field key. | ||
# @param [ Object ] value The value to mongoize. | ||
# | ||
# @return [ Object ] The mongoized value. | ||
def mongoize_for(operator, klass, key, value) | ||
field = klass.fields[key.to_s] | ||
return value unless field | ||
|
||
mongoized = field.mongoize(value) | ||
if Mongoid::Persistable::LIST_OPERATIONS.include?(operator) && field.resizable? && !value.is_a?(Array) | ||
return mongoized.first | ||
end | ||
|
||
mongoized | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# frozen_string_literal: true | ||
|
||
require 'spec_helper' | ||
|
||
describe Mongoid::AtomicUpdatePreparer do | ||
describe '#prepare' do | ||
let(:prepared) { described_class.prepare(hash, Band) } | ||
|
||
context 'when the hash already contains $set' do | ||
context 'when the $set is first' do | ||
let(:hash) do | ||
{ '$set' => { name: 'Tool' }, likes: 10, '$inc' => { plays: 1 } } | ||
end | ||
|
||
it 'moves the non hash values under the provided key' do | ||
expect(prepared).to eq( | ||
'$set' => { 'name' => 'Tool', 'likes' => 10 }, | ||
'$inc' => { 'plays' => 1 } | ||
) | ||
end | ||
end | ||
|
||
context 'when the $set is not first' do | ||
let(:hash) do | ||
{ likes: 10, '$inc' => { plays: 1 }, '$set' => { name: 'Tool' } } | ||
end | ||
|
||
it 'moves the non hash values under the provided key' do | ||
expect(prepared).to eq( | ||
'$set' => { 'likes' => 10, 'name' => 'Tool' }, | ||
'$inc' => { 'plays' => 1 } | ||
) | ||
end | ||
end | ||
end | ||
|
||
context 'when the hash does not contain $set' do | ||
let(:hash) do | ||
{ likes: 10, '$inc' => { plays: 1 }, name: 'Tool' } | ||
end | ||
|
||
it 'moves the non hash values under the provided key' do | ||
expect(prepared).to eq( | ||
'$set' => { 'likes' => 10, 'name' => 'Tool' }, | ||
'$inc' => { 'plays' => 1 } | ||
) | ||
end | ||
end | ||
|
||
context 'when the hash contains $rename' do | ||
let(:hash) { { likes: 10, '$rename' => { old: 'new' } } } | ||
|
||
it 'preserves the $rename operator' do | ||
expect(prepared).to eq( | ||
'$set' => { 'likes' => 10 }, | ||
'$rename' => { 'old' => 'new' } | ||
) | ||
end | ||
end | ||
|
||
context 'when the hash contains $addToSet' do | ||
let(:hash) { { likes: 10, '$addToSet' => { list: 'new' } } } | ||
|
||
it 'preserves the $addToSet operator' do | ||
expect(prepared).to eq( | ||
'$set' => { 'likes' => 10 }, | ||
'$addToSet' => { 'list' => 'new' } | ||
) | ||
end | ||
end | ||
|
||
context 'when the hash contains $push' do | ||
let(:hash) { { likes: 10, '$push' => { list: 14 } } } | ||
|
||
it 'preserves the $push operator' do | ||
expect(prepared).to eq( | ||
'$set' => { 'likes' => 10 }, | ||
'$push' => { 'list' => 14 } | ||
) | ||
end | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters