Module: Mongoid::Threaded

Extended by:
Threaded
Included in:
Threaded
Defined in:
lib/mongoid/threaded.rb,
lib/mongoid/threaded/lifecycle.rb

Overview

This module contains logic for easy access to objects that have a lifecycle on the current thread.

Defined Under Namespace

Modules: Lifecycle

Constant Summary collapse

STORAGE_KEY =

The key for the shared thread- and fiber-local storage. It must be a symbol because keys for fiber-local storage must be symbols.

:'[mongoid]'
STORAGE_OWNER_KEY =

Tracks which fiber owns the storage hash, to detect when a fiber has inherited (rather than created) its storage from a parent fiber.

:'[mongoid]:owner'
DATABASE_OVERRIDE_KEY =
'db-override'
CLIENT_OVERRIDE_KEY =

The key to override the client.

'client-override'
CURRENT_SCOPE_KEY =

The key for the current thread's scope stack.

'current-scope'
AUTOSAVES_KEY =
'autosaves'
VALIDATIONS_KEY =
'validations'
TOUCH_MERGED_KEY =
'touch-merged'
STACK_KEYS =
Hash.new do |hash, key|
  hash[key] = "#{key}-stack"
end
COLLECTION_MANAGEMENT =

The name of the stack tracking collection and index management.

:collection_management
SESSIONS_KEY =

The key for the current thread's sessions.

'sessions'
MODIFIED_DOCUMENTS_KEY =

The key for storing documents modified inside transactions.

'modified-documents'
EXECUTE_CALLBACKS =

The key storing the default value for whether or not callbacks are executed on documents.

'execute-callbacks'
REGEXP_BUDGET_KEY =

The key for the time left in the current in-memory regexp budget.

'regexp-budget'
BIND =
'bind'
ASSIGN =
'assign'
BUILD =
'build'
LOAD =
'load'
CREATE =
'create'

Instance Method Summary collapse

Instance Method Details

#add_modified_document(session, document) ⇒ Object

Store a reference to the document that was modified inside a transaction associated with the session.

Parameters:

  • session (Mongo::Session)

    Session in scope of which the document was modified.

  • document (Mongoid::Document)

    Mongoid document that was modified.



540
541
542
543
544
# File 'lib/mongoid/threaded.rb', line 540

def add_modified_document(session, document)
  return unless session&.in_transaction?

  modified_documents[session] << document
end

#autosaved?(document) ⇒ true | false

Is the document autosaved on the current thread?

Examples:

Is the document autosaved?

Threaded.autosaved?(doc)

Parameters:

  • document (Document)

    The document to check.

Returns:

  • (true | false)

    If the document is autosaved.



404
405
406
# File 'lib/mongoid/threaded.rb', line 404

def autosaved?(document)
  autosaves_for(document.class).include?(document._id)
end

#autosavesHash

Get all autosaves on the current thread.

Examples:

Get all autosaves.

Threaded.autosaves

Returns:

  • (Hash)

    The current autosaves.



439
440
441
# File 'lib/mongoid/threaded.rb', line 439

def autosaves
  get(AUTOSAVES_KEY) { {} }
end

#autosaves_for(klass) ⇒ Array

Get all autosaves on the current thread for the class.

Examples:

Get all autosaves.

Threaded.autosaves_for(Person)

Parameters:

  • klass (Class)

    The class to check.

Returns:

  • (Array)

    The current autosaves.



461
462
463
# File 'lib/mongoid/threaded.rb', line 461

def autosaves_for(klass)
  autosaves[klass] ||= []
end

#begin_autosave(document) ⇒ Object

Begin autosaving a document on the current thread.

Examples:

Begin autosave.

Threaded.begin_autosave(doc)

Parameters:

  • document (Document)

    The document to autosave.



234
235
236
# File 'lib/mongoid/threaded.rb', line 234

def begin_autosave(document)
  autosaves_for(document.class).push(document._id)
end

#begin_execution(name) ⇒ true

Begin entry into a named thread local stack.

Examples:

Begin entry into the stack.

Threaded.begin_execution(:create)

Parameters:

  • name (String)

    The name of the stack

Returns:

  • (true)

    True.



136
137
138
# File 'lib/mongoid/threaded.rb', line 136

def begin_execution(name)
  stack(name).push(true)
end

#begin_touch_merged(document) ⇒ Object

Mark that a document's touch updates have been merged into an atomic insert on the current thread.

Examples:

Begin touch merged.

Threaded.begin_touch_merged(doc)

Parameters:

  • document (Document)

    The embedded document being inserted.



255
256
257
# File 'lib/mongoid/threaded.rb', line 255

def begin_touch_merged(document)
  touch_merged_for(document.class).push(document._id)
end

#begin_validate(document) ⇒ Object

Begin validating a document on the current thread.

Examples:

Begin validation.

Threaded.begin_validate(doc)

Parameters:

  • document (Document)

    The document to validate.



244
245
246
# File 'lib/mongoid/threaded.rb', line 244

def begin_validate(document)
  validations_for(document.class).push(document._id)
end

#begin_without_default_scope(klass) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Begin suppressing default scopes for given model on the current thread.

Examples:

Begin without default scope stack.

Threaded.begin_without_default_scope(klass)

Parameters:

  • klass (Class)

    The model to suppress default scoping on.



297
298
299
# File 'lib/mongoid/threaded.rb', line 297

def begin_without_default_scope(klass)
  stack(:without_default_scope).push(klass)
end

#clear_modified_documents(session) ⇒ Set<Mongoid::Document>

Clears the set of modified documents for the given session, and return the content of the set before the clearance.

Parameters:

  • session (Mongo::Session)

    Session for which the modified documents set should be cleared.

Returns:

  • (Set<Mongoid::Document>)

    Collection of modified documents before it was cleared.



553
554
555
# File 'lib/mongoid/threaded.rb', line 553

def clear_modified_documents(session)
  modified_documents.delete(session) || []
end

#clear_session(client: nil) ⇒ nil

Note:

For backward compatibility it is allowed to call this method without

Clear the cached session for this thread for a client.

specifying client parameter.

Parameters:

  • client (Mongo::Client | nil) (defaults to: nil)

    The client to clear the session for.

Returns:

  • (nil)


530
531
532
# File 'lib/mongoid/threaded.rb', line 530

def clear_session(client: nil)
  sessions.delete(client)&.end_session
end

#client_overrideString | Symbol

Get the global client override.

Examples:

Get the global client override.

Threaded.client_override

Returns:

  • (String | Symbol)

    The override.



319
320
321
# File 'lib/mongoid/threaded.rb', line 319

def client_override
  get(CLIENT_OVERRIDE_KEY)
end

#client_override=(name) ⇒ String | Symbol

Set the global client override.

Examples:

Set the global client override.

Threaded.client_override = :testing

Parameters:

  • name (String | Symbol)

    The global override name.

Returns:

  • (String | Symbol)

    The override.



331
332
333
# File 'lib/mongoid/threaded.rb', line 331

def client_override=(name)
  set(CLIENT_OVERRIDE_KEY, name)
end

#current_scope(klass = nil) ⇒ Criteria

Get the current Mongoid scope.

Examples:

Get the scope.

Threaded.current_scope(klass)
Threaded.current_scope

Parameters:

  • klass (Klass) (defaults to: nil)

    The class type of the scope.

Returns:



344
345
346
347
348
349
350
351
352
# File 'lib/mongoid/threaded.rb', line 344

def current_scope(klass = nil)
  current_scope = get(CURRENT_SCOPE_KEY)

  if klass && current_scope.respond_to?(:keys)
    current_scope[current_scope.keys.find { |k| k <= klass }]
  else
    current_scope
  end
end

#current_scope=(scope) ⇒ Criteria

Set the current Mongoid scope.

Examples:

Set the scope.

Threaded.current_scope = scope

Parameters:

  • scope (Criteria)

    The current scope.

Returns:



362
363
364
# File 'lib/mongoid/threaded.rb', line 362

def current_scope=(scope)
  set(CURRENT_SCOPE_KEY, scope)
end

#database_overrideString | Symbol

Get the global database override.

Examples:

Get the global database override.

Threaded.database_override

Returns:

  • (String | Symbol)

    The override.



146
147
148
# File 'lib/mongoid/threaded.rb', line 146

def database_override
  get(DATABASE_OVERRIDE_KEY)
end

#database_override=(name) ⇒ String | Symbol

Set the global database override.

Examples:

Set the global database override.

Threaded.database_override = :testing

Parameters:

  • name (String | Symbol)

    The global override name.

Returns:

  • (String | Symbol)

    The override.



158
159
160
# File 'lib/mongoid/threaded.rb', line 158

def database_override=(name)
  set(DATABASE_OVERRIDE_KEY, name)
end

#delete(key) ⇒ Object

Removes the named variable from local storage.

Parameters:

  • key (String | Symbol)

    the name of the variable to remove.



115
116
117
# File 'lib/mongoid/threaded.rb', line 115

def delete(key)
  storage.delete(key)
end

#execute_callbacks=(flag) ⇒ Object

Indicates whether document callbacks should be invoked by default for the current thread. Individual documents may further override the callback behavior, but this will be used for the default behavior.

Parameters:

  • flag (true | false)

    Whether or not document callbacks should be executed by default.



579
580
581
# File 'lib/mongoid/threaded.rb', line 579

def execute_callbacks=(flag)
  set(EXECUTE_CALLBACKS, flag)
end

#execute_callbacks?true | false

Queries whether document callbacks should be executed by default for the current thread.

Unless otherwise indicated (by #execute_callbacks=), this will return true.

Returns:

  • (true | false)

    Whether or not document callbacks should be executed by default.



565
566
567
568
569
570
571
# File 'lib/mongoid/threaded.rb', line 565

def execute_callbacks?
  if has?(EXECUTE_CALLBACKS)
    get(EXECUTE_CALLBACKS)
  else
    true
  end
end

#executing?(name) ⇒ true

Are in the middle of executing the named stack

Examples:

Are we in the stack execution?

Threaded.executing?(:create)

Parameters:

  • name (Symbol)

    The name of the stack

Returns:

  • (true)

    If the stack is being executed.



170
171
172
# File 'lib/mongoid/threaded.rb', line 170

def executing?(name)
  !stack(name).empty?
end

#exit_autosave(document) ⇒ Object

Exit autosaving a document on the current thread.

Examples:

Exit autosave.

Threaded.exit_autosave(doc)

Parameters:

  • document (Document)

    The document to autosave.



265
266
267
# File 'lib/mongoid/threaded.rb', line 265

def exit_autosave(document)
  autosaves_for(document.class).delete_one(document._id)
end

#exit_execution(name) ⇒ true

Exit from a named thread local stack.

Examples:

Exit from the stack.

Threaded.exit_execution(:create)

Parameters:

  • name (Symbol)

    The name of the stack

Returns:

  • (true)

    True.



212
213
214
# File 'lib/mongoid/threaded.rb', line 212

def exit_execution(name)
  stack(name).pop
end

#exit_touch_merged(document) ⇒ Object

Clear the touch-merged flag for a document on the current thread.

Examples:

Exit touch merged.

Threaded.exit_touch_merged(doc)

Parameters:

  • document (Document)

    The document to clear.



285
286
287
# File 'lib/mongoid/threaded.rb', line 285

def exit_touch_merged(document)
  touch_merged_for(document.class).delete_one(document._id)
end

#exit_validate(document) ⇒ Object

Exit validating a document on the current thread.

Examples:

Exit validation.

Threaded.exit_validate(doc)

Parameters:

  • document (Document)

    The document to validate.



275
276
277
# File 'lib/mongoid/threaded.rb', line 275

def exit_validate(document)
  validations_for(document.class).delete_one(document._id)
end

#exit_without_default_scope(klass) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Exit suppressing default scopes for given model on the current thread.

Examples:

Exit without default scope stack.

Threaded.exit_without_default_scope(klass)

Parameters:

  • klass (Class)

    The model to unsuppress default scoping on.



309
310
311
# File 'lib/mongoid/threaded.rb', line 309

def exit_without_default_scope(klass)
  stack(:without_default_scope).delete(klass)
end

#get(key, &default) ⇒ Object | nil

Queries the thread- or fiber-local variable with the given name. If a block is given, and the variable does not already exist, the return value of the block will be set as the value of the variable before returning it.

It is very important that applications (and especially Mongoid) use this method instead of Thread#[], since Thread#[] is actually for fiber-local variables, and Mongoid uses Fibers as an implementation detail in some callbacks. Putting thread-local state in a fiber-local store will result in the state being invisible when relevant callbacks are run in a different fiber.

Affected callbacks are cascading callbacks on embedded children.

Parameters:

  • key (String | Symbol)

    the name of the variable to query

  • default (Proc)

    an optional block that must return the default (initial) value of this variable.

Returns:

  • (Object | nil)

    the value of the queried variable, or nil if it is not set and no default was given.



90
91
92
93
94
95
96
97
98
99
# File 'lib/mongoid/threaded.rb', line 90

def get(key, &default)
  result = storage[key]

  if result.nil? && default
    result = yield
    set(key, result)
  end

  result
end

#get_session(client: nil) ⇒ Mongo::Session | nil

Note:

For backward compatibility it is allowed to call this method without

Get the cached session for this thread for a client.

specifying client parameter.

Parameters:

  • client (Mongo::Client | nil) (defaults to: nil)

    The client to cache the session for.

Returns:

  • (Mongo::Session | nil)

    The session cached on this thread or nil.



518
519
520
# File 'lib/mongoid/threaded.rb', line 518

def get_session(client: nil)
  sessions[client]
end

#has?(key) ⇒ true | false

Queries the presence of a named variable in local storage.

Parameters:

  • key (String | Symbol)

    the name of the variable to query.

Returns:

  • (true | false)

    whether the given variable is present or not.



124
125
126
# File 'lib/mongoid/threaded.rb', line 124

def has?(key)
  storage.key?(key)
end

#managing_collection?true | false

Is collection or index management being executed?

Examples:

Is a collection being managed?

Threaded.managing_collection?

Returns:

  • (true | false)

    Whether collection or index management is in progress on the current thread.



200
201
202
# File 'lib/mongoid/threaded.rb', line 200

def managing_collection?
  executing?(COLLECTION_MANAGEMENT)
end

#modified_documentsHash<Mongo::Session, Set<Mongoid::Document>>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the thread store of modified documents.

Returns:

  • (Hash<Mongo::Session, Set<Mongoid::Document>>)

    The modified documents indexed by session.



598
599
600
# File 'lib/mongoid/threaded.rb', line 598

def modified_documents
  get(MODIFIED_DOCUMENTS_KEY) { Hash.new { |h, k| h[k] = Set.new } }
end

#reset!Object

Resets the current thread- or fiber-local storage to its initial state. This is useful for making sure the state is clean when starting a new thread or fiber.

The value of Mongoid::Config.real_isolation_level is used to determine whether to reset the storage for the current thread or fiber.



59
60
61
62
63
64
65
66
67
68
69
# File 'lib/mongoid/threaded.rb', line 59

def reset!
  case Config.real_isolation_level
  when :thread
    Thread.current.thread_variable_set(STORAGE_KEY, nil)
  when :fiber
    Fiber[STORAGE_KEY] = {}
    Fiber[STORAGE_OWNER_KEY] = Fiber.current.object_id
  else
    raise "Unknown isolation level: #{Config.real_isolation_level.inspect}"
  end
end

#sessionsHash<Integer, Set>

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the thread store of sessions.

Returns:

  • (Hash<Integer, Set>)

    The sessions indexed by client object ID.



588
589
590
# File 'lib/mongoid/threaded.rb', line 588

def sessions
  get(SESSIONS_KEY) { {}.compare_by_identity }
end

#set(key, value) ⇒ Object

Sets a variable in local storage with the given name to the given value. See #get for a discussion of why this method is necessary, and why Thread#[]= should be avoided in cascading callbacks on embedded children.

Parameters:

  • key (String | Symbol)

    the name of the variable to set.

  • value (Object | nil)

    the value of the variable to set (or nil if you wish to unset the variable)



108
109
110
# File 'lib/mongoid/threaded.rb', line 108

def set(key, value)
  storage[key] = value
end

#set_current_scope(scope, klass) ⇒ Criteria

Set the current Mongoid scope. Safe for multi-model scope chaining.

Examples:

Set the scope.

Threaded.current_scope(scope, klass)

Parameters:

  • scope (Criteria)

    The current scope.

  • klass (Class)

    The current model class.

Returns:



375
376
377
378
379
380
381
382
# File 'lib/mongoid/threaded.rb', line 375

def set_current_scope(scope, klass)
  if scope.nil?
    unset_current_scope(klass)
  else
    current_scope = get(CURRENT_SCOPE_KEY) { {} }
    current_scope[klass] = scope
  end
end

#set_session(session, client: nil) ⇒ Object

Note:

For backward compatibility it is allowed to call this method without

Cache a session for this thread for a client.

specifying client parameter.

Parameters:

  • session (Mongo::Session)

    The session to save.

  • client (Mongo::Client | nil) (defaults to: nil)

    The client to cache the session for.



506
507
508
# File 'lib/mongoid/threaded.rb', line 506

def set_session(session, client: nil)
  sessions[client] = session
end

#stack(name) ⇒ Array

Get the named stack.

Examples:

Get a stack by name

Threaded.stack(:create)

Parameters:

  • name (Symbol)

    The name of the stack

Returns:

  • (Array)

    The stack.



224
225
226
# File 'lib/mongoid/threaded.rb', line 224

def stack(name)
  get(STACK_KEYS[name]) { [] }
end

#touch_mergedHash

Get all touch-merged tracking on the current thread.

Examples:

Get all touch-merged.

Threaded.touch_merged

Returns:

  • (Hash)

    The current touch-merged tracking hash.



483
484
485
# File 'lib/mongoid/threaded.rb', line 483

def touch_merged
  get(TOUCH_MERGED_KEY) { {} }
end

#touch_merged?(document) ⇒ true | false

Is the document flagged as having had its touch updates merged into an atomic insert?

Examples:

Is the document touch-merged?

Threaded.touch_merged?(doc)

Parameters:

  • document (Document)

    The document to check.

Returns:

  • (true | false)

    If the document's touch was merged.



429
430
431
# File 'lib/mongoid/threaded.rb', line 429

def touch_merged?(document)
  touch_merged_for(document.class).include?(document._id)
end

#touch_merged_for(klass) ⇒ Array

Get all touch-merged document IDs on the current thread for the class.

Examples:

Get all touch-merged.

Threaded.touch_merged_for(Sofa)

Parameters:

  • klass (Class)

    The class to check.

Returns:

  • (Array)

    The current touch-merged document IDs.



495
496
497
# File 'lib/mongoid/threaded.rb', line 495

def touch_merged_for(klass)
  touch_merged[klass] ||= []
end

#validated?(document) ⇒ true | false

Is the document validated on the current thread?

Examples:

Is the document validated?

Threaded.validated?(doc)

Parameters:

  • document (Document)

    The document to check.

Returns:

  • (true | false)

    If the document is validated.



416
417
418
# File 'lib/mongoid/threaded.rb', line 416

def validated?(document)
  validations_for(document.class).include?(document._id)
end

#validationsHash

Get all validations on the current thread.

Examples:

Get all validations.

Threaded.validations

Returns:

  • (Hash)

    The current validations.



449
450
451
# File 'lib/mongoid/threaded.rb', line 449

def validations
  get(VALIDATIONS_KEY) { {} }
end

#validations_for(klass) ⇒ Array

Get all validations on the current thread for the class.

Examples:

Get all validations.

Threaded.validations_for(Person)

Parameters:

  • klass (Class)

    The class to check.

Returns:

  • (Array)

    The current validations.



473
474
475
# File 'lib/mongoid/threaded.rb', line 473

def validations_for(klass)
  validations[klass] ||= []
end

#with_collection_managementObject

Execute the block as collection or index management.

Creating, dropping and inspecting collections and indexes sends no document data, so these operations are exempt from the encryption schema check that PersistenceContext applies to reads and writes. Without the exemption, tasks such as db:mongoid:create_collections would need an encryption-capable client to run.

Examples:

Create a collection.

Threaded.with_collection_management { model.create_collection }

Returns:

  • (Object)

    The result of the block.



186
187
188
189
190
191
# File 'lib/mongoid/threaded.rb', line 186

def with_collection_management
  begin_execution(COLLECTION_MANAGEMENT)
  yield
ensure
  exit_execution(COLLECTION_MANAGEMENT)
end

#without_default_scope?(klass) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Is the given klass' default scope suppressed on the current thread?

Examples:

Is the given klass' default scope suppressed?

Threaded.without_default_scope?(klass)

Parameters:

  • klass (Class)

    The model to check for default scope suppression.

Returns:



392
393
394
# File 'lib/mongoid/threaded.rb', line 392

def without_default_scope?(klass)
  stack(:without_default_scope).include?(klass)
end