mongoid 9.0.3 → 9.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 421d53636b0e90abcfa52bf637229588d434ff73f14574b1d316b72009f7236b
4
- data.tar.gz: fafa07a7cc9b17de0abbb140ca3d021e3041bfe00b67a424c81f6c15018e607d
3
+ metadata.gz: 3ac7ac8e60ba9d953a5a809d17b7fca7a2501988cdec50d12348af3b7022f615
4
+ data.tar.gz: 27a2bceb132e4f6b33d8be6b70ac8074b8de421c5c1efdb03f3ad2fc97d62364
5
5
  SHA512:
6
- metadata.gz: d4f2011d1f85178a8693d653518f06e7ad55c1363958e3577f80d040e3986b595bdcaade0e71696ff762e008c195ee23b4579458708bb9e916f7ddfa8e3b9352
7
- data.tar.gz: 8b981e1bc4e904085ecedfe459d31c91d06ed353ba0ceef3d67865ea195f31a36539284246b54537fc5af8b54eafcf1a60415eb88c4850d7cdf3af378d449206
6
+ metadata.gz: 31cea6e77afe05358ff9cdaf5bc861807a93ce9c89e7cfd9209c0f35a2df816fb8d4d595faafbab2665b2dab776f523d0b96eece4b249626f976ec685765d5a5
7
+ data.tar.gz: 1367fb446dae452b73227c099e72ed2f13bd2702434e8050c09f3c1ae2ad97b5346371252ce3cfa3442151353bd8c8ef9a409cd2de75edb7cdf20ff3322107c7
@@ -23,7 +23,7 @@ module Mongoid
23
23
  # @return [ true | false ] If the document is new, or if the field is not
24
24
  # readonly.
25
25
  def attribute_writable?(name)
26
- new_record? || (!readonly_attributes.include?(name) && _loaded?(name))
26
+ new_record? || (!self.class.readonly_attributes.include?(name) && _loaded?(name))
27
27
  end
28
28
 
29
29
  private
@@ -63,12 +63,17 @@ module Mongoid
63
63
  # end
64
64
  #
65
65
  # @param [ Symbol... ] *names The names of the fields.
66
+ # @note When a parent class contains readonly attributes and is then
67
+ # inherited by a child class, the child class will inherit the
68
+ # parent's readonly attributes at the time of its creation.
69
+ # Updating the parent does not propagate down to child classes after wards.
66
70
  def attr_readonly(*names)
71
+ self.readonly_attributes = self.readonly_attributes.dup
67
72
  names.each do |name|
68
- readonly_attributes << database_field_name(name)
73
+ self.readonly_attributes << database_field_name(name)
69
74
  end
70
75
  end
71
76
  end
72
77
  end
73
78
  end
74
- end
79
+ end
@@ -553,7 +553,7 @@ module Mongoid
553
553
  # @return [ Selectable ] The new selectable.
554
554
  def not(*criteria)
555
555
  if criteria.empty?
556
- dup.tap { |query| query.negating = true }
556
+ dup.tap { |query| query.negating = !query.negating }
557
557
  else
558
558
  criteria.compact.inject(self.clone) do |c, new_s|
559
559
  if new_s.is_a?(Selectable)
@@ -18,6 +18,7 @@ module Mongoid
18
18
  #
19
19
  # @return [ Integer ] -1, 0, 1.
20
20
  def <=>(other)
21
+ return super unless other.is_a?(Mongoid::Equality)
21
22
  attributes["_id"].to_s <=> other.attributes["_id"].to_s
22
23
  end
23
24
 
@@ -10,6 +10,13 @@ module Mongoid
10
10
  # (See #model_paths.)
11
11
  DEFAULT_MODEL_PATHS = %w( ./app/models ./lib/models ).freeze
12
12
 
13
+ # The default list of glob patterns that match paths to ignore when loading
14
+ # models. Defaults to '*/models/concerns/*', which Rails uses for extensions
15
+ # to models (and which cause errors when loaded out of order).
16
+ #
17
+ # See #ignore_patterns.
18
+ DEFAULT_IGNORE_PATTERNS = %w( */models/concerns/* ).freeze
19
+
13
20
  # Search a list of model paths to get every model and require it, so
14
21
  # that indexing and inheritance work in both development and production
15
22
  # with the same results.
@@ -24,17 +31,47 @@ module Mongoid
24
31
  # for model files. These must either be absolute paths, or relative to
25
32
  # the current working directory.
26
33
  def load_models(paths = model_paths)
27
- paths.each do |path|
28
- if preload_models.resizable?
29
- files = preload_models.map { |model| "#{path}/#{model.underscore}.rb" }
34
+ files = files_under_paths(paths)
35
+
36
+ files.sort.each do |file|
37
+ load_model(file)
38
+ end
39
+
40
+ nil
41
+ end
42
+
43
+ # Given a list of paths, return all ruby files under that path (or, if
44
+ # `preload_models` is a list of model names, returns only the files for
45
+ # those named models).
46
+ #
47
+ # @param [ Array<String> ] paths the list of paths to search
48
+ #
49
+ # @return [ Array<String> ] the normalized file names, suitable for loading
50
+ # via `require_dependency` or `require`.
51
+ def files_under_paths(paths)
52
+ paths.flat_map { |path| files_under_path(path) }
53
+ end
54
+
55
+ # Given a single path, returns all ruby files under that path (or, if
56
+ # `preload_models` is a list of model names, returns only the files for
57
+ # those named models).
58
+ #
59
+ # @param [ String ] path the path to search
60
+ #
61
+ # @return [ Array<String> ] the normalized file names, suitable for loading
62
+ # via `require_dependency` or `require`.
63
+ def files_under_path(path)
64
+ files = if preload_models.resizable?
65
+ preload_models.
66
+ map { |model| "#{path}/#{model.underscore}.rb" }.
67
+ select { |file_name| File.exists?(file_name) }
30
68
  else
31
- files = Dir.glob("#{path}/**/*.rb")
69
+ Dir.glob("#{path}/**/*.rb").
70
+ reject { |file_name| ignored?(file_name) }
32
71
  end
33
72
 
34
- files.sort.each do |file|
35
- load_model(file.gsub(/^#{path}\// , "").gsub(/\.rb$/, ""))
36
- end
37
- end
73
+ # strip the path and the suffix from each entry
74
+ files.map { |file| file.gsub(/^#{path}\// , "").gsub(/\.rb$/, "") }
38
75
  end
39
76
 
40
77
  # A convenience method for loading a model's file. If Rails'
@@ -71,6 +108,14 @@ module Mongoid
71
108
  DEFAULT_MODEL_PATHS
72
109
  end
73
110
 
111
+ # Returns the array of glob patterns that determine whether a given
112
+ # path should be ignored by the model loader.
113
+ #
114
+ # @return [ Array<String> ] the array of ignore patterns
115
+ def ignore_patterns
116
+ @ignore_patterns ||= DEFAULT_IGNORE_PATTERNS.dup
117
+ end
118
+
74
119
  # Sets the model paths to the given array of paths. These are the paths
75
120
  # where the application's model definitions are located.
76
121
  #
@@ -78,6 +123,25 @@ module Mongoid
78
123
  def model_paths=(paths)
79
124
  @model_paths = paths
80
125
  end
126
+
127
+ # Sets the ignore patterns to the given array of patterns. These are glob
128
+ # patterns that determine whether a given path should be ignored by the
129
+ # model loader or not.
130
+ #
131
+ # @param [ Array<String> ] patterns The list of glob patterns
132
+ def ignore_patterns=(patterns)
133
+ @ignore_patterns = patterns
134
+ end
135
+
136
+ # Returns true if the given file path matches any of the ignore patterns.
137
+ #
138
+ # @param [ String ] file_path The file path to consider
139
+ #
140
+ # @return [ true | false ] whether or not the given file path should be
141
+ # ignored.
142
+ def ignored?(file_path)
143
+ ignore_patterns.any? { |pattern| File.fnmatch?(pattern, file_path) }
144
+ end
81
145
  end
82
146
 
83
147
  end
@@ -138,7 +138,7 @@ module Mongoid
138
138
  # @return [ Symbol ] The client name for this persistence
139
139
  # context.
140
140
  def client_name
141
- @client_name ||= options[:client] ||
141
+ @client_name ||= __evaluate__(options[:client]) ||
142
142
  Threaded.client_override ||
143
143
  __evaluate__(storage_options[:client])
144
144
  end
@@ -8,6 +8,29 @@ module Mongoid
8
8
  # around traversing the document graph.
9
9
  module Traversable
10
10
  extend ActiveSupport::Concern
11
+ # This code is extracted from ActiveSupport so that we do not depend on
12
+ # their private API that may change at any time.
13
+ # This code should be reviewed and maybe removed when implementing
14
+ # https://jira.mongodb.org/browse/MONGOID-5832
15
+ class << self
16
+ # @api private
17
+ def __redefine(owner, name, value)
18
+ if owner.singleton_class?
19
+ owner.redefine_method(name) { value }
20
+ owner.send(:public, name)
21
+ end
22
+ owner.redefine_singleton_method(name) { value }
23
+ owner.singleton_class.send(:public, name)
24
+ owner.redefine_singleton_method("#{name}=") do |new_value|
25
+ if owner.equal?(self)
26
+ value = new_value
27
+ else
28
+ ::Mongoid::Traversable.redefine(self, name, new_value)
29
+ end
30
+ end
31
+ owner.singleton_class.send(:public, "#{name}=")
32
+ end
33
+ end
11
34
 
12
35
  # Class-level methods for the Traversable behavior.
13
36
  module ClassMethods
@@ -105,11 +128,7 @@ module Mongoid
105
128
  if value
106
129
  Mongoid::Fields::Validators::Macro.validate_field_name(self, value)
107
130
  value = value.to_s
108
- if defined?(::ActiveSupport::ClassAttribute)
109
- ::ActiveSupport::ClassAttribute.redefine(self, 'discriminator_key', value)
110
- else
111
- super
112
- end
131
+ ::Mongoid::Traversable.__redefine(self, 'discriminator_key', value)
113
132
  else
114
133
  # When discriminator key is set to nil, replace the class's definition
115
134
  # of the discriminator key reader (provided by class_attribute earlier)
@@ -2,5 +2,5 @@
2
2
  # rubocop:todo all
3
3
 
4
4
  module Mongoid
5
- VERSION = "9.0.3"
5
+ VERSION = "9.0.4"
6
6
  end
@@ -266,7 +266,26 @@ describe Mongoid::Attributes::Readonly do
266
266
  expect(child.mother).to be_nil
267
267
  end
268
268
  end
269
+ end
270
+
271
+ context "when a subclass inherits readonly fields" do
272
+ let(:attributes) do
273
+ [:title, :terms]
274
+ end
275
+
276
+ before do
277
+ class OldPerson < Person
278
+ attr_readonly :age
279
+ end
280
+ end
269
281
 
282
+ it "ensures subclass inherits the readonly attributes from parent" do
283
+ expect(OldPerson.readonly_attributes.to_a).to include("title","terms")
284
+ end
285
+
286
+ it "ensures subclass does not modify parent's readonly attributes" do
287
+ expect(Person.readonly_attributes.to_a).not_to include("age")
288
+ end
270
289
  end
271
290
  end
272
291
  end
@@ -1939,6 +1939,35 @@ describe Mongoid::Criteria::Queryable::Selectable do
1939
1939
  end
1940
1940
  end
1941
1941
 
1942
+ describe "#not" do
1943
+ context "when negating a criterion" do
1944
+ let(:selection) do
1945
+ query.not(field: /value/)
1946
+ end
1947
+
1948
+ it "adds the $not selector" do
1949
+ expect(selection.selector).to eq({
1950
+ "field" => { "$not" => /value/ }
1951
+ })
1952
+ end
1953
+
1954
+ it "returns a cloned query" do
1955
+ expect(selection).to_not equal(query)
1956
+ end
1957
+
1958
+ context "when toggling negation state" do
1959
+ it "negates the negating value" do
1960
+ expect(query.negating).to be_nil
1961
+ negated_query = query.not
1962
+ expect(negated_query.negating).to be true
1963
+ double_negated_query = negated_query.not
1964
+ expect(double_negated_query.negating).to be false
1965
+ end
1966
+ end
1967
+ end
1968
+ end
1969
+
1970
+
1942
1971
  describe Symbol do
1943
1972
 
1944
1973
  describe "#all" do
@@ -189,6 +189,12 @@ describe Mongoid::Equality do
189
189
  it "compares based on the document id" do
190
190
  expect(first <=> second).to eq(-1)
191
191
  end
192
+
193
+ it "doesn't break if one isn't a document" do
194
+ expect do
195
+ first <=> "Foo"
196
+ end.to_not raise_error
197
+ end
192
198
  end
193
199
 
194
200
  describe "#eql?" do
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Mongoid::Loadable do
6
+ let(:lib_dir) { Pathname.new('../../lib').realpath(__dir__) }
7
+
8
+ shared_context 'with ignore_patterns' do
9
+ around do |example|
10
+ saved = Mongoid.ignore_patterns
11
+ Mongoid.ignore_patterns = ignore_patterns
12
+ example.run
13
+ ensure
14
+ Mongoid.ignore_patterns = saved
15
+ end
16
+ end
17
+
18
+ describe '#ignore_patterns' do
19
+ context 'when not explicitly set' do
20
+ it 'equals the default list of ignore patterns' do
21
+ expect(Mongoid.ignore_patterns).to eq Mongoid::Loadable::DEFAULT_IGNORE_PATTERNS
22
+ end
23
+ end
24
+
25
+ context 'when explicitly set' do
26
+ include_context 'with ignore_patterns'
27
+
28
+ let(:ignore_patterns) { %w[ pattern1 pattern2 ] }
29
+
30
+ it 'equals the list of specified patterns' do
31
+ expect(Mongoid.ignore_patterns).to eq ignore_patterns
32
+ end
33
+ end
34
+ end
35
+
36
+ describe '#files_under_path' do
37
+ let(:results) { Mongoid.files_under_path(lib_dir) }
38
+
39
+ include_context 'with ignore_patterns'
40
+
41
+ context 'when ignore_patterns is empty' do
42
+ let(:ignore_patterns) { [] }
43
+
44
+ it 'returns all ruby files' do
45
+ expect(results.length).to be > 10 # should be a bunch of them
46
+ expect(results).to include('rails/mongoid')
47
+ end
48
+ end
49
+
50
+ context 'when ignore_patterns is not empty' do
51
+ let(:ignore_patterns) { %w[ */rails/* ] }
52
+
53
+ it 'omits the ignored paths' do
54
+ expect(results.length).to be > 10 # should be a bunch of them
55
+ expect(results).not_to include('rails/mongoid')
56
+ end
57
+ end
58
+ end
59
+
60
+ describe '#files_under_paths' do
61
+ let(:paths) { [ lib_dir.join('mongoid'), lib_dir.join('rails') ] }
62
+ let(:results) { Mongoid.files_under_paths(paths) }
63
+
64
+ include_context 'with ignore_patterns'
65
+
66
+ context 'when ignore_patterns is empty' do
67
+ let(:ignore_patterns) { [] }
68
+
69
+ it 'returns all ruby files' do
70
+ expect(results.length).to be > 10 # should be a bunch
71
+ expect(results).to include('generators/mongoid/model/model_generator')
72
+ expect(results).to include('fields/encrypted')
73
+ end
74
+ end
75
+
76
+ context 'when ignore_patterns is not empty' do
77
+ let(:ignore_patterns) { %w[ */model/* */fields/* ] }
78
+
79
+ it 'returns all ruby files' do
80
+ expect(results.length).to be > 10 # should be a bunch
81
+ expect(results).not_to include('generators/mongoid/model/model_generator')
82
+ expect(results).not_to include('fields/encrypted')
83
+ end
84
+ end
85
+ end
86
+ end
@@ -584,6 +584,14 @@ describe Mongoid::PersistenceContext do
584
584
  expect(persistence_context.client).to eq(Mongoid::Clients.with_name(:alternative))
585
585
  end
586
586
 
587
+ context 'when the client option is a proc' do
588
+ let(:options) { { client: -> { :alternative } } }
589
+
590
+ it 'evaluates the proc' do
591
+ expect(persistence_context.client).to eq(Mongoid::Clients.with_name(:alternative))
592
+ end
593
+ end
594
+
587
595
  context 'when there is a client override' do
588
596
  persistence_context_override :client, :other
589
597
 
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongoid
3
3
  version: !ruby/object:Gem::Version
4
- version: 9.0.3
4
+ version: 9.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - The MongoDB Ruby Team
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2024-11-12 00:00:00.000000000 Z
10
+ date: 2025-01-08 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: activemodel
@@ -830,6 +829,7 @@ files:
830
829
  - spec/mongoid/inspectable_spec.rb
831
830
  - spec/mongoid/interceptable_spec.rb
832
831
  - spec/mongoid/interceptable_spec_models.rb
832
+ - spec/mongoid/loadable_spec.rb
833
833
  - spec/mongoid/loading_spec.rb
834
834
  - spec/mongoid/loggable_spec.rb
835
835
  - spec/mongoid/matcher/extract_attribute_data/numeric_keys.yml
@@ -1188,7 +1188,6 @@ metadata:
1188
1188
  documentation_uri: https://www.mongodb.com/docs/mongoid/
1189
1189
  homepage_uri: https://mongoid.org/
1190
1190
  source_code_uri: https://github.com/mongodb/mongoid
1191
- post_install_message:
1192
1191
  rdoc_options: []
1193
1192
  require_paths:
1194
1193
  - lib
@@ -1203,8 +1202,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
1203
1202
  - !ruby/object:Gem::Version
1204
1203
  version: 1.3.6
1205
1204
  requirements: []
1206
- rubygems_version: 3.4.19
1207
- signing_key:
1205
+ rubygems_version: 3.6.2
1208
1206
  specification_version: 4
1209
1207
  summary: Elegant Persistence in Ruby for MongoDB.
1210
1208
  test_files:
@@ -1552,6 +1550,7 @@ test_files:
1552
1550
  - spec/mongoid/inspectable_spec.rb
1553
1551
  - spec/mongoid/interceptable_spec.rb
1554
1552
  - spec/mongoid/interceptable_spec_models.rb
1553
+ - spec/mongoid/loadable_spec.rb
1555
1554
  - spec/mongoid/loading_spec.rb
1556
1555
  - spec/mongoid/loggable_spec.rb
1557
1556
  - spec/mongoid/matcher/extract_attribute_data/numeric_keys.yml