Skip to content

Commit

Permalink
Merge branch '9.0-stable' into 5831-9.0-backport
Browse files Browse the repository at this point in the history
  • Loading branch information
comandeo-mongo authored Dec 3, 2024
2 parents da38237 + 838eea4 commit 4c80d2e
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 9 deletions.
80 changes: 72 additions & 8 deletions lib/mongoid/loadable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ module Loadable
# (See #model_paths.)
DEFAULT_MODEL_PATHS = %w( ./app/models ./lib/models ).freeze

# The default list of glob patterns that match paths to ignore when loading
# models. Defaults to '*/models/concerns/*', which Rails uses for extensions
# to models (and which cause errors when loaded out of order).
#
# See #ignore_patterns.
DEFAULT_IGNORE_PATTERNS = %w( */models/concerns/* ).freeze

# Search a list of model paths to get every model and require it, so
# that indexing and inheritance work in both development and production
# with the same results.
Expand All @@ -24,17 +31,47 @@ module Loadable
# for model files. These must either be absolute paths, or relative to
# the current working directory.
def load_models(paths = model_paths)
paths.each do |path|
if preload_models.resizable?
files = preload_models.map { |model| "#{path}/#{model.underscore}.rb" }
files = files_under_paths(paths)

files.sort.each do |file|
load_model(file)
end

nil
end

# Given a list of paths, return all ruby files under that path (or, if
# `preload_models` is a list of model names, returns only the files for
# those named models).
#
# @param [ Array<String> ] paths the list of paths to search
#
# @return [ Array<String> ] the normalized file names, suitable for loading
# via `require_dependency` or `require`.
def files_under_paths(paths)
paths.flat_map { |path| files_under_path(path) }
end

# Given a single path, returns all ruby files under that path (or, if
# `preload_models` is a list of model names, returns only the files for
# those named models).
#
# @param [ String ] path the path to search
#
# @return [ Array<String> ] the normalized file names, suitable for loading
# via `require_dependency` or `require`.
def files_under_path(path)
files = if preload_models.resizable?
preload_models.
map { |model| "#{path}/#{model.underscore}.rb" }.
select { |file_name| File.exists?(file_name) }
else
files = Dir.glob("#{path}/**/*.rb")
Dir.glob("#{path}/**/*.rb").
reject { |file_name| ignored?(file_name) }
end

files.sort.each do |file|
load_model(file.gsub(/^#{path}\// , "").gsub(/\.rb$/, ""))
end
end
# strip the path and the suffix from each entry
files.map { |file| file.gsub(/^#{path}\// , "").gsub(/\.rb$/, "") }
end

# A convenience method for loading a model's file. If Rails'
Expand Down Expand Up @@ -71,13 +108,40 @@ def model_paths
DEFAULT_MODEL_PATHS
end

# Returns the array of glob patterns that determine whether a given
# path should be ignored by the model loader.
#
# @return [ Array<String> ] the array of ignore patterns
def ignore_patterns
@ignore_patterns ||= DEFAULT_IGNORE_PATTERNS.dup
end

# Sets the model paths to the given array of paths. These are the paths
# where the application's model definitions are located.
#
# @param [ Array<String> ] paths The list of model paths
def model_paths=(paths)
@model_paths = paths
end

# Sets the ignore patterns to the given array of patterns. These are glob
# patterns that determine whether a given path should be ignored by the
# model loader or not.
#
# @param [ Array<String> ] patterns The list of glob patterns
def ignore_patterns=(patterns)
@ignore_patterns = patterns
end

# Returns true if the given file path matches any of the ignore patterns.
#
# @param [ String ] file_path The file path to consider
#
# @return [ true | false ] whether or not the given file path should be
# ignored.
def ignored?(file_path)
ignore_patterns.any? { |pattern| File.fnmatch?(pattern, file_path) }
end
end

end
2 changes: 1 addition & 1 deletion lib/mongoid/persistence_context.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ def client
# @return [ Symbol ] The client name for this persistence
# context.
def client_name
@client_name ||= options[:client] ||
@client_name ||= __evaluate__(options[:client]) ||
Threaded.client_override ||
__evaluate__(storage_options[:client])
end
Expand Down
86 changes: 86 additions & 0 deletions spec/mongoid/loadable_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# frozen_string_literal: true

require 'spec_helper'

describe Mongoid::Loadable do
let(:lib_dir) { Pathname.new('../../lib').realpath(__dir__) }

shared_context 'with ignore_patterns' do
around do |example|
saved = Mongoid.ignore_patterns
Mongoid.ignore_patterns = ignore_patterns
example.run
ensure
Mongoid.ignore_patterns = saved
end
end

describe '#ignore_patterns' do
context 'when not explicitly set' do
it 'equals the default list of ignore patterns' do
expect(Mongoid.ignore_patterns).to eq Mongoid::Loadable::DEFAULT_IGNORE_PATTERNS
end
end

context 'when explicitly set' do
include_context 'with ignore_patterns'

let(:ignore_patterns) { %w[ pattern1 pattern2 ] }

it 'equals the list of specified patterns' do
expect(Mongoid.ignore_patterns).to eq ignore_patterns
end
end
end

describe '#files_under_path' do
let(:results) { Mongoid.files_under_path(lib_dir) }

include_context 'with ignore_patterns'

context 'when ignore_patterns is empty' do
let(:ignore_patterns) { [] }

it 'returns all ruby files' do
expect(results.length).to be > 10 # should be a bunch of them
expect(results).to include('rails/mongoid')
end
end

context 'when ignore_patterns is not empty' do
let(:ignore_patterns) { %w[ */rails/* ] }

it 'omits the ignored paths' do
expect(results.length).to be > 10 # should be a bunch of them
expect(results).not_to include('rails/mongoid')
end
end
end

describe '#files_under_paths' do
let(:paths) { [ lib_dir.join('mongoid'), lib_dir.join('rails') ] }
let(:results) { Mongoid.files_under_paths(paths) }

include_context 'with ignore_patterns'

context 'when ignore_patterns is empty' do
let(:ignore_patterns) { [] }

it 'returns all ruby files' do
expect(results.length).to be > 10 # should be a bunch
expect(results).to include('generators/mongoid/model/model_generator')
expect(results).to include('fields/encrypted')
end
end

context 'when ignore_patterns is not empty' do
let(:ignore_patterns) { %w[ */model/* */fields/* ] }

it 'returns all ruby files' do
expect(results.length).to be > 10 # should be a bunch
expect(results).not_to include('generators/mongoid/model/model_generator')
expect(results).not_to include('fields/encrypted')
end
end
end
end
8 changes: 8 additions & 0 deletions spec/mongoid/persistence_context_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -584,6 +584,14 @@
expect(persistence_context.client).to eq(Mongoid::Clients.with_name(:alternative))
end

context 'when the client option is a proc' do
let(:options) { { client: -> { :alternative } } }

it 'evaluates the proc' do
expect(persistence_context.client).to eq(Mongoid::Clients.with_name(:alternative))
end
end

context 'when there is a client override' do
persistence_context_override :client, :other

Expand Down

0 comments on commit 4c80d2e

Please sign in to comment.