mongoid 9.0.3 → 9.0.4

Sign up to get free protection for your applications and to get access to all the features.
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