Skip to content

Commit a4a614b

Browse files
johnnyshieldsp
andauthored
MONGOID-5099 Improve error when loading an empty or invalid config file (#5032)
* Fixes MONGOID-5099 Improves error when loading an empty or invalid file and adds specs to #load_yaml method. Note that certain errors (file missing, ERB, YAML parsing) bubble up as per existing behavior. This change is only intended to avoid an internal cryptic error happening within Mongoid's code (NoMethodError (undefined method `[]' for false:FalseClass). Please also note that this does not change the conditions under which an exception occurs, it just changes the class of the exception. * repair exception docstring * fix test setup * tweak exception message * raise a specific exception for the case of empty config file * exception constructors are private * fix the test Co-authored-by: shields <[email protected]> Co-authored-by: Oleg Pudeyev <[email protected]>
1 parent ecd5db5 commit a4a614b

File tree

7 files changed

+194
-9
lines changed

7 files changed

+194
-9
lines changed

lib/config/locales/en.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,12 @@ en:
8080
different collections so a simple id lookup is not sufficient enough."
8181
resolution: "Don't attempt to perform this action and have patience,
8282
maybe this will be supported in the future."
83+
empty_config_file:
84+
message: "Empty configuration file: %{path}."
85+
summary: "Your mongoid.yml configuration file appears to be empty."
86+
resolution: "Ensure your configuration file contains the correct contents.
87+
Please consult the following page with respect to Mongoid's configuration:
88+
https://docs.mongodb.com/mongoid/current/reference/configuration/"
8389
invalid_collection:
8490
message: "Access to the collection for %{klass} is not allowed."
8591
summary: "%{klass}.collection was called, and %{klass} is an embedded
@@ -94,6 +100,13 @@ en:
94100
A collation option is only supported if the query is executed on a MongoDB server
95101
with version >= 3.4."
96102
resolution: "Remove the collation option from the query."
103+
invalid_config_file:
104+
message: "Invalid configuration file: %{path}."
105+
summary: "Your mongoid.yml configuration file does not contain the
106+
correct file structure."
107+
resolution: "Ensure your configuration file contains the correct contents.
108+
Please consult the following page with respect to Mongoid's configuration:
109+
https://docs.mongodb.com/mongoid/current/reference/configuration/"
97110
invalid_config_option:
98111
message: "Invalid configuration option: %{name}."
99112
summary: "A invalid configuration option was provided in your

lib/mongoid/config/environment.rb

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,15 @@ def env_name
5252
# @api private
5353
def load_yaml(path, environment = nil)
5454
env = environment ? environment.to_s : env_name
55-
YAML.load(ERB.new(File.new(path).read).result)[env]
55+
contents = File.new(path).read
56+
if contents.empty?
57+
raise Mongoid::Errors::EmptyConfigFile.new(path)
58+
end
59+
data = YAML.load(ERB.new(contents).result)
60+
unless data.is_a?(Hash)
61+
raise Mongoid::Errors::InvalidConfigFile.new(path)
62+
end
63+
data[env]
5664
end
5765
end
5866
end

lib/mongoid/errors.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
require "mongoid/errors/document_not_destroyed"
99
require "mongoid/errors/document_not_found"
1010
require "mongoid/errors/eager_load"
11+
require "mongoid/errors/empty_config_file"
1112
require "mongoid/errors/in_memory_collation_not_supported"
1213
require "mongoid/errors/invalid_collection"
14+
require "mongoid/errors/invalid_config_file"
1315
require "mongoid/errors/invalid_config_option"
1416
require "mongoid/errors/invalid_dependent_strategy"
1517
require "mongoid/errors/invalid_field"
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# frozen_string_literal: true
2+
# encoding: utf-8
3+
4+
module Mongoid
5+
module Errors
6+
7+
# This error is raised when an empty configuration file is attempted to be
8+
# loaded.
9+
class EmptyConfigFile < MongoidError
10+
11+
# Create the new error.
12+
#
13+
# @param [ String ] path The path of the config file used.
14+
#
15+
# @api private
16+
def initialize(path)
17+
super(
18+
compose_message(
19+
"empty_config_file",
20+
{ path: path }
21+
)
22+
)
23+
end
24+
end
25+
end
26+
end
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
# frozen_string_literal: true
2+
# encoding: utf-8
3+
4+
module Mongoid
5+
module Errors
6+
7+
# This error is raised when a bad configuration file is attempted to be
8+
# loaded.
9+
class InvalidConfigFile < MongoidError
10+
11+
# Create the new error.
12+
#
13+
# @param [ String ] path The path of the config file used.
14+
#
15+
# @api private
16+
def initialize(path)
17+
super(
18+
compose_message(
19+
"invalid_config_file",
20+
{ path: path }
21+
)
22+
)
23+
end
24+
end
25+
end
26+
end

spec/mongoid/config/environment_spec.rb

Lines changed: 86 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,19 @@
55

66
describe Mongoid::Config::Environment do
77

8-
after(:all) do
9-
Rails = RailsTemp
10-
Object.send(:remove_const, :RailsTemp)
8+
around do |example|
9+
if defined?(Rails)
10+
SavedRails = Rails
11+
example.run
12+
Object.send(:remove_const, :Rails) if defined?(Rails)
13+
Rails = SavedRails
14+
Object.send(:remove_const, :SavedRails)
15+
else
16+
example.run
17+
if defined?(Rails)
18+
Object.send(:remove_const, :Rails)
19+
end
20+
end
1121
end
1222

1323
describe "#env_name" do
@@ -24,11 +34,6 @@ def env; "production"; end
2434
end
2535
end
2636

27-
after do
28-
RailsTemp = Rails
29-
Object.send(:remove_const, :Rails)
30-
end
31-
3237
it "returns the rails environment" do
3338
expect(described_class.env_name).to eq("production")
3439
end
@@ -86,4 +91,77 @@ def environment; :staging; end
8691
end
8792
end
8893
end
94+
95+
describe "#load_yaml" do
96+
let(:path) { 'mongoid.yml' }
97+
let(:environment) {}
98+
before { allow(Rails).to receive('env').and_return('test') }
99+
100+
subject { described_class.load_yaml(path, environment) }
101+
102+
context 'when file not found' do
103+
let(:path) { 'not/a/valid/path'}
104+
105+
it { expect { subject }.to raise_error(Errno::ENOENT) }
106+
end
107+
108+
context 'when file found' do
109+
before do
110+
allow(File).to receive(:new).with('mongoid.yml').and_return(StringIO.new(file_contents))
111+
end
112+
113+
let(:file_contents) do
114+
<<~FILE
115+
test:
116+
clients: ['test']
117+
development:
118+
clients: ['dev']
119+
FILE
120+
end
121+
122+
context 'when file cannot be parsed as YAML' do
123+
let(:file_contents) { "*\nbad:%123abc" }
124+
125+
it { expect { subject }.to raise_error(Psych::SyntaxError) }
126+
end
127+
128+
context 'when file contains ERB errors' do
129+
let(:file_contents) { '<%= foo %>' }
130+
131+
it { expect { subject }.to raise_error(NameError) }
132+
end
133+
134+
context 'when file is empty' do
135+
let(:file_contents) { '' }
136+
137+
it { expect { subject }.to raise_error(Mongoid::Errors::EmptyConfigFile) }
138+
end
139+
140+
context 'when file does not contain a YAML Hash object' do
141+
let(:file_contents) { '["this", "is", "an", "array"]' }
142+
143+
it { expect { subject }.to raise_error(Mongoid::Errors::InvalidConfigFile) }
144+
end
145+
146+
context 'when environment not specified' do
147+
it 'uses the rails environment' do
148+
is_expected.to eq("clients"=>["test"])
149+
end
150+
end
151+
152+
context 'when environment is specified' do
153+
let(:environment) { 'development' }
154+
155+
it 'uses the specified environment' do
156+
is_expected.to eq("clients"=>["dev"])
157+
end
158+
end
159+
160+
context 'when environment is missing' do
161+
let(:environment) { 'staging' }
162+
163+
it { is_expected.to be_nil }
164+
end
165+
end
166+
end
89167
end
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# frozen_string_literal: true
2+
# encoding: utf-8
3+
4+
require "spec_helper"
5+
6+
describe Mongoid::Errors::InvalidConfigFile do
7+
8+
describe "#message" do
9+
10+
let(:error) do
11+
described_class.new('/my/path')
12+
end
13+
14+
it "contains the problem in the message" do
15+
expect(error.message).to include(
16+
"Invalid configuration file: /my/path."
17+
)
18+
end
19+
20+
it "contains the summary in the message" do
21+
expect(error.message).to include(
22+
"Your mongoid.yml configuration file does not contain the"
23+
)
24+
end
25+
26+
it "contains the resolution in the message" do
27+
expect(error.message).to include(
28+
"Ensure your configuration file contains the correct contents."
29+
)
30+
end
31+
end
32+
end

0 commit comments

Comments
 (0)